github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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  }