File: shutdown.wrapper

package info (click to toggle)
runit 2.2.0-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,568 kB
  • sloc: ansic: 6,071; sh: 3,419; makefile: 399
file content (112 lines) | stat: -rwxr-xr-x 4,105 bytes parent folder | download
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