chiark / gitweb /
container: when shutting down in a container don't detach loop devices
[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                 need_loop_detach = false;
188         }
189
190         /* Unmount all mountpoints, swaps, and loopback devices */
191         for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
192                 bool changed = false;
193
194                 if (use_watchdog)
195                         watchdog_ping();
196
197                 if (need_umount) {
198                         log_info("Unmounting file systems.");
199                         r = umount_all(&changed);
200                         if (r == 0)
201                                 need_umount = false;
202                         else if (r > 0)
203                                 log_info("Not all file systems unmounted, %d left.", r);
204                         else
205                                 log_error("Failed to unmount file systems: %s", strerror(-r));
206                 }
207
208                 if (need_swapoff) {
209                         log_info("Disabling swaps.");
210                         r = swapoff_all(&changed);
211                         if (r == 0)
212                                 need_swapoff = false;
213                         else if (r > 0)
214                                 log_info("Not all swaps are turned off, %d left.", r);
215                         else
216                                 log_error("Failed to turn off swaps: %s", strerror(-r));
217                 }
218
219                 if (need_loop_detach) {
220                         log_info("Detaching loop devices.");
221                         r = loopback_detach_all(&changed);
222                         if (r == 0)
223                                 need_loop_detach = false;
224                         else if (r > 0)
225                                 log_info("Not all loop devices detached, %d left.", r);
226                         else
227                                 log_error("Failed to detach loop devices: %s", strerror(-r));
228                 }
229
230                 if (need_dm_detach) {
231                         log_info("Detaching DM devices.");
232                         r = dm_detach_all(&changed);
233                         if (r == 0)
234                                 need_dm_detach = false;
235                         else if (r > 0)
236                                 log_warning("Not all DM devices detached, %d left.", r);
237                         else
238                                 log_error("Failed to detach DM devices: %s", strerror(-r));
239                 }
240
241                 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
242                         if (retries > 0)
243                                 log_info("All filesystems, swaps, loop devices, DM devices detached.");
244                         /* Yay, done */
245                         break;
246                 }
247
248                 /* If in this iteration we didn't manage to
249                  * unmount/deactivate anything, we simply give up */
250                 if (!changed) {
251                         log_error("Cannot finalize remaining file systems and devices, giving up.");
252                         break;
253                 }
254
255                 log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
256         }
257
258         if (retries >= FINALIZE_ATTEMPTS)
259                 log_error("Too many iterations, giving up.");
260
261         arguments[0] = NULL;
262         arguments[1] = argv[1];
263         arguments[2] = NULL;
264         execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments);
265
266         /* If we are in a container, just exit, this will kill our
267          * container for good. */
268         if (in_container) {
269                 log_error("Exiting container.");
270                 exit(0);
271         }
272
273         if (access("/run/initramfs/shutdown", X_OK) == 0) {
274
275                 if (prepare_new_root() >= 0 &&
276                     pivot_to_new_root() >= 0) {
277                         execv("/shutdown", argv);
278                         log_error("Failed to execute shutdown binary: %m");
279                 }
280         }
281
282         sync();
283
284         if (cmd == LINUX_REBOOT_CMD_KEXEC) {
285                 /* We cheat and exec kexec to avoid doing all its work */
286                 pid_t pid = fork();
287
288                 if (pid < 0)
289                         log_error("Could not fork: %m. Falling back to normal reboot.");
290                 else if (pid > 0) {
291                         wait_for_terminate_and_warn("kexec", pid);
292                         log_warning("kexec failed. Falling back to normal reboot.");
293                 } else {
294                         /* Child */
295                         const char *args[3] = { "/sbin/kexec", "-e", NULL };
296                         execv(args[0], (char * const *) args);
297                         return EXIT_FAILURE;
298                 }
299
300                 cmd = RB_AUTOBOOT;
301         }
302
303         reboot(cmd);
304         log_error("Failed to invoke reboot(): %m");
305         r = -errno;
306
307   error:
308         log_error("Critical error while doing system shutdown: %s", strerror(-r));
309
310         freeze();
311         return EXIT_FAILURE;
312 }