• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14554080340

19 Apr 2025 11:46AM UTC coverage: 72.101% (-0.03%) from 72.13%
14554080340

push

github

web-flow
Add two new paragraphs to coding style about header files (#37188)

296880 of 411754 relevant lines covered (72.1%)

687547.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

44.97
/src/shared/killall.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
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 "constants.h"
13
#include "dirent-util.h"
14
#include "errno-util.h"
15
#include "fd-util.h"
16
#include "format-util.h"
17
#include "initrd-util.h"
18
#include "killall.h"
19
#include "log.h"
20
#include "parse-util.h"
21
#include "process-util.h"
22
#include "set.h"
23
#include "stdio-util.h"
24
#include "string-util.h"
25
#include "terminal-util.h"
26

27
static int argv_has_at(const PidRef *pid) {
45✔
28
        int r;
45✔
29

30
        assert(pidref_is_set(pid));
45✔
31
        assert(!pidref_is_remote(pid));
45✔
32

33
        const char *p = procfs_file_alloca(pid->pid, "cmdline");
45✔
34
        _cleanup_fclose_ FILE *f = fopen(p, "re");
90✔
35
        if (!f)
45✔
36
                return log_debug_errno(errno, "Failed to open %s, ignoring: %m", p);
×
37

38
        /* Try to read the first character of the command line. If the cmdline is empty (which might be the case for
39
         * kernel threads but potentially also other stuff), this line won't do anything, but we don't care much, as
40
         * actual kernel threads are already filtered out above. */
41
        char c = 0;
45✔
42
        (void) fread(&c, 1, 1, f);
45✔
43

44
        r = pidref_verify(pid);
45✔
45
        if (r < 0)
45✔
46
                return log_debug_errno(r, "Failed to verify pid " PID_FMT ", ignoring: %m", pid->pid);
×
47

48
        /* Processes with argv[0][0] = '@' we ignore from the killing spree.
49
         *
50
         * https://systemd.io/ROOT_STORAGE_DAEMONS */
51
        return c == '@';
45✔
52
}
53

54
static bool is_in_survivor_cgroup(const PidRef *pid) {
45✔
55
        _cleanup_free_ char *cgroup_path = NULL;
45✔
56
        int r;
45✔
57

58
        assert(pidref_is_set(pid));
45✔
59

60
        r = cg_pidref_get_path(/* root= */ NULL, pid, &cgroup_path);
45✔
61
        if (r == -EUNATCH) {
45✔
62
                log_warning_errno(r, "Process " PID_FMT " appears to originate in foreign namespace, ignoring.", pid->pid);
×
63
                return true;
×
64
        }
65
        if (r < 0) {
45✔
66
                log_warning_errno(r, "Failed to get cgroup path of process " PID_FMT ", ignoring: %m", pid->pid);
×
67
                return false;
×
68
        }
69

70
        r = cg_get_xattr_bool(cgroup_path, "user.survive_final_kill_signal");
45✔
71
        /* user xattr support was added to kernel v5.7, try with the trusted namespace as a fallback */
72
        if (ERRNO_IS_NEG_XATTR_ABSENT(r))
45✔
73
                r = cg_get_xattr_bool(cgroup_path, "trusted.survive_final_kill_signal");
45✔
74
        if (r < 0 && !ERRNO_IS_NEG_XATTR_ABSENT(r))
45✔
75
                log_debug_errno(r,
×
76
                                "Failed to get survive_final_kill_signal xattr of %s, ignoring: %m",
77
                                cgroup_path);
78

79
        return r > 0;
45✔
80
}
81

82
static bool ignore_proc(PidRef *pid, bool warn_rootfs) {
933✔
83
        uid_t uid;
933✔
84

85
        assert(pidref_is_set(pid));
933✔
86

87
        /* We are PID 1, let's not commit suicide */
88
        if (pid->pid == 1)
933✔
89
                return true;
933✔
90

91
        /* Ignore kernel threads */
92
        if (pidref_is_kernel_thread(pid) != 0)
924✔
93
                return true; /* also ignore processes where we can't determine this */
94

95
        /* Ignore processes that are part of a cgroup marked with the user.survive_final_kill_signal xattr */
96
        if (is_in_survivor_cgroup(pid))
45✔
97
                return true;
98

99
        if (pidref_get_uid(pid, &uid) < 0)
45✔
100
                return true; /* not really, but better safe than sorry */
101

102
        /* Non-root processes otherwise are always subject to be killed */
103
        if (uid != 0)
45✔
104
                return false;
105

106
        if (argv_has_at(pid) == 0)
45✔
107
                return false; /* if this fails, ignore the process */
108

109
        if (warn_rootfs &&
×
110
            pidref_from_same_root_fs(pid, NULL) > 0) {
×
111
                _cleanup_free_ char *comm = NULL;
×
112

113
                (void) pidref_get_comm(pid, &comm);
×
114

115
                log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is "
×
116
                           "running from the root file system, and thus likely to block re-mounting of the "
117
                           "root file system to read-only. Please consider moving it into an initrd file "
118
                           "system instead.", pid->pid, strna(comm));
119
        }
120

121
        return true;
122
}
123

