152 lines
3.8 KiB
Bash
Executable file
152 lines
3.8 KiB
Bash
Executable file
#!/bin/sh
|
|
#
|
|
# shutdown -- wrapper script to guard against accidental shutdowns
|
|
#
|
|
# Copyright © martin f. krafft <madduck@madduck.net>
|
|
# Released under the terms of the Artistic Licence 2.0
|
|
#
|
|
set -eu
|
|
|
|
ME=molly-guard
|
|
VERSION=0.8
|
|
|
|
SCRIPTSDIR="@cfgdir@/run.d"
|
|
|
|
CMD="${0##*/}"
|
|
|
|
case "$CMD" in
|
|
halt|reboot|shutdown|poweroff|coldreboot|pm-hibernate|pm-suspend|pm-suspend-hybrid)
|
|
if ! EXEC=$(command -v "$CMD.no-molly-guard"); then
|
|
if ! EXEC=$(command -v "$CMD.no-molly-guard.usr-is-merged"); then
|
|
echo "E: not a regular file: $EXEC" >&2
|
|
exit 4
|
|
fi
|
|
fi
|
|
if [ "$EXEC" -ef /usr/lib/molly-guard/molly-guard ]; then
|
|
# Symlink forwards to ourselves. Resolve!
|
|
LINKTARGET=$(readlink "$EXEC")
|
|
if ! EXEC=$(command -v "$LINKTARGET.no-molly-guard"); then
|
|
if ! EXEC=$(command -v "$LINKTARGET.no-molly-guard.usr-is-merged"); then
|
|
echo "E: not a regular file $EXEC" >&2
|
|
exit 4
|
|
fi
|
|
fi
|
|
fi
|
|
if [ ! -x $EXEC ]; then
|
|
echo "E: not an executable: $EXEC" >&2
|
|
exit 3
|
|
fi
|
|
;;
|
|
*)
|
|
echo "E: unsupported command: $CMD" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
usage()
|
|
{
|
|
cat <<-_eousage
|
|
Usage: $ME [options] [-- script options]
|
|
(shielding $EXEC)
|
|
|
|
molly-guard's primary goal is to guard against accidental
|
|
shutdowns/reboots. $ME will run all scripts in $SCRIPTSDIR and only
|
|
invokes $EXEC if all scripts exited successfully.
|
|
|
|
Specifying --molly-guard-do-nothing as argument to the command will
|
|
make $ME echo the command it would execute rather than actually
|
|
executing it.
|
|
|
|
Options following the double hyphen will be passed unchanged to the
|
|
scripts.
|
|
|
|
Please see molly-guard(8) for more information.
|
|
|
|
The actual command's help output follows:
|
|
|
|
_eousage
|
|
}
|
|
|
|
CMDARGS=
|
|
SCRIPTARGS=
|
|
END_OF_ARGS=0
|
|
DO_NOTHING=0
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
(*-molly-guard-do-nothing) DO_NOTHING=1;;
|
|
(*-help)
|
|
usage 2>&1
|
|
( exec bash -c 'exec -a "$0" "$@"' "$CMD" "$EXEC" --help 2>&1 )
|
|
exit 0
|
|
;;
|
|
--) END_OF_ARGS=1;;
|
|
*\"*)
|
|
echo 'E: cannot use double-quotes (") in arguments' >&2
|
|
exit 1
|
|
;;
|
|
*)
|
|
if [ $END_OF_ARGS -eq 0 ]; then
|
|
CMDARGS="${CMDARGS:+$CMDARGS }\"$arg\""
|
|
else
|
|
SCRIPTARGS="${SCRIPTARGS:+$SCRIPTARGS }--arg \"$arg\""
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
do_real_cmd()
|
|
{
|
|
if [ $DO_NOTHING -eq 1 ]; then
|
|
echo "$ME: would run: $EXEC $CMDARGS"
|
|
exit 0
|
|
else
|
|
eval exec bash -c "'"'exec -a "$0" "$@"'"'" "'$CMD'" "'$EXEC'" "$CMDARGS"
|
|
fi
|
|
}
|
|
|
|
if [ $DO_NOTHING -eq 1 ]; then
|
|
echo "I: demo mode; $ME will not do anything due to --molly-guard-do-nothing." >&2
|
|
fi
|
|
|
|
if [ -n "${MOLLYGUARD_CMD:-}" ]; then
|
|
do_real_cmd
|
|
fi
|
|
|
|
MOLLYGUARD_CMD=$CMD; export MOLLYGUARD_CMD
|
|
MOLLYGUARD_DO_NOTHING=$DO_NOTHING; export MOLLYGUARD_DO_NOTHING
|
|
MOLLYGUARD_SETTINGS="@cfgdir@/rc"; export MOLLYGUARD_SETTINGS
|
|
|
|
# pass through certain commands
|
|
case "$CMD $CMDARGS" in
|
|
(*shutdown\ *-c*|*halt\ *-w*|*halt\ *-f*|*reboot\ *-f*)
|
|
# allow canceling shutdowns, only write wtmp and force immediate halt
|
|
echo "I: executing $CMD $CMDARGS regardless of check results." >&2
|
|
do_real_cmd
|
|
;;
|
|
esac
|
|
|
|
# Check for execution from configuration management tools that might use an
|
|
# interactive shell.
|
|
CONFIGURATION_MANAGEMENT=0
|
|
|
|
[ -f "$MOLLYGUARD_SETTINGS" ] && . "$MOLLYGUARD_SETTINGS"
|
|
|
|
# Ansible uses an interactive shell, but by default puts Ansible in the
|
|
# broadcast message, we can look for that. Allow for it be changed per site.
|
|
ANSIBLE_SEARCH_STRING="${ANSIBLE_SEARCH_STRING:-ansible}"
|
|
if echo $CMDARGS | grep -qi $ANSIBLE_SEARCH_STRING; then
|
|
CONFIGURATION_MANAGEMENT=1
|
|
fi
|
|
|
|
export CONFIGURATION_MANAGEMENT
|
|
|
|
for script in $(run-parts --test $SCRIPTSDIR); do
|
|
ret=0
|
|
eval $script $SCRIPTARGS || ret=$?
|
|
if [ $ret -ne 0 ]; then
|
|
echo "W: aborting $CMD due to ${script##*/} exiting with code $ret." >&2
|
|
exit $ret
|
|
fi
|
|
done
|
|
|
|
do_real_cmd
|