]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/killall.c
Merge pull request #12510 from keszybz/test-directives
[thirdparty/systemd.git] / src / core / killall.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright © 2010 ProFUSION embedded systems
4 ***/
5
6 #include <errno.h>
7 #include <signal.h>
8 #include <sys/wait.h>
9 #include <unistd.h>
10
11 #include "alloc-util.h"
12 #include "def.h"
13 #include "dirent-util.h"
14 #include "fd-util.h"
15 #include "format-util.h"
16 #include "killall.h"
17 #include "parse-util.h"
18 #include "process-util.h"
19 #include "set.h"
20 #include "string-util.h"
21 #include "terminal-util.h"
22 #include "util.h"
23
24 static bool ignore_proc(pid_t pid, bool warn_rootfs) {
25 _cleanup_fclose_ FILE *f = NULL;
26 const char *p;
27 char c = 0;
28 uid_t uid;
29 int r;
30
31 /* We are PID 1, let's not commit suicide */
32 if (pid <= 1)
33 return true;
34
35 /* Ignore kernel threads */
36 r = is_kernel_thread(pid);
37 if (r != 0)
38 return true; /* also ignore processes where we can't determine this */
39
40 r = get_process_uid(pid, &uid);
41 if (r < 0)
42 return true; /* not really, but better safe than sorry */
43
44 /* Non-root processes otherwise are always subject to be killed */
45 if (uid != 0)
46 return false;
47
48 p = procfs_file_alloca(pid, "cmdline");
49 f = fopen(p, "re");
50 if (!f)
51 return true; /* not really, but has the desired effect */
52
53 /* Try to read the first character of the command line. If the cmdline is empty (which might be the case for
54 * kernel threads but potentially also other stuff), this line won't do anything, but we don't care much, as
55 * actual kernel threads are already filtered out above. */
56 (void) fread(&c, 1, 1, f);
57
58 /* Processes with argv[0][0] = '@' we ignore from the killing spree.
59 *
60 * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */
61 if (c != '@')
62 return false;
63
64 if (warn_rootfs &&
65 pid_from_same_root_fs(pid) == 0) {
66
67 _cleanup_free_ char *comm = NULL;
68
69 (void) get_process_comm(pid, &comm);
70
71 log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is "
72 "running from the root file system, and thus likely to block re-mounting of the "
73 "root file system to read-only. Please consider moving it into an initrd file "
74 "system instead.", pid, strna(comm));
75 }
76
77 return true;
78 }
79
80 static void log_children_no_yet_killed(Set *pids) {
81 void *p;
82 Iterator i;
83 _cleanup_free_ char *lst_child = NULL;
84
85 SET_FOREACH(p, pids, i) {
86 _cleanup_free_ char *s = NULL;
87
88 if (get_process_comm(PTR_TO_PID(p), &s) == 0) {
89 if (!strextend(&lst_child, ", ", s, NULL))
90 break;
91 }
92 }
93
94 if (!isempty(lst_child))
95 log_notice("Waiting for process:%s", lst_child + 1);
96 }
97
98 static int wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
99 usec_t until;
100 usec_t date_log_child;
101 usec_t n;
102
103 assert(mask);
104
105 /* Return the number of children remaining in the pids set: That correspond to the number
106 * of processes still "alive" after the timeout */
107
108 if (set_isempty(pids))
109 return 0;
110
111 n = now(CLOCK_MONOTONIC);
112 until = usec_add(n, timeout);
113 date_log_child = usec_add(n, 10u * USEC_PER_SEC);
114 if (date_log_child > until)
115 date_log_child = usec_add(n, timeout / 2u);
116
117 for (;;) {
118 struct timespec ts;
119 int k;
120 void *p;
121 Iterator i;
122
123 /* First, let the kernel inform us about killed
124 * children. Most processes will probably be our
125 * children, but some are not (might be our
126 * grandchildren instead...). */
127 for (;;) {
128 pid_t pid;
129
130 pid = waitpid(-1, NULL, WNOHANG);
131 if (pid == 0)
132 break;
133 if (pid < 0) {
134 if (errno == ECHILD)
135 break;
136
137 return log_error_errno(errno, "waitpid() failed: %m");
138 }
139
140 (void) set_remove(pids, PID_TO_PTR(pid));
141 }
142
143 /* Now explicitly check who might be remaining, who
144 * might not be our child. */
145 SET_FOREACH(p, pids, i) {
146
147 /* kill(pid, 0) sends no signal, but it tells
148 * us whether the process still exists. */
149 if (kill(PTR_TO_PID(p), 0) == 0)
150 continue;
151
152 if (errno != ESRCH)
153 continue;
154
155 set_remove(pids, p);
156 }
157
158 if (set_isempty(pids))
159 return 0;
160
161 n = now(CLOCK_MONOTONIC);
162 if (date_log_child > 0 && n >= date_log_child) {
163 log_children_no_yet_killed(pids);
164 /* Log the children not yet killed only once */
165 date_log_child = 0;
166 }
167
168 if (n >= until)
169 return set_size(pids);
170
171 if (date_log_child > 0)
172 timespec_store(&ts, MIN(until - n, date_log_child - n));
173 else
174 timespec_store(&ts, until - n);
175
176 k = sigtimedwait(mask, NULL, &ts);
177 if (k != SIGCHLD) {
178
179 if (k < 0 && errno != EAGAIN)
180 return log_error_errno(errno, "sigtimedwait() failed: %m");
181
182 if (k >= 0)
183 log_warning("sigtimedwait() returned unexpected signal.");
184 }
185 }
186 }
187
188 static int killall(int sig, Set *pids, bool send_sighup) {
189 _cleanup_closedir_ DIR *dir = NULL;
190 struct dirent *d;
191 int n_killed = 0;
192
193 /* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
194 * Returns the number of processes to which the specified signal was sent */
195
196 dir = opendir("/proc");
197 if (!dir)
198 return log_warning_errno(errno, "opendir(/proc) failed: %m");
199
200 FOREACH_DIRENT_ALL(d, dir, break) {
201 pid_t pid;
202 int r;
203
204 if (!IN_SET(d->d_type, DT_DIR, DT_UNKNOWN))
205 continue;
206
207 if (parse_pid(d->d_name, &pid) < 0)
208 continue;
209
210 if (ignore_proc(pid, sig == SIGKILL && !in_initrd()))
211 continue;
212
213 if (sig == SIGKILL) {
214 _cleanup_free_ char *s = NULL;
215
216 get_process_comm(pid, &s);
217 log_notice("Sending SIGKILL to PID "PID_FMT" (%s).", pid, strna(s));
218 }
219
220 if (kill(pid, sig) >= 0) {
221 n_killed++;
222 if (pids) {
223 r = set_put(pids, PID_TO_PTR(pid));
224 if (r < 0)
225 log_oom();
226 }
227 } else if (errno != ENOENT)
228 log_warning_errno(errno, "Could not kill %d: %m", pid);
229
230 if (send_sighup) {
231 /* Optionally, also send a SIGHUP signal, but
232 only if the process has a controlling
233 tty. This is useful to allow handling of
234 shells which ignore SIGTERM but react to
235 SIGHUP. We do not send this to processes that
236 have no controlling TTY since we don't want to
237 trigger reloads of daemon processes. Also we
238 make sure to only send this after SIGTERM so
239 that SIGTERM is always first in the queue. */
240
241 if (get_ctty_devnr(pid, NULL) >= 0)
242 /* it's OK if the process is gone, just ignore the result */
243 (void) kill(pid, SIGHUP);
244 }
245 }
246
247 return n_killed;
248 }
249
250 int broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout) {
251 int n_children_left;
252 sigset_t mask, oldmask;
253 _cleanup_set_free_ Set *pids = NULL;
254
255 /* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
256 * Return:
257 * - The number of processes still "alive" after the timeout (that should have been killed)
258 * if the function needs to wait for the end of the processes (wait_for_exit).
259 * - Otherwise, the number of processes to which the specified signal was sent */
260
261 if (wait_for_exit)
262 pids = set_new(NULL);
263
264 assert_se(sigemptyset(&mask) == 0);
265 assert_se(sigaddset(&mask, SIGCHLD) == 0);
266 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
267
268 if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
269 log_warning_errno(errno, "kill(-1, SIGSTOP) failed: %m");
270
271 n_children_left = killall(sig, pids, send_sighup);
272
273 if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
274 log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m");
275
276 if (wait_for_exit && n_children_left > 0)
277 n_children_left = wait_for_children(pids, &mask, timeout);
278
279 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
280
281 return n_children_left;
282 }