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