better dry-run handling, separate "--copy" option.

This commit is contained in:
2021-04-28 14:55:50 +02:00
parent 9b3fb864cd
commit edbdab3a20

View File

@@ -20,7 +20,7 @@
# #
# DESCRIPTION # DESCRIPTION
# Duplicate SRC disk partitions to same structured DST disk ones. # Duplicate SRC disk partitions to same structured DST disk ones.
# if SRC is omitted, tue running system disk (where root partition # if SRC is omitted, the running system disk (where root partition
# resides) will be used. # resides) will be used.
# Both SRC and DST *must* have same partition base LABELs - as 'LABEL' # Both SRC and DST *must* have same partition base LABELs - as 'LABEL'
# field for lsblk(1) and blkid(1), with an ending character (unique per # field for lsblk(1) and blkid(1), with an ending character (unique per
@@ -31,8 +31,17 @@
# must be 'root2', 'export2, and 'swap2'. # must be 'root2', 'export2, and 'swap2'.
# #
# OPTIONS # OPTIONS
# -d, -n, --dry-run, --no # -a, --autofs=DIR
# Dry-run: nothing will be written to disk. # Use DIR as autofs "LABEL-based" automount. See AUTOFS below. Default
# is /mnt.
#
# -c, --copy=ACTION
# ACTION can be 'yes' (all eligible partitions will be copied), 'no'
# (no partition will be copied), or 'ask' (will ask for all eligible
# partitions). Default is 'no'.
#
# -d, --dry-run
# Dry-run: nothing will be really be done.
# #
# -g, --grub # -g, --grub
# Install grub on destination disk. # Install grub on destination disk.
@@ -52,10 +61,6 @@
# Mandatory if SRC is provided, forbidden otherwise. # Mandatory if SRC is provided, forbidden otherwise.
# PARTNUM is root partition number on SRC disk. # PARTNUM is root partition number on SRC disk.
# #
# -y, --yes
# Do not ask for actions confirmation. Default is to display next
# action and ask user to [y] do it, [q] quit, [s] skip.
#
# EXAMPLES # EXAMPLES
# Copy sda to sdb, root partition is partition (sda1/sdb1) # Copy sda to sdb, root partition is partition (sda1/sdb1)
# $ sudo dup-live-disk.sh --root 1 sda sdb # $ sudo dup-live-disk.sh --root 1 sda sdb
@@ -92,7 +97,10 @@ Usage: $CMD [OPTIONS] [SRC] DST
Duplicate SRC (or live system) disk partitions to DST disk partitions. Duplicate SRC (or live system) disk partitions to DST disk partitions.
Options: Options:
-d, -n, --dry-run, --no dry-run: nothing will be written to disk -a, --autofs=DIR autofs "LABEL-based" directory. Default is '/mnt'.
-c, --copy=ACTION do partitions copies ('yes'), do not copy then ('no')
or ask for each of them ('ask'). Default is 'no'.
-d, --dry-run dry-run: nothing will be written to disk
-g, --grub install grub on destination disk -g, --grub install grub on destination disk
-h, --help this help -h, --help this help
-m, --man display a "man-like" page and exit -m, --man display a "man-like" page and exit
@@ -100,8 +108,6 @@ Options:
after copies after copies
-r, --root=PARTNUM root partition number on SRC device -r, --root=PARTNUM root partition number on SRC device
mandatory if and only if SRC is provided mandatory if and only if SRC is provided
-y, --yes DANGER ! perform all actions without user
confirmation
SRC and DST have strong constraints on partitions schemes and naming. SRC and DST have strong constraints on partitions schemes and naming.
Type '$CMD --man" for more details" Type '$CMD --man" for more details"
@@ -112,19 +118,19 @@ _EOF
# mariadb start/stop # mariadb start/stop
function mariadb_maybe_stop { function mariadb_maybe_stop {
if [[ $MARIADB == yes ]] && systemctl is-active --quiet mysql; then if [[ $MARIADB == yes ]] && systemctl is-active --quiet mysql; then
log -n "stopping mariadb/mysql... " #log -n "stopping mariadb/mysql... "
systemctl stop mariadb echorun systemctl stop mariadb
# bug if script stops here # bug if script stops here
MARIADBSTOPPED=yes MARIADBSTOPPED=yes
log "done." #log "done."
fi fi
} }
function mariadb_maybe_start { function mariadb_maybe_start {
if [[ $MARIADB == yes && $MARIADBSTOPPED == yes ]]; then if [[ $MARIADB == yes && $MARIADBSTOPPED == yes ]]; then
log -n "restarting mariadb/mysql... " #log -n "restarting mariadb/mysql... "
systemctl start mariadb echorun systemctl start mariadb
MARIADBSTOPPED=no MARIADBSTOPPED=no
log "done." #log "done."
fi fi
} }
@@ -155,9 +161,12 @@ function log {
# prints out and run a command. # prints out and run a command.
function echorun { function echorun {
if [[ "$DRYRUN" == 'yes' ]]; then
log "%s " "dry-run: " "$@"
else
log "%s " "$@" log "%s " "$@"
"$@" "$@"
return $? fi
} }
function error_handler { function error_handler {
@@ -168,15 +177,19 @@ function error_handler {
trap 'error_handler $LINENO $?' ERR SIGHUP SIGINT SIGTERM trap 'error_handler $LINENO $?' ERR SIGHUP SIGINT SIGTERM
function exit_handler { function exit_handler {
local mnt
log "exit handler (at line $1)" log "exit handler (at line $1)"
mariadb_maybe_start mariadb_maybe_start
if [[ -v DSTMNT ]]; then if [[ -n "$DSTMNT" ]] && mountpoint -q "$DSTMNT"; then
umount "$DSTMNT/dev" for mnt in "$DSTMNT"/{dev,proc,sys}; do
umount "$DSTMNT/proc" if mountpoint -q "$mnt"; then
umount "$DSTMNT/sys" echorun umount "$mnt"
fi
done
fi fi
} }
trap 'exit_handler $LINENO' EXIT trap 'exit_handler $LINENO' EXIT
function check_block_device { function check_block_device {
@@ -237,14 +250,16 @@ SRC=""
DST="" DST=""
SRCROOT="" SRCROOT=""
ROOTPARTNUM="" ROOTPARTNUM=""
DOIT=manual
MARIADB=no DRYRUN=no # dry-run
MARIADBSTOPPED=no GRUB=no # install grub
GRUBINSTALL=no COPY=no # do FS copies
MARIADB=no # stop/start mysql/mariadb
MARIADBSTOPPED=no # mysql stopped ?
# short and long options # short and long options
SOPTS="dnghmr:y" SOPTS="c:dghmr:"
LOPTS="dry-run,no,grub,help,man,mariadb,root:,yes" LOPTS="copy:,dry-run,grub,help,man,mariadb,root:"
if ! TMP=$(getopt -o "$SOPTS" -l "$LOPTS" -n "$CMD" -- "$@"); then if ! TMP=$(getopt -o "$SOPTS" -l "$LOPTS" -n "$CMD" -- "$@"); then
log "Use '$CMD --help' or '$CMD --man' for help." log "Use '$CMD --help' or '$CMD --man' for help."
@@ -256,14 +271,22 @@ unset TMP
while true; do while true; do
case "$1" in case "$1" in
'-d'|'-n'|'--dry-run'|'--no') '-c'|'--copy')
DOIT=no case "$2" in
"no") COPY=no;;
"yes") COPY=yes;;
"ask") COPY=ask;;
*) log "invalid '$2' --copy flag"
usage
exit 1
esac
shift shift
continue ;;
'-d'|'--dry-run')
DRYRUN=yes
;; ;;
'-g'|'--grub') '-g'|'--grub')
GRUBINSTALL=yes GRUB=yes
shift
;; ;;
'-h'|'--help') '-h'|'--help')
usage usage
@@ -275,7 +298,6 @@ while true; do
;; ;;
'--mariadb') '--mariadb')
MARIADB=yes MARIADB=yes
shift
;; ;;
'-r'|'--root') '-r'|'--root')
ROOTPARTNUM="$2" ROOTPARTNUM="$2"
@@ -283,13 +305,7 @@ while true; do
log "$CMD: $ROOTPARTNUM must be a partition number." log "$CMD: $ROOTPARTNUM must be a partition number."
exit 1 exit 1
fi fi
shift 2
continue
;;
'-y'|'--yes')
DOIT=yes
shift shift
continue
;; ;;
'--') '--')
shift shift
@@ -301,9 +317,9 @@ while true; do
exit 1 exit 1
;; ;;
esac esac
shift
done done
case "$#" in case "$#" in
1) 1)
if [[ -n "$ROOTPARTNUM" ]]; then if [[ -n "$ROOTPARTNUM" ]]; then
@@ -355,7 +371,7 @@ declare -a LABELS=(${SRCLABELS[@]%?})
#log "LABELS=${#LABELS[@]} - ${LABELS[*]}" #log "LABELS=${#LABELS[@]} - ${LABELS[*]}"
declare -a SRCDEVS SRCFS SRCDOIT declare -a SRCDEVS SRCFS SRC_VALID_FS
# ... and corresponding partition device and fstype # ... and corresponding partition device and fstype
for ((i=0; i<${#LABELS[@]}; ++i)); do for ((i=0; i<${#LABELS[@]}; ++i)); do
TMP="${LABELS[$i]}$SRCCHAR" TMP="${LABELS[$i]}$SRCCHAR"
@@ -365,8 +381,8 @@ for ((i=0; i<${#LABELS[@]}; ++i)); do
log "found LABEL=$TMP DEV=$TMPDEV FSTYPE=$TMPFS" log "found LABEL=$TMP DEV=$TMPDEV FSTYPE=$TMPFS"
SRCDEVS[$i]="$TMPDEV" SRCDEVS[$i]="$TMPDEV"
SRCFS[$i]="$TMPFS" SRCFS[$i]="$TMPFS"
SRCDOIT[$i]=n SRC_VALID_FS[$i]=n
in_array "$TMPFS" VALIDFS && SRCDOIT[$i]=y in_array "$TMPFS" VALIDFS && SRC_VALID_FS[$i]=y
unset TMP TMPDEV TMPFS unset TMP TMPDEV TMPFS
done done
@@ -386,9 +402,8 @@ fi
# log "ROOTLABEL=$ROOTLABEL" # log "ROOTLABEL=$ROOTLABEL"
# log "SRCROOTLABEL=%s DSTROOTLABEL=%s" "$SRCROOTLABEL" "$DSTROOTLABEL" # log "SRCROOTLABEL=%s DSTROOTLABEL=%s" "$SRCROOTLABEL" "$DSTROOTLABEL"
# log "SRCCHAR=%s DSTCHAR=%s" "$SRCCHAR" "$DSTCHAR" # log "SRCCHAR=%s DSTCHAR=%s" "$SRCCHAR" "$DSTCHAR"
# log "DOIT=%s\n" "$DOIT"
declare -a DSTLABELS DSTDEVS DSTFS DSTDOIT declare -a DSTLABELS DSTDEVS DSTFS DST_VALID_FS
# Do the same for correponding DST partitions labels, device, and fstype # Do the same for correponding DST partitions labels, device, and fstype
for ((i=0; i<${#LABELS[@]}; ++i)); do for ((i=0; i<${#LABELS[@]}; ++i)); do
TMP="${LABELS[$i]}$DSTCHAR" TMP="${LABELS[$i]}$DSTCHAR"
@@ -408,8 +423,8 @@ for ((i=0; i<${#LABELS[@]}; ++i)); do
DSTLABELS[$i]="$TMP" DSTLABELS[$i]="$TMP"
DSTDEVS[$i]="$TMPDEV" DSTDEVS[$i]="$TMPDEV"
DSTFS[$i]="$TMPFS" DSTFS[$i]="$TMPFS"
DSTDOIT[$i]=n DST_VALID_FS[$i]=n
in_array "$TMPFS" VALIDFS && DSTDOIT[$i]=y in_array "$TMPFS" VALIDFS && DST_VALID_FS[$i]=y
unset TMP TMPDEV TMPFS unset TMP TMPDEV TMPFS
done done
@@ -417,56 +432,47 @@ for ((i=0; i<${#LABELS[@]}; ++i)); do
log -n "%s %s " "${SRCDEVS[$i]}" "${DSTDEVS[$i]}" log -n "%s %s " "${SRCDEVS[$i]}" "${DSTDEVS[$i]}"
log -n "%s %s " "${SRCLABELS[$i]}" "${DSTLABELS[$i]}" log -n "%s %s " "${SRCLABELS[$i]}" "${DSTLABELS[$i]}"
log -n "%s %s " "${SRCFS[$i]}" "${DSTFS[$i]}" log -n "%s %s " "${SRCFS[$i]}" "${DSTFS[$i]}"
log -n "%s %s " "${SRCDOIT[$i]}" "${DSTDOIT[$i]}" log -n "%s %s " "${SRC_VALID_FS[$i]}" "${DST_VALID_FS[$i]}"
[[ "$DSTROOTLABEL" == "${DSTLABELS[$i]}" ]] && log "*" [[ "$DSTROOTLABEL" == "${DSTLABELS[$i]}" ]] && log "*"
echo echo
done | column -N DEV1,DEV2,LABEL1,LABEL2,FS1,FS2,SDOIT,DDOIT,ROOT -t -o " | " done | column -N DEV1,DEV2,LABEL1,LABEL2,FS1,FS2,SVALID\?,DVALID\?,ROOT -t -o " | "
RSYNCOPTS="-axH --delete --delete-excluded" RSYNCOPTS="-axH --delete --delete-excluded"
FILTER=--filter="dir-merge .rsync-disk-copy" FILTER=--filter="dir-merge .rsync-disk-copy"
# copy loop # copy loop
for ((i=0; i<${#LABELS[@]}; ++i)); do for ((i=0; i<${#LABELS[@]}; ++i)); do
if [[ "${SRCDOIT[$i]}" != y ]] || [[ "${DSTDOIT[$i]}" != y ]]; then if [[ "${SRC_VALID_FS[$i]}" != y ]] || [[ "${DST_VALID_FS[$i]}" != y ]]; then
log "skipping label %s" "${LABELS[$i]}" log "skipping label %s" "${LABELS[$i]}"
continue continue
fi fi
SRCPART=/mnt/${SRCLABELS[$i]}/ SRCPART=/mnt/${SRCLABELS[$i]}/
DSTPART=/mnt/${DSTLABELS[$i]} DSTPART=/mnt/${DSTLABELS[$i]}
log -n "%s -> %s : " "$SRCPART" "$DSTPART" #log -n "%s -> %s : " "$SRCPART" "$DSTPART"
#log "\t%s %s %s %s %s" rsync "${RSYNCOPTS}" "$FILTER" "$SRCPART" "$DSTPART" #log "\t%s %s %s %s %s" rsync "${RSYNCOPTS}" "$FILTER" "$SRCPART" "$DSTPART"
skip=y copy="$COPY"
case "$DOIT" in if [[ "$COPY" == 'ask' ]]; then
yes) yesno "Copy $SRCPART to $DSTPART ? [y/n/q]" && copy=yes || copy=no
skip=n fi
;; if [[ "$copy" == yes ]]; then
no)
log "skipping (dry run)."
;;
manual)
yesno "proceed ? [y/n/q]" && skip=n
;;
esac
if [[ "$skip" == n ]]; then
# shellcheck disable=SC2086
mariadb_maybe_stop mariadb_maybe_stop
# shellcheck disable=SC2086
echorun rsync "$FILTER" ${RSYNCOPTS} "$SRCPART" "$DSTPART" echorun rsync "$FILTER" ${RSYNCOPTS} "$SRCPART" "$DSTPART"
fi fi
log "" #log ""
done done
# grub install # grub install
# mount virtual devices if [[ "$GRUB" == yes ]]; then
if [[ $GRUBINSTALL == yes ]]; then
log "installing grub on $DST..." log "installing grub on $DST..."
DSTMNT="/mnt/$DSTROOTLABEL" DSTMNT="/mnt/$DSTROOTLABEL"
mount -o bind /sys "$DSTMNT/sys" # mount virtual devices
mount -o bind /proc "$DSTMNT/proc" echorun mount -o bind /sys "$DSTMNT/sys"
mount -o bind /dev "$DSTMNT/dev" echorun mount -o bind /proc "$DSTMNT/proc"
echorun mount -o bind /dev "$DSTMNT/dev"
chroot "$DSTMNT" update-grub
chroot "$DSTMNT" grub-install "$DST"
echorun chroot "$DSTMNT" update-grub
echorun chroot "$DSTMNT" grub-install "$DST"
fi fi
exit 0 exit 0