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  }