]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shutdown.c
umount: try to get rid of DM devices
[thirdparty/systemd.git] / src / 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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU 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 <dirent.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <signal.h>
31 #include <stdbool.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "log.h"
36 #include "umount.h"
37 #include "util.h"
38
39 #define TIMEOUT_USEC (5 * USEC_PER_SEC)
40 #define FINALIZE_ATTEMPTS 50
41 #define FINALIZE_CRITICAL_ATTEMPTS 10
42
43 static bool ignore_proc(pid_t pid) {
44 if (pid == 1)
45 return true;
46
47 /* TODO: add more ignore rules here: device-mapper, etc */
48
49 return false;
50 }
51
52 static bool is_kernel_thread(pid_t pid)
53 {
54 char buf[PATH_MAX];
55 FILE *f;
56 char c;
57 size_t count;
58
59 snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long)pid);
60 f = fopen(buf, "re");
61 if (!f)
62 return true; /* not really, but has the desired effect */
63
64 count = fread(&c, 1, 1, f);
65 fclose(f);
66 return count != 1;
67 }
68
69 static int killall(int sign) {
70 DIR *dir;
71 struct dirent *d;
72 unsigned int processes = 0;
73
74 if ((dir = opendir("/proc")) == NULL)
75 return -errno;
76
77 while ((d = readdir(dir))) {
78 pid_t pid;
79
80 if (parse_pid(d->d_name, &pid) < 0)
81 continue;
82
83 if (is_kernel_thread(pid))
84 continue;
85
86 if (ignore_proc(pid))
87 continue;
88
89 if (kill(pid, sign) == 0)
90 processes++;
91 else
92 log_warning("Could not kill %d: %m", pid);
93 }
94
95 closedir(dir);
96
97 return processes;
98 }
99
100 static int send_signal(int sign) {
101 sigset_t mask, oldmask;
102 usec_t until;
103 int processes;
104 struct timespec ts;
105
106 assert_se(sigemptyset(&mask) == 0);
107 assert_se(sigaddset(&mask, SIGCHLD) == 0);
108 if (sigprocmask(SIG_BLOCK, &mask, &oldmask) != 0)
109 return -errno;
110
111 if (kill(-1, SIGSTOP) < 0)
112 log_warning("Failed kill(-1, SIGSTOP): %m");
113
114 processes = killall(sign);
115
116 if (kill(-1, SIGCONT) < 0)
117 log_warning("Failed kill(-1, SIGCONT): %m");
118
119 if (processes <= 0)
120 goto finish;
121
122 until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
123 for (;;) {
124 usec_t n = now(CLOCK_MONOTONIC);
125 for (;;) {
126 pid_t pid = waitpid(-1, NULL, WNOHANG);
127 if (pid == 0)
128 break;
129 else if (pid < 0 && errno == ECHILD) {
130 processes = 0;
131 goto finish;
132 }
133
134 if (--processes == 0)
135 goto finish;
136 }
137
138 if (n >= until)
139 goto finish;
140
141 timespec_store(&ts, until - n);
142 if (sigtimedwait(&mask, NULL, &ts) != SIGCHLD)
143 log_warning("Failed: sigtimedwait did not return SIGCHLD: %m");
144 }
145
146 finish:
147 sigprocmask(SIG_SETMASK, &oldmask, NULL);
148
149 return processes;
150 }
151
152 static int rescue_send_signal(int sign) {
153 sigset_t mask, oldmask;
154 usec_t until;
155 struct timespec ts;
156 int r;
157
158 sigemptyset(&mask);
159 sigaddset(&mask, SIGCHLD);
160 if (sigprocmask(SIG_BLOCK, &mask, &oldmask) != 0)
161 return -errno;
162
163 if (kill(-1, SIGSTOP) < 0)
164 log_warning("Failed kill(-1, SIGSTOP): %m");
165
166 r = kill(-1, sign);
167 if (r < 0)
168 log_warning("Failed kill(-1, %d): %m", sign);
169
170 if (kill(-1, SIGCONT) < 0)
171 log_warning("Failed kill(-1, SIGCONT): %m");
172
173 if (r < 0)
174 goto finish;
175
176 until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
177 for (;;) {
178 usec_t n = now(CLOCK_MONOTONIC);
179 for (;;) {
180 pid_t pid = waitpid(-1, NULL, WNOHANG);
181 if (pid == 0)
182 break;
183 else if (pid < 0 && errno == ECHILD)
184 goto finish;
185 }
186
187 if (n >= until)
188 goto finish;
189
190 timespec_store(&ts, until - n);
191 if (sigtimedwait(&mask, NULL, &ts) != SIGCHLD)
192 log_warning("Failed: sigtimedwait did not return SIGCHLD: %m");
193 }
194
195 finish:
196 sigprocmask(SIG_SETMASK, &oldmask, NULL);
197
198 return r;
199 }
200
201 int main(int argc, char *argv[]) {
202 int cmd, r, retries;
203 bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true;
204
205 log_parse_environment();
206 log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
207 log_open();
208
209 if (getpid() != 1) {
210 log_error("Not executed by init (pid 1).");
211 r = -EPERM;
212 goto error;
213 }
214
215 if (argc != 2) {
216 log_error("Invalid number of arguments.");
217 r = -EINVAL;
218 goto error;
219 }
220
221 if (streq(argv[1], "reboot"))
222 cmd = RB_AUTOBOOT;
223 else if (streq(argv[1], "poweroff"))
224 cmd = RB_POWER_OFF;
225 else if (streq(argv[1], "halt"))
226 cmd = RB_HALT_SYSTEM;
227 else if (streq(argv[1], "kexec"))
228 cmd = LINUX_REBOOT_CMD_KEXEC;
229 else {
230 log_error("Unknown action '%s'.", argv[1]);
231 r = -EINVAL;
232 goto error;
233 }
234
235 /* lock us into memory */
236 if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0)
237 log_warning("Cannot lock process memory: %m");
238
239 log_info("Sending SIGTERM to processes");
240 r = send_signal(SIGTERM);
241 if (r < 0)
242 log_warning("Cannot send SIGTERM to all process: %s", strerror(r));
243
244 log_info("Sending SIGKILL to processes");
245 r = send_signal(SIGKILL);
246 if (r < 0)
247 log_warning("Cannot send SIGKILL to all process: %s", strerror(r));
248
249 /* Unmount all mountpoints, swaps, and loopback devices */
250 retries = FINALIZE_ATTEMPTS;
251 for (;;) {
252 if (need_umount) {
253 log_info("Unmounting filesystems.");
254 r = umount_all();
255 if (r == 0)
256 need_umount = false;
257 else if (r > 0)
258 log_warning("Not all filesystems unmounted, %d left.", r);
259 else
260 log_error("Error unmounting filesystems: %s", strerror(-r));
261 }
262
263 if (need_swapoff) {
264 log_info("Disabling swaps.");
265 r = swapoff_all();
266 if (r == 0)
267 need_swapoff = false;
268 else if (r > 0)
269 log_warning("Not all swaps are off, %d left.", r);
270 else
271 log_error("Error turning off swaps: %s", strerror(-r));
272 }
273
274 if (need_loop_detach) {
275 log_info("Detaching loop devices.");
276 r = loopback_detach_all();
277 if (r == 0)
278 need_loop_detach = false;
279 else if (r > 0)
280 log_warning("Not all loop devices detached, %d left.", r);
281 else
282 log_error("Error detaching loop devices: %s", strerror(-r));
283 }
284
285 if (need_dm_detach) {
286 log_info("Detaching DM devices.");
287 r = dm_detach_all();
288 if (r == 0)
289 need_dm_detach = false;
290 else if (r > 0)
291 log_warning("Not all dm devices detached, %d left.", r);
292 else
293 log_error("Error detaching dm devices: %s", strerror(-r));
294 }
295
296 if (need_umount || need_swapoff || need_loop_detach || need_dm_detach) {
297 retries--;
298
299 if (retries == FINALIZE_CRITICAL_ATTEMPTS) {
300 log_warning("Approaching critical level to finalize filesystem and devices, try to kill all processes.");
301 rescue_send_signal(SIGTERM);
302 rescue_send_signal(SIGKILL);
303 }
304
305 if (retries > 0)
306 log_info("Action still required, %d tries left.", retries);
307 else {
308 log_error("Giving up. Actions left: Umount=%s, Swap off=%s, Loop detach=%s, dm detach=%s",
309 yes_no(need_umount), yes_no(need_swapoff), yes_no(need_loop_detach), yes_no(need_dm_detach));
310 break;
311 }
312 } else
313 break;
314 }
315
316 sync();
317
318 if (cmd == LINUX_REBOOT_CMD_KEXEC) {
319 /* we cheat and exec kexec to avoid doing all its work */
320 pid_t pid = fork();
321 if (pid < 0)
322 log_error("Could not fork: %m. Falling back to normal reboot.");
323 else if (pid > 0) {
324 wait_for_terminate_and_warn("kexec", pid);
325 log_warning("kexec failed. Falling back to normal reboot.");
326 } else {
327 /* Child */
328 const char *args[5] = { KEXEC_BINARY_PATH, "-e", "-f", "-x", NULL };
329 execv(args[0], (char * const *) args);
330 return EXIT_FAILURE;
331 }
332
333 cmd = RB_AUTOBOOT;
334 }
335
336 reboot(cmd);
337 log_error("Failed to invoke reboot(): %m");
338 r = -errno;
339
340 error:
341 sync();
342 log_error("Critical error while doing system shutdown: %s", strerror(-r));
343
344 freeze();
345 return EXIT_FAILURE;
346 }