Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : : /***
3 : : Copyright © 2010 ProFUSION embedded systems
4 : : ***/
5 : :
6 : : #include <errno.h>
7 : : #include <getopt.h>
8 : : #include <linux/reboot.h>
9 : : #include <signal.h>
10 : : #include <stdbool.h>
11 : : #include <stdlib.h>
12 : : #include <sys/mman.h>
13 : : #include <sys/mount.h>
14 : : #include <sys/reboot.h>
15 : : #include <sys/stat.h>
16 : : #include <unistd.h>
17 : :
18 : : #include "alloc-util.h"
19 : : #include "async.h"
20 : : #include "cgroup-util.h"
21 : : #include "def.h"
22 : : #include "exec-util.h"
23 : : #include "fd-util.h"
24 : : #include "fileio.h"
25 : : #include "killall.h"
26 : : #include "log.h"
27 : : #include "missing.h"
28 : : #include "parse-util.h"
29 : : #include "process-util.h"
30 : : #include "reboot-util.h"
31 : : #include "rlimit-util.h"
32 : : #include "signal-util.h"
33 : : #include "string-util.h"
34 : : #include "switch-root.h"
35 : : #include "sysctl-util.h"
36 : : #include "terminal-util.h"
37 : : #include "umount.h"
38 : : #include "util.h"
39 : : #include "virt.h"
40 : : #include "watchdog.h"
41 : :
42 : : #define SYNC_PROGRESS_ATTEMPTS 3
43 : : #define SYNC_TIMEOUT_USEC (10*USEC_PER_SEC)
44 : :
45 : : static char* arg_verb;
46 : : static uint8_t arg_exit_code;
47 : : static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
48 : :
49 : 0 : static int parse_argv(int argc, char *argv[]) {
50 : : enum {
51 : : ARG_LOG_LEVEL = 0x100,
52 : : ARG_LOG_TARGET,
53 : : ARG_LOG_COLOR,
54 : : ARG_LOG_LOCATION,
55 : : ARG_EXIT_CODE,
56 : : ARG_TIMEOUT,
57 : : };
58 : :
59 : : static const struct option options[] = {
60 : : { "log-level", required_argument, NULL, ARG_LOG_LEVEL },
61 : : { "log-target", required_argument, NULL, ARG_LOG_TARGET },
62 : : { "log-color", optional_argument, NULL, ARG_LOG_COLOR },
63 : : { "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
64 : : { "exit-code", required_argument, NULL, ARG_EXIT_CODE },
65 : : { "timeout", required_argument, NULL, ARG_TIMEOUT },
66 : : {}
67 : : };
68 : :
69 : : int c, r;
70 : :
71 [ # # ]: 0 : assert(argc >= 1);
72 [ # # ]: 0 : assert(argv);
73 : :
74 : : /* "-" prevents getopt from permuting argv[] and moving the verb away
75 : : * from argv[1]. Our interface to initrd promises it'll be there. */
76 [ # # ]: 0 : while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0)
77 [ # # # # : 0 : switch (c) {
# # # #
# ]
78 : :
79 : 0 : case ARG_LOG_LEVEL:
80 : 0 : r = log_set_max_level_from_string(optarg);
81 [ # # ]: 0 : if (r < 0)
82 [ # # ]: 0 : log_error_errno(r, "Failed to parse log level %s, ignoring: %m", optarg);
83 : :
84 : 0 : break;
85 : :
86 : 0 : case ARG_LOG_TARGET:
87 : 0 : r = log_set_target_from_string(optarg);
88 [ # # ]: 0 : if (r < 0)
89 [ # # ]: 0 : log_error_errno(r, "Failed to parse log target %s, ignoring: %m", optarg);
90 : :
91 : 0 : break;
92 : :
93 : 0 : case ARG_LOG_COLOR:
94 : :
95 [ # # ]: 0 : if (optarg) {
96 : 0 : r = log_show_color_from_string(optarg);
97 [ # # ]: 0 : if (r < 0)
98 [ # # ]: 0 : log_error_errno(r, "Failed to parse log color setting %s, ignoring: %m", optarg);
99 : : } else
100 : 0 : log_show_color(true);
101 : :
102 : 0 : break;
103 : :
104 : 0 : case ARG_LOG_LOCATION:
105 [ # # ]: 0 : if (optarg) {
106 : 0 : r = log_show_location_from_string(optarg);
107 [ # # ]: 0 : if (r < 0)
108 [ # # ]: 0 : log_error_errno(r, "Failed to parse log location setting %s, ignoring: %m", optarg);
109 : : } else
110 : 0 : log_show_location(true);
111 : :
112 : 0 : break;
113 : :
114 : 0 : case ARG_EXIT_CODE:
115 : 0 : r = safe_atou8(optarg, &arg_exit_code);
116 [ # # ]: 0 : if (r < 0)
117 [ # # ]: 0 : log_error_errno(r, "Failed to parse exit code %s, ignoring: %m", optarg);
118 : :
119 : 0 : break;
120 : :
121 : 0 : case ARG_TIMEOUT:
122 : 0 : r = parse_sec(optarg, &arg_timeout);
123 [ # # ]: 0 : if (r < 0)
124 [ # # ]: 0 : log_error_errno(r, "Failed to parse shutdown timeout %s, ignoring: %m", optarg);
125 : :
126 : 0 : break;
127 : :
128 : 0 : case '\001':
129 [ # # ]: 0 : if (!arg_verb)
130 : 0 : arg_verb = optarg;
131 : : else
132 [ # # ]: 0 : log_error("Excess arguments, ignoring");
133 : 0 : break;
134 : :
135 : 0 : case '?':
136 : 0 : return -EINVAL;
137 : :
138 : 0 : default:
139 : 0 : assert_not_reached("Unhandled option code.");
140 : : }
141 : :
142 [ # # ]: 0 : if (!arg_verb)
143 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
144 : : "Verb argument missing.");
145 : :
146 : 0 : return 0;
147 : : }
148 : :
149 : 0 : static int switch_root_initramfs(void) {
150 [ # # ]: 0 : if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0)
151 [ # # ]: 0 : return log_error_errno(errno, "Failed to mount bind /run/initramfs on /run/initramfs: %m");
152 : :
153 [ # # ]: 0 : if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0)
154 [ # # ]: 0 : return log_error_errno(errno, "Failed to make /run/initramfs private mount: %m");
155 : :
156 : : /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file descriptors.
157 : : * /run/initramfs/shutdown will take care of these.
158 : : * Also do not detach the old root, because /run/initramfs/shutdown needs to access it.
159 : : */
160 : 0 : return switch_root("/run/initramfs", "/oldroot", false, MS_BIND);
161 : : }
162 : :
163 : : /* Read the following fields from /proc/meminfo:
164 : : *
165 : : * NFS_Unstable
166 : : * Writeback
167 : : * Dirty
168 : : *
169 : : * Return true if the sum of these fields is greater than the previous
170 : : * value input. For all other issues, report the failure and indicate that
171 : : * the sync is not making progress.
172 : : */
173 : 0 : static int sync_making_progress(unsigned long long *prev_dirty) {
174 : 0 : _cleanup_fclose_ FILE *f = NULL;
175 : 0 : unsigned long long val = 0;
176 : : int ret;
177 : :
178 : 0 : f = fopen("/proc/meminfo", "re");
179 [ # # ]: 0 : if (!f)
180 [ # # ]: 0 : return log_warning_errno(errno, "Failed to open /proc/meminfo: %m");
181 : :
182 : 0 : for (;;) {
183 [ # # # # ]: 0 : _cleanup_free_ char *line = NULL;
184 : 0 : unsigned long long ull = 0;
185 : : int q;
186 : :
187 : 0 : q = read_line(f, LONG_LINE_MAX, &line);
188 [ # # ]: 0 : if (q < 0)
189 [ # # ]: 0 : return log_warning_errno(q, "Failed to parse /proc/meminfo: %m");
190 [ # # ]: 0 : if (q == 0)
191 : 0 : break;
192 : :
193 [ # # # # : 0 : if (!first_word(line, "NFS_Unstable:") && !first_word(line, "Writeback:") && !first_word(line, "Dirty:"))
# # ]
194 : 0 : continue;
195 : :
196 : 0 : errno = 0;
197 [ # # ]: 0 : if (sscanf(line, "%*s %llu %*s", &ull) != 1) {
198 [ # # ]: 0 : if (errno != 0)
199 [ # # ]: 0 : log_warning_errno(errno, "Failed to parse /proc/meminfo: %m");
200 : : else
201 [ # # ]: 0 : log_warning("Failed to parse /proc/meminfo");
202 : :
203 : 0 : return false;
204 : : }
205 : :
206 : 0 : val += ull;
207 : : }
208 : :
209 : 0 : ret = *prev_dirty > val;
210 : 0 : *prev_dirty = val;
211 : 0 : return ret;
212 : : }
213 : :
214 : 0 : static void sync_with_progress(void) {
215 : 0 : unsigned long long dirty = ULLONG_MAX;
216 : : unsigned checks;
217 : : pid_t pid;
218 : : int r;
219 : :
220 [ # # # # ]: 0 : BLOCK_SIGNALS(SIGCHLD);
221 : :
222 : : /* Due to the possibility of the sync operation hanging, we fork a child process and monitor the progress. If
223 : : * the timeout lapses, the assumption is that that particular sync stalled. */
224 : :
225 : 0 : r = asynchronous_sync(&pid);
226 [ # # ]: 0 : if (r < 0) {
227 [ # # ]: 0 : log_error_errno(r, "Failed to fork sync(): %m");
228 : 0 : return;
229 : : }
230 : :
231 [ # # ]: 0 : log_info("Syncing filesystems and block devices.");
232 : :
233 : : /* Start monitoring the sync operation. If more than
234 : : * SYNC_PROGRESS_ATTEMPTS lapse without progress being made,
235 : : * we assume that the sync is stalled */
236 [ # # ]: 0 : for (checks = 0; checks < SYNC_PROGRESS_ATTEMPTS; checks++) {
237 : 0 : r = wait_for_terminate_with_timeout(pid, SYNC_TIMEOUT_USEC);
238 [ # # ]: 0 : if (r == 0)
239 : : /* Sync finished without error.
240 : : * (The sync itself does not return an error code) */
241 : 0 : return;
242 [ # # ]: 0 : else if (r == -ETIMEDOUT) {
243 : : /* Reset the check counter if the "Dirty" value is
244 : : * decreasing */
245 [ # # ]: 0 : if (sync_making_progress(&dirty) > 0)
246 : 0 : checks = 0;
247 : : } else {
248 [ # # ]: 0 : log_error_errno(r, "Failed to sync filesystems and block devices: %m");
249 : 0 : return;
250 : : }
251 : : }
252 : :
253 : : /* Only reached in the event of a timeout. We should issue a kill
254 : : * to the stray process. */
255 [ # # ]: 0 : log_error("Syncing filesystems and block devices - timed out, issuing SIGKILL to PID "PID_FMT".", pid);
256 : 0 : (void) kill(pid, SIGKILL);
257 : : }
258 : :
259 : 0 : static int read_current_sysctl_printk_log_level(void) {
260 : 0 : _cleanup_free_ char *sysctl_printk_vals = NULL, *sysctl_printk_curr = NULL;
261 : : int current_lvl;
262 : : const char *p;
263 : : int r;
264 : :
265 : 0 : r = sysctl_read("kernel/printk", &sysctl_printk_vals);
266 [ # # ]: 0 : if (r < 0)
267 [ # # ]: 0 : return log_debug_errno(r, "Cannot read sysctl kernel.printk: %m");
268 : :
269 : 0 : p = sysctl_printk_vals;
270 : 0 : r = extract_first_word(&p, &sysctl_printk_curr, NULL, 0);
271 [ # # ]: 0 : if (r < 0)
272 [ # # ]: 0 : return log_debug_errno(r, "Failed to split out kernel printk priority: %m");
273 [ # # ]: 0 : if (r == 0)
274 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Short read while reading kernel.printk sysctl");
275 : :
276 : 0 : r = safe_atoi(sysctl_printk_curr, ¤t_lvl);
277 [ # # ]: 0 : if (r < 0)
278 [ # # ]: 0 : return log_debug_errno(r, "Failed to parse kernel.printk sysctl: %s", sysctl_printk_vals);
279 : :
280 : 0 : return current_lvl;
281 : : }
282 : :
283 : 0 : static void bump_sysctl_printk_log_level(int min_level) {
284 : : int current_lvl, r;
285 : :
286 : : /* Set the logging level to be able to see messages with log level smaller or equal to min_level */
287 : :
288 : 0 : current_lvl = read_current_sysctl_printk_log_level();
289 [ # # # # ]: 0 : if (current_lvl < 0 || current_lvl >= min_level + 1)
290 : 0 : return;
291 : :
292 : 0 : r = sysctl_writef("kernel/printk", "%i", min_level + 1);
293 [ # # ]: 0 : if (r < 0)
294 [ # # ]: 0 : log_debug_errno(r, "Failed to bump kernel.printk to %i: %m", min_level + 1);
295 : : }
296 : :
297 : 0 : int main(int argc, char *argv[]) {
298 : 0 : bool need_umount, need_swapoff, need_loop_detach, need_dm_detach, in_container, use_watchdog = false, can_initrd;
299 : 0 : _cleanup_free_ char *cgroup = NULL;
300 : : char *arguments[3], *watchdog_device;
301 : 0 : int cmd, r, umount_log_level = LOG_INFO;
302 : : static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};
303 : :
304 : : /* The log target defaults to console, but the original systemd process will pass its log target in through a
305 : : * command line argument, which will override this default. Also, ensure we'll never log to the journal or
306 : : * syslog, as these logging daemons are either already dead or will die very soon. */
307 : :
308 : 0 : log_set_target(LOG_TARGET_CONSOLE);
309 : 0 : log_set_prohibit_ipc(true);
310 : 0 : log_parse_environment();
311 : :
312 : 0 : r = parse_argv(argc, argv);
313 [ # # ]: 0 : if (r < 0)
314 : 0 : goto error;
315 : :
316 : 0 : log_open();
317 : :
318 : 0 : umask(0022);
319 : :
320 [ # # ]: 0 : if (getpid_cached() != 1) {
321 [ # # ]: 0 : log_error("Not executed by init (PID 1).");
322 : 0 : r = -EPERM;
323 : 0 : goto error;
324 : : }
325 : :
326 [ # # ]: 0 : if (streq(arg_verb, "reboot"))
327 : 0 : cmd = RB_AUTOBOOT;
328 [ # # ]: 0 : else if (streq(arg_verb, "poweroff"))
329 : 0 : cmd = RB_POWER_OFF;
330 [ # # ]: 0 : else if (streq(arg_verb, "halt"))
331 : 0 : cmd = RB_HALT_SYSTEM;
332 [ # # ]: 0 : else if (streq(arg_verb, "kexec"))
333 : 0 : cmd = LINUX_REBOOT_CMD_KEXEC;
334 [ # # ]: 0 : else if (streq(arg_verb, "exit"))
335 : 0 : cmd = 0; /* ignored, just checking that arg_verb is valid */
336 : : else {
337 [ # # ]: 0 : log_error("Unknown action '%s'.", arg_verb);
338 : 0 : r = -EINVAL;
339 : 0 : goto error;
340 : : }
341 : :
342 : 0 : (void) cg_get_root_path(&cgroup);
343 : 0 : in_container = detect_container() > 0;
344 : :
345 : : /* If the logging messages are going to KMSG, and if we are not running from a container, then try to
346 : : * update the sysctl kernel.printk current value in order to see "info" messages; This current log
347 : : * level is not updated if already big enough.
348 : : */
349 [ # # ]: 0 : if (!in_container &&
350 [ # # # # ]: 0 : IN_SET(log_get_target(),
351 : : LOG_TARGET_AUTO,
352 : : LOG_TARGET_JOURNAL_OR_KMSG,
353 : : LOG_TARGET_SYSLOG_OR_KMSG,
354 : : LOG_TARGET_KMSG))
355 : 0 : bump_sysctl_printk_log_level(LOG_WARNING);
356 : :
357 : 0 : use_watchdog = getenv("WATCHDOG_USEC");
358 : 0 : watchdog_device = getenv("WATCHDOG_DEVICE");
359 [ # # ]: 0 : if (watchdog_device) {
360 : 0 : r = watchdog_set_device(watchdog_device);
361 [ # # ]: 0 : if (r < 0)
362 [ # # ]: 0 : log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m",
363 : : watchdog_device);
364 : : }
365 : :
366 : : /* Lock us into memory */
367 : 0 : (void) mlockall(MCL_CURRENT|MCL_FUTURE);
368 : :
369 : : /* Synchronize everything that is not written to disk yet at this point already. This is a good idea so that
370 : : * slow IO is processed here already and the final process killing spree is not impacted by processes
371 : : * desperately trying to sync IO to disk within their timeout. Do not remove this sync, data corruption will
372 : : * result. */
373 [ # # ]: 0 : if (!in_container)
374 : 0 : sync_with_progress();
375 : :
376 : 0 : disable_coredumps();
377 : :
378 [ # # ]: 0 : log_info("Sending SIGTERM to remaining processes...");
379 : 0 : broadcast_signal(SIGTERM, true, true, arg_timeout);
380 : :
381 [ # # ]: 0 : log_info("Sending SIGKILL to remaining processes...");
382 : 0 : broadcast_signal(SIGKILL, true, false, arg_timeout);
383 : :
384 : 0 : need_umount = !in_container;
385 : 0 : need_swapoff = !in_container;
386 : 0 : need_loop_detach = !in_container;
387 : 0 : need_dm_detach = !in_container;
388 [ # # # # : 0 : can_initrd = !in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0;
# # ]
389 : :
390 : : /* Unmount all mountpoints, swaps, and loopback devices */
391 : 0 : for (;;) {
392 : 0 : bool changed = false;
393 : :
394 [ # # ]: 0 : if (use_watchdog)
395 : 0 : (void) watchdog_ping();
396 : :
397 : : /* Let's trim the cgroup tree on each iteration so
398 : : that we leave an empty cgroup tree around, so that
399 : : container managers get a nice notify event when we
400 : : are down */
401 [ # # ]: 0 : if (cgroup)
402 : 0 : (void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
403 : :
404 [ # # ]: 0 : if (need_umount) {
405 [ # # ]: 0 : log_info("Unmounting file systems.");
406 : 0 : r = umount_all(&changed, umount_log_level);
407 [ # # ]: 0 : if (r == 0) {
408 : 0 : need_umount = false;
409 [ # # ]: 0 : log_info("All filesystems unmounted.");
410 [ # # ]: 0 : } else if (r > 0)
411 [ # # ]: 0 : log_info("Not all file systems unmounted, %d left.", r);
412 : : else
413 [ # # ]: 0 : log_error_errno(r, "Failed to unmount file systems: %m");
414 : : }
415 : :
416 [ # # ]: 0 : if (need_swapoff) {
417 [ # # ]: 0 : log_info("Deactivating swaps.");
418 : 0 : r = swapoff_all(&changed);
419 [ # # ]: 0 : if (r == 0) {
420 : 0 : need_swapoff = false;
421 [ # # ]: 0 : log_info("All swaps deactivated.");
422 [ # # ]: 0 : } else if (r > 0)
423 [ # # ]: 0 : log_info("Not all swaps deactivated, %d left.", r);
424 : : else
425 [ # # ]: 0 : log_error_errno(r, "Failed to deactivate swaps: %m");
426 : : }
427 : :
428 [ # # ]: 0 : if (need_loop_detach) {
429 [ # # ]: 0 : log_info("Detaching loop devices.");
430 : 0 : r = loopback_detach_all(&changed, umount_log_level);
431 [ # # ]: 0 : if (r == 0) {
432 : 0 : need_loop_detach = false;
433 [ # # ]: 0 : log_info("All loop devices detached.");
434 [ # # ]: 0 : } else if (r > 0)
435 [ # # ]: 0 : log_info("Not all loop devices detached, %d left.", r);
436 : : else
437 [ # # ]: 0 : log_error_errno(r, "Failed to detach loop devices: %m");
438 : : }
439 : :
440 [ # # ]: 0 : if (need_dm_detach) {
441 [ # # ]: 0 : log_info("Detaching DM devices.");
442 : 0 : r = dm_detach_all(&changed, umount_log_level);
443 [ # # ]: 0 : if (r == 0) {
444 : 0 : need_dm_detach = false;
445 [ # # ]: 0 : log_info("All DM devices detached.");
446 [ # # ]: 0 : } else if (r > 0)
447 [ # # ]: 0 : log_info("Not all DM devices detached, %d left.", r);
448 : : else
449 [ # # ]: 0 : log_error_errno(r, "Failed to detach DM devices: %m");
450 : : }
451 : :
452 [ # # # # : 0 : if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
# # # # ]
453 [ # # ]: 0 : log_info("All filesystems, swaps, loop devices and DM devices detached.");
454 : : /* Yay, done */
455 : 0 : break;
456 : : }
457 : :
458 [ # # # # : 0 : if (!changed && umount_log_level == LOG_INFO && !can_initrd) {
# # ]
459 : : /* There are things we cannot get rid of. Loop one more time
460 : : * with LOG_ERR to inform the user. Note that we don't need
461 : : * to do this if there is a initrd to switch to, because that
462 : : * one is likely to get rid of the remounting mounts. If not,
463 : : * it will log about them. */
464 : 0 : umount_log_level = LOG_ERR;
465 : 0 : continue;
466 : : }
467 : :
468 : : /* If in this iteration we didn't manage to
469 : : * unmount/deactivate anything, we simply give up */
470 [ # # ]: 0 : if (!changed) {
471 [ # # # # : 0 : log_info("Cannot finalize remaining%s%s%s%s continuing.",
# # # # #
# ]
472 : : need_umount ? " file systems," : "",
473 : : need_swapoff ? " swap devices," : "",
474 : : need_loop_detach ? " loop devices," : "",
475 : : need_dm_detach ? " DM devices," : "");
476 : 0 : break;
477 : : }
478 : :
479 [ # # # # : 0 : log_debug("Couldn't finalize remaining %s%s%s%s trying again.",
# # # # #
# ]
480 : : need_umount ? " file systems," : "",
481 : : need_swapoff ? " swap devices," : "",
482 : : need_loop_detach ? " loop devices," : "",
483 : : need_dm_detach ? " DM devices," : "");
484 : : }
485 : :
486 : : /* We're done with the watchdog. */
487 : 0 : watchdog_free_device();
488 : :
489 : 0 : arguments[0] = NULL;
490 : 0 : arguments[1] = arg_verb;
491 : 0 : arguments[2] = NULL;
492 : 0 : (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
493 : :
494 : 0 : (void) rlimit_nofile_safe();
495 : :
496 [ # # ]: 0 : if (can_initrd) {
497 : 0 : r = switch_root_initramfs();
498 [ # # ]: 0 : if (r >= 0) {
499 : 0 : argv[0] = (char*) "/shutdown";
500 : :
501 : 0 : (void) setsid();
502 : 0 : (void) make_console_stdio();
503 : :
504 [ # # ]: 0 : log_info("Successfully changed into root pivot.\n"
505 : : "Returning to initrd...");
506 : :
507 : 0 : execv("/shutdown", argv);
508 [ # # ]: 0 : log_error_errno(errno, "Failed to execute shutdown binary: %m");
509 : : } else
510 [ # # ]: 0 : log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m");
511 : : }
512 : :
513 [ # # # # : 0 : if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
# # # # ]
514 [ # # # # : 0 : log_error("Failed to finalize %s%s%s%s ignoring",
# # # # #
# ]
515 : : need_umount ? " file systems," : "",
516 : : need_swapoff ? " swap devices," : "",
517 : : need_loop_detach ? " loop devices," : "",
518 : : need_dm_detach ? " DM devices," : "");
519 : :
520 : : /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
521 : : * sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we
522 : : * sync'ed things already once above, but we did some more work since then which might have caused IO, hence
523 : : * let's do it once more. Do not remove this sync, data corruption will result. */
524 [ # # ]: 0 : if (!in_container)
525 : 0 : sync_with_progress();
526 : :
527 [ # # ]: 0 : if (streq(arg_verb, "exit")) {
528 [ # # ]: 0 : if (in_container)
529 : 0 : return arg_exit_code;
530 : :
531 : 0 : cmd = RB_POWER_OFF; /* We cannot exit() on the host, fallback on another method. */
532 : : }
533 : :
534 [ # # # # : 0 : switch (cmd) {
# ]
535 : :
536 : 0 : case LINUX_REBOOT_CMD_KEXEC:
537 : :
538 [ # # ]: 0 : if (!in_container) {
539 : : /* We cheat and exec kexec to avoid doing all its work */
540 [ # # ]: 0 : log_info("Rebooting with kexec.");
541 : :
542 : 0 : r = safe_fork("(sd-kexec)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL);
543 [ # # ]: 0 : if (r == 0) {
544 : 0 : const char * const args[] = {
545 : : KEXEC, "-e", NULL
546 : : };
547 : :
548 : : /* Child */
549 : :
550 : 0 : execv(args[0], (char * const *) args);
551 : 0 : _exit(EXIT_FAILURE);
552 : : }
553 : :
554 : : /* If we are still running, then the kexec can't have worked, let's fall through */
555 : : }
556 : :
557 : 0 : cmd = RB_AUTOBOOT;
558 : : _fallthrough_;
559 : :
560 : 0 : case RB_AUTOBOOT:
561 : 0 : (void) reboot_with_parameter(REBOOT_LOG);
562 [ # # ]: 0 : log_info("Rebooting.");
563 : 0 : break;
564 : :
565 : 0 : case RB_POWER_OFF:
566 [ # # ]: 0 : log_info("Powering off.");
567 : 0 : break;
568 : :
569 : 0 : case RB_HALT_SYSTEM:
570 [ # # ]: 0 : log_info("Halting system.");
571 : 0 : break;
572 : :
573 : 0 : default:
574 : 0 : assert_not_reached("Unknown magic");
575 : : }
576 : :
577 : 0 : (void) reboot(cmd);
578 [ # # # # ]: 0 : if (errno == EPERM && in_container) {
579 : : /* If we are in a container, and we lacked
580 : : * CAP_SYS_BOOT just exit, this will kill our
581 : : * container for good. */
582 [ # # ]: 0 : log_info("Exiting container.");
583 : 0 : return EXIT_SUCCESS;
584 : : }
585 : :
586 [ # # ]: 0 : r = log_error_errno(errno, "Failed to invoke reboot(): %m");
587 : :
588 : 0 : error:
589 [ # # ]: 0 : log_emergency_errno(r, "Critical error while doing system shutdown: %m");
590 : 0 : freeze();
591 : : }
|