github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/cmd/system-shutdown/system-shutdown.c (about) 1 /* 2 * Copyright (C) 2016-2017 Canonical Ltd 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 3 as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 * 16 */ 17 18 #include <stdbool.h> // bools 19 #include <stdarg.h> // va_* 20 #include <sys/mount.h> // umount 21 #include <sys/stat.h> // mkdir 22 #include <unistd.h> // getpid, close 23 #include <stdlib.h> // exit 24 #include <stdio.h> // fprintf, stderr 25 #include <string.h> // strerror 26 #include <sys/ioctl.h> // ioctl 27 #include <linux/loop.h> // LOOP_CLR_FD 28 #include <sys/reboot.h> // reboot, RB_* 29 #include <fcntl.h> // open 30 #include <errno.h> // errno, sys_errlist 31 #include <linux/reboot.h> // LINUX_REBOOT_MAGIC* 32 #include <sys/syscall.h> // SYS_reboot 33 34 #include "system-shutdown-utils.h" 35 #include "../libsnap-confine-private/panic.h" 36 #include "../libsnap-confine-private/string-utils.h" 37 #include "../libsnap-confine-private/utils.h" 38 39 static void show_error(const char *fmt, va_list ap, int errno_copy) 40 { 41 fprintf(stderr, "snapd system-shutdown helper: "); 42 fprintf(stderr, "*** "); 43 vfprintf(stderr, fmt, ap); 44 if (errno_copy != 0) { 45 fprintf(stderr, ": %s", strerror(errno_copy)); 46 } 47 fprintf(stderr, "\n"); 48 } 49 50 static void sync_and_halt(void) 51 { 52 sync(); 53 reboot(RB_HALT_SYSTEM); 54 } 55 56 int main(int argc, char *argv[]) 57 { 58 sc_set_panic_msg_fn(show_error); 59 sc_set_panic_exit_fn(sync_and_halt); 60 61 // 256 should be more than enough... 62 char reboot_arg[256] = { 0 }; 63 64 errno = 0; 65 if (getpid() != 1) { 66 fprintf(stderr, 67 "This is a shutdown helper program; don't call it directly.\n"); 68 exit(1); 69 } 70 71 kmsg("started."); 72 73 /* 74 This program is started by systemd exec'ing the "shutdown" binary 75 inside what used to be /run/initramfs. That is: the system's 76 /run/initramfs is now /, and the old / is now /oldroot. Our job is 77 to disentagle /oldroot and /oldroot/writable, which contain each 78 other in the "live" system. We do this by creating a new /writable 79 and moving the old mount there, previous to which we need to unmount 80 as much as we can. Having done that we should be able to detach the 81 oldroot loop device and finally unmount writable itself. 82 */ 83 84 /* 85 There are two¹ ways out of this program: we die, which calls sync 86 before halting the system; or we umount everything successfully 87 before doing whatever we were told to do, in which case there's 88 nothing left to sync. 89 90 1) ... apart from the third way that we never talk about: we somehow 91 are unable to umount everything cleanly, but go ahead with the 92 reboot anyway because no error was returned. That's the only path 93 we need to sync on explicitly. 94 */ 95 96 if (mkdir("/writable", 0755) < 0) { 97 die("cannot create directory /writable"); 98 } 99 // We are reading a file from /run and need to do this before unmounting 100 if (sc_read_reboot_arg(reboot_arg, sizeof reboot_arg) < 0) { 101 kmsg("no reboot parameter"); 102 } 103 104 if (umount_all()) { 105 kmsg("- found no hard-to-unmount writable partition."); 106 } else { 107 if (mount("/oldroot/writable", "/writable", NULL, MS_MOVE, NULL) 108 < 0) { 109 die("cannot move writable out of the way"); 110 } 111 112 if (umount_all()) { 113 kmsg("- was able to unmount writable cleanly"); 114 } else { 115 kmsg("* was *NOT* able to unmount writable cleanly"); 116 sync(); // we don't know what happened but we're going ahead 117 } 118 } 119 120 // argv[1] can be one of at least: halt, reboot, poweroff. 121 // FIXME: might also be kexec, hibernate or hybrid-sleep -- support those! 122 123 int cmd = RB_HALT_SYSTEM; 124 125 if (argc < 2) { 126 kmsg("* called without verb; halting."); 127 } else { 128 if (sc_streq("reboot", argv[1])) { 129 cmd = RB_AUTOBOOT; 130 kmsg("- rebooting."); 131 } else if (sc_streq("poweroff", argv[1])) { 132 cmd = RB_POWER_OFF; 133 kmsg("- powering off."); 134 } else if (sc_streq("halt", argv[1])) { 135 kmsg("- halting."); 136 } else { 137 kmsg("* called with unsupported verb %s; halting.", 138 argv[1]); 139 } 140 } 141 142 // glibc reboot wrapper does not expose the optional reboot syscall 143 // parameter 144 145 long ret; 146 if (cmd == RB_AUTOBOOT && reboot_arg[0] != '\0') { 147 ret = syscall(SYS_reboot, 148 LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, 149 LINUX_REBOOT_CMD_RESTART2, reboot_arg); 150 } else { 151 ret = reboot(cmd); 152 } 153 154 if (ret == -1) { 155 kmsg("cannot reboot the system: %s", strerror(errno)); 156 } 157 158 return 0; 159 }