chiark / gitweb /
systemctl: add commands set-default and get-default
[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 #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         /* Work-around for a kernel bug: for some reason the kernel
108          * refuses switching root if any file systems are mounted
109          * MS_SHARED. Hence remount them MS_PRIVATE here as a
110          * work-around.
111          *
112          * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
113         if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
114                 log_warning("Failed to make \"/\" private mount: %m");
115
116         if (pivot_root(".", "oldroot") < 0) {
117                 log_error("pivot failed: %m");
118                 /* only chroot if pivot root succeeded */
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, true);
180
181         log_info("Sending SIGKILL to remaining processes...");
182         broadcast_signal(SIGKILL, true);
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                                 log_info("All filesystems unmounted.");
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("Deactivating swaps.");
211                         r = swapoff_all(&changed);
212                         if (r == 0) {
213                                 need_swapoff = false;
214                                 log_info("All swaps deactivated.");
215                         } else if (r > 0)
216                                 log_info("Not all swaps deactivated, %d left.", r);
217                         else
218                                 log_error("Failed to deactivate swaps: %s", strerror(-r));
219                 }
220
221                 if (need_loop_detach) {
222                         log_info("Detaching loop devices.");
223                         r = loopback_detach_all(&changed);
224                         if (r == 0) {
225                                 need_loop_detach = false;
226                                 log_info("All loop devices detached.");
227                         } else if (r > 0)
228                                 log_info("Not all loop devices detached, %d left.", r);
229                         else
230                                 log_error("Failed to detach loop devices: %s", strerror(-r));
231                 }
232
233                 if (need_dm_detach) {
234                         log_info("Detaching DM devices.");
235                         r = dm_detach_all(&changed);
236                         if (r == 0) {
237                                 need_dm_detach = false;
238                                 log_info("All DM devices detached.");
239                         } else if (r > 0)
240                                 log_info("Not all DM devices detached, %d left.", r);
241                         else
242                                 log_error("Failed to detach DM devices: %s", strerror(-r));
243                 }
244
245                 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
246                         if (retries > 0)
247                                 log_info("All filesystems, swaps, loop devices, DM devices detached.");
248                         /* Yay, done */
249                         break;
250                 }
251
252                 /* If in this iteration we didn't manage to
253                  * unmount/deactivate anything, we simply give up */
254                 if (!changed) {
255                         log_error("Cannot finalize remaining file systems and devices, giving up.");
256                         break;
257                 }
258
259                 log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
260         }
261
262         if (retries >= FINALIZE_ATTEMPTS)
263                 log_error("Too many iterations, giving up.");
264         else
265                 log_info("Storage is finalized.");
266
267         arguments[0] = NULL;
268         arguments[1] = argv[1];
269         arguments[2] = NULL;
270         execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments);
271
272         if (!in_container && !in_initrd() &&
273             access("/run/initramfs/shutdown", X_OK) == 0) {
274
275                 if (prepare_new_root() >= 0 &&
276                     pivot_to_new_root() >= 0) {
277
278                         log_info("Returning to initrd...");
279
280                         execv("/shutdown", argv);
281                         log_error("Failed to execute shutdown binary: %m");
282                 }
283         }
284
285         /* The kernel will automaticall flush ATA disks and suchlike
286          * on reboot(), but the file systems need to be synce'd
287          * explicitly in advance. So let's do this here, but not
288          * needlessly slow down containers. */
289         if (!in_container)
290                 sync();
291
292         if (cmd == LINUX_REBOOT_CMD_KEXEC) {
293
294                 if (!in_container) {
295                         /* We cheat and exec kexec to avoid doing all its work */
296                         pid_t pid = fork();
297
298                         if (pid < 0)
299                                 log_error("Could not fork: %m. Falling back to normal reboot.");
300                         else if (pid > 0) {
301                                 wait_for_terminate_and_warn("kexec", pid);
302                                 log_warning("kexec failed. Falling back to normal reboot.");
303                         } else {
304                                 /* Child */
305                                 const char *args[3] = { "/sbin/kexec", "-e", NULL };
306                                 execv(args[0], (char * const *) args);
307                                 return EXIT_FAILURE;
308                         }
309                 }
310
311                 cmd = RB_AUTOBOOT;
312         }
313
314         reboot(cmd);
315
316         if (errno == EPERM && in_container) {
317                 /* If we are in a container, and we lacked
318                  * CAP_SYS_BOOT just exit, this will kill our
319                  * container for good. */
320                 log_error("Exiting container.");
321                 exit(0);
322         }
323
324         log_error("Failed to invoke reboot(): %m");
325         r = -errno;
326
327   error:
328         log_error("Critical error while doing system shutdown: %s", strerror(-r));
329
330         freeze();
331         return EXIT_FAILURE;
332 }