]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/killall.c
tree-wide: beautify remaining copyright statements
[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 char c;
27 const char *p;
28 size_t count;
29 uid_t uid;
30 int r;
31
32 /* We are PID 1, let's not commit suicide */
33 if (pid == 1)
34 return true;
35
36 r = get_process_uid(pid, &uid);
37 if (r < 0)
38 return true; /* not really, but better safe than sorry */
39
40 /* Non-root processes otherwise are always subject to be killed */
41 if (uid != 0)
42 return false;
43
44 p = procfs_file_alloca(pid, "cmdline");
45 f = fopen(p, "re");
46 if (!f)
47 return true; /* not really, but has the desired effect */
48
49 count = fread(&c, 1, 1, f);
50
51 /* Kernel threads have an empty cmdline */
52 if (count <= 0)
53 return true;
54
55 /* Processes with argv[0][0] = '@' we ignore from the killing spree.
56 *
57 * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */
58 if (c != '@')
59 return false;
60
61 if (warn_rootfs &&
62 pid_from_same_root_fs(pid) == 0) {
63
64 _cleanup_free_ char *comm = NULL;
65
66 get_process_comm(pid, &comm);
67
68 log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is "
69 "running from the root file system, and thus likely to block re-mounting of the "
70 "root file system to read-only. Please consider moving it into an initrd file "
71 "system instead.", pid, strna(comm));
72 }
73
74 return true;
75 }
76
77 static void wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
78 usec_t until;
79
80 assert(mask);
81
82 if (set_isempty(pids))
83 return;
84
85 until = now(CLOCK_MONOTONIC) + timeout;
86 for (;;) {
87 struct timespec ts;
88 int k;
89 usec_t n;
90 void *p;
91 Iterator i;
92
93 /* First, let the kernel inform us about killed
94 * children. Most processes will probably be our
95 * children, but some are not (might be our
96 * grandchildren instead...). */
97 for (;;) {
98 pid_t pid;
99
100 pid = waitpid(-1, NULL, WNOHANG);
101 if (pid == 0)
102 break;
103 if (pid < 0) {
104 if (errno == ECHILD)
105 break;
106
107 log_error_errno(errno, "waitpid() failed: %m");
108 return;
109 }
110
111 (void) set_remove(pids, PID_TO_PTR(pid));
112 }
113
114 /* Now explicitly check who might be remaining, who
115 * might not be our child. */
116 SET_FOREACH(p, pids, i) {
117
118 /* kill(pid, 0) sends no signal, but it tells
119 * us whether the process still exists. */
120 if (kill(PTR_TO_PID(p), 0) == 0)
121 continue;
122
123 if (errno != ESRCH)
124 continue;
125
126 set_remove(pids, p);
127 }
128
129 if (set_isempty(pids))
130 return;
131
132 n = now(CLOCK_MONOTONIC);
133 if (n >= until)
134 return;
135
136 timespec_store(&ts, until - n);
137 k = sigtimedwait(mask, NULL, &ts);
138 if (k != SIGCHLD) {
139
140 if (k < 0 && errno != EAGAIN) {
141 log_error_errno(errno, "sigtimedwait() failed: %m");
142 return;
143 }
144
145 if (k >= 0)
146 log_warning("sigtimedwait() returned unexpected signal.");
147 }
148 }
149 }
150
151 static int killall(int sig, Set *pids, bool send_sighup) {
152 _cleanup_closedir_ DIR *dir = NULL;
153 struct dirent *d;
154
155 dir = opendir("/proc");
156 if (!dir)
157 return -errno;
158
159 FOREACH_DIRENT_ALL(d, dir, break) {
160 pid_t pid;
161 int r;
162
163 if (!IN_SET(d->d_type, DT_DIR, DT_UNKNOWN))
164 continue;
165
166 if (parse_pid(d->d_name, &pid) < 0)
167 continue;
168
169 if (ignore_proc(pid, sig == SIGKILL && !in_initrd()))
170 continue;
171
172 if (sig == SIGKILL) {
173 _cleanup_free_ char *s = NULL;
174
175 get_process_comm(pid, &s);
176 log_notice("Sending SIGKILL to PID "PID_FMT" (%s).", pid, strna(s));
177 }
178
179 if (kill(pid, sig) >= 0) {
180 if (pids) {
181 r = set_put(pids, PID_TO_PTR(pid));
182 if (r < 0)
183 log_oom();
184 }
185 } else if (errno != ENOENT)
186 log_warning_errno(errno, "Could not kill %d: %m", pid);
187
188 if (send_sighup) {
189 /* Optionally, also send a SIGHUP signal, but
190 only if the process has a controlling
191 tty. This is useful to allow handling of
192 shells which ignore SIGTERM but react to
193 SIGHUP. We do not send this to processes that
194 have no controlling TTY since we don't want to
195 trigger reloads of daemon processes. Also we
196 make sure to only send this after SIGTERM so
197 that SIGTERM is always first in the queue. */
198
199 if (get_ctty_devnr(pid, NULL) >= 0)
200 /* it's OK if the process is gone, just ignore the result */
201 (void) kill(pid, SIGHUP);
202 }
203 }
204
205 return set_size(pids);
206 }
207
208 void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout) {
209 sigset_t mask, oldmask;
210 _cleanup_set_free_ Set *pids = NULL;
211
212 if (wait_for_exit)
213 pids = set_new(NULL);
214
215 assert_se(sigemptyset(&mask) == 0);
216 assert_se(sigaddset(&mask, SIGCHLD) == 0);
217 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
218
219 if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
220 log_warning_errno(errno, "kill(-1, SIGSTOP) failed: %m");
221
222 killall(sig, pids, send_sighup);
223
224 if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
225 log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m");
226
227 if (wait_for_exit)
228 wait_for_children(pids, &mask, timeout);
229
230 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
231 }