From 8500a256d3d7c198fd5859f4464dd4d8f25eae93 Mon Sep 17 00:00:00 2001 From: Erich Eckner Date: Wed, 19 Dec 2018 11:23:02 +0100 Subject: initial commit --- dd-resume | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100755 dd-resume diff --git a/dd-resume b/dd-resume new file mode 100755 index 0000000..848c459 --- /dev/null +++ b/dd-resume @@ -0,0 +1,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}" -- cgit v1.2.3-54-g00ecf