From 91c0684b1b65ffe5b7bdceb479f0eea2241ca6a6 Mon Sep 17 00:00:00 2001 From: Erich Eckner Date: Wed, 19 Dec 2018 13:43:58 +0100 Subject: Makefile new --- .gitignore | 2 + Makefile | 57 ++++++++++++++ dd-resume | 137 -------------------------------- dd-resume.in | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 308 insertions(+), 137 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile delete mode 100755 dd-resume create mode 100644 dd-resume.in diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f27ff1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +dd-resume +dd-resume.1 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..87cae05 --- /dev/null +++ b/Makefile @@ -0,0 +1,57 @@ +# +# dd-resume - resumably copy with dd via network +# +# Copyright (c) 2018 Erich Eckner +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +# USA. +# + +DESTDIR = +BINDIR = /usr/bin +MANDIR = /usr/share/man + +VERSION = 0.0 + +all: dd-resume dd-resume.1 + +%: %.in + sed " \ + s/#VERSION#/$(VERSION)/; \ + s@#BINDIR#@$(BINDIR)@; \ + s@#HELPTEXT#\(\s\+\)#@ --help \1display this help and exit\n --version\1display version and exit@; \ + " $< > $@ && \ + ( [[ "$@" = *.* ]] || chmod +x "$@" ) + +%.1: % + help2man -o "$@" -N --no-discard-stderr "./$<" + +.PHONY: install dist clean + +install: all + install -D -m0755 -t $(DESTDIR)$(BINDIR) dd-resume + install -D -m0644 -t $(DESTDIR)$(MANDIR)/man1 dd-resume.1 + +clean: + git clean -x -d -f + +dist: clean + git status --porcelain 2> /dev/null | grep -q "\S" && (git add .; git commit -m"neue Version: $(VERSION)") || true + ! git tag -d v$(VERSION) 2> /dev/null + git tag v$(VERSION) + git push + git push --tags + +# End of file diff --git a/dd-resume b/dd-resume deleted file mode 100755 index 848c459..0000000 --- a/dd-resume +++ /dev/null @@ -1,137 +0,0 @@ -#!/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}" diff --git a/dd-resume.in b/dd-resume.in new file mode 100644 index 0000000..8f1d525 --- /dev/null +++ b/dd-resume.in @@ -0,0 +1,249 @@ +#!/bin/bash + +# dd-resume version #VERSION# + +usage() { + >&2 printf 'Usage: dd-resume [options]\n' + >&2 printf ' copy resumably with dd via network\n\n' + >&2 printf 'Options (all options are mandatory unless explicitely stated):\n' + >&2 printf ' --input $input-device name of input device\n' + >&2 printf ' --host $input-host name of input device'"'"'s host (target only)\n' + >&2 printf ' --log-file $log-file name of log file (target only)\n' + >&2 printf ' --output $output-device name of output device (target only)\n' + >&2 printf ' --port $remote-port port of target host (source only)\n' + >&2 printf ' --start $start-position start position for transfer (source only)\n' + >&2 printf ' --required print list of required programs and exit\n' + >&2 printf '#HELPTEXT# #\n' + [ -n "$1" ] && exit $1 + exit 1 +} + +dd_options='bs=2 count=2' +# bs=1M' +needed_programs='dd nc ss screen pgrep kill ssh' + +eval set -- "$( + getopt -o '' \ + --long input: \ + --long host: \ + --long log-file: \ + --long output: \ + --long port: \ + --long start: \ + --long required \ + --long help \ + --long version \ + -n "$(basename "$0")" \ + -- "$@" \ + || echo "usage" +)" + +while true; do + case $1 in + --input) + if [ -n "${input_device}" ]; then + >&2 printf 'cannot handle multiple input-devices\n\n' + usage + fi + shift + input_device="$1" + ;; + --host) + if [ -n "${host}" ]; then + >&2 printf 'cannot handle multiple hosts\n\n' + usage + fi + shift + host="$1" + ;; + --log-file) + if [ -n "${log_file}" ]; then + >&2 printf 'cannot handle multiple log files\n\n' + usage + fi + shift + log_file="$1" + ;; + --output) + if [ -n "${output_device}" ]; then + >&2 printf 'cannot handle multiple output devices\n\n' + usage + fi + shift + output_device="$1" + ;; + --port) + if [ -n "${remote_port}" ]; then + >&2 printf 'cannot handle multiple ports\n\n' + usage + fi + shift + remote_port="$1" + ;; + --start) + if [ -n "${start}" ]; then + >&2 printf 'cannot handle multiple starts\n\n' + usage + fi + shift + start="$1" + ;; + --required) + printf '%s\n' ${needed_programs} + exit 0 + ;; + --help) + usage 0 + ;; + --version) + >&2 printf '%s\n' '#VERSION#' + exit 0 + ;; + --) + shift + break + ;; + *) + >&2 printf 'oops, option "%s" is unkknown\n' "$1" + exit 1 + esac + shift +done + +if [ $# -ne 0 ]; then + >&2 printf 'too many arguments\n\n' + usage +fi + +for needed in ${needed_programs}; do + if ! which "${needed}" >/dev/null; then + >&2 printf 'please install required program %s\n' "${needed}" + exit 1 + fi +done + +if [ -z "${input_device}" ]; then + >&2 printf 'no input device specified\n\n' + usage +fi + +if [ -n "${host}" ]; then + # was run on the receiver side + + if [ -z "${log_file}" ] || \ + [ -z "${output_device}" ] || \ + [ -n "${remote_port}" ] || \ + [ -n "${start}" ]; then + >&2 printf 'conflicting options\n\n' + usage + fi + + if [ ! -w "${log_file%/*}" ] && [ ! -w "${log_file}" ]; then + >&2 printf 'cannot write/create log-file %s\n\n' "${log_file}" + usage + fi + + if [ ! -w "${output_device}" ]; then + >&2 printf 'cannot write to output device %s\n\n' "${output_device}" + 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 + + previously_running=$( + pgrep -x 'dd|nc' + ) + + printf 'start: %s\n' "${start}" > \ + "${log_file}" + + screen -d -m bash -c ' + set +o pipefail + nc -l -c | \ + dd of="'"${output_device}"'" '"${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 | \ + grep -vxF "$(printf '%s\n' "${previously_running}")" + ) + dd_pid=$( + pgrep -x dd | \ + grep -vxF "$(printf '%s\n' "${previously_running}")" + ) + 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 "${host}" "$0 --input ${input_device} --start ${start} --port ${port}" + printf '\nssh-exit: %s\n' $? >>"${log_file}" + +else + # was run on the sender side + + if [ -n "${log_file}" ] || \ + [ -n "${output_device}" ] || \ + [ -z "${remote_port}" ] || \ + [ -z "${start}" ]; then + >&2 printf 'conflicting options\n\n' + usage + fi + + remote_ip="${SSH_CLIENT%% *}" + if [ -z "${remote_ip}" ]; then + >&2 printf 'cannot obtain remote ip\n' + exit 1 + fi + if [ ! -r "${input_device}" ]; then + >&2 printf 'cannot read from %s\n\n' "${input_device}" + usage + fi + set +o pipefail + dd if="${input_device}" ${dd_options} skip="${start}" 2>/dev/null | \ + nc -cn "${remote_ip}" "${remote_port}" + exit $? + +fi -- cgit v1.2.3-54-g00ecf