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