chiark / gitweb /
e7771c968e79e848771b18771666233318e2ac39
[elogind.git] / src / core / shutdown.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 ProFUSION embedded systems
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/mman.h>
23 #include <sys/types.h>
24 #include <sys/reboot.h>
25 #include <linux/reboot.h>
26 #include <sys/wait.h>
27 #include <sys/stat.h>
28 #include <sys/mount.h>
29 #include <sys/syscall.h>
30 #include <fcntl.h>
31 #include <dirent.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <stdbool.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <getopt.h>
39
40 #include "missing.h"
41 #include "log.h"
42 #include "fileio.h"
43 #include "umount.h"
44 #include "util.h"
45 #include "mkdir.h"
46 #include "virt.h"
47 #include "watchdog.h"
48 #include "killall.h"
49 #include "cgroup-util.h"
50 #include "def.h"
51
52 #define FINALIZE_ATTEMPTS 50
53
54 static char* arg_verb;
55
56 static int parse_argv(int argc, char *argv[]) {
57         enum {
58                 ARG_LOG_LEVEL = 0x100,
59                 ARG_LOG_TARGET,
60                 ARG_LOG_COLOR,
61                 ARG_LOG_LOCATION,
62         };
63
64         static const struct option options[] = {
65                 { "log-level",     required_argument, NULL, ARG_LOG_LEVEL    },
66                 { "log-target",    required_argument, NULL, ARG_LOG_TARGET   },
67                 { "log-color",     optional_argument, NULL, ARG_LOG_COLOR    },
68                 { "log-location",  optional_argument, NULL, ARG_LOG_LOCATION },
69                 {}
70         };
71
72         int c, r;
73
74         assert(argc >= 1);
75         assert(argv);
76
77         opterr = 0;
78
79         while ((c = getopt_long(argc, argv, ":", options, NULL)) >= 0)
80                 switch (c) {
81
82                 case ARG_LOG_LEVEL:
83                         r = log_set_max_level_from_string(optarg);
84                         if (r < 0)
85                                 log_error("Failed to parse log level %s, ignoring.", optarg);
86
87                         break;
88
89                 case ARG_LOG_TARGET:
90                         r = log_set_target_from_string(optarg);
91                         if (r < 0)
92                                 log_error("Failed to parse log target %s, ignoring", optarg);
93
94                         break;
95
96                 case ARG_LOG_COLOR:
97
98                         if (optarg) {
99                                 r = log_show_color_from_string(optarg);
100                                 if (r < 0)
101                                         log_error("Failed to parse log color setting %s, ignoring", optarg);
102                         } else
103                                 log_show_color(true);
104
105                         break;
106
107                 case ARG_LOG_LOCATION:
108                         if (optarg) {
109                                 r = log_show_location_from_string(optarg);
110                                 if (r < 0)
111                                         log_error("Failed to parse log location setting %s, ignoring", optarg);
112                         } else
113                                 log_show_location(true);
114
115                         break;
116
117                 case '?':
118                         log_error("Unknown option %s.", argv[optind-1]);
119                         return -EINVAL;
120
121                 case ':':
122                         log_error("Missing argument to %s.", argv[optind-1]);
123                         return -EINVAL;
124
125                 default:
126                         assert_not_reached("Unhandled option code.");
127                 }
128
129         if (optind >= argc) {
130                 log_error("Verb argument missing.");
131                 return -EINVAL;
132         }
133
134         arg_verb = argv[optind];
135
136         if (optind + 1 < argc)
137                 log_error("Excess arguments, ignoring");
138         return 0;
139 }
140
141 static int prepare_new_root(void) {
142         static const char dirs[] =
143                 "/run/initramfs/oldroot\0"
144                 "/run/initramfs/proc\0"
145                 "/run/initramfs/sys\0"
146                 "/run/initramfs/dev\0"
147                 "/run/initramfs/run\0";
148
149         const char *dir;
150
151         if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) {
152                 log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m");
153                 return -errno;
154         }
155
156         if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) {
157                 log_error("Failed to make /run/initramfs private mount: %m");
158                 return -errno;
159         }
160
161         NULSTR_FOREACH(dir, dirs)
162                 if (mkdir_p_label(dir, 0755) < 0 && errno != EEXIST) {
163                         log_error("Failed to mkdir %s: %m", dir);
164                         return -errno;
165                 }
166
167         if (mount("/sys", "/run/initramfs/sys", NULL, MS_BIND, NULL) < 0) {
168                 log_error("Failed to mount bind /sys on /run/initramfs/sys: %m");
169                 return -errno;
170         }
171
172         if (mount("/proc", "/run/initramfs/proc", NULL, MS_BIND, NULL) < 0) {
173                 log_error("Failed to mount bind /proc on /run/initramfs/proc: %m");
174                 return -errno;
175         }
176
177         if (mount("/dev", "/run/initramfs/dev", NULL, MS_BIND, NULL) < 0) {
178                 log_error("Failed to mount bind /dev on /run/initramfs/dev: %m");
179                 return -errno;
180         }
181
182         if (mount("/run", "/run/initramfs/run", NULL, MS_BIND, NULL) < 0) {
183                 log_error("Failed to mount bind /run on /run/initramfs/run: %m");
184                 return -errno;
185         }
186
187         return 0;
188 }
189
190 static int pivot_to_new_root(void) {
191
192         if (chdir("/run/initramfs") < 0) {
193                 log_error("Failed to change directory to /run/initramfs: %m");
194                 return -errno;
195         }
196
197         /* Work-around for a kernel bug: for some reason the kernel
198          * refuses switching root if any file systems are mounted
199          * MS_SHARED. Hence remount them MS_PRIVATE here as a
200          * work-around.
201          *
202          * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
203         if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
204                 log_warning("Failed to make \"/\" private mount: %m");
205
206         if (pivot_root(".", "oldroot") < 0) {
207                 log_error("pivot failed: %m");
208                 /* only chroot if pivot root succeeded */
209                 return -errno;
210         }
211
212         chroot(".");
213
214         setsid();
215         make_console_stdio();
216
217         log_info("Successfully changed into root pivot.");
218
219         return 0;
220 }
221
222 int main(int argc, char *argv[]) {
223         bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
224         bool in_container, use_watchdog = false;
225         _cleanup_free_ char *cgroup = NULL;
226         char *arguments[3];
227         unsigned retries;
228         int cmd, r;
229
230         log_parse_environment();
231         r = parse_argv(argc, argv);
232         if (r < 0)
233                 goto error;
234
235         /* journald will die if not gone yet. The log target defaults
236          * to console, but may have been changed by commandline options. */
237
238         log_close_console(); /* force reopen of /dev/console */
239         log_open();
240
241         umask(0022);
242
243         if (getpid() != 1) {
244                 log_error("Not executed by init (PID 1).");
245                 r = -EPERM;
246                 goto error;
247         }
248
249         if (streq(arg_verb, "reboot"))
250                 cmd = RB_AUTOBOOT;
251         else if (streq(arg_verb, "poweroff"))
252                 cmd = RB_POWER_OFF;
253         else if (streq(arg_verb, "halt"))
254                 cmd = RB_HALT_SYSTEM;
255         else if (streq(arg_verb, "kexec"))
256                 cmd = LINUX_REBOOT_CMD_KEXEC;
257         else {
258                 r = -EINVAL;
259                 log_error("Unknown action '%s'.", arg_verb);
260                 goto error;
261         }
262
263         cg_get_root_path(&cgroup);
264
265         use_watchdog = !!getenv("WATCHDOG_USEC");
266
267         /* lock us into memory */
268         mlockall(MCL_CURRENT|MCL_FUTURE);
269
270         log_info("Sending SIGTERM to remaining processes...");
271         broadcast_signal(SIGTERM, true, true);
272
273         log_info("Sending SIGKILL to remaining processes...");
274         broadcast_signal(SIGKILL, true, false);
275
276         in_container = detect_container(NULL) > 0;
277
278         need_umount = true;
279         need_swapoff = !in_container;
280         need_loop_detach = !in_container;
281         need_dm_detach = !in_container;
282
283         /* Unmount all mountpoints, swaps, and loopback devices */
284         for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
285                 bool changed = false;
286
287                 if (use_watchdog)
288                         watchdog_ping();
289
290                 /* Let's trim the cgroup tree on each iteration so
291                    that we leave an empty cgroup tree around, so that
292                    container managers get a nice notify event when we
293                    are down */
294                 if (cgroup)
295                         cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
296
297                 if (need_umount) {
298                         log_info("Unmounting file systems.");
299                         r = umount_all(&changed);
300                         if (r == 0) {
301                                 need_umount = false;
302                                 log_info("All filesystems unmounted.");
303                         } else if (r > 0)
304                                 log_info("Not all file systems unmounted, %d left.", r);
305                         else
306                                 log_error("Failed to unmount file systems: %s", strerror(-r));
307                 }
308
309                 if (need_swapoff) {
310                         log_info("Deactivating swaps.");
311                         r = swapoff_all(&changed);
312                         if (r == 0) {
313                                 need_swapoff = false;
314                                 log_info("All swaps deactivated.");
315                         } else if (r > 0)
316                                 log_info("Not all swaps deactivated, %d left.", r);
317                         else
318                                 log_error("Failed to deactivate swaps: %s", strerror(-r));
319                 }
320
321                 if (need_loop_detach) {
322                         log_info("Detaching loop devices.");
323                         r = loopback_detach_all(&changed);
324                         if (r == 0) {
325                                 need_loop_detach = false;
326                                 log_info("All loop devices detached.");
327                         } else if (r > 0)
328                                 log_info("Not all loop devices detached, %d left.", r);
329                         else
330                                 log_error("Failed to detach loop devices: %s", strerror(-r));
331                 }
332
333                 if (need_dm_detach) {
334                         log_info("Detaching DM devices.");
335                         r = dm_detach_all(&changed);
336                         if (r == 0) {
337                                 need_dm_detach = false;
338                                 log_info("All DM devices detached.");
339                         } else if (r > 0)
340                                 log_info("Not all DM devices detached, %d left.", r);
341                         else
342                                 log_error("Failed to detach DM devices: %s", strerror(-r));
343                 }
344
345                 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
346                         if (retries > 0)
347                                 log_info("All filesystems, swaps, loop devices, DM devices detached.");
348                         /* Yay, done */
349                         goto initrd_jump;
350                 }
351
352                 /* If in this iteration we didn't manage to
353                  * unmount/deactivate anything, we simply give up */
354                 if (!changed) {
355                         log_info("Cannot finalize remaining%s%s%s%s continuing.",
356                                  need_umount ? " file systems," : "",
357                                  need_swapoff ? " swap devices," : "",
358                                  need_loop_detach ? " loop devices," : "",
359                                  need_dm_detach ? " DM devices," : "");
360                         goto initrd_jump;
361                 }
362
363                 log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.",
364                           retries + 1,
365                           need_umount ? " file systems," : "",
366                           need_swapoff ? " swap devices," : "",
367                           need_loop_detach ? " loop devices," : "",
368                           need_dm_detach ? " DM devices," : "");
369         }
370
371         log_error("Too many iterations, giving up.");
372
373  initrd_jump:
374
375         arguments[0] = NULL;
376         arguments[1] = arg_verb;
377         arguments[2] = NULL;
378         execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments, NULL);
379
380         if (!in_container && !in_initrd() &&
381             access("/run/initramfs/shutdown", X_OK) == 0) {
382
383                 if (prepare_new_root() >= 0 &&
384                     pivot_to_new_root() >= 0) {
385                         arguments[0] = (char*) "/shutdown";
386
387                         log_info("Returning to initrd...");
388
389                         execv("/shutdown", arguments);
390                         log_error("Failed to execute shutdown binary: %m");
391                 }
392         }
393
394         if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
395                 log_error("Failed to finalize %s%s%s%s ignoring",
396                           need_umount ? " file systems," : "",
397                           need_swapoff ? " swap devices," : "",
398                           need_loop_detach ? " loop devices," : "",
399                           need_dm_detach ? " DM devices," : "");
400
401         /* The kernel will automaticall flush ATA disks and suchlike
402          * on reboot(), but the file systems need to be synce'd
403          * explicitly in advance. So let's do this here, but not
404          * needlessly slow down containers. */
405         if (!in_container)
406                 sync();
407
408         switch (cmd) {
409
410         case LINUX_REBOOT_CMD_KEXEC:
411
412                 if (!in_container) {
413                         /* We cheat and exec kexec to avoid doing all its work */
414                         pid_t pid;
415
416                         log_info("Rebooting with kexec.");
417
418                         pid = fork();
419                         if (pid < 0)
420                                 log_error("Failed to fork: %m");
421                         else if (pid == 0) {
422
423                                 const char * const args[] = {
424                                         KEXEC, "-e", NULL
425                                 };
426
427                                 /* Child */
428
429                                 execv(args[0], (char * const *) args);
430                                 _exit(EXIT_FAILURE);
431                         } else
432                                 wait_for_terminate_and_warn("kexec", pid);
433                 }
434
435                 cmd = RB_AUTOBOOT;
436                 /* Fall through */
437
438         case RB_AUTOBOOT:
439
440                 if (!in_container) {
441                         _cleanup_free_ char *param = NULL;
442
443                         if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
444                                 log_info("Rebooting with argument '%s'.", param);
445                                 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
446                                         LINUX_REBOOT_CMD_RESTART2, param);
447                         }
448                 }
449
450                 log_info("Rebooting.");
451                 break;
452
453         case RB_POWER_OFF:
454                 log_info("Powering off.");
455                 break;
456
457         case RB_HALT_SYSTEM:
458                 log_info("Halting system.");
459                 break;
460
461         default:
462                 assert_not_reached("Unknown magic");
463         }
464
465         reboot(cmd);
466         if (errno == EPERM && in_container) {
467                 /* If we are in a container, and we lacked
468                  * CAP_SYS_BOOT just exit, this will kill our
469                  * container for good. */
470                 log_info("Exiting container.");
471                 exit(0);
472         }
473
474         log_error("Failed to invoke reboot(): %m");
475         r = -errno;
476
477   error:
478         log_error("Critical error while doing system shutdown: %s", strerror(-r));
479
480         freeze();
481 }