]> git.proxmox.com Git - systemd.git/blame - src/core/shutdown.c
Imported Upstream version 219
[systemd.git] / src / core / shutdown.c
CommitLineData
663996b3
MS
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>
663996b3
MS
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>
60f067b4 38#include <getopt.h>
663996b3
MS
39
40#include "missing.h"
41#include "log.h"
14228c0d 42#include "fileio.h"
663996b3
MS
43#include "umount.h"
44#include "util.h"
45#include "mkdir.h"
46#include "virt.h"
47#include "watchdog.h"
48#include "killall.h"
60f067b4
JS
49#include "cgroup-util.h"
50#include "def.h"
5eef597e 51#include "switch-root.h"
e735f4d4 52#include "strv.h"
663996b3
MS
53
54#define FINALIZE_ATTEMPTS 50
55
60f067b4
JS
56static char* arg_verb;
57
58static 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
f47781d8
MP
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)
60f067b4
JS
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
f47781d8
MP
119 case '\001':
120 if (!arg_verb)
121 arg_verb = optarg;
122 else
123 log_error("Excess arguments, ignoring");
124 break;
125
60f067b4 126 case '?':
60f067b4
JS
127 return -EINVAL;
128
129 default:
130 assert_not_reached("Unhandled option code.");
131 }
132
f47781d8 133 if (!arg_verb) {
60f067b4
JS
134 log_error("Verb argument missing.");
135 return -EINVAL;
136 }
137
60f067b4
JS
138 return 0;
139}
140
5eef597e 141static int switch_root_initramfs(void) {
f47781d8
MP
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");
663996b3 144
f47781d8
MP
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");
663996b3 147
e735f4d4 148 /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file descriptors.
5eef597e
MP
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);
663996b3
MS
153}
154
663996b3
MS
155
156int main(int argc, char *argv[]) {
e842803a 157 bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
663996b3 158 bool in_container, use_watchdog = false;
60f067b4 159 _cleanup_free_ char *cgroup = NULL;
663996b3 160 char *arguments[3];
60f067b4
JS
161 unsigned retries;
162 int cmd, r;
e735f4d4 163 static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};
663996b3 164
60f067b4
JS
165 log_parse_environment();
166 r = parse_argv(argc, argv);
167 if (r < 0)
168 goto error;
14228c0d 169
60f067b4 170 /* journald will die if not gone yet. The log target defaults
f47781d8 171 * to console, but may have been changed by command line options. */
14228c0d 172
60f067b4 173 log_close_console(); /* force reopen of /dev/console */
663996b3
MS
174 log_open();
175
176 umask(0022);
177
178 if (getpid() != 1) {
60f067b4 179 log_error("Not executed by init (PID 1).");
663996b3
MS
180 r = -EPERM;
181 goto error;
182 }
183
60f067b4 184 if (streq(arg_verb, "reboot"))
663996b3 185 cmd = RB_AUTOBOOT;
60f067b4 186 else if (streq(arg_verb, "poweroff"))
663996b3 187 cmd = RB_POWER_OFF;
60f067b4 188 else if (streq(arg_verb, "halt"))
663996b3 189 cmd = RB_HALT_SYSTEM;
60f067b4 190 else if (streq(arg_verb, "kexec"))
663996b3
MS
191 cmd = LINUX_REBOOT_CMD_KEXEC;
192 else {
663996b3 193 r = -EINVAL;
60f067b4 194 log_error("Unknown action '%s'.", arg_verb);
663996b3
MS
195 goto error;
196 }
197
60f067b4
JS
198 cg_get_root_path(&cgroup);
199
663996b3
MS
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...");
60f067b4 206 broadcast_signal(SIGTERM, true, true);
663996b3
MS
207
208 log_info("Sending SIGKILL to remaining processes...");
60f067b4 209 broadcast_signal(SIGKILL, true, false);
663996b3 210
e842803a
MB
211 in_container = detect_container(NULL) > 0;
212
5eef597e 213 need_umount = !in_container;
e842803a
MB
214 need_swapoff = !in_container;
215 need_loop_detach = !in_container;
216 need_dm_detach = !in_container;
663996b3
MS
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
60f067b4
JS
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
663996b3
MS
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
f47781d8 241 log_error_errno(r, "Failed to unmount file systems: %m");
663996b3
MS
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
f47781d8 253 log_error_errno(r, "Failed to deactivate swaps: %m");
663996b3
MS
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
f47781d8 265 log_error_errno(r, "Failed to detach loop devices: %m");
663996b3
MS
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
f47781d8 277 log_error_errno(r, "Failed to detach DM devices: %m");
663996b3
MS
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 */
e842803a 284 goto initrd_jump;
663996b3
MS
285 }
286
287 /* If in this iteration we didn't manage to
288 * unmount/deactivate anything, we simply give up */
289 if (!changed) {
e842803a
MB
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;
663996b3
MS
296 }
297
e842803a
MB
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," : "");
663996b3
MS
304 }
305
e842803a
MB
306 log_error("Too many iterations, giving up.");
307
308 initrd_jump:
663996b3
MS
309
310 arguments[0] = NULL;
60f067b4 311 arguments[1] = arg_verb;
663996b3 312 arguments[2] = NULL;
e735f4d4 313 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
663996b3
MS
314
315 if (!in_container && !in_initrd() &&
316 access("/run/initramfs/shutdown", X_OK) == 0) {
5eef597e
MP
317 r = switch_root_initramfs();
318 if (r >= 0) {
319 argv[0] = (char*) "/shutdown";
663996b3 320
5eef597e
MP
321 setsid();
322 make_console_stdio();
663996b3 323
5eef597e
MP
324 log_info("Successfully changed into root pivot.\n"
325 "Returning to initrd...");
663996b3 326
5eef597e 327 execv("/shutdown", argv);
f47781d8 328 log_error_errno(errno, "Failed to execute shutdown binary: %m");
5eef597e 329 } else
f47781d8 330 log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m");
5eef597e 331
663996b3
MS
332 }
333
e842803a
MB
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
663996b3
MS
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
60f067b4
JS
348 switch (cmd) {
349
350 case LINUX_REBOOT_CMD_KEXEC:
663996b3
MS
351
352 if (!in_container) {
353 /* We cheat and exec kexec to avoid doing all its work */
60f067b4 354 pid_t pid;
663996b3 355
60f067b4
JS
356 log_info("Rebooting with kexec.");
357
358 pid = fork();
663996b3 359 if (pid < 0)
f47781d8 360 log_error_errno(errno, "Failed to fork: %m");
60f067b4
JS
361 else if (pid == 0) {
362
363 const char * const args[] = {
364 KEXEC, "-e", NULL
365 };
366
663996b3 367 /* Child */
60f067b4 368
663996b3 369 execv(args[0], (char * const *) args);
60f067b4
JS
370 _exit(EXIT_FAILURE);
371 } else
f47781d8 372 wait_for_terminate_and_warn("kexec", pid, true);
663996b3
MS
373 }
374
375 cmd = RB_AUTOBOOT;
60f067b4
JS
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);
5eef597e 385 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
60f067b4
JS
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");
663996b3
MS
402 }
403
404 reboot(cmd);
663996b3
MS
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. */
60f067b4 409 log_info("Exiting container.");
663996b3
MS
410 exit(0);
411 }
412
f47781d8 413 log_error_errno(errno, "Failed to invoke reboot(): %m");
663996b3
MS
414 r = -errno;
415
416 error:
f47781d8 417 log_emergency_errno(r, "Critical error while doing system shutdown: %m");
663996b3
MS
418
419 freeze();
663996b3 420}