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