chiark / gitweb /
core/killall: use procfs_file_alloca
[elogind.git] / src / core / killall.c
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
22 #include <sys/wait.h>
23 #include <signal.h>
24 #include <errno.h>
25 #include <unistd.h>
26
27 #include "util.h"
28 #include "def.h"
29 #include "killall.h"
30 #include "set.h"
31
32 #define TIMEOUT_USEC (10 * USEC_PER_SEC)
33
34 static bool ignore_proc(pid_t pid) {
35         _cleanup_fclose_ FILE *f = NULL;
36         char c;
37         size_t count;
38         uid_t uid;
39         int r;
40
41         /* We are PID 1, let's not commit suicide */
42         if (pid == 1)
43                 return true;
44
45         r = get_process_uid(pid, &uid);
46         if (r < 0)
47                 return true; /* not really, but better safe than sorry */
48
49         /* Non-root processes otherwise are always subject to be killed */
50         if (uid != 0)
51                 return false;
52
53         f = fopen(procfs_file_alloca(pid, "cmdline"), "re");
54         if (!f)
55                 return true; /* not really, but has the desired effect */
56
57         count = fread(&c, 1, 1, f);
58
59         /* Kernel threads have an empty cmdline */
60         if (count <= 0)
61                 return true;
62
63         /* Processes with argv[0][0] = '@' we ignore from the killing
64          * spree.
65          *
66          * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */
67         if (count == 1 && c == '@')
68                 return true;
69
70         return false;
71 }
72
73 static void wait_for_children(Set *pids, sigset_t *mask) {
74         usec_t until;
75
76         assert(mask);
77
78         if (set_isempty(pids))
79                 return;
80
81         until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
82         for (;;) {
83                 struct timespec ts;
84                 int k;
85                 usec_t n;
86                 void *p;
87                 Iterator i;
88
89                 /* First, let the kernel inform us about killed
90                  * children. Most processes will probably be our
91                  * children, but some are not (might be our
92                  * grandchildren instead...). */
93                 for (;;) {
94                         pid_t pid;
95
96                         pid = waitpid(-1, NULL, WNOHANG);
97                         if (pid == 0)
98                                 break;
99                         if (pid < 0) {
100                                 if (errno == ECHILD)
101                                         break;
102
103                                 log_error("waitpid() failed: %m");
104                                 return;
105                         }
106
107                         set_remove(pids, ULONG_TO_PTR(pid));
108                 }
109
110                 /* Now explicitly check who might be remaining, who
111                  * might not be our child. */
112                 SET_FOREACH(p, pids, i) {
113
114                         /* We misuse getpgid as a check whether a
115                          * process still exists. */
116                         if (getpgid((pid_t) PTR_TO_ULONG(p)) >= 0)
117                                 continue;
118
119                         if (errno != ESRCH)
120                                 continue;
121
122                         set_remove(pids, p);
123                 }
124
125                 if (set_isempty(pids))
126                         return;
127
128                 n = now(CLOCK_MONOTONIC);
129                 if (n >= until)
130                         return;
131
132                 timespec_store(&ts, until - n);
133                 k = sigtimedwait(mask, NULL, &ts);
134                 if (k != SIGCHLD) {
135
136                         if (k < 0 && errno != EAGAIN) {
137                                 log_error("sigtimedwait() failed: %m");
138                                 return;
139                         }
140
141                         if (k >= 0)
142                                 log_warning("sigtimedwait() returned unexpected signal.");
143                 }
144         }
145 }
146
147 static int killall(int sig, Set *pids) {
148         _cleanup_closedir_ DIR *dir = NULL;
149         struct dirent *d;
150
151         dir = opendir("/proc");
152         if (!dir)
153                 return -errno;
154
155         while ((d = readdir(dir))) {
156                 pid_t pid;
157
158                 if (d->d_type != DT_DIR &&
159                     d->d_type != DT_UNKNOWN)
160                         continue;
161
162                 if (parse_pid(d->d_name, &pid) < 0)
163                         continue;
164
165                 if (ignore_proc(pid))
166                         continue;
167
168                 if (sig == SIGKILL) {
169                         _cleanup_free_ char *s;
170
171                         get_process_comm(pid, &s);
172                         log_notice("Sending SIGKILL to PID %lu (%s).", (unsigned long) pid, strna(s));
173                 }
174
175                 if (kill(pid, sig) >= 0) {
176                         if (pids)
177                                 set_put(pids, ULONG_TO_PTR((unsigned long) pid));
178                 } else if (errno != ENOENT)
179                         log_warning("Could not kill %d: %m", pid);
180         }
181
182         return set_size(pids);
183 }
184
185 void broadcast_signal(int sig, bool wait_for_exit) {
186         sigset_t mask, oldmask;
187         Set *pids = NULL;
188
189         if (wait_for_exit)
190                 pids = set_new(trivial_hash_func, trivial_compare_func);
191
192         assert_se(sigemptyset(&mask) == 0);
193         assert_se(sigaddset(&mask, SIGCHLD) == 0);
194         assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
195
196         if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
197                 log_warning("kill(-1, SIGSTOP) failed: %m");
198
199         killall(sig, pids);
200
201         if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
202                 log_warning("kill(-1, SIGCONT) failed: %m");
203
204         if (wait_for_exit)
205                 wait_for_children(pids, &mask);
206
207         assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
208
209         set_free(pids);
210 }