Files
grub-btrfs/grub-btrfsd
Pascal Jäger de68795d70 grub-btrfsd: better support commandline timeshift
When a snapshot is created with `timeshift --create` an the commandline,
sometimes the daemon is not fast enough between the
first (/run/timeshift) inotifywait and the second (an the actual
snapshot directory) inotifywait. Even without any `sleep` between them.
So now the grub (sub)menu is always created once in its own process when the daemon sees a
timeshift startup.

Signed-off-by: Pascal Jäger <pascal.jaeger@leimstift.de>
2022-10-21 08:27:51 +02:00

224 lines
7.0 KiB
Bash
Executable File

#!/bin/sh
# Copyright 2022 Pascal Jaeger
# Distributed under the terms of the GNU General Public License v3
# Update GRUB when new BTRFS snapshots are created.
# init
timeshift_pid=-1
watchtime=0
logfile=0
snapshots=-1
timeshift_auto=false
verbose=false
syslog=false
# helper functions
GREEN=$'\033[0;32m'
RED=$'\033[0;31m'
CYAN=$'\033[;36m'
RESET=$'\033[0m'
print_help() {
echo "${CYAN}[?] Usage:"
echo "${0##*/} [-h, --help] [-t, --timeshift-auto] [-l, --log-file LOG_FILE] [-v, --verbose] [-s, --syslog] SNAPSHOTS_DIR"
echo
echo "SNAPSHOTS_DIR Snapshot directory to watch, without effect when --timeshift-auto"
echo
echo "Optional arguments:"
echo "-t, --timeshift-auto Automatically detect Timeshifts snapshot directory"
echo "-l, --log-file Specify a logfile to write to"
echo "-v, --verbose Let the log of the daemon be more verbose"
echo "-s, --syslog Write to syslog"
echo "-h, --help Display this message${RESET}"
}
log() {
echo "${2}"$1"${RESET}"
if [ ${syslog} = true ]; then
logger -p user.notice -t ${0##*/}"["$$"]" "$1"
fi
if [ ${#logfile} -gt 1 ]; then
echo "$(date) ${1}" >> ${logfile}
fi
}
vlog() {
if [ ${verbose} = true ]; then
echo "${2}"$1"${RESET}"
if [ ${syslog} = true ]; then
logger -p user.notice -t ${0##*/} "$1"
fi
if [ ${#logfile} -gt 1 ]; then
echo "$(date) ${1}" >> ${logfile}
fi
fi
}
err() {
echo "${2}"${1}"${RESET}" >&2
if [ ${syslog} = true ]; then
logger -p user.error -t ${0##*/} "$1"
fi
if [ ${#logfile} -gt 1 ]; then
echo "$(date) error: ${1}" >> ${logfile}
fi
}
# parse arguments
while getopts :l:tvsh-: opt; do
case "$opt" in
-)
case "${OPTARG}" in
log-file)
logfile="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
;;
timeshift-auto)
timeshift_auto=true
;;
verbose)
verbose=true
;;
syslog)
syslog=true
;;
*)
if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
err "[!] Unknown option --${OPTARG}" "${RED}" >&2
echo
fi
print_help
exit 1
;;
esac;;
l)
logfile="${OPTARG}"
;;
t)
timeshift_auto=true
;;
v)
verbose=true
;;
s)
syslog=true
;;
h)
print_help
exit 0
;;
*)
if [ "$OPTERR" = 1 ] || [ "${optspec:0:1}" = ":" ]; then
err "[!] Non-option argument: '-${OPTARG}'" "${RED}" >&2
echo
fi
print_help
exit 1
;;
esac
done
shift $(( OPTIND - 1 ))
snapshots="${1}"
if [ ${#logfile} -gt 1 ]; then
touch "${logfile}"
echo "GRUB-BTRFSD log $(date)" >> "${logfile}"
fi
log "grub-btrfsd starting up..." "${GREEN}"
if [ ${verbose} = true ]; then
inotify_qiet_flag=""
else
inotify_qiet_flag=" -q -q "
fi
vlog "Arguments:"
vlog "Snapshot directory: $snapshots"
vlog "Timestift autodetection: $timeshift_auto"
vlog "Logfile: $logfile"
if ! [ -d "$snapshots" ] && ! [ ${timeshift_auto} = true ]; then
err "[!] No directory found at ${snapshots}" "${RED}" >&2
err "[!] Please specify a valid snapshot directory" "${RED}" >&2
exit 1
fi
if [ ${timeshift_auto} = true ]; then
watchtime=15
[ -d /run/timeshift ] || mkdir /run/timeshift
else
watchtime=0
fi
create_grub_menu() {
# create the grub submenu of the whole grub menu, depending on wether the submenu already exists
# and gives feedback if it worked
if [ -s "${GRUB_BTRFS_GRUB_DIRNAME:-/boot/grub}/grub-btrfs.cfg" ]; then
if /etc/grub.d/41_snapshots-btrfs; then
log "Grub submenu recreated" "${GREEN}"
else
err "[!] Error during grub submenu creation (grub-btrfs error)" "${RED}"
fi
else
if ${GRUB_BTRFS_MKCONFIG:-grub-mkconfig} -o ${GRUB_BTRFS_GRUB_DIRNAME:-/boot/grub}/grub.cfg; then
log "Grub menu recreated" "${GREEN}"
else
err "[!] Error during grub menu creation (grub/ grub-btrfs error)" "${RED}"
fi
fi
}
# start the actual daemon
vlog "Snapshot dir watchtimeout: $watchtime"
vlog "Entering infinite while" "${GREEN}"
while true; do
runs=false
if [ ${timeshift_auto} = true ] && ! [ "${timeshift_pid}" -gt 0 ] ; then
# watch the timeshift folder for a folder that is created when timeshift starts up
sleep 1 # for safety so the outer while is not going crazy
if [ "${timeshift_pid}" -eq -2 ]; then
log "detected timeshift shutdown"
fi
timeshift_pid=$(ps ax | awk '{sub(/.*\//, "", $5)} $5 ~ /timeshift/ {print $1}')
if [ "${#timeshift_pid}" -gt 0 ]; then
snapshots="/run/timeshift/${timeshift_pid}/backup/timeshift-btrfs/snapshots"
log "detected running Timeshift at daemon startup, PID is: $timeshift_pid"
vlog "new snapshots directory is $snapshots"
else
log "Watching /run/timeshift for timeshift to start"
inotifywait ${inotify_qiet_flag} -e create -e delete /run/timeshift && {
sleep 1
timeshift_pid=$(ps ax | awk '{sub(/.*\//, "", $5)} $5 ~ /timeshift/ {print $1}')
snapshots="/run/timeshift/${timeshift_pid}/backup/timeshift-btrfs/snapshots"
log "detected Timeshift startup, PID is: $timeshift_pid" "${CYAN}"
vlog "new snapshots directory is $snapshots" "${CYAN}"
(create_grub_menu) # create the grub menu once immidiatly in a forking process. Snapshots from commandline using timeshift --create need this
}
fi
runs=false
else
while [ -d "$snapshots" ]; do
# watch the actual snapshots folder for a new snapshot or a deletion of a snapshot
if [ ${runs} = false ] && [ ${verbose} = false ]; then
log "Watching $snapshots for new snapshots..." "${CYAN}"
else
vlog "Watching $snapshots for new snapshots..." "${CYAN}"
fi
runs=true
inotifywait ${inotify_qiet_flag} -e create -e delete -e unmount -t "$watchtime" "$snapshots" && {
log "Detected snapshot creation/ deletion, recreating Grub menu" "${CYAN}"
sleep 5
create_grub_menu
}
sleep 1
done
timeshift_pid=-2
fi
if ! [ ${timeshift_auto} = true ] && ! [ -d "${snapshots}" ] ; then # in case someone deletes the snapshots folder (in snapper mode) to prevent the while loop from going wild
break
fi
done
exit 0 # tradition is tradition