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