124
static void log_children_not_yet_killed(Set *pids) {
×
125
        _cleanup_free_ char *lst_child = NULL;
×
126
        int r;
×
127

128
        void *p;
×
129
        SET_FOREACH(p, pids) {
×
130
                _cleanup_free_ char *s = NULL;
×
131

132
                if (pid_get_comm(PTR_TO_PID(p), &s) >= 0)
×
133
                        r = strextendf_with_separator(&lst_child, ", ", PID_FMT " (%s)", PTR_TO_PID(p), s);
×
134
                else
135
                        r = strextendf_with_separator(&lst_child, ", ", PID_FMT, PTR_TO_PID(p));
×
136
                if (r < 0)
×
137
                        return (void) log_oom_warning();
×
138
        }
139

140
        if (isempty(lst_child))
×
141
                return;
142

143
        log_warning("Waiting for process: %s", lst_child);
×
144
}
145

146
static int wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
×
147
        usec_t until, date_log_child, n;
×
148

149
        assert(mask);
×
150

151
        /* Return the number of children remaining in the pids set: That correspond to the number
152
         * of processes still "alive" after the timeout */
153

154
        if (set_isempty(pids))
×
155
                return 0;
156

157
        n = now(CLOCK_MONOTONIC);
×
158
        until = usec_add(n, timeout);
×
159
        date_log_child = usec_add(n, 10u * USEC_PER_SEC);
×
160
        if (date_log_child > until)
×
161
                date_log_child = usec_add(n, timeout / 2u);
×
162

163
        for (;;) {
×
164
                struct timespec ts;
×
165
                int k;
×
166
                void *p;
×
167

168
                /* First, let the kernel inform us about killed
169
                 * children. Most processes will probably be our
170
                 * children, but some are not (might be our
171
                 * grandchildren instead...). */
172
                for (;;) {
×
173
                        pid_t pid;
×
174

175
                        pid = waitpid(-1, NULL, WNOHANG);
×
176
                        if (pid == 0)
×
177
                                break;
178
                        if (pid < 0) {
×
179
                                if (errno == ECHILD)
×
180
                                        break;
181

182
                                return log_error_errno(errno, "waitpid() failed: %m");
×
183
                        }
184

185
                        (void) set_remove(pids, PID_TO_PTR(pid));
×
186
                }
187

188
                /* Now explicitly check who might be remaining, who
189
                 * might not be our child. */
190
                SET_FOREACH(p, pids) {
×
191

192
                        /* kill(pid, 0) sends no signal, but it tells
193
                         * us whether the process still exists. */
194
                        if (kill(PTR_TO_PID(p), 0) == 0)
×
195
                                continue;
×
196

197
                        if (errno != ESRCH)
×
198
                                continue;
×
199

200
                        set_remove(pids, p);
×
201
                }
202

203
                if (set_isempty(pids))
×
204
                        return 0;
205

206
                n = now(CLOCK_MONOTONIC);
×
207
                if (date_log_child > 0 && n >= date_log_child) {
×
208
                        log_children_not_yet_killed(pids);
×
209
                        /* Log the children not yet killed only once */
210
                        date_log_child = 0;
211
                }
212

213
                if (n >= until)
×
214
                        return set_size(pids);
×
215

216
                if (date_log_child > 0)
×
217
                        timespec_store(&ts, MIN(until - n, date_log_child - n));
×
218
                else
219
                        timespec_store(&ts, until - n);
×
220

221
                k = sigtimedwait(mask, NULL, &ts);
×
222
                if (k != SIGCHLD) {
×
223

224
                        if (k < 0 && errno != EAGAIN)
×
225
                                return log_error_errno(errno, "sigtimedwait() failed: %m");
×
226

227
                        if (k >= 0)
228
                                log_warning("sigtimedwait() returned unexpected signal.");
×
229
                }
230
        }
