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