chiark / gitweb /
20cf526ba2716b34fe1a2fc9bde05a396b160502
[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 #include "switch-root.h"
52
53 #define FINALIZE_ATTEMPTS 50
54
55 static char* arg_verb;
56
57 static int parse_argv(int argc, char *argv[]) {
58         enum {
59                 ARG_LOG_LEVEL = 0x100,
60                 ARG_LOG_TARGET,
61                 ARG_LOG_COLOR,
62                 ARG_LOG_LOCATION,
63         };
64
65         static const struct option options[] = {
66                 { "log-level",     required_argument, NULL, ARG_LOG_LEVEL    },
67                 { "log-target",    required_argument, NULL, ARG_LOG_TARGET   },
68                 { "log-color",     optional_argument, NULL, ARG_LOG_COLOR    },
69                 { "log-location",  optional_argument, NULL, ARG_LOG_LOCATION },
70                 {}
71         };
72
73         int c, r;
74
75         assert(argc >= 1);
76         assert(argv);
77
78         while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
79                 switch (c) {
80
81                 case ARG_LOG_LEVEL:
82                         r = log_set_max_level_from_string(optarg);
83                         if (r < 0)
84                                 log_error("Failed to parse log level %s, ignoring.", optarg);
85
86                         break;
87
88                 case ARG_LOG_TARGET:
89                         r = log_set_target_from_string(optarg);
90                         if (r < 0)
91                                 log_error("Failed to parse log target %s, ignoring", optarg);
92
93                         break;
94
95                 case ARG_LOG_COLOR:
96
97                         if (optarg) {
98                                 r = log_show_color_from_string(optarg);
99                                 if (r < 0)
100                                         log_error("Failed to parse log color setting %s, ignoring", optarg);
101                         } else
102                                 log_show_color(true);
103
104                         break;
105
106                 case ARG_LOG_LOCATION:
107                         if (optarg) {
108                                 r = log_show_location_from_string(optarg);
109                                 if (r < 0)
110                                         log_error("Failed to parse log location setting %s, ignoring", optarg);
111                         } else
112                                 log_show_location(true);
113
114                         break;
115
116                 case '?':
117                         return -EINVAL;
118
119                 default:
120                         assert_not_reached("Unhandled option code.");
121                 }
122
123         if (optind >= argc) {
124                 log_error("Verb argument missing.");
125                 return -EINVAL;
126         }
127
128         arg_verb = argv[optind];
129
130         if (optind + 1 < argc)
131                 log_error("Excess arguments, ignoring");
132         return 0;
133 }
134
135 static int switch_root_initramfs(void) {
136         if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) {
137                 log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m");
138                 return -errno;
139         }
140
141         if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) {
142                 log_error("Failed to make /run/initramfs private mount: %m");
143                 return -errno;
144         }
145
146         /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file desriptors.
147          * /run/initramfs/shutdown will take care of these.
148          * Also do not detach the old root, because /run/initramfs/shutdown needs to access it.
149          */
150         return switch_root("/run/initramfs", "/oldroot", false, MS_BIND);
151 }
152
153
154 int main(int argc, char *argv[]) {
155         bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
156         bool in_container, use_watchdog = false;
157         _cleanup_free_ char *cgroup = NULL;
158         char *arguments[3];
159         unsigned retries;
160         int cmd, r;
161
162         log_parse_environment();
163         r = parse_argv(argc, argv);
164         if (r < 0)
165                 goto error;
166
167         /* journald will die if not gone yet. The log target defaults
168          * to console, but may have been changed by commandline options. */
169
170         log_close_console(); /* force reopen of /dev/console */
171         log_open();
172
173         umask(0022);
174
175         if (getpid() != 1) {
176                 log_error("Not executed by init (PID 1).");
177                 r = -EPERM;
178                 goto error;
179         }
180
181         if (streq(arg_verb, "reboot"))
182                 cmd = RB_AUTOBOOT;
183         else if (streq(arg_verb, "poweroff"))
184                 cmd = RB_POWER_OFF;
185         else if (streq(arg_verb, "halt"))
186                 cmd = RB_HALT_SYSTEM;
187         else if (streq(arg_verb, "kexec"))
188                 cmd = LINUX_REBOOT_CMD_KEXEC;
189         else {
190                 r = -EINVAL;
191                 log_error("Unknown action '%s'.", arg_verb);
192                 goto error;
193         }
194
195         cg_get_root_path(&cgroup);
196
197         use_watchdog = !!getenv("WATCHDOG_USEC");
198
199         /* lock us into memory */
200         mlockall(MCL_CURRENT|MCL_FUTURE);
201
202         log_info("Sending SIGTERM to remaining processes...");
203         broadcast_signal(SIGTERM, true, true);
204
205         log_info("Sending SIGKILL to remaining processes...");
206         broadcast_signal(SIGKILL, true, false);
207
208         in_container = detect_container(NULL) > 0;
209
210         need_umount = !in_container;
211         need_swapoff = !in_container;
212         need_loop_detach = !in_container;
213         need_dm_detach = !in_container;
214
215         /* Unmount all mountpoints, swaps, and loopback devices */
216         for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
217                 bool changed = false;
218
219                 if (use_watchdog)
220                         watchdog_ping();
221
222                 /* Let's trim the cgroup tree on each iteration so
223                    that we leave an empty cgroup tree around, so that
224                    container managers get a nice notify event when we
225                    are down */
226                 if (cgroup)
227                         cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
228
229                 if (need_umount) {
230                         log_info("Unmounting file systems.");
231                         r = umount_all(&changed);
232                         if (r == 0) {
233                                 need_umount = false;
234                                 log_info("All filesystems unmounted.");
235                         } else if (r > 0)
236                                 log_info("Not all file systems unmounted, %d left.", r);
237                         else
238                                 log_error("Failed to unmount file systems: %s", strerror(-r));
239                 }
240
241                 if (need_swapoff) {
242                         log_info("Deactivating swaps.");
243                         r = swapoff_all(&changed);
244                         if (r == 0) {
245                                 need_swapoff = false;
246                                 log_info("All swaps deactivated.");
247                         } else if (r > 0)
248                                 log_info("Not all swaps deactivated, %d left.", r);
249                         else
250                                 log_error("Failed to deactivate swaps: %s", strerror(-r));
251                 }
252
253                 if (need_loop_detach) {
254                         log_info("Detaching loop devices.");
255                         r = loopback_detach_all(&changed);
256                         if (r == 0) {
257                                 need_loop_detach = false;
258                                 log_info("All loop devices detached.");
259                         } else if (r > 0)
260                                 log_info("Not all loop devices detached, %d left.", r);
261                         else
262                                 log_error("Failed to detach loop devices: %s", strerror(-r));
263                 }
264
265                 if (need_dm_detach) {
266                         log_info("Detaching DM devices.");
267                         r = dm_detach_all(&changed);
268                         if (r == 0) {
269                                 need_dm_detach = false;
270                                 log_info("All DM devices detached.");
271                         } else if (r > 0)
272                                 log_info("Not all DM devices detached, %d left.", r);
273                         else
274                                 log_error("Failed to detach DM devices: %s", strerror(-r));
275                 }
276
277                 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
278                         if (retries > 0)
279                                 log_info("All filesystems, swaps, loop devices, DM devices detached.");
280                         /* Yay, done */
281                         goto initrd_jump;
282                 }
283
284                 /* If in this iteration we didn't manage to
285                  * unmount/deactivate anything, we simply give up */
286                 if (!changed) {
287                         log_info("Cannot finalize remaining%s%s%s%s continuing.",
288                                  need_umount ? " file systems," : "",
289                                  need_swapoff ? " swap devices," : "",
290                                  need_loop_detach ? " loop devices," : "",
291                                  need_dm_detach ? " DM devices," : "");
292                         goto initrd_jump;
293                 }
294
295                 log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.",
296                           retries + 1,
297                           need_umount ? " file systems," : "",
298                           need_swapoff ? " swap devices," : "",
299                           need_loop_detach ? " loop devices," : "",
300                           need_dm_detach ? " DM devices," : "");
301         }
302
303         log_error("Too many iterations, giving up.");
304
305  initrd_jump:
306
307         arguments[0] = NULL;
308         arguments[1] = arg_verb;
309         arguments[2] = NULL;
310         execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
311
312         if (!in_container && !in_initrd() &&
313             access("/run/initramfs/shutdown", X_OK) == 0) {
314                 r = switch_root_initramfs();
315                 if (r >= 0) {
316                         argv[0] = (char*) "/shutdown";
317
318                         setsid();
319                         make_console_stdio();
320
321                         log_info("Successfully changed into root pivot.\n"
322                                  "Returning to initrd...");
323
324                         execv("/shutdown", argv);
325                         log_error("Failed to execute shutdown binary: %m");
326                 } else
327                         log_error("Failed to switch root to \"/run/initramfs\": %s", strerror(-r));
328
329         }
330
331         if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
332                 log_error("Failed to finalize %s%s%s%s ignoring",
333                           need_umount ? " file systems," : "",
334                           need_swapoff ? " swap devices," : "",
335                           need_loop_detach ? " loop devices," : "",
336                           need_dm_detach ? " DM devices," : "");
337
338         /* The kernel will automaticall flush ATA disks and suchlike
339          * on reboot(), but the file systems need to be synce'd
340          * explicitly in advance. So let's do this here, but not
341          * needlessly slow down containers. */
342         if (!in_container)
343                 sync();
344
345         switch (cmd) {
346
347         case LINUX_REBOOT_CMD_KEXEC:
348
349                 if (!in_container) {
350                         /* We cheat and exec kexec to avoid doing all its work */
351                         pid_t pid;
352
353                         log_info("Rebooting with kexec.");
354
355                         pid = fork();
356                         if (pid < 0)
357                                 log_error("Failed to fork: %m");
358                         else if (pid == 0) {
359
360                                 const char * const args[] = {
361                                         KEXEC, "-e", NULL
362                                 };
363
364                                 /* Child */
365
366                                 execv(args[0], (char * const *) args);
367                                 _exit(EXIT_FAILURE);
368                         } else
369                                 wait_for_terminate_and_warn("kexec", pid);
370                 }
371
372                 cmd = RB_AUTOBOOT;
373                 /* Fall through */
374
375         case RB_AUTOBOOT:
376
377                 if (!in_container) {
378                         _cleanup_free_ char *param = NULL;
379
380                         if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
381                                 log_info("Rebooting with argument '%s'.", param);
382                                 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
383                         }
384                 }
385
386                 log_info("Rebooting.");
387                 break;
388
389         case RB_POWER_OFF:
390                 log_info("Powering off.");
391                 break;
392
393         case RB_HALT_SYSTEM:
394                 log_info("Halting system.");
395                 break;
396
397         default:
398                 assert_not_reached("Unknown magic");
399         }
400
401         reboot(cmd);
402         if (errno == EPERM && in_container) {
403                 /* If we are in a container, and we lacked
404                  * CAP_SYS_BOOT just exit, this will kill our
405                  * container for good. */
406                 log_info("Exiting container.");
407                 exit(0);
408         }
409
410         log_error("Failed to invoke reboot(): %m");
411         r = -errno;
412
413   error:
414         log_error("Critical error while doing system shutdown: %s", strerror(-r));
415
416         freeze();
417 }