Add grub management / remove old dead code.
This commit is contained in:
@@ -20,26 +20,34 @@
|
|||||||
#
|
#
|
||||||
# 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 resides) will
|
# if SRC is omitted, tue running system disk (where root partition
|
||||||
# be used.
|
# resides) will be used.
|
||||||
# Both SRC and DST *must* have same partition base LABELs - as 'LABEL' field for
|
# Both SRC and DST *must* have same partition base LABELs - as 'LABEL'
|
||||||
# lsblk(1) and blkid(1), with an ending character (unique per disk) to
|
# field for lsblk(1) and blkid(1), with an ending character (unique per
|
||||||
# differentiate them.
|
# disk) to differentiate them.
|
||||||
# For example, if partitions base labels are 'root', 'export', and 'swap',
|
# For example, if partitions base labels are 'root', 'export', and 'swap',
|
||||||
# SRC disk the ending character '1' and DST disk the character '2', SRC
|
# SRC disk the ending character '1' and DST disk the character '2', SRC
|
||||||
# partitions must be 'root1', 'export1, and 'swap1', and DST partitions must be
|
# partitions must be 'root1', 'export1, and 'swap1', and DST partitions
|
||||||
# 'root2', 'export2, and 'swap2'.
|
# must be 'root2', 'export2, and 'swap2'.
|
||||||
#
|
#
|
||||||
# OPTIONS
|
# OPTIONS
|
||||||
# -d, -n, --dry-run, --no
|
# -d, -n, --dry-run, --no
|
||||||
# Dry-run: nothing will be written to disk.
|
# Dry-run: nothing will be written to disk.
|
||||||
#
|
#
|
||||||
|
# -g, --grub
|
||||||
|
# Install grub on destination disk.
|
||||||
|
# Warning: Only works if root partition contains all necessary for
|
||||||
|
# grub: /boot, /usr, etc...
|
||||||
|
#
|
||||||
# -h, --help
|
# -h, --help
|
||||||
# Display short help and exit.
|
# Display short help and exit.
|
||||||
#
|
#
|
||||||
# -m, --man
|
# -m, --man
|
||||||
# Display a "man-like" description and exit.
|
# Display a "man-like" description and exit.
|
||||||
#
|
#
|
||||||
|
# --mariadb
|
||||||
|
# Stop mysql/mariadb before effective copies, restart after.
|
||||||
|
#
|
||||||
# -r, --root=PARTNUM
|
# -r, --root=PARTNUM
|
||||||
# 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.
|
||||||
@@ -61,16 +69,18 @@
|
|||||||
# don't use it if you don't *fully* understand it.
|
# don't use it if you don't *fully* understand it.
|
||||||
#
|
#
|
||||||
# TODO
|
# TODO
|
||||||
# Write about autofs.
|
# Write about autofs configuration.
|
||||||
|
# Log levels
|
||||||
|
# Separate dry-run and copies/mysql/grub
|
||||||
#%MAN_END%
|
#%MAN_END%
|
||||||
|
|
||||||
# command line
|
# command line
|
||||||
SCRIPT="${0}"
|
SCRIPT="$0"
|
||||||
CMD="${0##*/}"
|
CMD="${0##*/}"
|
||||||
|
|
||||||
# valid filesystems
|
# valid filesystems
|
||||||
# shellcheck disable=2034
|
# shellcheck disable=2034
|
||||||
VALIDFS=(ext3 ext4 btrfs vfat reiserfs)
|
VALIDFS=(ext3 ext4 btrfs vfat reiserfs xfs zfs)
|
||||||
|
|
||||||
function man {
|
function man {
|
||||||
sed -n '/^#%MAN_BEGIN%/,/^#%MAN_END%$/{//!p}' "$SCRIPT" | sed -E 's/^# ?//'
|
sed -n '/^#%MAN_BEGIN%/,/^#%MAN_END%$/{//!p}' "$SCRIPT" | sed -E 's/^# ?//'
|
||||||
@@ -83,8 +93,11 @@ 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
|
-d, -n, --dry-run, --no dry-run: nothing will be written to 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
|
||||||
|
--mariadb stop and restart mysql/mariadb server before and
|
||||||
|
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
|
-y, --yes DANGER ! perform all actions without user
|
||||||
@@ -96,6 +109,25 @@ _EOF
|
|||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# mariadb start/stop
|
||||||
|
function mariadb_maybe_stop {
|
||||||
|
if [[ $MARIADB == yes ]] && systemctl is-active --quiet mysql; then
|
||||||
|
log -n "stopping mariadb/mysql... "
|
||||||
|
systemctl stop mariadb
|
||||||
|
# bug if script stops here
|
||||||
|
MARIADBSTOPPED=yes
|
||||||
|
log "done."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
function mariadb_maybe_start {
|
||||||
|
if [[ $MARIADB == yes && $MARIADBSTOPPED == yes ]]; then
|
||||||
|
log -n "restarting mariadb/mysql... "
|
||||||
|
systemctl start mariadb
|
||||||
|
MARIADBSTOPPED=no
|
||||||
|
log "done."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# log function
|
# log function
|
||||||
# parameters:
|
# parameters:
|
||||||
# -l, -s: long, or short prefix (default: none). Last one is used.
|
# -l, -s: long, or short prefix (default: none). Last one is used.
|
||||||
@@ -115,6 +147,7 @@ function log {
|
|||||||
done
|
done
|
||||||
[[ $prefix != "" ]] && printf "%s " "$prefix"
|
[[ $prefix != "" ]] && printf "%s " "$prefix"
|
||||||
printf "%s" "$timestr"
|
printf "%s" "$timestr"
|
||||||
|
# shellcheck disable=SC2059
|
||||||
printf "$@"
|
printf "$@"
|
||||||
[[ $newline = y ]] && printf "\n"
|
[[ $newline = y ]] && printf "\n"
|
||||||
return 0
|
return 0
|
||||||
@@ -134,21 +167,33 @@ function error_handler {
|
|||||||
}
|
}
|
||||||
trap 'error_handler $LINENO $?' ERR SIGHUP SIGINT SIGTERM
|
trap 'error_handler $LINENO $?' ERR SIGHUP SIGINT SIGTERM
|
||||||
|
|
||||||
|
function exit_handler {
|
||||||
|
log "exit handler (at line $1)"
|
||||||
|
mariadb_maybe_start
|
||||||
|
if [[ -v DSTMNT ]]; then
|
||||||
|
umount "$DSTMNT/dev"
|
||||||
|
umount "$DSTMNT/proc"
|
||||||
|
umount "$DSTMNT/sys"
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
trap 'exit_handler $LINENO' EXIT
|
||||||
|
|
||||||
function check_block_device {
|
function check_block_device {
|
||||||
local devtype="$1"
|
local devtype="$1"
|
||||||
local mode="$2"
|
local mode="$2"
|
||||||
local dev="$3"
|
local dev="$3"
|
||||||
|
|
||||||
if [[ ! -b "$dev" ]]; then
|
if [[ ! -b "$dev" ]]; then
|
||||||
log "$CMD: $devtype '$dev' is not a block device." >&2
|
log "$CMD: $devtype '$dev' is not a block device."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [[ ! -r "$dev" ]]; then
|
if [[ ! -r "$dev" ]]; then
|
||||||
log "$CMD: $devtype '$dev' is not readable." >&2
|
log "$CMD: $devtype '$dev' is not readable."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [[ $mode = "w" && ! -w "$dev" ]]; then
|
if [[ $mode = "w" && ! -w "$dev" ]]; then
|
||||||
log "$CMD: $devtype '$dev' is not writable." >&2
|
log "$CMD: $devtype '$dev' is not writable."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
@@ -164,25 +209,47 @@ function in_array {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# get y/n/q user input
|
||||||
|
function yesno {
|
||||||
|
local input
|
||||||
|
while true; do
|
||||||
|
printf "%s " "$1"
|
||||||
|
read -r input
|
||||||
|
case "$input" in
|
||||||
|
y|Y)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
q|Q)
|
||||||
|
log "aborting..."
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
n|N)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf "invalid answer. "
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# source and destination devices, root partition
|
# source and destination devices, root partition
|
||||||
SRC=""
|
SRC=""
|
||||||
DST=""
|
DST=""
|
||||||
SRCROOT=""
|
SRCROOT=""
|
||||||
ROOTPARTNUM=""
|
ROOTPARTNUM=""
|
||||||
DOIT=manual
|
DOIT=manual
|
||||||
|
MARIADB=no
|
||||||
|
MARIADBSTOPPED=no
|
||||||
|
GRUBINSTALL=no
|
||||||
|
|
||||||
# short and long options
|
# short and long options
|
||||||
SOPTS="dnhmr:y"
|
SOPTS="dnghmr:y"
|
||||||
LOPTS="dry-run,no,help,man,root:,yes"
|
LOPTS="dry-run,no,grub,help,man,mariadb,root:,yes"
|
||||||
|
|
||||||
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."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# if (( $? > 1 )); then
|
|
||||||
# echo 'Terminating...' >&2
|
|
||||||
# exit 1
|
|
||||||
# fi
|
|
||||||
|
|
||||||
eval set -- "$TMP"
|
eval set -- "$TMP"
|
||||||
unset TMP
|
unset TMP
|
||||||
@@ -194,6 +261,10 @@ while true; do
|
|||||||
shift
|
shift
|
||||||
continue
|
continue
|
||||||
;;
|
;;
|
||||||
|
'-g'|'--grub')
|
||||||
|
GRUBINSTALL=yes
|
||||||
|
shift
|
||||||
|
;;
|
||||||
'-h'|'--help')
|
'-h'|'--help')
|
||||||
usage
|
usage
|
||||||
exit 0
|
exit 0
|
||||||
@@ -202,10 +273,14 @@ while true; do
|
|||||||
man
|
man
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
|
'--mariadb')
|
||||||
|
MARIADB=yes
|
||||||
|
shift
|
||||||
|
;;
|
||||||
'-r'|'--root')
|
'-r'|'--root')
|
||||||
ROOTPARTNUM="$2"
|
ROOTPARTNUM="$2"
|
||||||
if ! [[ "$ROOTPARTNUM" =~ ^[[:digit:]]+$ ]]; then
|
if ! [[ "$ROOTPARTNUM" =~ ^[[:digit:]]+$ ]]; then
|
||||||
log "$CMD: $ROOTPARTNUM must be a partition number." >&2
|
log "$CMD: $ROOTPARTNUM must be a partition number."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
shift 2
|
shift 2
|
||||||
@@ -222,7 +297,7 @@ while true; do
|
|||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
usage
|
||||||
log 'Internal error!' >&2
|
log 'Internal error!'
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -232,8 +307,8 @@ done
|
|||||||
case "$#" in
|
case "$#" in
|
||||||
1)
|
1)
|
||||||
if [[ -n "$ROOTPARTNUM" ]]; then
|
if [[ -n "$ROOTPARTNUM" ]]; then
|
||||||
log "$CMD: cannot have --root option for live system." >&2
|
log "$CMD: cannot have --root option for live system."
|
||||||
log "Use '$CMD --help' or '$CMD --man' for help." >&2
|
log "Use '$CMD --help' or '$CMD --man' for help."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# guess root partition disk name
|
# guess root partition disk name
|
||||||
@@ -244,8 +319,8 @@ case "$#" in
|
|||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
if [[ -z "$ROOTPARTNUM" ]]; then
|
if [[ -z "$ROOTPARTNUM" ]]; then
|
||||||
log "$CMD: missing --root option for non live system." >&2
|
log "$CMD: missing --root option for non live system."
|
||||||
log "Use '$CMD --help' or '$CMD --man' for help." >&2
|
log "Use '$CMD --help' or '$CMD --man' for help."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
SRC="/dev/$1"
|
SRC="/dev/$1"
|
||||||
@@ -253,7 +328,7 @@ case "$#" in
|
|||||||
DST="/dev/$2"
|
DST="/dev/$2"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
usage >&2
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@@ -295,7 +370,6 @@ for ((i=0; i<${#LABELS[@]}; ++i)); do
|
|||||||
unset TMP TMPDEV TMPFS
|
unset TMP TMPDEV TMPFS
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
DSTROOT="$DST$ROOTPARTNUM"
|
DSTROOT="$DST$ROOTPARTNUM"
|
||||||
check_block_device "destination root partition" w "$DSTROOT"
|
check_block_device "destination root partition" w "$DSTROOT"
|
||||||
DSTROOTLABEL=$(lsblk -no label "$DSTROOT")
|
DSTROOTLABEL=$(lsblk -no label "$DSTROOT")
|
||||||
@@ -338,14 +412,15 @@ for ((i=0; i<${#LABELS[@]}; ++i)); do
|
|||||||
in_array "$TMPFS" VALIDFS && DSTDOIT[$i]=y
|
in_array "$TMPFS" VALIDFS && DSTDOIT[$i]=y
|
||||||
unset TMP TMPDEV TMPFS
|
unset TMP TMPDEV TMPFS
|
||||||
done
|
done
|
||||||
echo ZOB "${SRCDEVS[0]}" "${DSTDEVS[0]}"
|
|
||||||
for ((i=0; i<${#LABELS[@]}; ++i)); do
|
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 "%s %s" "${SRCDOIT[$i]}" "${DSTDOIT[$i]}"
|
log -n "%s %s " "${SRCDOIT[$i]}" "${DSTDOIT[$i]}"
|
||||||
|
[[ "$DSTROOTLABEL" == "${DSTLABELS[$i]}" ]] && log "*"
|
||||||
echo
|
echo
|
||||||
done | column -N DEV1,DEV2,LABEL1,LABEL2,FS1,FS2,SDOIT,DDOIT -t -o " | "
|
done | column -N DEV1,DEV2,LABEL1,LABEL2,FS1,FS2,SDOIT,DDOIT,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"
|
||||||
@@ -369,104 +444,29 @@ for ((i=0; i<${#LABELS[@]}; ++i)); do
|
|||||||
log "skipping (dry run)."
|
log "skipping (dry run)."
|
||||||
;;
|
;;
|
||||||
manual)
|
manual)
|
||||||
log -n "proceed ? [y/N/q] "
|
yesno "proceed ? [y/n/q]" && skip=n
|
||||||
read -r key
|
|
||||||
case "$key" in
|
|
||||||
y|Y)
|
|
||||||
log "copying..."
|
|
||||||
skip=n
|
|
||||||
;;
|
;;
|
||||||
q|Q)
|
|
||||||
log "aborting..."
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
n|N|*)
|
|
||||||
log "skipping..."
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
esac
|
esac
|
||||||
if [[ "$skip" == n ]]; then
|
if [[ "$skip" == n ]]; then
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
|
mariadb_maybe_stop
|
||||||
echorun rsync "$FILTER" ${RSYNCOPTS} "$SRCPART" "$DSTPART"
|
echorun rsync "$FILTER" ${RSYNCOPTS} "$SRCPART" "$DSTPART"
|
||||||
fi
|
fi
|
||||||
log ""
|
log ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
# array of partitions to copy
|
|
||||||
TO_COPY=(root export EFI)
|
|
||||||
|
|
||||||
# An alternative to SRCNUM, DSTNUM, and TO_COPY variables would be to have
|
|
||||||
# an array containing src and destination partitions:
|
|
||||||
# (partsrc1 partdst1 partsrc2 partdst2 etc...)
|
|
||||||
# example:
|
|
||||||
# TO_COPY=(root2 root1 export2 export1)
|
|
||||||
# declare -i i
|
|
||||||
# for ((i=0; i<${#TO_COPY[@]}; i+=2)); do
|
|
||||||
# SRC=${#TO_COPY[$i]}
|
|
||||||
# DST=${#TO_COPY[$i + 1]}
|
|
||||||
# etc...
|
|
||||||
|
|
||||||
# where we will configure/install grub: mount point, device
|
|
||||||
GRUB_ROOT=/mnt/root${DSTNUM}
|
|
||||||
GRUB_DEV=/dev/$(lsblk -no pkname /dev/disk/by-label/root${DSTNUM})
|
|
||||||
|
|
||||||
# we will use ".rsync-disk-copy" files to exclude files/dirs
|
|
||||||
|
|
||||||
# stop what could be problematic (databases, etc...)
|
|
||||||
systemctl stop mysql
|
|
||||||
|
|
||||||
# partitions copy
|
|
||||||
for part in ${TO_COPY[@]}; do
|
|
||||||
SRCPART=/mnt/${part}${SRCNUM}/
|
|
||||||
DSTPART=/mnt/${part}${DSTNUM}
|
|
||||||
|
|
||||||
echo copy from $SRCPART to $DSTPART
|
|
||||||
echo -n "press a key to continue..."
|
|
||||||
read -r key
|
|
||||||
echo rsync ${RSYNCOPTS} "$FILTER" "$SRCPART" "$DSTPART"
|
|
||||||
rsync ${RSYNCOPTS} "$FILTER" "$SRCPART" "$DSTPART"
|
|
||||||
done
|
|
||||||
|
|
||||||
# grub install
|
# grub install
|
||||||
# mount virtual devices
|
# mount virtual devices
|
||||||
mount -o bind /sys ${GRUB_ROOT}/sys
|
if [[ $GRUBINSTALL == yes ]]; then
|
||||||
mount -o bind /proc ${GRUB_ROOT}/proc
|
log "installing grub on $DST..."
|
||||||
mount -o bind /dev ${GRUB_ROOT}/dev
|
DSTMNT="/mnt/$DSTROOTLABEL"
|
||||||
|
mount -o bind /sys "$DSTMNT/sys"
|
||||||
|
mount -o bind /proc "$DSTMNT/proc"
|
||||||
|
mount -o bind /dev "$DSTMNT/dev"
|
||||||
|
|
||||||
chroot ${GRUB_ROOT} update-grub
|
chroot "$DSTMNT" update-grub
|
||||||
chroot ${GRUB_ROOT} grub-install ${GRUB_DEV}
|
chroot "$DSTMNT" grub-install "$DST"
|
||||||
|
|
||||||
# restart stopped process (db, etc...)
|
|
||||||
systemctl start mysql
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
###############declare -a DSTLABELS_CHECK=(${SRCLABELS[@]/%?/$DSTCHAR})
|
|
||||||
|
|
||||||
# find corresponding LABELS on DEST disk
|
|
||||||
# declare -a LABELS=(${SRCLABELS[@]/%?/})
|
|
||||||
# for ((i=0; i<${#SRCLABELS[@]}; ++i)); do
|
|
||||||
# TMP="${LABELS[$i]}$DSTCHAR"
|
|
||||||
# echo -n "looking for partition 'LABEL=$TMP'... "
|
|
||||||
# if ! DSTFS=$(findfs LABEL="$TMP"); then
|
|
||||||
# echo "not found."
|
|
||||||
# exit 1
|
|
||||||
# fi
|
|
||||||
# echo "$DSTFS"
|
|
||||||
# DSTLABELS[$i]="$TMP"
|
|
||||||
|
|
||||||
# done
|
|
||||||
|
|
||||||
# #DSTLABELS=($(lsblk -lno LABEL "$DST"))
|
|
||||||
# # check all partitions types
|
|
||||||
# for ((i=0; i<${#SRCLABELS[@]}; ++1)); do
|
|
||||||
# check_block_device "source ${LABELS[$i]} partition" r "${SRCLABELS[$i]}"
|
|
||||||
# #check_block_device "destination ${LABELS[$i]} partition" w "${DSTLABELS[$i]}"
|
|
||||||
|
|
||||||
# done
|
|
||||||
|
|
||||||
# echo "DSTLABELS=${#DSTLABELS[@]} - ${DSTLABELS[*]}"
|
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
Reference in New Issue
Block a user