chiark / gitweb /
systemctl: prefix list-units and list-machines output with a circle indicating a...
[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 = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true;
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         in_container = detect_container(NULL) > 0;
250
251         if (streq(arg_verb, "reboot"))
252                 cmd = RB_AUTOBOOT;
253         else if (streq(arg_verb, "poweroff"))
254                 cmd = RB_POWER_OFF;
255         else if (streq(arg_verb, "halt"))
256                 cmd = RB_HALT_SYSTEM;
257         else if (streq(arg_verb, "kexec"))
258                 cmd = LINUX_REBOOT_CMD_KEXEC;
259         else {
260                 r = -EINVAL;
261                 log_error("Unknown action '%s'.", arg_verb);
262                 goto error;
263         }
264
265         cg_get_root_path(&cgroup);
266
267         use_watchdog = !!getenv("WATCHDOG_USEC");
268
269         /* lock us into memory */
270         mlockall(MCL_CURRENT|MCL_FUTURE);
271
272         log_info("Sending SIGTERM to remaining processes...");
273         broadcast_signal(SIGTERM, true, true);
274
275         log_info("Sending SIGKILL to remaining processes...");
276         broadcast_signal(SIGKILL, true, false);
277
278         if (in_container) {
279                 need_swapoff = false;
280                 need_dm_detach = false;
281                 need_loop_detach = false;
282         }
283
284         /* Unmount all mountpoints, swaps, and loopback devices */
285         for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
286                 bool changed = false;
287
288                 if (use_watchdog)
289                         watchdog_ping();
290
291                 /* Let's trim the cgroup tree on each iteration so
292                    that we leave an empty cgroup tree around, so that
293                    container managers get a nice notify event when we
294                    are down */
295                 if (cgroup)
296                         cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
297
298                 if (need_umount) {
299                         log_info("Unmounting file systems.");
300                         r = umount_all(&changed);
301                         if (r == 0) {
302                                 need_umount = false;
303                                 log_info("All filesystems unmounted.");
304                         } else if (r > 0)
305                                 log_info("Not all file systems unmounted, %d left.", r);
306                         else
307                                 log_error("Failed to unmount file systems: %s", strerror(-r));
308                 }
309
310                 if (need_swapoff) {
311                         log_info("Deactivating swaps.");
312                         r = swapoff_all(&changed);
313                         if (r == 0) {
314                                 need_swapoff = false;
315                                 log_info("All swaps deactivated.");
316                         } else if (r > 0)
317                                 log_info("Not all swaps deactivated, %d left.", r);
318                         else
319                                 log_error("Failed to deactivate swaps: %s", strerror(-r));
320                 }
321
322                 if (need_loop_detach) {
323                         log_info("Detaching loop devices.");
324                         r = loopback_detach_all(&changed);
325                         if (r == 0) {
326                                 need_loop_detach = false;
327                                 log_info("All loop devices detached.");
328                         } else if (r > 0)
329                                 log_info("Not all loop devices detached, %d left.", r);
330                         else
331                                 log_error("Failed to detach loop devices: %s", strerror(-r));
332                 }
333
334                 if (need_dm_detach) {
335                         log_info("Detaching DM devices.");
336                         r = dm_detach_all(&changed);
337                         if (r == 0) {
338                                 need_dm_detach = false;
339                                 log_info("All DM devices detached.");
340                         } else if (r > 0)
341                                 log_info("Not all DM devices detached, %d left.", r);
342                         else
343                                 log_error("Failed to detach DM devices: %s", strerror(-r));
344                 }
345
346                 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
347                         if (retries > 0)
348                                 log_info("All filesystems, swaps, loop devices, DM devices detached.");
349                         /* Yay, done */
350                         break;
351                 }
352
353                 /* If in this iteration we didn't manage to
354                  * unmount/deactivate anything, we simply give up */
355                 if (!changed) {
356                         log_error("Cannot finalize remaining file systems and devices, giving up.");
357                         break;
358                 }
359
360                 log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
361         }
362
363         if (retries >= FINALIZE_ATTEMPTS)
364                 log_error("Too many iterations, giving up.");
365         else
366                 log_info("Storage is finalized.");
367
368         arguments[0] = NULL;
369         arguments[1] = arg_verb;
370         arguments[2] = NULL;
371         execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
372
373         if (!in_container && !in_initrd() &&
374             access("/run/initramfs/shutdown", X_OK) == 0) {
375
376                 if (prepare_new_root() >= 0 &&
377                     pivot_to_new_root() >= 0) {
378                         arguments[0] = (char*) "/shutdown";
379
380                         log_info("Returning to initrd...");
381
382                         execv("/shutdown", arguments);
383                         log_error("Failed to execute shutdown binary: %m");
384                 }
385         }
386
387         /* The kernel will automaticall flush ATA disks and suchlike
388          * on reboot(), but the file systems need to be synce'd
389          * explicitly in advance. So let's do this here, but not
390          * needlessly slow down containers. */
391         if (!in_container)
392                 sync();
393
394         switch (cmd) {
395
396         case LINUX_REBOOT_CMD_KEXEC:
397
398                 if (!in_container) {
399                         /* We cheat and exec kexec to avoid doing all its work */
400                         pid_t pid;
401
402                         log_info("Rebooting with kexec.");
403
404                         pid = fork();
405                         if (pid < 0)
406                                 log_error("Failed to fork: %m");
407                         else if (pid == 0) {
408
409                                 const char * const args[] = {
410                                         KEXEC, "-e", NULL
411                                 };
412
413                                 /* Child */
414
415                                 execv(args[0], (char * const *) args);
416                                 _exit(EXIT_FAILURE);
417                         } else
418                                 wait_for_terminate_and_warn("kexec", pid);
419                 }
420
421                 cmd = RB_AUTOBOOT;
422                 /* Fall through */
423
424         case RB_AUTOBOOT:
425
426                 if (!in_container) {
427                         _cleanup_free_ char *param = NULL;
428
429                         if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
430                                 log_info("Rebooting with argument '%s'.", param);
431                                 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
432                                         LINUX_REBOOT_CMD_RESTART2, param);
433                         }
434                 }
435
436                 log_info("Rebooting.");
437                 break;
438
439         case RB_POWER_OFF:
440                 log_info("Powering off.");
441                 break;
442
443         case RB_HALT_SYSTEM:
444                 log_info("Halting system.");
445                 break;
446
447         default:
448                 assert_not_reached("Unknown magic");
449         }
450
451         reboot(cmd);
452         if (errno == EPERM && in_container) {
453                 /* If we are in a container, and we lacked
454                  * CAP_SYS_BOOT just exit, this will kill our
455                  * container for good. */
456                 log_info("Exiting container.");
457                 exit(0);
458         }
459
460         log_error("Failed to invoke reboot(): %m");
461         r = -errno;
462
463   error:
464         log_error("Critical error while doing system shutdown: %s", strerror(-r));
465
466         freeze();
467 }