diff --git a/client/usr/bin/rs-backup-run b/client/usr/bin/rs-backup-run index d9ae23f..1205e75 100755 --- a/client/usr/bin/rs-backup-run +++ b/client/usr/bin/rs-backup-run @@ -52,6 +52,12 @@ _VERBOSE_MODE=false _SHOW_PROGRESS=false _DRY_RUN=false _FORCE_RUN=false +_PRE_HOOK="" +_PRE_HOOK_RUN=false +_POST_HOOK="" +_POST_HOOK_RUN=false +_FORCED_POST_HOOK="" +_FORCED_POST_HOOK_RUN=false _ERROR_COUNT=0 if [ $(id -u) -eq 0 ]; then @@ -85,31 +91,43 @@ user will be backed up. Usage: $(basename $0) [OPTION]... Options: - -r, --remote-host=HOST The remote host to connect to - --remote-user=NAME The username to use for logging into the remote server - (%h will be replaced with the host name of this - machine and %u with your username) - --push-module=NAME The remote rsync server module - --ssh-options=OPTS Additional SSH options (will be merged with the default - options set in the rs-backup client-config file) - -o, --rsync-options=OPTS Additional options for rsync - -n, --dry-run Perform a test run (same as the --dry-run option for - rsync). Enable --verbose mode for useful control output - -s, --no-home-dirs Don't back up home dirs, only perform global system - backup (root only) - -i, --include-from=FILE Specify an alternate inclusion pattern file - This will override the default setting. If the script - is run as root, only the system backup will be - performed, no additional home directories will be - backed up - -l, --log-level=NUM Set log level to NUM (between 0 and 4) - --log-file=FILE Log to this file instead of syslog - -f, --force-run Force rs-backup to run, even if a lock file exists - -q, --quiet Don't print any error messages or warnings to the - screen (only write to log file) - -v, --verbose Print all messages of the current debug level - -p, --progress Print file transfer information to the terminal - -h, --help Print this help and exit + -r, --remote-host=HOST The remote host to connect to + --remote-user=NAME The username to use for logging into the remote server + (%h will be replaced with the host name of this + machine and %u with your username) + --push-module=NAME The remote rsync server module + --ssh-options=OPTS Additional SSH options (will be merged with the default + options set in the rs-backup client-config file) + -o, --rsync-options=OPTS Additional options for rsync + -n, --dry-run Perform a test run (same as the --dry-run option for + rsync). Enable --verbose mode for useful control output + -s, --no-home-dirs Don't back up home dirs, only perform global system + backup (root only) + -i, --include-from=FILE Specify an alternate inclusion pattern file + This will override the default setting. If the script + is run as root, only the system backup will be + performed, no additional home directories will be + backed up + -l, --log-level=NUM Set log level to NUM (between 0 and 4) + --log-file=FILE Log to this file instead of syslog + -f, --force-run Force rs-backup to run, even if a lock file exists + -q, --quiet Don't print any error messages or warnings to the + screen (only write to log file) + -v, --verbose Print all messages of the current debug level + -p, --progress Print file transfer information to the terminal + --pre-hook=CMD Command to be run before the backup. Will only be run + once for multi-user / global system backup. The hook + command will be executed as the user who started the + backup command + --post-hook=CMD Similar to --pre-hook, but run after the backup + has successfully finished. If an error occurred + during the backup, the post hook will not be run. + --forced-post-hook=CMD Same as --post-hook, but will always be run, regardless + of wether an error occurred or not. Will also be run + if the backup was interrupted by SIGINT or SIGTERM. + If both --post-hook and --forced-post-hook are specified, + --post-hook is run first + -h, --help Print this help and exit HELP } @@ -211,14 +229,57 @@ remove_runfile() { rmdir "$(dirname $_RUNFILE)" > /dev/null 2>&1 } +# Run the user-specified pre hook +# +# Usage: run_pre_hook +# +run_pre_hook() { + if ! $_PRE_HOOK_RUN && [ "" != "$_PRE_HOOK" ]; then + $SHELL -c "$_PRE_HOOK" + _PRE_HOOK_RUN=true + fi +} + +# Run the user-specified post hook +# +# Usage: run_post_hook +# +run_post_hook() { + if ! $_POST_HOOK_RUN && [ "" != "$_POST_HOOK" ]; then + $SHELL -c "$_POST_HOOK" + _POST_HOOK_RUN=true + fi +} + +# Run the user-specified forced post hook +# +# Usage: run_forced_post_hook +# +run_forced_post_hook() { + if ! $_FORCED_POST_HOOK_RUN && [ "" != "$_FORCED_POST_HOOK" ]; then + $SHELL -c "$_FORCED_POST_HOOK" + _FORCED_POST_HOOK_RUN=true + fi +} + +# Exit cleanly with given exit code. +# Removes any run files and runs the forced post hook. +# +# Usage clean_exit +# +clean_exit() { + remove_runfile + run_forced_post_hook + exit $@ +} + # Handle script termination by external signals. # # Usage: handle_signals # handle_exit_signal() { write_log 1 "Program terminated upon user request." - remove_runfile - exit 1 + clean_exit 1 } # Show a desktop notification using notify-send @@ -526,16 +587,15 @@ parse_cmd_args() { getopt -T > /dev/null if [ $? -ne 4 ]; then write_log 1 "Need GNU getopt for command line parameter parsing!" - exit 1; + exit 1 fi - args=$(getopt \ - -s sh \ - -o "r:o:nsi:l:fqvph" \ - -l "remote-host:,remote-user:,push-module:,ssh-options:,rsync-options:,dry-run,no-home-dirs,include-from:,log-level:,log-file:,force-run,quiet,verbose,progress,help" \ - -n "${name}" \ - -- "${@}") + short_opts="r:o:nsi:l:fqvph" + long_opts="remote-host:,remote-user:,push-module:,ssh-options:,rsync-options:," + long_opts+="dry-run,no-home-dirs,include-from:,log-level:,log-file:,force-run" + long_opts+="quiet,verbose,progress,pre-hook:,post-hook:,forced-post-hook:,help" + args=$(getopt -s sh -o "$short_opts" -l "$long_opts" -n "${name}" -- "${@}") if [ $? -ne 0 ]; then exit 1 fi @@ -567,17 +627,17 @@ parse_cmd_args() { shift ;; "-i"|"--include-from") # File must exist and be readable - ! test_file_perms "r" "${2}" && echo "$name: '${2}' does not exist or is not readable!" >&2 && exit 1 + ! test_file_perms "r" "${2}" && echo "$name: '${2}' does not exist or is not readable!" >&2 && exit 1 _FORCED_INCLUSION_PATTERN_FILE=$2 _SKIP_HOME_DIRS=true shift 2 ;; "-l"|"--log-level") - LOG_LEVEL=$2; + LOG_LEVEL="$2" shift 2 ;; "--log-file") # Test if file is writeable ! test_file_perms "w" "${2}" && echo "$name: '${2}' is not writeable!" >&2 && exit 1 - _FORCED_LOG_FILE=$2 + _FORCED_LOG_FILE="$2" shift 2 ;; "-f"|"--force-run") _FORCE_RUN=true @@ -591,6 +651,15 @@ parse_cmd_args() { "-p"|"--progress") ! $_QUIET_MODE && _SHOW_PROGRESS=true shift ;; + "--pre-hook") + _PRE_HOOK="$2" + shift 2 ;; + "--post-hook") + _POST_HOOK="$2" + shift 2 ;; + "--forced-post-hook") + _FORCED_POST_HOOK="$2" + shift 2 ;; "-h"|"--help") print_help exit ;; @@ -625,6 +694,7 @@ elif $_FORCE_RUN; then write_log 4 "Backup already running as PID $(<$_RUNFILE), forcing parallel backup..." fi +run_pre_hook create_runfile # Backup exit code (0 if all backups have finished successfully) @@ -663,5 +733,8 @@ remove_runfile write_log 4 "Done." if [ $_ERROR_COUNT -gt 0 ]; then - exit 1 + clean_exit 1 fi + +run_post_hook +run_forced_post_hook