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