]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/killall.c
update TODO
[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 * https://systemd.io/ROOT_STORAGE_DAEMONS */
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 _cleanup_free_ char *lst_child = NULL;
82 Iterator i;
83 void *p;
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 (void) asprintf(&s, PID_FMT, PTR_TO_PID(p));
90
91 if (!strextend(&lst_child, ", ", s, NULL)) {
92 log_oom();
93 return;
94 }
95 }
96
97 if (isempty(lst_child))
98 return;
99
100 log_warning("Waiting for process: %s", lst_child + 2);
101 }
102
103 static int wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
104 usec_t until, date_log_child, n;
105
106 assert(mask);
107
108 /* Return the number of children remaining in the pids set: That correspond to the number
109 * of processes still "alive" after the timeout */
110
111 if (set_isempty(pids))
112 return 0;
113
114 n = now(CLOCK_MONOTONIC);
115 until = usec_add(n, timeout);
116 date_log_child = usec_add(n, 10u * USEC_PER_SEC);
117 if (date_log_child > until)
118 date_log_child = usec_add(n, timeout / 2u);
119
120 for (;;) {
121 struct timespec ts;
122 int k;
123 void *p;
124 Iterator i;
125
126 /* First, let the kernel inform us about killed
127 * children. Most processes will probably be our
128 * children, but some are not (might be our
129 * grandchildren instead...). */
130 for (;;) {
131 pid_t pid;
132
133 pid = waitpid(-1, NULL, WNOHANG);
134 if (pid == 0)
135 break;
136 if (pid < 0) {
137 if (errno == ECHILD)
138 break;
139
140 return log_error_errno(errno, "waitpid() failed: %m");
141 }
142
143 (void) set_remove(pids, PID_TO_PTR(pid));
144 }
145
146 /* Now explicitly check who might be remaining, who
147 * might not be our child. */
148 SET_FOREACH(p, pids, i) {
149
150 /* kill(pid, 0) sends no signal, but it tells
151 * us whether the process still exists. */
152 if (kill(PTR_TO_PID(p), 0) == 0)
153 continue;
154
155 if (errno != ESRCH)
156 continue;
157
158 set_remove(pids, p);
159 }
160
161 if (set_isempty(pids))
162 return 0;
163
164 n = now(CLOCK_MONOTONIC);
165 if (date_log_child > 0 && n >= date_log_child) {
166 log_children_no_yet_killed(pids);
167 /* Log the children not yet killed only once */
168 date_log_child = 0;
169 }
170
171 if (n >= until)
172 return set_size(pids);
173
174 if (date_log_child > 0)
175 timespec_store(&ts, MIN(until - n, date_log_child - n));
176 else
177 timespec_store(&ts, until - n);
178
179 k = sigtimedwait(mask, NULL, &ts);
180 if (k != SIGCHLD) {
181
182 if (k < 0 && errno != EAGAIN)
183 return log_error_errno(errno, "sigtimedwait() failed: %m");
184
185 if (k >= 0)
186 log_warning("sigtimedwait() returned unexpected signal.");
187 }
188 }
189 }
190
191 static int killall(int sig, Set *pids, bool send_sighup) {
192 _cleanup_closedir_ DIR *dir = NULL;
193 struct dirent *d;
194 int n_killed = 0;
195
196 /* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
197 * Returns the number of processes to which the specified signal was sent */
198
199 dir = opendir("/proc");
200 if (!dir)
201 return log_warning_errno(errno, "opendir(/proc) failed: %m");
202
203 FOREACH_DIRENT_ALL(d, dir, break) {
204 pid_t pid;
205 int r;
206
207 if (!IN_SET(d->d_type, DT_DIR, DT_UNKNOWN))
208 continue;
209
210 if (parse_pid(d->d_name, &pid) < 0)
211 continue;
212
213 if (ignore_proc(pid, sig == SIGKILL && !in_initrd()))
214 continue;
215
216 if (sig == SIGKILL) {
217 _cleanup_free_ char *s = NULL;
218
219 get_process_comm(pid, &s);
220 log_notice("Sending SIGKILL to PID "PID_FMT" (%s).", pid, strna(s));
221 }
222
223 if (kill(pid, sig) >= 0) {
224 n_killed++;
225 if (pids) {
226 r = set_put(pids, PID_TO_PTR(pid));
227 if (r < 0)
228 log_oom();
229 }
230 } else if (errno != ENOENT)
231 log_warning_errno(errno, "Could not kill %d: %m", pid);
232
233 if (send_sighup) {
234 /* Optionally, also send a SIGHUP signal, but
235 only if the process has a controlling
236 tty. This is useful to allow handling of
237 shells which ignore SIGTERM but react to
238 SIGHUP. We do not send this to processes that
239 have no controlling TTY since we don't want to
240 trigger reloads of daemon processes. Also we
241 make sure to only send this after SIGTERM so
242 that SIGTERM is always first in the queue. */
243
244 if (get_ctty_devnr(pid, NULL) >= 0)
245 /* it's OK if the process is gone, just ignore the result */
246 (void) kill(pid, SIGHUP);
247 }
248 }
249
250 return n_killed;
251 }
252
253 int broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout) {
254 int n_children_left;
255 sigset_t mask, oldmask;
256 _cleanup_set_free_ Set *pids = NULL;
257
258 /* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
259 * Return:
260 * - The number of processes still "alive" after the timeout (that should have been killed)
261 * if the function needs to wait for the end of the processes (wait_for_exit).
262 * - Otherwise, the number of processes to which the specified signal was sent */
263
264 if (wait_for_exit)
265 pids = set_new(NULL);
266
267 assert_se(sigemptyset(&mask) == 0);
268 assert_se(sigaddset(&mask, SIGCHLD) == 0);
269 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
270
271 if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
272 log_warning_errno(errno, "kill(-1, SIGSTOP) failed: %m");
273
274 n_children_left = killall(sig, pids, send_sighup);
275
276 if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
277 log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m");
278
279 if (wait_for_exit && n_children_left > 0)
280 n_children_left = wait_for_children(pids, &mask, timeout);
281
282 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
283
284 return n_children_left;
285 }