]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/shutdown.c
Spelling fixes.
[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 "umount.h"
43 #include "util.h"
44 #include "mkdir.h"
45 #include "virt.h"
46 #include "watchdog.h"
47 #include "killall.h"
48
49 #define FINALIZE_ATTEMPTS 50
50
51 static int prepare_new_root(void) {
52 static const char dirs[] =
53 "/run/initramfs/oldroot\0"
54 "/run/initramfs/proc\0"
55 "/run/initramfs/sys\0"
56 "/run/initramfs/dev\0"
57 "/run/initramfs/run\0";
58
59 const char *dir;
60
61 if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) {
62 log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m");
63 return -errno;
64 }
65
66 if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) {
67 log_error("Failed to make /run/initramfs private mount: %m");
68 return -errno;
69 }
70
71 NULSTR_FOREACH(dir, dirs)
72 if (mkdir_p_label(dir, 0755) < 0 && errno != EEXIST) {
73 log_error("Failed to mkdir %s: %m", dir);
74 return -errno;
75 }
76
77 if (mount("/sys", "/run/initramfs/sys", NULL, MS_BIND, NULL) < 0) {
78 log_error("Failed to mount bind /sys on /run/initramfs/sys: %m");
79 return -errno;
80 }
81
82 if (mount("/proc", "/run/initramfs/proc", NULL, MS_BIND, NULL) < 0) {
83 log_error("Failed to mount bind /proc on /run/initramfs/proc: %m");
84 return -errno;
85 }
86
87 if (mount("/dev", "/run/initramfs/dev", NULL, MS_BIND, NULL) < 0) {
88 log_error("Failed to mount bind /dev on /run/initramfs/dev: %m");
89 return -errno;
90 }
91
92 if (mount("/run", "/run/initramfs/run", NULL, MS_BIND, NULL) < 0) {
93 log_error("Failed to mount bind /run on /run/initramfs/run: %m");
94 return -errno;
95 }
96
97 return 0;
98 }
99
100 static int pivot_to_new_root(void) {
101
102 if (chdir("/run/initramfs") < 0) {
103 log_error("Failed to change directory to /run/initramfs: %m");
104 return -errno;
105 }
106
107 /*
108 In case some evil process made "/" MS_SHARED
109 It works for pivot_root, but the ref count for the root device
110 is not decreasing :-/
111 */
112 if (mount(NULL, "/", NULL, MS_PRIVATE, NULL) < 0) {
113 log_error("Failed to make \"/\" private mount %m");
114 return -errno;
115 }
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 int cmd, r;
135 unsigned retries;
136 bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true;
137 bool in_container, use_watchdog = false;
138 char *arguments[3];
139
140 log_parse_environment();
141 log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
142 log_open();
143
144 umask(0022);
145
146 if (getpid() != 1) {
147 log_error("Not executed by init (pid 1).");
148 r = -EPERM;
149 goto error;
150 }
151
152 if (argc != 2) {
153 log_error("Invalid number of arguments.");
154 r = -EINVAL;
155 goto error;
156 }
157
158 in_container = detect_container(NULL) > 0;
159
160 if (streq(argv[1], "reboot"))
161 cmd = RB_AUTOBOOT;
162 else if (streq(argv[1], "poweroff"))
163 cmd = RB_POWER_OFF;
164 else if (streq(argv[1], "halt"))
165 cmd = RB_HALT_SYSTEM;
166 else if (streq(argv[1], "kexec"))
167 cmd = LINUX_REBOOT_CMD_KEXEC;
168 else {
169 log_error("Unknown action '%s'.", argv[1]);
170 r = -EINVAL;
171 goto error;
172 }
173
174 use_watchdog = !!getenv("WATCHDOG_USEC");
175
176 /* lock us into memory */
177 mlockall(MCL_CURRENT|MCL_FUTURE);
178
179 log_info("Sending SIGTERM to remaining processes...");
180 broadcast_signal(SIGTERM);
181
182 log_info("Sending SIGKILL to remaining processes...");
183 broadcast_signal(SIGKILL);
184
185 if (in_container) {
186 need_swapoff = false;
187 need_dm_detach = false;
188 need_loop_detach = false;
189 }
190
191 /* Unmount all mountpoints, swaps, and loopback devices */
192 for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
193 bool changed = false;
194
195 if (use_watchdog)
196 watchdog_ping();
197
198 if (need_umount) {
199 log_info("Unmounting file systems.");
200 r = umount_all(&changed);
201 if (r == 0)
202 need_umount = false;
203 else if (r > 0)
204 log_info("Not all file systems unmounted, %d left.", r);
205 else
206 log_error("Failed to unmount file systems: %s", strerror(-r));
207 }
208
209 if (need_swapoff) {
210 log_info("Disabling swaps.");
211 r = swapoff_all(&changed);
212 if (r == 0)
213 need_swapoff = false;
214 else if (r > 0)
215 log_info("Not all swaps are turned off, %d left.", r);
216 else
217 log_error("Failed to turn off swaps: %s", strerror(-r));
218 }
219
220 if (need_loop_detach) {
221 log_info("Detaching loop devices.");
222 r = loopback_detach_all(&changed);
223 if (r == 0)
224 need_loop_detach = false;
225 else if (r > 0)
226 log_info("Not all loop devices detached, %d left.", r);
227 else
228 log_error("Failed to detach loop devices: %s", strerror(-r));
229 }
230
231 if (need_dm_detach) {
232 log_info("Detaching DM devices.");
233 r = dm_detach_all(&changed);
234 if (r == 0)
235 need_dm_detach = false;
236 else if (r > 0)
237 log_warning("Not all DM devices detached, %d left.", r);
238 else
239 log_error("Failed to detach DM devices: %s", strerror(-r));
240 }
241
242 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
243 if (retries > 0)
244 log_info("All filesystems, swaps, loop devices, DM devices detached.");
245 /* Yay, done */
246 break;
247 }
248
249 /* If in this iteration we didn't manage to
250 * unmount/deactivate anything, we simply give up */
251 if (!changed) {
252 log_error("Cannot finalize remaining file systems and devices, giving up.");
253 break;
254 }
255
256 log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
257 }
258
259 if (retries >= FINALIZE_ATTEMPTS)
260 log_error("Too many iterations, giving up.");
261
262 arguments[0] = NULL;
263 arguments[1] = argv[1];
264 arguments[2] = NULL;
265 execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments);
266
267 /* If we are in a container, just exit, this will kill our
268 * container for good. */
269 if (in_container) {
270 log_error("Exiting container.");
271 exit(0);
272 }
273
274 if (access("/run/initramfs/shutdown", X_OK) == 0) {
275
276 if (prepare_new_root() >= 0 &&
277 pivot_to_new_root() >= 0) {
278 execv("/shutdown", argv);
279 log_error("Failed to execute shutdown binary: %m");
280 }
281 }
282
283 sync();
284
285 if (cmd == LINUX_REBOOT_CMD_KEXEC) {
286 /* We cheat and exec kexec to avoid doing all its work */
287 pid_t pid = fork();
288
289 if (pid < 0)
290 log_error("Could not fork: %m. Falling back to normal reboot.");
291 else if (pid > 0) {
292 wait_for_terminate_and_warn("kexec", pid);
293 log_warning("kexec failed. Falling back to normal reboot.");
294 } else {
295 /* Child */
296 const char *args[3] = { "/sbin/kexec", "-e", NULL };
297 execv(args[0], (char * const *) args);
298 return EXIT_FAILURE;
299 }
300
301 cmd = RB_AUTOBOOT;
302 }
303
304 reboot(cmd);
305 log_error("Failed to invoke reboot(): %m");
306 r = -errno;
307
308 error:
309 log_error("Critical error while doing system shutdown: %s", strerror(-r));
310
311 freeze();
312 return EXIT_FAILURE;
313 }