]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/basic/pidref.c
basic/include: replace _Static_assert() with static_assert()
[thirdparty/systemd.git] / src / basic / pidref.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <sys/wait.h>
4#include <unistd.h>
5
6#include "alloc-util.h"
7#include "errno-util.h"
8#include "fd-util.h"
9#include "format-util.h"
10#include "hash-funcs.h"
11#include "log.h"
12#include "parse-util.h"
13#include "pidfd-util.h"
14#include "pidref.h"
15#include "process-util.h"
16#include "siphash24.h"
17
18int pidref_acquire_pidfd_id(PidRef *pidref) {
19 int r;
20
21 assert(pidref);
22
23 if (!pidref_is_set(pidref))
24 return -ESRCH;
25
26 if (pidref_is_remote(pidref))
27 return -EREMOTE;
28
29 if (pidref->fd < 0)
30 return -ENOMEDIUM;
31
32 if (pidref->fd_id > 0)
33 return 0;
34
35 r = pidfd_get_inode_id(pidref->fd, &pidref->fd_id);
36 if (r < 0) {
37 if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
38 log_debug_errno(r, "Failed to get inode number of pidfd for pid " PID_FMT ": %m",
39 pidref->pid);
40 return r;
41 }
42
43 return 0;
44}
45
46bool pidref_equal(PidRef *a, PidRef *b) {
47
48 /* If this is the very same structure, it definitely refers to the same process */
49 if (a == b)
50 return true;
51
52 if (!pidref_is_set(a))
53 return !pidref_is_set(b);
54
55 if (!pidref_is_set(b))
56 return false;
57
58 if (a->pid != b->pid)
59 return false;
60
61 if (pidref_is_remote(a)) {
62 /* If one is remote and the other isn't, they are not the same */
63 if (!pidref_is_remote(b))
64 return false;
65
66 /* If both are remote, compare fd IDs if we have both, otherwise don't bother, and cut things short */
67 if (a->fd_id == 0 || b->fd_id == 0)
68 return true;
69 } else {
70 /* If the other side is remote, then this is not the same */
71 if (pidref_is_remote(b))
72 return false;
73
74 /* PID1 cannot exit, hence it cannot change pidfs ids, hence no point in comparing them, we
75 * can shortcut things */
76 if (a->pid == 1)
77 return true;
78
79 /* Try to compare pidfds using their inode numbers. This way we can ensure that we
80 * don't spuriously consider two PidRefs equal if the pid has been reused once. Note
81 * that we ignore all errors here, not only EOPNOTSUPP, as fstat() might fail due to
82 * many reasons. */
83 if (pidref_acquire_pidfd_id(a) < 0 || pidref_acquire_pidfd_id(b) < 0)
84 return true;
85 }
86
87 return a->fd_id == b->fd_id;
88}
89
90int pidref_set_pid(PidRef *pidref, pid_t pid) {
91 uint64_t pidfdid = 0;
92 int fd;
93
94 assert(pidref);
95
96 if (pid < 0)
97 return -ESRCH;
98 if (pid == 0) {
99 pid = getpid_cached();
100 (void) pidfd_get_inode_id_self_cached(&pidfdid);
101 }
102
103 fd = pidfd_open(pid, 0);
104 if (fd < 0) {
105 /* Graceful fallback in case the kernel is out of fds */
106 if (!ERRNO_IS_RESOURCE(errno))
107 return log_debug_errno(errno, "Failed to open pidfd for pid " PID_FMT ": %m", pid);
108
109 fd = -EBADF;
110 }
111
112 *pidref = (PidRef) {
113 .fd = fd,
114 .pid = pid,
115 .fd_id = pidfdid,
116 };
117
118 return 0;
119}
120
121int pidref_set_pidstr(PidRef *pidref, const char *pid) {
122 pid_t nr;
123 int r;
124
125 assert(pidref);
126
127 r = parse_pid(pid, &nr);
128 if (r < 0)
129 return r;
130
131 return pidref_set_pid(pidref, nr);
132}
133
134int pidref_set_pidfd(PidRef *pidref, int fd) {
135 int r;
136
137 assert(pidref);
138
139 if (fd < 0)
140 return -EBADF;
141
142 int fd_copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
143 if (fd_copy < 0) {
144 pid_t pid;
145
146 if (!ERRNO_IS_RESOURCE(errno))
147 return -errno;
148
149 /* Graceful fallback if we are out of fds */
150 r = pidfd_get_pid(fd, &pid);
151 if (r < 0)
152 return r;
153
154 *pidref = PIDREF_MAKE_FROM_PID(pid);
155 return 0;
156 }
157
158 return pidref_set_pidfd_consume(pidref, fd_copy);
159}
160
161int pidref_set_pidfd_take(PidRef *pidref, int fd) {
162 pid_t pid;
163 int r;
164
165 assert(pidref);
166
167 if (fd < 0)
168 return -EBADF;
169
170 r = pidfd_get_pid(fd, &pid);
171 if (r < 0)
172 return r;
173
174 *pidref = (PidRef) {
175 .fd = fd,
176 .pid = pid,
177 };
178
179 return 0;
180}
181
182int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
183 int r;
184
185 r = pidref_set_pidfd_take(pidref, fd);
186 if (r < 0)
187 safe_close(fd);
188
189 return r;
190}
191
192int pidref_set_parent(PidRef *ret) {
193 _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
194 pid_t ppid;
195 int r;
196
197 assert(ret);
198
199 /* Acquires a pidref to our parent process. Deals with the fact that parent processes might exit, and
200 * we get reparented to other processes, with our old parent's PID already being recycled. */
201
202 ppid = getppid();
203 for (;;) {
204 r = pidref_set_pid(&parent, ppid);
205 if (r < 0)
206 return r;
207
208 if (parent.fd < 0) /* If pidfds are not available, then we are done */
209 break;
210
211 pid_t now_ppid = getppid();
212 if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
213 break;
214
215 /* Otherwise let's try again with the new ppid */
216 ppid = now_ppid;
217 pidref_done(&parent);
218 }
219
220 *ret = TAKE_PIDREF(parent);
221 return 0;
222}
223
224void pidref_done(PidRef *pidref) {
225 assert(pidref);
226
227 *pidref = (PidRef) {
228 .fd = safe_close(pidref->fd),
229 };
230}
231
232PidRef* pidref_free(PidRef *pidref) {
233 /* Regularly, this is an embedded structure. But sometimes we want it on the heap too */
234 if (!pidref)
235 return NULL;
236
237 pidref_done(pidref);
238 return mfree(pidref);
239}
240
241int pidref_copy(const PidRef *pidref, PidRef *ret) {
242 _cleanup_(pidref_done) PidRef copy = PIDREF_NULL;
243
244 /* If NULL is passed we'll generate a PidRef that refers to no process. This makes it easy to
245 * copy pidref fields that might or might not reference a process yet. */
246
247 assert(ret);
248
249 if (pidref) {
250 if (pidref_is_remote(pidref)) /* Propagate remote flag */
251 copy.fd = -EREMOTE;
252 else if (pidref->fd >= 0) {
253 copy.fd = fcntl(pidref->fd, F_DUPFD_CLOEXEC, 3);
254 if (copy.fd < 0) {
255 if (!ERRNO_IS_RESOURCE(errno))
256 return -errno;
257
258 copy.fd = -EBADF;
259 }
260 }
261
262 copy.pid = pidref->pid;
263 copy.fd_id = pidref->fd_id;
264 }
265
266 *ret = TAKE_PIDREF(copy);
267 return 0;
268}
269
270int pidref_dup(const PidRef *pidref, PidRef **ret) {
271 _cleanup_(pidref_freep) PidRef *dup_pidref = NULL;
272 int r;
273
274 /* Allocates a new PidRef on the heap, making it a copy of the specified pidref. This does not try to
275 * acquire a pidfd if we don't have one yet! */
276
277 assert(ret);
278
279 dup_pidref = newdup(PidRef, &PIDREF_NULL, 1);
280 if (!dup_pidref)
281 return -ENOMEM;
282
283 r = pidref_copy(pidref, dup_pidref);
284 if (r < 0)
285 return r;
286
287 *ret = TAKE_PTR(dup_pidref);
288 return 0;
289}
290
291int pidref_new_from_pid(pid_t pid, PidRef **ret) {
292 _cleanup_(pidref_freep) PidRef *n = NULL;
293 int r;
294
295 assert(ret);
296
297 if (pid < 0)
298 return -ESRCH;
299
300 n = new(PidRef, 1);
301 if (!n)
302 return -ENOMEM;
303
304 *n = PIDREF_NULL;
305
306 r = pidref_set_pid(n, pid);
307 if (r < 0)
308 return r;
309
310 *ret = TAKE_PTR(n);
311 return 0;
312}
313
314int pidref_kill(const PidRef *pidref, int sig) {
315
316 if (!pidref)
317 return -ESRCH;
318
319 if (pidref_is_remote(pidref))
320 return -EREMOTE;
321
322 if (pidref->fd >= 0)
323 return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
324
325 if (pidref->pid > 0)
326 return RET_NERRNO(kill(pidref->pid, sig));
327
328 return -ESRCH;
329}
330
331int pidref_kill_and_sigcont(const PidRef *pidref, int sig) {
332 int r;
333
334 r = pidref_kill(pidref, sig);
335 if (r < 0)
336 return r;
337
338 if (!IN_SET(sig, SIGCONT, SIGKILL))
339 (void) pidref_kill(pidref, SIGCONT);
340
341 return 0;
342}
343
344int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
345
346 if (!pidref)
347 return -ESRCH;
348
349 if (pidref_is_remote(pidref))
350 return -EREMOTE;
351
352 if (pidref->fd >= 0) {
353 siginfo_t si;
354
355 /* We can't use structured initialization here, since the structure contains various unions
356 * and these fields lie in overlapping (carefully aligned) unions that LLVM is allergic to
357 * allow assignments to */
358 zero(si);
359 si.si_signo = sig;
360 si.si_code = SI_QUEUE;
361 si.si_pid = getpid_cached();
362 si.si_uid = getuid();
363 si.si_value.sival_int = value;
364
365 return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
366 }
367
368 if (pidref->pid > 0)
369 return RET_NERRNO(sigqueue(pidref->pid, sig, (const union sigval) { .sival_int = value }));
370
371 return -ESRCH;
372}
373
374int pidref_verify(const PidRef *pidref) {
375 int r;
376
377 /* This is a helper that is supposed to be called after reading information from procfs via a
378 * PidRef. It ensures that the PID we track still matches the PIDFD we pin. If this value differs
379 * after a procfs read, we might have read the data from a recycled PID. */
380
381 if (!pidref_is_set(pidref))
382 return -ESRCH;
383
384 if (pidref_is_remote(pidref))
385 return -EREMOTE;
386
387 if (pidref->pid == 1)
388 return 1; /* PID 1 can never go away, hence never be recycled to a different process → return 1 */
389
390 if (pidref->fd < 0)
391 return 0; /* If we don't have a pidfd we cannot validate it, hence we assume it's all OK → return 0 */
392
393 r = pidfd_verify_pid(pidref->fd, pidref->pid);
394 if (r < 0)
395 return r;
396
397 return 1; /* We have a pidfd and it still points to the PID we have, hence all is *really* OK → return 1 */
398}
399
400bool pidref_is_self(PidRef *pidref) {
401 if (!pidref_is_set(pidref))
402 return false;
403
404 if (pidref_is_remote(pidref))
405 return false;
406
407 if (pidref->pid != getpid_cached())
408 return false;
409
410 /* PID1 cannot exit, hence no point in comparing pidfd IDs, they can never change */
411 if (pidref->pid == 1)
412 return true;
413
414 /* Also compare pidfd ID if we can get it */
415 if (pidref_acquire_pidfd_id(pidref) < 0)
416 return true;
417
418 uint64_t self_id;
419 if (pidfd_get_inode_id_self_cached(&self_id) < 0)
420 return true;
421
422 return pidref->fd_id == self_id;
423}
424
425int pidref_wait(PidRef *pidref, siginfo_t *ret, int options) {
426 int r;
427
428 if (!pidref_is_set(pidref))
429 return -ESRCH;
430
431 if (pidref_is_remote(pidref))
432 return -EREMOTE;
433
434 if (pidref->pid == 1 || pidref_is_self(pidref))
435 return -ECHILD;
436
437 siginfo_t si = {};
438 if (pidref->fd >= 0)
439 r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
440 else
441 r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options));
442 if (r < 0)
443 return r;
444
445 if (ret)
446 *ret = si;
447
448 return 0;
449}
450
451int pidref_wait_for_terminate(PidRef *pidref, siginfo_t *ret) {
452 int r;
453
454 for (;;) {
455 r = pidref_wait(pidref, ret, WEXITED);
456 if (r != -EINTR)
457 return r;
458 }
459}
460
461bool pidref_is_automatic(const PidRef *pidref) {
462 return pidref && pid_is_automatic(pidref->pid);
463}
464
465void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
466 siphash24_compress_typesafe(pidref->pid, state);
467}
468
469int pidref_compare_func(const PidRef *a, const PidRef *b) {
470 int r;
471
472 assert(a);
473 assert(b);
474
475 r = CMP(pidref_is_set(a), pidref_is_set(b));
476 if (r != 0)
477 return r;
478
479 r = CMP(pidref_is_automatic(a), pidref_is_automatic(b));
480 if (r != 0)
481 return r;
482
483 r = CMP(pidref_is_remote(a), pidref_is_remote(b));
484 if (r != 0)
485 return r;
486
487 r = CMP(a->pid, b->pid);
488 if (r != 0)
489 return r;
490
491 if (a->fd_id != 0 && b->fd_id != 0)
492 return CMP(a->fd_id, b->fd_id);
493
494 return 0;
495}
496
497DEFINE_HASH_OPS(pidref_hash_ops, PidRef, pidref_hash_func, pidref_compare_func);
498
499DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(pidref_hash_ops_free,
500 PidRef, pidref_hash_func, pidref_compare_func,
501 pidref_free);