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