snap-sync: trap handling, use prinf, snapper description handling
- substitute 'echo' calls with 'printf' calls where appropriate - prepend $progname in notify messages - trap handling in posix form - update snapper description on successfull backup - usage update: introduce subvolid - usage update: introduce -t, --target
This commit is contained in:
109
bin/snap-sync
109
bin/snap-sync
@@ -56,16 +56,18 @@ selected_subvol='none'
|
|||||||
check_prerequisites () {
|
check_prerequisites () {
|
||||||
|
|
||||||
# requested binaries:
|
# requested binaries:
|
||||||
which awk >/dev/null 2>&1 || { echo "'awk' is not installed." && exit 1; }
|
which awk >/dev/null 2>&1 || { printf "'awk' is not installed." && exit 1; }
|
||||||
which sed >/dev/null 2>&1 || { echo "'sed' is not installed." && exit 1; }
|
which sed >/dev/null 2>&1 || { printf "'sed' is not installed." && exit 1; }
|
||||||
which tee >/dev/null 2>&1 || { echo "'tee' is not installed." && exit 1; }
|
which tee >/dev/null 2>&1 || { printf "'tee' is not installed." && exit 1; }
|
||||||
which btrfs >/dev/null 2>&1 || { echo "'btrfs' is not installed." && exit 1; }
|
which btrfs >/dev/null 2>&1 || { printf "'btrfs' is not installed." && exit 1; }
|
||||||
which findmnt >/dev/null 2>&1 || { echo "'findmnt' is not installed." && exit 1; }
|
which findmnt >/dev/null 2>&1 || { printf "'findmnt' is not installed." && exit 1; }
|
||||||
which systemd-cat >/dev/null 2>&1 || { echo "'systemd-cat' is not installed." && exit 1; }
|
which systemd-cat >/dev/null 2>&1 || { printf "'systemd-cat' is not installed." && exit 1; }
|
||||||
which wc >/dev/null 2>&1 || { echo "'wc' is not installed." && exit 1; }
|
which wc >/dev/null 2>&1 || { printf "'wc' is not installed." && exit 1; }
|
||||||
which notify-send >/dev/null 2>&1 || { echo "'notify-send' is not installed." && exit 1; }
|
|
||||||
|
|
||||||
if [ $(id -u) -ne 0 ] ; then echo "Script must be run as root" ; exit 1 ; fi
|
# optional binaries:
|
||||||
|
which notify-send >/dev/null 2>&1 && { donotify=1; }
|
||||||
|
|
||||||
|
if [ $(id -u) -ne 0 ] ; then printf "Script must be run as root" ; exit 1 ; fi
|
||||||
|
|
||||||
if [ ! -r "$SNAPPER_CONFIG" ]; then
|
if [ ! -r "$SNAPPER_CONFIG" ]; then
|
||||||
die "$SNAPPER_CONFIG does not exist."
|
die "$SNAPPER_CONFIG does not exist."
|
||||||
@@ -78,7 +80,7 @@ die () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
error () {
|
error () {
|
||||||
printf "==> ERROR: %s\n" "$@"
|
printf "\n==> ERROR: %s\n" "$@"
|
||||||
notify_error 'Error' 'Check journal for more information.'
|
notify_error 'Error' 'Check journal for more information.'
|
||||||
} >&2
|
} >&2
|
||||||
|
|
||||||
@@ -139,27 +141,26 @@ get_disk_infos () {
|
|||||||
done
|
done
|
||||||
i=0
|
i=0
|
||||||
for fs_option in $fs_options; do
|
for fs_option in $fs_options; do
|
||||||
eval "fs_options_$i='$fs_option'"
|
subvolid=$(eval echo \$fs_option | sed -e 's/.*subvolid=\([0-9]*\).*/\1/')
|
||||||
subvolid=$(eval echo \$fs_options | sed -e 's/.*subvolid=\([0-9]*\).*/\1/')
|
|
||||||
if [ "$subvolid" = "$subvolid_cmdline" ]; then
|
if [ "$subvolid" = "$subvolid_cmdline" ]; then
|
||||||
disk_subvolid_match="$i"
|
disk_subvolid_match="$i"
|
||||||
disk_subvolid_match_count=$(($disk_subvolid_match_count+1))
|
disk_subvolid_match_count=$(($disk_subvolid_match_count+1))
|
||||||
fi
|
fi
|
||||||
eval "disk_target_$i='$disk_target'"
|
eval "fs_options_$i='$fs_option'"
|
||||||
i=$((i+1))
|
i=$((i+1))
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
notify () {
|
notify () {
|
||||||
# estimation: batch calls should just log
|
# estimation: batch calls should just log
|
||||||
if [ nonotify ]; then
|
if [ $donotify ]; then
|
||||||
printf "%s %s\n" "$progname" "$2"
|
|
||||||
else
|
|
||||||
for u in $(users | sed 's/ /\n/' | sort -u); do
|
for u in $(users | sed 's/ /\n/' | sort -u); do
|
||||||
sudo -u $u DISPLAY=:0 \
|
sudo -u $u DISPLAY=:0 \
|
||||||
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(sudo -u $u id -u)/bus \
|
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(sudo -u $u id -u)/bus \
|
||||||
notify-send -a $progname "$1" "$2" --icon="dialog-$3"
|
notify-send -a $progname "$progname: $1" "$2" --icon="dialog-$3"
|
||||||
done
|
done
|
||||||
|
else
|
||||||
|
printf "%s %s\n" "$progname" "$2"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,23 +191,19 @@ parse_params () {
|
|||||||
description="$2"
|
description="$2"
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
--dry-run)
|
|
||||||
dryrun=1
|
|
||||||
shift 1
|
|
||||||
;;
|
|
||||||
-l|--TARGET)
|
|
||||||
target_cmdline="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-n|--noconfirm)
|
-n|--noconfirm)
|
||||||
noconfirm=1
|
noconfirm=1
|
||||||
nonotify=1
|
donotify=0
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-s|--subvolid|--SUBVOLID)
|
-s|--subvolid|--SUBVOLID)
|
||||||
subvolid_cmdline="$2"
|
subvolid_cmdline="$2"
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
-t|--TARGET)
|
||||||
|
target_cmdline="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
-u|--UUID)
|
-u|--UUID)
|
||||||
uuid_cmdline="$2"
|
uuid_cmdline="$2"
|
||||||
shift 2
|
shift 2
|
||||||
@@ -219,6 +216,10 @@ parse_params () {
|
|||||||
selected_config=${1#*=}
|
selected_config=${1#*=}
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--dry-run)
|
||||||
|
dryrun=1
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
--remote)
|
--remote)
|
||||||
remote=$2
|
remote=$2
|
||||||
ssh="ssh $remote"
|
ssh="ssh $remote"
|
||||||
@@ -241,7 +242,7 @@ parse_params () {
|
|||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
-*)
|
-*)
|
||||||
echo "WARN: Unknown option (ignored): $1" >&2
|
printf "WARN: Unknown option (ignored): $1" >&2
|
||||||
#shift
|
#shift
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
@@ -258,21 +259,25 @@ parse_params () {
|
|||||||
description=${description:-"latest incremental backup"}
|
description=${description:-"latest incremental backup"}
|
||||||
uuid_cmdline=${uuid_cmdline:-"none"}
|
uuid_cmdline=${uuid_cmdline:-"none"}
|
||||||
target_cmdline=${target_cmdline:-"none"}
|
target_cmdline=${target_cmdline:-"none"}
|
||||||
|
subvolid_cmdline=${subvolid_cmdline:-"none"}
|
||||||
|
|
||||||
if [ -z $remote ]; then
|
if [ -z $remote ]; then
|
||||||
ssh=""
|
ssh=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$verbose" ]; then
|
if [ "$verbose" ]; then
|
||||||
echo "Snap UUID : '$uuid_cmdline'"
|
printf "Snap UUID: '%s'\n" "$uuid_cmdline"
|
||||||
echo "Snap TARGET: '$target_cmdline'"
|
printf "Snap TARGET: '%s'\n" "$target_cmdline"
|
||||||
echo "Snap Description: '$description'"
|
printf "Snap SUBVOLID: '%s'\n" "$subvolid_cmdline"
|
||||||
echo "Snap Config: '$selected_config'"
|
printf "Snap Backupdir: '%s'\n" "$backupdir_cmdline"
|
||||||
echo "Snap Remote: '$ssh'"
|
printf "Snap Description: '%s'\n" "$description"
|
||||||
|
printf "Snap Config: '%s'\n" "$selected_config"
|
||||||
|
printf "Snap Remote: '%s'\n" "$ssh"
|
||||||
|
|
||||||
if [ "$verbose" ]; then snap_sync_options="verbose=true"; fi
|
if [ "$verbose" ]; then snap_sync_options="verbose=true"; fi
|
||||||
if [ "$dryrun" ]; then snap_sync_options="${snap_sync_options} dry-run=true"; fi
|
if [ "$dryrun" ]; then snap_sync_options="${snap_sync_options} dry-run=true"; fi
|
||||||
if [ "$noconfirm" ]; then snap_sync_options="${snap_sync_options} noconfirm=true"; fi
|
if [ "$noconfirm" ]; then snap_sync_options="${snap_sync_options} noconfirm=true"; fi
|
||||||
echo "Options: ${snap_sync_options}"
|
printf "Options: '%s'\n" "${snap_sync_options}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +296,7 @@ run_config () {
|
|||||||
count=$(eval snapper -c $selected_config list -t single | awk '/subvolid='"$selected_subvol"'/, /uuid='"$selected_uuid"'/ {cnt++} END {print cnt}')
|
count=$(eval snapper -c $selected_config list -t single | awk '/subvolid='"$selected_subvol"'/, /uuid='"$selected_uuid"'/ {cnt++} END {print cnt}')
|
||||||
#count=$(eval snapper -c $selected_config list -t single | grep -c -e "subvolid=$selected_subvol" -e 'uuid=$selected_uuid')
|
#count=$(eval snapper -c $selected_config list -t single | grep -c -e "subvolid=$selected_subvol" -e 'uuid=$selected_uuid')
|
||||||
if [ -n "$count" ] && [ "$count" -gt 1 ]; then
|
if [ -n "$count" ] && [ "$count" -gt 1 ]; then
|
||||||
error "More than one snapper entry found with UUID $selected_uuid and SUBVOL $selected_subvol for configuration '$selected_config'. Skipping configuration '$selected_config'."
|
error "More than one snapper entry found with UUID $selected_uuid and SUBVOLID $selected_subvol for configuration '$selected_config'. Skipping configuration '$selected_config'."
|
||||||
selected_configs=$(echo $selected_configs | sed -e "s/\($selected_config*\)//")
|
selected_configs=$(echo $selected_configs | sed -e "s/\($selected_config*\)//")
|
||||||
if [ "$verbose" ]; then
|
if [ "$verbose" ]; then
|
||||||
printf "Counter=%s" "$count"
|
printf "Counter=%s" "$count"
|
||||||
@@ -526,7 +531,7 @@ run_backup () {
|
|||||||
cmd="btrfs send $snapper_new_snapshot | $ssh btrfs receive $snapper_target_snapshots"
|
cmd="btrfs send $snapper_new_snapshot | $ssh btrfs receive $snapper_target_snapshots"
|
||||||
printf "Sending first snapshot for snapper config '%s' ...\n" "$selected_config" | tee $PIPE
|
printf "Sending first snapshot for snapper config '%s' ...\n" "$selected_config" | tee $PIPE
|
||||||
if [ "$verbose" ]; then
|
if [ "$verbose" ]; then
|
||||||
echo "btrfs send $snapper_new_snapshot | $ssh btrfs receive $snapper_target_snapshots"
|
printf "btrfs send %s | %s btrfs receive %s\n" "$snapper_new_snapshot" "$ssh" "$snapper_target_snapshots"
|
||||||
cmd="btrfs send -v $snapper_new_snapshot | $ssh btrfs receive -v $snapper_target_snapshots"
|
cmd="btrfs send -v $snapper_new_snapshot | $ssh btrfs receive -v $snapper_target_snapshots"
|
||||||
fi
|
fi
|
||||||
if [ ! "$dryrun" ]; then
|
if [ ! "$dryrun" ]; then
|
||||||
@@ -552,7 +557,7 @@ run_backup () {
|
|||||||
printf "dryrun: btrfs send %s -c %s %s | %s btrfs receive %s %s\n" \
|
printf "dryrun: btrfs send %s -c %s %s | %s btrfs receive %s %s\n" \
|
||||||
"$verbose_flag" "$snapper_sync_snapshot" "$snapper_new_snapshot" \
|
"$verbose_flag" "$snapper_sync_snapshot" "$snapper_new_snapshot" \
|
||||||
"$ssh" "$verbose_flag" "$snapper_target_snapshots"
|
"$ssh" "$verbose_flag" "$snapper_target_snapshots"
|
||||||
printf "dryrun: snapper -c %s delete %a\n" "$selected_config" "$snapper_sync_id"
|
printf "dryrun: snapper --config %s delete %s\n" "$selected_config" "$snapper_sync_id"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -584,7 +589,7 @@ run_backup () {
|
|||||||
target_userdata="subvolid=$src_subvolid, uuid=$src_uuid, host=$src_host"
|
target_userdata="subvolid=$src_subvolid, uuid=$src_uuid, host=$src_host"
|
||||||
|
|
||||||
# Tag new snapshot as the latest
|
# Tag new snapshot as the latest
|
||||||
printf "Tagging new snapshot as latest backup for '%s' ...\n" "$selected_config" | tee $PIPE
|
printf "Tagging snapper metadata for configuration '%s' ...\n" "$selected_config" | tee $PIPE
|
||||||
if [ ! "$dryrun" ]; then
|
if [ ! "$dryrun" ]; then
|
||||||
snapper -v -c "$selected_config" modify -d "$description" -u "$userdata" "$snapper_new_id"
|
snapper -v -c "$selected_config" modify -d "$description" -u "$userdata" "$snapper_new_id"
|
||||||
$ssh snapper -v -c "$snapper_target_subvol" modify -d \"$target_description\" -u \"$target_userdata\" "$snapper_new_id"
|
$ssh snapper -v -c "$snapper_target_subvol" modify -d \"$target_description\" -u \"$target_userdata\" "$snapper_new_id"
|
||||||
@@ -594,10 +599,15 @@ run_backup () {
|
|||||||
cmd="$ssh snapper -v -c $snapper_target_subvol modify -d $target_description -u $target_userdata $snapper_new_id"
|
cmd="$ssh snapper -v -c $snapper_target_subvol modify -d $target_description -u $target_userdata $snapper_new_id"
|
||||||
printf "dryrun: %s\n" "$cmd"
|
printf "dryrun: %s\n" "$cmd"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Cleanup old source snapshots
|
||||||
|
source_description="snap-sync backup"
|
||||||
|
$(eval snapper --verbose --config "$selected_config" modify --description \"$source_description\" --cleanup timeline "$snapper_sync_id")
|
||||||
|
#snapper -c "$selected_config" delete "$snapper_sync_id"
|
||||||
|
sync
|
||||||
|
|
||||||
printf "Backup complete for snapper configuration '%s'.\n" "$selected_config" > $PIPE
|
printf "Backup complete for snapper configuration '%s'.\n" "$selected_config" > $PIPE
|
||||||
done
|
done
|
||||||
|
|
||||||
exec 3>&-
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select_target_disk () {
|
select_target_disk () {
|
||||||
@@ -622,7 +632,7 @@ select_target_disk () {
|
|||||||
eval "disk_selected_$i='$disk_subvolid_match'"
|
eval "disk_selected_$i='$disk_subvolid_match'"
|
||||||
disk=$(eval echo \$disk_uuid_$disk_subvolid_match)
|
disk=$(eval echo \$disk_uuid_$disk_subvolid_match)
|
||||||
subvolid=$(eval echo \$disk_subvolid_$disk_subvolid_match)
|
subvolid=$(eval echo \$disk_subvolid_$disk_subvolid_match)
|
||||||
fs_options=$(eval echo \$fs_options_$disk_target_match | sed -e 's/.*,\(subvolid=[0-9]*\).*,\(subvol=[0-9]*\)/\1,\2/')
|
fs_options=$(eval echo \$fs_options_$disk_subvolid_match | sed -e 's/.*,\(subvolid=[0-9]*\).*,\(subvol=[0-9]*\)/\1,\2/')
|
||||||
disk_selected=$disk_subvolid_match
|
disk_selected=$disk_subvolid_match
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
@@ -685,13 +695,12 @@ select_target_disk () {
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
if [ "$disk_selected" = x ]; then
|
if [ "$disk_selected" = x ]; then
|
||||||
exit x
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
selected_uuid=$(eval echo \$disk_uuid_$disk_selected)
|
selected_uuid=$(eval echo \$disk_uuid_$disk_selected)
|
||||||
selected_target=$(eval echo \$disk_target_$disk_selected)
|
selected_target=$(eval echo \$disk_target_$disk_selected)
|
||||||
selected_subvol=$(eval echo \$fs_options_$disk_selected | sed -e 's/.*subvolid=\([0-9]*\).*/\1/')
|
selected_subvol=$(eval echo \$fs_options_$disk_selected | sed -e 's/.*subvolid=\([0-9]*\).*/\1/')
|
||||||
#fs_options=$(eval echo \$fs_options_$disk_selected | sed -e 's/.*,\(subvolid=[0-9]*\).*,\(subvol=[0-9]*\)/\1,\2/')
|
|
||||||
if [ "$verbose" ]; then
|
if [ "$verbose" ]; then
|
||||||
printf "Selected Subvol-ID=%s: %s on %s\n" "$selected_subvol" "$selected_target" "$selected_uuid"
|
printf "Selected Subvol-ID=%s: %s on %s\n" "$selected_subvol" "$selected_target" "$selected_uuid"
|
||||||
fi
|
fi
|
||||||
@@ -722,9 +731,10 @@ Options:
|
|||||||
(e.g. -c "root home").
|
(e.g. -c "root home").
|
||||||
-n, --noconfirm Do not ask for confirmation for each configuration. Will still prompt for backup
|
-n, --noconfirm Do not ask for confirmation for each configuration. Will still prompt for backup
|
||||||
directory name on first backup"
|
directory name on first backup"
|
||||||
|
-s, --subvolid <subvlid> Specify the subvolume id of the mounted BTRFS subvolume to back up to. Defaults to 5.
|
||||||
-u, --UUID <UUID> Specify the UUID of the mounted BTRFS subvolume to back up to. Otherwise will prompt."
|
-u, --UUID <UUID> Specify the UUID of the mounted BTRFS subvolume to back up to. Otherwise will prompt."
|
||||||
If multiple mount points are found with the same UUID, will prompt user."
|
If multiple mount points are found with the same UUID, will prompt user."
|
||||||
-l, --TARGET <target> Specify the mountpoint of the BTRFS subvolume to back up to.
|
-t, --TARGET <target> Specify the mountpoint of the BTRFS subvolume to back up to.
|
||||||
--remote <address> Send the snapshot backup to a remote machine. The snapshot will be sent via ssh. You
|
--remote <address> Send the snapshot backup to a remote machine. The snapshot will be sent via ssh. You
|
||||||
should specify the remote machine's hostname or ip address. The 'root' user must be
|
should specify the remote machine's hostname or ip address. The 'root' user must be
|
||||||
permitted to login on the remote machine.
|
permitted to login on the remote machine.
|
||||||
@@ -787,10 +797,10 @@ verify_snapper_structure () {
|
|||||||
cwd=`pwd`
|
cwd=`pwd`
|
||||||
ssh=""
|
ssh=""
|
||||||
|
|
||||||
# this bashism has to be adapted
|
# this bashism and can't be ported to dash (ERR is not supported)
|
||||||
#trap 'traperror ${LINENO} $? "$BASH_COMMAND" $BASH_LINENO "${FUNCNAME[@]}"' ERR
|
#trap 'traperror ${LINENO} $? "$BASH_COMMAND" $BASH_LINENO "${FUNCNAME[@]}"' ERR
|
||||||
trap 'traperror ${LINENO} $?"' ERR
|
#trap 'traperror ${LINENO} $?' ERR
|
||||||
trap trapkill SIGTERM SIGINT
|
trap trapkill TERM INT
|
||||||
|
|
||||||
parse_params $@
|
parse_params $@
|
||||||
|
|
||||||
@@ -799,14 +809,14 @@ check_prerequisites
|
|||||||
# read mounted BTRFS structures
|
# read mounted BTRFS structures
|
||||||
get_disk_infos
|
get_disk_infos
|
||||||
|
|
||||||
if [ "target_cmdline" != "none" ]; then
|
if [ "$target_cmdline" != "none" ]; then
|
||||||
if [ -z "$ssh" ]; then
|
if [ -z "$ssh" ]; then
|
||||||
notify_info "Backup started" "Starting backups to '$target_cmdline' ..."
|
notify_info "Backup started" "Starting backups to '$target_cmdline' ..."
|
||||||
else
|
else
|
||||||
notify_info "Backup started" "Starting backups to '$target_cmdline' at $remote ..."
|
notify_info "Backup started" "Starting backups to '$target_cmdline' at $remote ..."
|
||||||
fi
|
fi
|
||||||
elif [ "$uuid_cmdline" != "none" ]; then
|
elif [ "$uuid_cmdline" != "none" ]; then
|
||||||
if [ -z $ssh ]; then
|
if [ -z "$ssh" ]; then
|
||||||
notify_info "Backup started" "Starting backups to $uuid_cmdline..."
|
notify_info "Backup started" "Starting backups to $uuid_cmdline..."
|
||||||
else
|
else
|
||||||
notify_info "Backup started" "Starting backups to $uuid_cmdline at $remote..."
|
notify_info "Backup started" "Starting backups to $uuid_cmdline at $remote..."
|
||||||
@@ -835,6 +845,9 @@ run_config
|
|||||||
# run backups using btrfs-send -> btrfs-receive
|
# run backups using btrfs-send -> btrfs-receive
|
||||||
run_backup
|
run_backup
|
||||||
|
|
||||||
|
printf "\nDone!\n" | tee $PIPE
|
||||||
|
exec 3>&-
|
||||||
|
|
||||||
if [ "$uuid_cmdline" != "none" ]; then
|
if [ "$uuid_cmdline" != "none" ]; then
|
||||||
notify_info "Finished" "Backups to $uuid_cmdline complete!"
|
notify_info "Finished" "Backups to $uuid_cmdline complete!"
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user