chiark / gitweb /
def20f50228657f3dbdc107e8cfa8a948e1e12f2
[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 #include <getopt.h>
39
40 #include "missing.h"
41 #include "log.h"
42 #include "fileio.h"
43 #include "umount.h"
44 #include "util.h"
45 #include "mkdir.h"
46 #include "virt.h"
47 #include "watchdog.h"
48 #include "killall.h"
49 #include "cgroup-util.h"
50 #include "def.h"
51 #include "switch-root.h"
52
53 #define FINALIZE_ATTEMPTS 50
54
55 static char* arg_verb;
56
57 static int parse_argv(int argc, char *argv[]) {
58         enum {
59                 ARG_LOG_LEVEL = 0x100,
60                 ARG_LOG_TARGET,
61                 ARG_LOG_COLOR,
62                 ARG_LOG_LOCATION,
63         };
64
65         static const struct option options[] = {
66                 { "log-level",     required_argument, NULL, ARG_LOG_LEVEL    },
67                 { "log-target",    required_argument, NULL, ARG_LOG_TARGET   },
68                 { "log-color",     optional_argument, NULL, ARG_LOG_COLOR    },
69                 { "log-location",  optional_argument, NULL, ARG_LOG_LOCATION },
70                 {}
71         };
72
73         int c, r;
74
75         assert(argc >= 1);
76         assert(argv);
77
78         /* "-" prevents getopt from permuting argv[] and moving the verb away
79          * from argv[1]. Our interface to initrd promises it'll be there. */
80         while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0)
81                 switch (c) {
82
83                 case ARG_LOG_LEVEL:
84                         r = log_set_max_level_from_string(optarg);
85                         if (r < 0)
86                                 log_error("Failed to parse log level %s, ignoring.", optarg);
87
88                         break;
89
90                 case ARG_LOG_TARGET:
91                         r = log_set_target_from_string(optarg);
92                         if (r < 0)
93                                 log_error("Failed to parse log target %s, ignoring", optarg);
94
95                         break;
96
97                 case ARG_LOG_COLOR:
98
99                         if (optarg) {
100                                 r = log_show_color_from_string(optarg);
101                                 if (r < 0)
102                                         log_error("Failed to parse log color setting %s, ignoring", optarg);
103                         } else
104                                 log_show_color(true);
105
106                         break;
107
108                 case ARG_LOG_LOCATION:
109                         if (optarg) {
110                                 r = log_show_location_from_string(optarg);
111                                 if (r < 0)
112                                         log_error("Failed to parse log location setting %s, ignoring", optarg);
113                         } else
114                                 log_show_location(true);
115
116                         break;
117
118                 case '\001':
119                         if (!arg_verb)
120                                 arg_verb = optarg;
121                         else
122                                 log_error("Excess arguments, ignoring");
123                         break;
124
125                 case '?':
126                         return -EINVAL;
127
128                 default:
129                         assert_not_reached("Unhandled option code.");
130                 }
131
132         if (!arg_verb) {
133                 log_error("Verb argument missing.");
134                 return -EINVAL;
135         }
136
137         return 0;
138 }
139
140 static int switch_root_initramfs(void) {
141         if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0)
142                 return log_error_errno(errno, "Failed to mount bind /run/initramfs on /run/initramfs: %m");
143
144         if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0)
145                 return log_error_errno(errno, "Failed to make /run/initramfs private mount: %m");
146
147         /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file desriptors.
148          * /run/initramfs/shutdown will take care of these.
149          * Also do not detach the old root, because /run/initramfs/shutdown needs to access it.
150          */
151         return switch_root("/run/initramfs", "/oldroot", false, MS_BIND);
152 }
153
154
155 int main(int argc, char *argv[]) {
156         bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
157         bool in_container, use_watchdog = false;
158         _cleanup_free_ char *cgroup = NULL;
159         char *arguments[3];
160         unsigned retries;
161         int cmd, r;
162
163         log_parse_environment();
164         r = parse_argv(argc, argv);
165         if (r < 0)
166                 goto error;
167
168         /* journald will die if not gone yet. The log target defaults
169          * to console, but may have been changed by command line options. */
170
171         log_close_console(); /* force reopen of /dev/console */
172         log_open();
173
174         umask(0022);
175
176         if (getpid() != 1) {
177                 log_error("Not executed by init (PID 1).");
178                 r = -EPERM;
179                 goto error;
180         }
181
182         if (streq(arg_verb, "reboot"))
183                 cmd = RB_AUTOBOOT;
184         else if (streq(arg_verb, "poweroff"))
185                 cmd = RB_POWER_OFF;
186         else if (streq(arg_verb, "halt"))
187                 cmd = RB_HALT_SYSTEM;
188         else if (streq(arg_verb, "kexec"))
189                 cmd = LINUX_REBOOT_CMD_KEXEC;
190         else {
191                 r = -EINVAL;
192                 log_error("Unknown action '%s'.", arg_verb);
193                 goto error;
194         }
195
196         cg_get_root_path(&cgroup);
197
198         use_watchdog = !!getenv("WATCHDOG_USEC");
199
200         /* lock us into memory */
201         mlockall(MCL_CURRENT|MCL_FUTURE);
202
203         log_info("Sending SIGTERM to remaining processes...");
204         broadcast_signal(SIGTERM, true, true);
205
206         log_info("Sending SIGKILL to remaining processes...");
207         broadcast_signal(SIGKILL, true, false);
208
209         in_container = detect_container(NULL) > 0;
210
211         need_umount = !in_container;
212         need_swapoff = !in_container;
213         need_loop_detach = !in_container;
214         need_dm_detach = !in_container;
215
216         /* Unmount all mountpoints, swaps, and loopback devices */
217         for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
218                 bool changed = false;
219
220                 if (use_watchdog)
221                         watchdog_ping();
222
223                 /* Let's trim the cgroup tree on each iteration so
224                    that we leave an empty cgroup tree around, so that
225                    container managers get a nice notify event when we
226                    are down */
227                 if (cgroup)
228                         cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
229
230                 if (need_umount) {
231                         log_info("Unmounting file systems.");
232                         r = umount_all(&changed);
233                         if (r == 0) {
234                                 need_umount = false;
235                                 log_info("All filesystems unmounted.");
236                         } else if (r > 0)
237                                 log_info("Not all file systems unmounted, %d left.", r);
238                         else
239                                 log_error_errno(r, "Failed to unmount file systems: %m");
240                 }
241
242                 if (need_swapoff) {
243                         log_info("Deactivating swaps.");
244                         r = swapoff_all(&changed);
245                         if (r == 0) {
246                                 need_swapoff = false;
247                                 log_info("All swaps deactivated.");
248                         } else if (r > 0)
249                                 log_info("Not all swaps deactivated, %d left.", r);
250                         else
251                                 log_error_errno(r, "Failed to deactivate swaps: %m");
252                 }
253
254                 if (need_loop_detach) {
255                         log_info("Detaching loop devices.");
256                         r = loopback_detach_all(&changed);
257                         if (r == 0) {
258                                 need_loop_detach = false;
259                                 log_info("All loop devices detached.");
260                         } else if (r > 0)
261                                 log_info("Not all loop devices detached, %d left.", r);
262                         else
263                                 log_error_errno(r, "Failed to detach loop devices: %m");
264                 }
265
266                 if (need_dm_detach) {
267                         log_info("Detaching DM devices.");
268                         r = dm_detach_all(&changed);
269                         if (r == 0) {
270                                 need_dm_detach = false;
271                                 log_info("All DM devices detached.");
272                         } else if (r > 0)
273                                 log_info("Not all DM devices detached, %d left.", r);
274                         else
275                                 log_error_errno(r, "Failed to detach DM devices: %m");
276                 }
277
278                 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
279                         if (retries > 0)
280                                 log_info("All filesystems, swaps, loop devices, DM devices detached.");
281                         /* Yay, done */
282                         goto initrd_jump;
283                 }
284
285                 /* If in this iteration we didn't manage to
286                  * unmount/deactivate anything, we simply give up */
287                 if (!changed) {
288                         log_info("Cannot finalize remaining%s%s%s%s continuing.",
289                                  need_umount ? " file systems," : "",
290                                  need_swapoff ? " swap devices," : "",
291                                  need_loop_detach ? " loop devices," : "",
292                                  need_dm_detach ? " DM devices," : "");
293                         goto initrd_jump;
294                 }
295
296                 log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.",
297                           retries + 1,
298                           need_umount ? " file systems," : "",
299                           need_swapoff ? " swap devices," : "",
300                           need_loop_detach ? " loop devices," : "",
301                           need_dm_detach ? " DM devices," : "");
302         }
303
304         log_error("Too many iterations, giving up.");
305
306  initrd_jump:
307
308         arguments[0] = NULL;
309         arguments[1] = arg_verb;
310         arguments[2] = NULL;
311         execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
312
313         if (!in_container && !in_initrd() &&
314             access("/run/initramfs/shutdown", X_OK) == 0) {
315                 r = switch_root_initramfs();
316                 if (r >= 0) {
317                         argv[0] = (char*) "/shutdown";
318
319                         setsid();
320                         make_console_stdio();
321
322                         log_info("Successfully changed into root pivot.\n"
323                                  "Returning to initrd...");
324
325                         execv("/shutdown", argv);
326                         log_error_errno(errno, "Failed to execute shutdown binary: %m");
327                 } else
328                         log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m");
329
330         }
331
332         if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
333                 log_error("Failed to finalize %s%s%s%s ignoring",
334                           need_umount ? " file systems," : "",
335                           need_swapoff ? " swap devices," : "",
336                           need_loop_detach ? " loop devices," : "",
337                           need_dm_detach ? " DM devices," : "");
338
339         /* The kernel will automaticall flush ATA disks and suchlike
340          * on reboot(), but the file systems need to be synce'd
341          * explicitly in advance. So let's do this here, but not
342          * needlessly slow down containers. */
343         if (!in_container)
344                 sync();
345
346         switch (cmd) {
347
348         case LINUX_REBOOT_CMD_KEXEC:
349
350                 if (!in_container) {
351                         /* We cheat and exec kexec to avoid doing all its work */
352                         pid_t pid;
353
354                         log_info("Rebooting with kexec.");
355
356                         pid = fork();
357                         if (pid < 0)
358                                 log_error_errno(errno, "Failed to fork: %m");
359                         else if (pid == 0) {
360
361                                 const char * const args[] = {
362                                         KEXEC, "-e", NULL
363                                 };
364
365                                 /* Child */
366
367                                 execv(args[0], (char * const *) args);
368                                 _exit(EXIT_FAILURE);
369                         } else
370                                 wait_for_terminate_and_warn("kexec", pid, true);
371                 }
372
373                 cmd = RB_AUTOBOOT;
374                 /* Fall through */
375
376         case RB_AUTOBOOT:
377
378                 if (!in_container) {
379                         _cleanup_free_ char *param = NULL;
380
381                         if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
382                                 log_info("Rebooting with argument '%s'.", param);
383                                 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
384                         }
385                 }
386
387                 log_info("Rebooting.");
388                 break;
389
390         case RB_POWER_OFF:
391                 log_info("Powering off.");
392                 break;
393
394         case RB_HALT_SYSTEM:
395                 log_info("Halting system.");
396                 break;
397
398         default:
399                 assert_not_reached("Unknown magic");
400         }
401
402         reboot(cmd);
403         if (errno == EPERM && in_container) {
404                 /* If we are in a container, and we lacked
405                  * CAP_SYS_BOOT just exit, this will kill our
406                  * container for good. */
407                 log_info("Exiting container.");
408                 exit(0);
409         }
410
411         log_error_errno(errno, "Failed to invoke reboot(): %m");
412         r = -errno;
413
414   error:
415         log_emergency_errno(r, "Critical error while doing system shutdown: %m");
416
417         freeze();
418 }