github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/cmd/system-shutdown/system-shutdown-utils.c (about) 1 /* 2 * Copyright (C) 2016 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 "system-shutdown-utils.h" 19 20 #include <errno.h> // errno, sys_errlist 21 #include <fcntl.h> // open 22 #include <linux/loop.h> // LOOP_CLR_FD 23 #include <linux/major.h> 24 #include <stdarg.h> // va_* 25 #include <stdio.h> // fprintf, stderr 26 #include <stdlib.h> // exit 27 #include <string.h> // strcmp, strncmp 28 #include <sys/ioctl.h> // ioctl 29 #include <sys/mount.h> // umount 30 #include <sys/reboot.h> // reboot, RB_* 31 #include <sys/stat.h> // mkdir 32 #include <unistd.h> // getpid, close 33 34 #include "../libsnap-confine-private/mountinfo.h" 35 #include "../libsnap-confine-private/string-utils.h" 36 #include "../libsnap-confine-private/utils.h" 37 38 __attribute__((format(printf, 1, 2))) 39 void kmsg(const char *fmt, ...) 40 { 41 static FILE *kmsg = NULL; 42 static char *head = NULL; 43 if (!kmsg) { 44 // TODO: figure out why writing to /dev/kmsg doesn't work from here 45 kmsg = stderr; 46 head = "snapd system-shutdown helper: "; 47 } 48 49 va_list va; 50 va_start(va, fmt); 51 fputs(head, kmsg); 52 vfprintf(kmsg, fmt, va); 53 fprintf(kmsg, "\n"); 54 va_end(va); 55 } 56 57 int sc_read_reboot_arg(char *arg, size_t max_size) 58 { 59 FILE *f; 60 61 // This file is used by systemd to pass around a reboot parameter See 62 // https://github.com/systemd/systemd/blob/v229/src/basic/def.h#L44 63 f = fopen("/run/systemd/reboot-param", "r"); 64 if (!f) { 65 return -1; 66 } 67 68 if (!fgets(arg, max_size, f)) { 69 fclose(f); 70 return -1; 71 } 72 arg[strcspn(arg, "\n")] = '\0'; 73 74 kmsg("reboot arg is %s", arg); 75 fclose(f); 76 return 0; 77 } 78 79 static void detach_loop(const char *src) 80 { 81 int fd = open(src, O_RDONLY); 82 if (fd < 0) { 83 kmsg("* unable to open loop device %s: %s", src, 84 strerror(errno)); 85 } else { 86 if (ioctl(fd, LOOP_CLR_FD) < 0) { 87 kmsg("* unable to disassociate loop device %s: %s", 88 src, strerror(errno)); 89 } 90 close(fd); 91 } 92 } 93 94 // tries to umount all (well, most) things. Returns whether in the last pass it 95 // no longer found writable. 96 bool umount_all(void) 97 { 98 bool did_umount = true; 99 bool had_writable = false; 100 101 for (int i = 0; i < 10 && did_umount; i++) { 102 sc_mountinfo *mounts = sc_parse_mountinfo(NULL); 103 if (!mounts) { 104 // oh dear 105 die("unable to get mount info; giving up"); 106 } 107 sc_mountinfo_entry *cur = sc_first_mountinfo_entry(mounts); 108 109 had_writable = false; 110 did_umount = false; 111 while (cur) { 112 const char *dir = cur->mount_dir; 113 const char *src = cur->mount_source; 114 unsigned major = cur->dev_major; 115 116 cur = sc_next_mountinfo_entry(cur); 117 118 if (sc_streq("/", dir)) { 119 continue; 120 } 121 122 if (sc_streq("/dev", dir)) { 123 continue; 124 } 125 126 if (sc_streq("/proc", dir)) { 127 continue; 128 } 129 130 if (major != 0 && major != LOOP_MAJOR 131 && sc_endswith(dir, "/writable")) { 132 had_writable = true; 133 } 134 135 if (umount(dir) == 0) { 136 if (major == LOOP_MAJOR) { 137 detach_loop(src); 138 } 139 140 did_umount = true; 141 } 142 } 143 sc_cleanup_mountinfo(&mounts); 144 } 145 146 return !had_writable; 147 }