summaryrefslogtreecommitdiff
path: root/dd-resume
blob: 848c459c9e79efbcc55a9814365a1b791095dbf6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/bin/bash

# copy resumably with dd via network

script_name=$(basename "$0")

usage() {
  >&2 printf 'usage:\n'
  >&2 printf '  %s in-host in-dev out-dev log-file\n' "${script_name}"
  >&2 printf '  %s -s in-dev start-position remote-port\n' "${script_name}"
  exit 1
}

dd_options='bs=1M'

for needed in dd nc ss screen pgrep kill ssh; do
  if ! which "${needed}" >/dev/null; then
    >&2 printf 'please install required program %s\n' "${needed}"
    exit 1
  fi
done

if [ "x$1" = 'x-s' ]; then
  shift
  if [ $# -ne 3 ]; then
    >&2 printf 'expected 3 arguments, got %s:\n' "$#"
    >&2 printf '"%s" ' "$@"
    >&2 printf '\n'
    usage
  fi
  dev="$1"
  start="$2"
  remote_ip="${SSH_CLIENT%% *}"
  if [ -z "${remote_ip}" ]; then
    >&2 printf 'cannot obtain remote ip\n'
    exit 1
  fi
  remote_port="$3"
  if [ ! -r "${dev}" ]; then
    >&2 printf 'cannot read from %s\n\n' "${dev}"
    usage
  fi
  set +o pipefail
  dd if="${dev}" ${dd_options} skip="${start}" 2>/dev/null | \
    nc -cn "${remote_ip}" "${remote_port}"
  exit $?
fi

if [ $# -ne 4 ]; then
  usage
fi
remote_host="$1"
remote_dev="$2"
local_dev="$3"
log_file="$4"

if [ ! -w "${log_file%/*}" ] && [ ! -w "${log_file}" ]; then
  >&2 printf 'cannot write/create log-file %s\n\n' "${log_file}"
  usage
fi

if [ ! -w "${local_dev}" ]; then
  >&2 printf 'cannot write to out-dev %s\n\n' "${local_dev}"
  usage
fi

start=0
if [ -f "${log_file}" ]; then
  start=$(
    sed -n '
      1 {
        s/^start: //
        p
        q
      }
    ' "${log_file}"
  )
  copied=$(
    sed '
      s/^\([0-9]\+\)+0 records out$/\1/
      t
      d
    ' "${log_file}" | \
    sort -n | \
    tail -n1
  )
  if [ -n "${copied}" ]; then
    start=$((start+copied))
  fi
fi

if pgrep -ax dd || pgrep -ax nc; then
  >&2 printf 'another dd or nc is already running - this won'"'"'t work\n'
  exit 1
fi

printf 'start: %s\n' "${start}" > \
  "${log_file}"

screen -d -m bash -c '
  set +o pipefail
  nc -l -c | \
    dd of="'"${local_dev}"'" '"${dd_options}"' seek='"${start}"' 2>>"'"${log_file}"'" >/dev/null
  printf '"'"'\nwrite-pipe-exit: %s\n'"'"' $? >>"'"${log_file}"'"
'

for i in {1..20}; do
  if [ -n "${port}" ]; then
    break
  elif [ ${i} -gt 1 ]; then
    sleep 0.1
  fi
  nc_pid=$(
    pgrep -x nc
  )
  dd_pid=$(
    pgrep -x dd
  )
  port=$(
    ss -nlp | \
      grep -F ",pid=${nc_pid}," | \
      awk '{print $5}' | \
      sed 's/^.*://'
  )
done
if [ -z "${port}" ]; then
  >&2 printf 'could not determine listening port\n'
  exit 1
fi
screen -d -m bash -c '
  while kill -SIGUSR1 '"${dd_pid}"'; do
    sleep 1
  done
'

ssh "${remote_host}" "${script_name} -s ${remote_dev} ${start} ${port}"
printf '\nssh-exit: %s\n' $? >>"${log_file}"