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