1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
#!/bin/sh
#
# This is a wrapper around the /usr/lib/runit/shutdown that's shipped by the
# Debian runit package. Its purpose is to add support for reboot times
# other than "now", by way of scheduling the future reboot as an at(1) job;
# but while I'm at it I might as well add support for all the other systemd
# shutdown options as well.
#
# Copyright (c) 2025 AndrĂ¡s Korn.
#
# Licensed under the GPL v3, or, at your option, and only if it's included
# in the runit package, under the BSD-3-clause license.
real_shutdown=/usr/lib/runit/shutdown.distrib
rundir=/run/runit/scheduled-shutdown
[ $# -eq 0 ] && exec "$real_shutdown"
wallmessage=""
timespec=""
wallonly=0
wall=1
shutdown_args=""
cancel_shutdown() {
if cd "$rundir" 2>/dev/null; then
j=$(echo *)
if ! [ "$j" = "*" ]; then
atrm $j
ret=$?
rm $j
cd / # currently superfluous as nothing that is run after calling cancel_shutdown is sensitive to PWD, but it's good hygiene
return $ret
else
return 0
fi
else
return 0
fi
}
show_shutdown() {
if cd "$rundir" 2>/dev/null; then
j=$(echo *)
if ! [ "$j" = "*" ]; then
timespec=$(cat $j | sort -t: -n -k1,1 -k2,2 | head -1)
echo "shutdown or reboot scheduled for $timespec, use 'shutdown -c' to cancel."
fi
fi
exit 0
}
while [ -n "$1" ]; do
case "$1" in
-[hrfFHP]) shutdown_args="$shutdown_args $1";;
now) shift; [ $# -gt 0 ] && wallmessage="${*}"; break;;
-k) wallonly=1;;
--no-wall) wall=0;;
-c) cancel_shutdown; exit $?;;
--show) show_shutdown;; # doesn't return
+[0-9]*|[012][0-9]:[0-5][0-9]) timespec="$1"; shift; [ $# -gt 0 ] && wallmessage="${*}"; break;;
*) echo "$0: WARNING: argument '$1' is unknown. Ignoring";; # Ignore unknown arguments. Reasoning: it's better to be able to shut down without supporting some bell or whistle than to prevent shutdown.
esac
shift
done
case "$timespec" in
"") [ -n "$wallmessage" ] && [ "$wall" = 1 ] && wall "The system is going down NOW for: $wallmessage";;
+[0-9]*) timespec="$(date --date "now $timespec minutes" '+%H:%M')";; # needs GNU date(1)
esac
if [ -n "$timespec" ]; then
if [ -x /usr/bin/at ]; then
# possible improvements: 1. include kind of shutdown in wall message (reboot or halt); 2. schedule some wall messages a few minutes before the actual shutdown.
cd / # make sure the pwd of the atjob is /, not some directory on e.g. a network fs
if atoutput=$({
if [ "$wall" = 1 ]; then
if [ -n "$wallmessage" ]; then
echo "wall 'The system is going down NOW for: $wallmessage'"
else
echo "wall 'The system is going down NOW!'"
fi
[ "$wallonly" = 0 ] && echo "exec \"$real_shutdown\" $shutdown_args"
fi
} | at "$timespec" 2>&1); then
jobnum=$(echo "$atoutput" | sed -n '/^job /{s/^job //;s/ .*//;p}')
case "$jobnum" in # Is $jobnum an integer?
''|*[!0-9]*)
echo "$0: WARNING: I tried to schedule an at(1) job for $timespec to perform a 'shutdown $shutdown_args', and the job number is apparently '$jobnum', which is not an integer. I don't know what happened, or whether the shutdown will take place at the scheduled time. Please investigate." >&2;;
*)
mkdir -p "$rundir"
cancel_shutdown # if there was a shutdown scheduled, cancel it -- we just scheduled a new one
echo "$timespec" >"$rundir/$jobnum" # this is needed by show_shutdown and cancel_shutdown
;;
esac
else
echo "$0: WARNING: I tried to schedule an at(1) job for $timespec to perform a 'shutdown $shutdown_args', and at(1) returned an error. The system will probably not shut down at the scheduled time. For your reference, here is the entire output of at(1):" >&2
echo "$atoutput" >&2
exit 2
fi
else
echo "FATAL: can't schedule shutdown for $timespec because at(1) is apparently not available (/usr/bin/at can't be executed)." >&2
exit 1
fi
else
if [ -n "$wallmessage" ] && [ "$wall" = 1 ]; then
echo "wall 'The system is going down NOW for: $wallmessage'"
else
echo "wall 'The system is going down NOW!'"
fi
[ "$wallonly" = 0 ] && exec "$real_shutdown" $shutdown_args
fi
exit 0
|