]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
bd3fa1d2 LP |
2 | /*** |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright 2010 ProFUSION embedded systems | |
6 | ||
7 | systemd is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU Lesser General Public License as published by | |
9 | the Free Software Foundation; either version 2.1 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | systemd is distributed in the hope that it will be useful, but | |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Lesser General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Lesser General Public License | |
18 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
19 | ***/ | |
20 | ||
bd3fa1d2 | 21 | #include <errno.h> |
07630cea LP |
22 | #include <signal.h> |
23 | #include <sys/wait.h> | |
aaf7eb81 | 24 | #include <unistd.h> |
bd3fa1d2 | 25 | |
b5efdb8a | 26 | #include "alloc-util.h" |
d4506129 | 27 | #include "def.h" |
8fb3f009 | 28 | #include "dirent-util.h" |
3ffd4af2 | 29 | #include "fd-util.h" |
f97b34a6 | 30 | #include "format-util.h" |
3ffd4af2 | 31 | #include "killall.h" |
6bedfcbb | 32 | #include "parse-util.h" |
0b452006 | 33 | #include "process-util.h" |
07630cea LP |
34 | #include "set.h" |
35 | #include "string-util.h" | |
288a74cc | 36 | #include "terminal-util.h" |
07630cea | 37 | #include "util.h" |
bd3fa1d2 | 38 | |
1359fffa | 39 | static bool ignore_proc(pid_t pid, bool warn_rootfs) { |
31885cd5 | 40 | _cleanup_fclose_ FILE *f = NULL; |
b68fa010 SP |
41 | char c; |
42 | const char *p; | |
bd3fa1d2 LP |
43 | size_t count; |
44 | uid_t uid; | |
45 | int r; | |
46 | ||
47 | /* We are PID 1, let's not commit suicide */ | |
48 | if (pid == 1) | |
49 | return true; | |
50 | ||
51 | r = get_process_uid(pid, &uid); | |
52 | if (r < 0) | |
53 | return true; /* not really, but better safe than sorry */ | |
54 | ||
55 | /* Non-root processes otherwise are always subject to be killed */ | |
56 | if (uid != 0) | |
57 | return false; | |
58 | ||
5841bd80 ZJS |
59 | p = procfs_file_alloca(pid, "cmdline"); |
60 | f = fopen(p, "re"); | |
bd3fa1d2 LP |
61 | if (!f) |
62 | return true; /* not really, but has the desired effect */ | |
63 | ||
64 | count = fread(&c, 1, 1, f); | |
bd3fa1d2 LP |
65 | |
66 | /* Kernel threads have an empty cmdline */ | |
67 | if (count <= 0) | |
68 | return true; | |
69 | ||
9e615117 | 70 | /* Processes with argv[0][0] = '@' we ignore from the killing spree. |
bd3fa1d2 LP |
71 | * |
72 | * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */ | |
9e615117 ZJS |
73 | if (c != '@') |
74 | return false; | |
75 | ||
76 | if (warn_rootfs && | |
77 | pid_from_same_root_fs(pid) == 0) { | |
1359fffa | 78 | |
9e615117 | 79 | _cleanup_free_ char *comm = NULL; |
1359fffa MS |
80 | |
81 | get_process_comm(pid, &comm); | |
82 | ||
9e615117 ZJS |
83 | log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is " |
84 | "running from the root file system, and thus likely to block re-mounting of the " | |
85 | "root file system to read-only. Please consider moving it into an initrd file " | |
86 | "system instead.", pid, strna(comm)); | |
87 | } | |
bd3fa1d2 | 88 | |
9e615117 | 89 | return true; |
bd3fa1d2 LP |
90 | } |
91 | ||
aaf7eb81 | 92 | static void wait_for_children(Set *pids, sigset_t *mask) { |
bd3fa1d2 LP |
93 | usec_t until; |
94 | ||
95 | assert(mask); | |
96 | ||
aaf7eb81 LP |
97 | if (set_isempty(pids)) |
98 | return; | |
99 | ||
d4506129 | 100 | until = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC; |
bd3fa1d2 LP |
101 | for (;;) { |
102 | struct timespec ts; | |
103 | int k; | |
104 | usec_t n; | |
aaf7eb81 LP |
105 | void *p; |
106 | Iterator i; | |
bd3fa1d2 | 107 | |
aaf7eb81 LP |
108 | /* First, let the kernel inform us about killed |
109 | * children. Most processes will probably be our | |
110 | * children, but some are not (might be our | |
111 | * grandchildren instead...). */ | |
bd3fa1d2 | 112 | for (;;) { |
aaf7eb81 | 113 | pid_t pid; |
bd3fa1d2 | 114 | |
aaf7eb81 | 115 | pid = waitpid(-1, NULL, WNOHANG); |
bd3fa1d2 LP |
116 | if (pid == 0) |
117 | break; | |
aaf7eb81 LP |
118 | if (pid < 0) { |
119 | if (errno == ECHILD) | |
120 | break; | |
bd3fa1d2 | 121 | |
56f64d95 | 122 | log_error_errno(errno, "waitpid() failed: %m"); |
bd3fa1d2 | 123 | return; |
aaf7eb81 LP |
124 | } |
125 | ||
fea72cc0 | 126 | (void) set_remove(pids, PID_TO_PTR(pid)); |
aaf7eb81 | 127 | } |
bd3fa1d2 | 128 | |
aaf7eb81 LP |
129 | /* Now explicitly check who might be remaining, who |
130 | * might not be our child. */ | |
131 | SET_FOREACH(p, pids, i) { | |
132 | ||
3448a969 AJ |
133 | /* kill(pid, 0) sends no signal, but it tells |
134 | * us whether the process still exists. */ | |
135 | if (kill(PTR_TO_PID(p), 0) == 0) | |
aaf7eb81 LP |
136 | continue; |
137 | ||
138 | if (errno != ESRCH) | |
139 | continue; | |
140 | ||
141 | set_remove(pids, p); | |
bd3fa1d2 LP |
142 | } |
143 | ||
aaf7eb81 LP |
144 | if (set_isempty(pids)) |
145 | return; | |
146 | ||
bd3fa1d2 LP |
147 | n = now(CLOCK_MONOTONIC); |
148 | if (n >= until) | |
149 | return; | |
150 | ||
151 | timespec_store(&ts, until - n); | |
aaf7eb81 LP |
152 | k = sigtimedwait(mask, NULL, &ts); |
153 | if (k != SIGCHLD) { | |
bd3fa1d2 LP |
154 | |
155 | if (k < 0 && errno != EAGAIN) { | |
56f64d95 | 156 | log_error_errno(errno, "sigtimedwait() failed: %m"); |
bd3fa1d2 LP |
157 | return; |
158 | } | |
159 | ||
160 | if (k >= 0) | |
161 | log_warning("sigtimedwait() returned unexpected signal."); | |
162 | } | |
163 | } | |
164 | } | |
165 | ||
0bee65f0 | 166 | static int killall(int sig, Set *pids, bool send_sighup) { |
aaf7eb81 | 167 | _cleanup_closedir_ DIR *dir = NULL; |
bd3fa1d2 | 168 | struct dirent *d; |
bd3fa1d2 LP |
169 | |
170 | dir = opendir("/proc"); | |
171 | if (!dir) | |
172 | return -errno; | |
173 | ||
8fb3f009 | 174 | FOREACH_DIRENT_ALL(d, dir, break) { |
bd3fa1d2 | 175 | pid_t pid; |
60053efb | 176 | int r; |
bd3fa1d2 | 177 | |
ec2ce0c5 | 178 | if (!IN_SET(d->d_type, DT_DIR, DT_UNKNOWN)) |
bd3fa1d2 LP |
179 | continue; |
180 | ||
181 | if (parse_pid(d->d_name, &pid) < 0) | |
182 | continue; | |
183 | ||
1359fffa | 184 | if (ignore_proc(pid, sig == SIGKILL && !in_initrd())) |
bd3fa1d2 LP |
185 | continue; |
186 | ||
df758e98 | 187 | if (sig == SIGKILL) { |
3e09eb5c | 188 | _cleanup_free_ char *s = NULL; |
df758e98 KS |
189 | |
190 | get_process_comm(pid, &s); | |
ccd06097 | 191 | log_notice("Sending SIGKILL to PID "PID_FMT" (%s).", pid, strna(s)); |
df758e98 KS |
192 | } |
193 | ||
aaf7eb81 | 194 | if (kill(pid, sig) >= 0) { |
60053efb | 195 | if (pids) { |
fea72cc0 | 196 | r = set_put(pids, PID_TO_PTR(pid)); |
60053efb TA |
197 | if (r < 0) |
198 | log_oom(); | |
199 | } | |
aaf7eb81 | 200 | } else if (errno != ENOENT) |
56f64d95 | 201 | log_warning_errno(errno, "Could not kill %d: %m", pid); |
0bee65f0 LP |
202 | |
203 | if (send_sighup) { | |
204 | /* Optionally, also send a SIGHUP signal, but | |
205 | only if the process has a controlling | |
206 | tty. This is useful to allow handling of | |
207 | shells which ignore SIGTERM but react to | |
208 | SIGHUP. We do not send this to processes that | |
209 | have no controlling TTY since we don't want to | |
210 | trigger reloads of daemon processes. Also we | |
211 | make sure to only send this after SIGTERM so | |
212 | that SIGTERM is always first in the queue. */ | |
213 | ||
214 | ||
215 | if (get_ctty_devnr(pid, NULL) >= 0) | |
5c5d9f26 ZJS |
216 | /* it's OK if the process is gone, just ignore the result */ |
217 | (void) kill(pid, SIGHUP); | |
0bee65f0 | 218 | } |
bd3fa1d2 LP |
219 | } |
220 | ||
aaf7eb81 | 221 | return set_size(pids); |
bd3fa1d2 LP |
222 | } |
223 | ||
6301a98c | 224 | void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup) { |
bd3fa1d2 | 225 | sigset_t mask, oldmask; |
e1d75803 | 226 | _cleanup_set_free_ Set *pids = NULL; |
aaf7eb81 LP |
227 | |
228 | if (wait_for_exit) | |
d5099efc | 229 | pids = set_new(NULL); |
bd3fa1d2 LP |
230 | |
231 | assert_se(sigemptyset(&mask) == 0); | |
232 | assert_se(sigaddset(&mask, SIGCHLD) == 0); | |
233 | assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0); | |
234 | ||
235 | if (kill(-1, SIGSTOP) < 0 && errno != ESRCH) | |
56f64d95 | 236 | log_warning_errno(errno, "kill(-1, SIGSTOP) failed: %m"); |
bd3fa1d2 | 237 | |
0bee65f0 | 238 | killall(sig, pids, send_sighup); |
bd3fa1d2 LP |
239 | |
240 | if (kill(-1, SIGCONT) < 0 && errno != ESRCH) | |
56f64d95 | 241 | log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m"); |
bd3fa1d2 | 242 | |
3d141780 | 243 | if (wait_for_exit) |
aaf7eb81 LP |
244 | wait_for_children(pids, &mask); |
245 | ||
246 | assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0); | |
bd3fa1d2 | 247 | } |