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