diff options
Diffstat (limited to 'modules/system/upstart-events/shutdown.nix')
-rw-r--r-- | modules/system/upstart-events/shutdown.nix | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/modules/system/upstart-events/shutdown.nix b/modules/system/upstart-events/shutdown.nix new file mode 100644 index 000000000000..59fbcc0d878b --- /dev/null +++ b/modules/system/upstart-events/shutdown.nix @@ -0,0 +1,162 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +{ + + jobs.shutdown = + { name = "shutdown"; + + task = true; + + stopOn = ""; # must override the default ("starting shutdown") + + environment = { MODE = "poweroff"; }; + + extraConfig = "console owner"; + + script = + '' + set +e # continue in case of errors + + ${pkgs.kbd}/bin/chvt 1 + + exec < /dev/console > /dev/console 2>&1 + echo "" + if test "$MODE" = maintenance; then + echo "[1;32m<<< Entering maintenance mode >>>[0m" + else + echo "[1;32m<<< System shutdown >>>[0m" + fi + echo "" + + ${config.powerManagement.powerDownCommands} + + export PATH=${pkgs.utillinux}/bin:${pkgs.utillinux}/sbin:$PATH + + + # Do an initial sync just in case. + sync + + + # Kill all remaining processes except init, this one and any + # Upstart jobs that don't stop on the "starting shutdown" + # event, as these are necessary to complete the shutdown. + omittedPids=$(initctl list | sed -e 's/.*process \([0-9]\+\)/-o \1/;t;d') + #echo "saved PIDs: $omittedPids" + + echo "sending the TERM signal to all processes..." + ${pkgs.sysvtools}/bin/killall5 -15 $job $omittedPids + + sleep 1 # wait briefly + + echo "sending the KILL signal to all processes..." + ${pkgs.sysvtools}/bin/killall5 -9 $job $omittedPids + + + # If maintenance mode is requested, start a root shell, and + # afterwards emit the "startup" event to bring everything + # back up. + if test "$MODE" = maintenance; then + echo "" + echo "[1;32m<<< Maintenance shell >>>[0m" + echo "" + ${pkgs.shadow}/bin/login root + initctl emit -n startup + exit 0 + fi + + + # Write a shutdown record to wtmp while /var/log is still writable. + reboot --wtmp-only + + + # Set the hardware clock to the system time. + echo "setting the hardware clock..." + hwclock --systohc ${if config.time.hardwareClockInLocalTime then "--localtime" else "--utc"} + + + # Stop all swap devices. + swapoff -a + + + # Unmount file systems. We repeat this until no more file systems + # can be unmounted. This is to handle loopback devices, file + # systems mounted on other file systems and so on. + tryAgain=1 + while test -n "$tryAgain"; do + tryAgain= + failed= # list of mount points that couldn't be unmounted/remounted + + # Get rid of loopback devices. + loDevices=$(losetup -a | sed 's#^\(/dev/loop[0-9]\+\).*#\1#') + if [ -n "$loDevices" ]; then + echo "removing loopback devices $loDevices..." + losetup -d $loDevices + fi + + cp /proc/mounts /dev/.mounts # don't read /proc/mounts while it's changing + exec 4< /dev/.mounts + while read -u 4 device mp fstype options rest; do + # Skip various special filesystems. Non-existent + # mount points are typically tmpfs/aufs mounts from + # the initrd. + if [ "$mp" = /proc -o "$mp" = /sys -o "$mp" = /dev -o "$device" = "rootfs" -o "$mp" = /run -o "$mp" = /var/run -o "$mp" = /var/lock -o ! -e "$mp" ]; then continue; fi + + echo "unmounting $mp..." + + # We need to remount,ro before attempting any + # umount, or bind mounts may get confused, with + # the fs not being properly flushed at the end. + + # `-i' is to workaround a bug in mount.cifs (it + # doesn't recognise the `remount' option, and + # instead mounts the FS again). + success= + if mount -t "$fstype" -n -i -o remount,ro "device" "$mp"; then success=1; fi + + # Note: don't use `umount -f'; it's very buggy. + # (For instance, when applied to a bind-mount it + # unmounts the target of the bind-mount.) !!! But + # we should use `-f' for NFS. + if [ "$mp" != / -a "$mp" != /nix -a "$mp" != /nix/store ]; then + if umount -n "$mp"; then success=1; tryAgain=1; fi + fi + + if [ -z "$success" ]; then failed="$failed $mp"; fi + done + done + + + # Warn about filesystems that could not be unmounted or + # remounted read-only. + if [ -n "$failed" ]; then + echo "[1;31mwarning:[0m the following filesystems could not be unmounted:" + for mp in $failed; do echo " $mp"; done + echo Enter 'i' to launch a shell, or wait 10 seconds to continue. + read -t 10 A + if [ "$A" == "i" ]; then + ${pkgs.bashInteractive}/bin/bash -i < /dev/console &> /dev/console + fi + sleep 5 + fi + + + # Final sync. + sync + + + # Either reboot or power-off the system. + if test "$MODE" = reboot; then + echo "rebooting..." + sleep 1 + exec reboot -f + else + echo "powering off..." + sleep 1 + exec halt -f -p + fi + ''; + }; + +} |