]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/shutdown.c
Support additional argument in reboot
[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/types.h>
28 #include <sys/stat.h>
29 #include <sys/mount.h>
30 #include <sys/syscall.h>
31 #include <fcntl.h>
32 #include <dirent.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <stdbool.h>
37 #include <stdlib.h>
38 #include <string.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 int prepare_new_root(void) {
55 static const char dirs[] =
56 "/run/initramfs/oldroot\0"
57 "/run/initramfs/proc\0"
58 "/run/initramfs/sys\0"
59 "/run/initramfs/dev\0"
60 "/run/initramfs/run\0";
61
62 const char *dir;
63
64 if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) {
65 log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m");
66 return -errno;
67 }
68
69 if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) {
70 log_error("Failed to make /run/initramfs private mount: %m");
71 return -errno;
72 }
73
74 NULSTR_FOREACH(dir, dirs)
75 if (mkdir_p_label(dir, 0755) < 0 && errno != EEXIST) {
76 log_error("Failed to mkdir %s: %m", dir);
77 return -errno;
78 }
79
80 if (mount("/sys", "/run/initramfs/sys", NULL, MS_BIND, NULL) < 0) {
81 log_error("Failed to mount bind /sys on /run/initramfs/sys: %m");
82 return -errno;
83 }
84
85 if (mount("/proc", "/run/initramfs/proc", NULL, MS_BIND, NULL) < 0) {
86 log_error("Failed to mount bind /proc on /run/initramfs/proc: %m");
87 return -errno;
88 }
89
90 if (mount("/dev", "/run/initramfs/dev", NULL, MS_BIND, NULL) < 0) {
91 log_error("Failed to mount bind /dev on /run/initramfs/dev: %m");
92 return -errno;
93 }
94
95 if (mount("/run", "/run/initramfs/run", NULL, MS_BIND, NULL) < 0) {
96 log_error("Failed to mount bind /run on /run/initramfs/run: %m");
97 return -errno;
98 }
99
100 return 0;
101 }
102
103 static int pivot_to_new_root(void) {
104
105 if (chdir("/run/initramfs") < 0) {
106 log_error("Failed to change directory to /run/initramfs: %m");
107 return -errno;
108 }
109
110 /* Work-around for a kernel bug: for some reason the kernel
111 * refuses switching root if any file systems are mounted
112 * MS_SHARED. Hence remount them MS_PRIVATE here as a
113 * work-around.
114 *
115 * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
116 if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
117 log_warning("Failed to make \"/\" private mount: %m");
118
119 if (pivot_root(".", "oldroot") < 0) {
120 log_error("pivot failed: %m");
121 /* only chroot if pivot root succeeded */
122 return -errno;
123 }
124
125 chroot(".");
126
127 setsid();
128 make_console_stdio();
129
130 log_info("Successfully changed into root pivot.");
131
132 return 0;
133 }
134
135 int main(int argc, char *argv[]) {
136 bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true;
137 bool in_container, use_watchdog = false;
138 _cleanup_free_ char *line = NULL, *cgroup = NULL, *param = NULL;
139 char *arguments[3];
140 unsigned retries;
141 int cmd, r;
142
143 /* suppress shutdown status output if 'quiet' is used */
144 r = proc_cmdline(&line);
145 if (r > 0) {
146 char *w, *state;
147 size_t l;
148
149 FOREACH_WORD_QUOTED(w, l, line, state) {
150 if (l == 5 && memcmp(w, "quiet", 5) == 0) {
151 log_set_max_level(LOG_WARNING);
152 break;
153 }
154 }
155 }
156
157 log_parse_environment();
158 log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
159 log_open();
160
161 umask(0022);
162
163 if (getpid() != 1) {
164 log_error("Not executed by init (pid 1).");
165 r = -EPERM;
166 goto error;
167 }
168
169 if (argc != 2) {
170 log_error("Invalid number of arguments.");
171 r = -EINVAL;
172 goto error;
173 }
174
175 in_container = detect_container(NULL) > 0;
176
177 if (streq(argv[1], "reboot")) {
178 cmd = RB_AUTOBOOT;
179 /* if this fails, that's OK */
180 read_one_line_file(REBOOT_PARAM_FILE, &param);
181 } else if (streq(argv[1], "poweroff"))
182 cmd = RB_POWER_OFF;
183 else if (streq(argv[1], "halt"))
184 cmd = RB_HALT_SYSTEM;
185 else if (streq(argv[1], "kexec"))
186 cmd = LINUX_REBOOT_CMD_KEXEC;
187 else {
188 log_error("Unknown action '%s'.", argv[1]);
189 r = -EINVAL;
190 goto error;
191 }
192
193 cg_get_root_path(&cgroup);
194
195 use_watchdog = !!getenv("WATCHDOG_USEC");
196
197 /* lock us into memory */
198 mlockall(MCL_CURRENT|MCL_FUTURE);
199
200 log_info("Sending SIGTERM to remaining processes...");
201 broadcast_signal(SIGTERM, true);
202
203 log_info("Sending SIGKILL to remaining processes...");
204 broadcast_signal(SIGKILL, true);
205
206 if (in_container) {
207 need_swapoff = false;
208 need_dm_detach = false;
209 need_loop_detach = false;
210 }
211
212 /* Unmount all mountpoints, swaps, and loopback devices */
213 for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
214 bool changed = false;
215
216 if (use_watchdog)
217 watchdog_ping();
218
219 /* Let's trim the cgroup tree on each iteration so
220 that we leave an empty cgroup tree around, so that
221 container managers get a nice notify event when we
222 are down */
223 if (cgroup)
224 cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
225
226 if (need_umount) {
227 log_info("Unmounting file systems.");
228 r = umount_all(&changed);
229 if (r == 0) {
230 need_umount = false;
231 log_info("All filesystems unmounted.");
232 } else if (r > 0)
233 log_info("Not all file systems unmounted, %d left.", r);
234 else
235 log_error("Failed to unmount file systems: %s", strerror(-r));
236 }
237
238 if (need_swapoff) {
239 log_info("Deactivating swaps.");
240 r = swapoff_all(&changed);
241 if (r == 0) {
242 need_swapoff = false;
243 log_info("All swaps deactivated.");
244 } else if (r > 0)
245 log_info("Not all swaps deactivated, %d left.", r);
246 else
247 log_error("Failed to deactivate swaps: %s", strerror(-r));
248 }
249
250 if (need_loop_detach) {
251 log_info("Detaching loop devices.");
252 r = loopback_detach_all(&changed);
253 if (r == 0) {
254 need_loop_detach = false;
255 log_info("All loop devices detached.");
256 } else if (r > 0)
257 log_info("Not all loop devices detached, %d left.", r);
258 else
259 log_error("Failed to detach loop devices: %s", strerror(-r));
260 }
261
262 if (need_dm_detach) {
263 log_info("Detaching DM devices.");
264 r = dm_detach_all(&changed);
265 if (r == 0) {
266 need_dm_detach = false;
267 log_info("All DM devices detached.");
268 } else if (r > 0)
269 log_info("Not all DM devices detached, %d left.", r);
270 else
271 log_error("Failed to detach DM devices: %s", strerror(-r));
272 }
273
274 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
275 if (retries > 0)
276 log_info("All filesystems, swaps, loop devices, DM devices detached.");
277 /* Yay, done */
278 break;
279 }
280
281 /* If in this iteration we didn't manage to
282 * unmount/deactivate anything, we simply give up */
283 if (!changed) {
284 log_error("Cannot finalize remaining file systems and devices, giving up.");
285 break;
286 }
287
288 log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
289 }
290
291 if (retries >= FINALIZE_ATTEMPTS)
292 log_error("Too many iterations, giving up.");
293 else
294 log_info("Storage is finalized.");
295
296 arguments[0] = NULL;
297 arguments[1] = argv[1];
298 arguments[2] = NULL;
299 execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments);
300
301 if (!in_container && !in_initrd() &&
302 access("/run/initramfs/shutdown", X_OK) == 0) {
303
304 if (prepare_new_root() >= 0 &&
305 pivot_to_new_root() >= 0) {
306
307 log_info("Returning to initrd...");
308
309 execv("/shutdown", argv);
310 log_error("Failed to execute shutdown binary: %m");
311 }
312 }
313
314 /* The kernel will automaticall flush ATA disks and suchlike
315 * on reboot(), but the file systems need to be synce'd
316 * explicitly in advance. So let's do this here, but not
317 * needlessly slow down containers. */
318 if (!in_container)
319 sync();
320
321 if (cmd == LINUX_REBOOT_CMD_KEXEC) {
322
323 if (!in_container) {
324 /* We cheat and exec kexec to avoid doing all its work */
325 pid_t pid = fork();
326
327 if (pid < 0)
328 log_error("Could not fork: %m. Falling back to normal reboot.");
329 else if (pid > 0) {
330 wait_for_terminate_and_warn("kexec", pid);
331 log_warning("kexec failed. Falling back to normal reboot.");
332 } else {
333 /* Child */
334 const char *args[3] = { KEXEC, "-e", NULL };
335 execv(args[0], (char * const *) args);
336 return EXIT_FAILURE;
337 }
338 }
339
340 cmd = RB_AUTOBOOT;
341 }
342
343 if (param)
344 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
345 LINUX_REBOOT_CMD_RESTART2, param);
346 else
347 reboot(cmd);
348
349 if (errno == EPERM && in_container) {
350 /* If we are in a container, and we lacked
351 * CAP_SYS_BOOT just exit, this will kill our
352 * container for good. */
353 log_error("Exiting container.");
354 exit(0);
355 }
356
357 log_error("Failed to invoke reboot(): %m");
358 r = -errno;
359
360 error:
361 log_error("Critical error while doing system shutdown: %s", strerror(-r));
362
363 freeze();
364 return EXIT_FAILURE;
365 }