231
}
232

233
static int killall(int sig, Set *pids, bool send_sighup) {
9✔
234
        _cleanup_closedir_ DIR *dir = NULL;
9✔
235
        int n_killed = 0, r;
9✔
236

237
        /* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
238
         * Returns the number of processes to which the specified signal was sent */
239

240
        r = proc_dir_open(&dir);
9✔
241
        if (r < 0)
9✔
242
                return log_warning_errno(r, "Failed to open /proc/: %m");
×
243

244
        for (;;) {
942✔
245
                _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
54✔
246

247
                r = proc_dir_read_pidref(dir, &pidref);
942✔
248
                if (r < 0)
942✔
249
                        return log_warning_errno(r, "Failed to enumerate /proc/: %m");
×
250
                if (r == 0)
942✔
251
                        break;
252

253
                if (ignore_proc(&pidref, sig == SIGKILL && !in_initrd()))
933✔
254
                        continue;
888✔
255

256
                if (sig == SIGKILL) {
45✔
257
                        _cleanup_free_ char *s = NULL;
×
258

259
                        (void) pidref_get_comm(&pidref, &s);
×
260
                        log_notice("Sending SIGKILL to PID "PID_FMT" (%s).", pidref.pid, strna(s));
×
261
                }
262

263
                r = pidref_kill(&pidref, sig);
45✔
264
                if (r < 0) {
45✔
265
                        if (r != -ESRCH)
×
266
                                log_warning_errno(errno, "Could not kill " PID_FMT ", ignoring: %m", pidref.pid);
×
267
                } else {
268
                        n_killed++;
45✔
269
                        if (pids) {
45✔
270
                                r = set_put(pids, PID_TO_PTR(pidref.pid));
×
271
                                if (r < 0)
×
272
                                        (void) log_oom_warning();
×
273
                        }
274
                }
275

276
                if (send_sighup) {
45✔
277
                        /* Optionally, also send a SIGHUP signal, but only if the process has a controlling
278
                         * tty. This is useful to allow handling of shells which ignore SIGTERM but react to
279
                         * SIGHUP. We do not send this to processes that have no controlling TTY since we
280
                         * don't want to trigger reloads of daemon processes. Also we make sure to only send
281
                         * this after SIGTERM so that SIGTERM is always first in the queue. */
282

283
                        if (get_ctty_devnr(pidref.pid, NULL) >= 0)
45✔
284
                                /* it's OK if the process is gone, just ignore the result */
285
                                (void) pidref_kill(&pidref, SIGHUP);
×
286
                }
287
        }
288

289
        return n_killed;
9✔
290
}
291

292
int broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout) {
9✔
293
        int n_children_left;
9✔
294
        sigset_t mask, oldmask;
9✔
295
        _cleanup_set_free_ Set *pids = NULL;
×
296

297
        /* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
298
         * Return:
299
         *  - The number of processes still "alive" after the timeout (that should have been killed)
300
         *    if the function needs to wait for the end of the processes (wait_for_exit).
301
         *  - Otherwise, the number of processes to which the specified signal was sent */
302

303
        if (wait_for_exit)
9✔
304
                pids = set_new(NULL);
×
305

306
        assert_se(sigemptyset(&mask) == 0);
9✔
307
        assert_se(sigaddset(&mask, SIGCHLD) == 0);
9✔
308
        assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
9✔
309

310
        if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
9✔
311
                log_warning_errno(errno, "kill(-1, SIGSTOP) failed: %m");
×
312

313
        n_children_left = killall(sig, pids, send_sighup);
9✔
314

315
        if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
9✔
316
                log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m");
×
317

318
        if (wait_for_exit && n_children_left > 0)
9✔
319
                n_children_left = wait_for_children(pids, &mask, timeout);
×
320

321
        assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
9✔
322

323
        return n_children_left;
9✔
324
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc