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