]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/pidref.c
e4c51dd9527ee47e852a81d9936ef9aca727cd84
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
7 #include "errno-util.h"
9 #include "format-util.h"
10 #include "hash-funcs.h"
12 #include "parse-util.h"
13 #include "pidfd-util.h"
15 #include "process-util.h"
16 #include "siphash24.h"
18 int pidref_acquire_pidfd_id(PidRef
*pidref
) {
23 if (!pidref_is_set(pidref
))
26 if (pidref_is_remote(pidref
))
32 if (pidref
->fd_id
> 0)
35 r
= pidfd_get_inode_id(pidref
->fd
, &pidref
->fd_id
);
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",
46 bool pidref_equal(PidRef
*a
, PidRef
*b
) {
48 /* If this is the very same structure, it definitely refers to the same process */
52 if (!pidref_is_set(a
))
53 return !pidref_is_set(b
);
55 if (!pidref_is_set(b
))
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
))
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)
70 /* If the other side is remote, then this is not the same */
71 if (pidref_is_remote(b
))
74 /* PID1 cannot exit, hence it cannot change pidfs ids, hence no point in comparing them, we
75 * can shortcut things */
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
83 if (pidref_acquire_pidfd_id(a
) < 0 || pidref_acquire_pidfd_id(b
) < 0)
87 return a
->fd_id
== b
->fd_id
;
90 int pidref_set_pid(PidRef
*pidref
, pid_t pid
) {
99 pid
= getpid_cached();
100 (void) pidfd_get_inode_id_self_cached(&pidfdid
);
103 fd
= pidfd_open(pid
, 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
);
121 int pidref_set_pidstr(PidRef
*pidref
, const char *pid
) {
127 r
= parse_pid(pid
, &nr
);
131 return pidref_set_pid(pidref
, nr
);
134 int pidref_set_pidfd(PidRef
*pidref
, int fd
) {
142 int fd_copy
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
146 if (!ERRNO_IS_RESOURCE(errno
))
149 /* Graceful fallback if we are out of fds */
150 r
= pidfd_get_pid(fd
, &pid
);
154 *pidref
= PIDREF_MAKE_FROM_PID(pid
);
158 return pidref_set_pidfd_consume(pidref
, fd_copy
);
161 int pidref_set_pidfd_take(PidRef
*pidref
, int fd
) {
170 r
= pidfd_get_pid(fd
, &pid
);
182 int pidref_set_pidfd_consume(PidRef
*pidref
, int fd
) {
185 r
= pidref_set_pidfd_take(pidref
, fd
);
192 int pidref_set_parent(PidRef
*ret
) {
193 _cleanup_(pidref_done
) PidRef parent
= PIDREF_NULL
;
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. */
204 r
= pidref_set_pid(&parent
, ppid
);
208 if (parent
.fd
< 0) /* If pidfds are not available, then we are done */
211 pid_t now_ppid
= getppid();
212 if (now_ppid
== ppid
) /* If our ppid is still the same, then we are done */
215 /* Otherwise let's try again with the new ppid */
217 pidref_done(&parent
);
220 *ret
= TAKE_PIDREF(parent
);
224 void pidref_done(PidRef
*pidref
) {
228 .fd
= safe_close(pidref
->fd
),
232 PidRef
* pidref_free(PidRef
*pidref
) {
233 /* Regularly, this is an embedded structure. But sometimes we want it on the heap too */
238 return mfree(pidref
);
241 int pidref_copy(const PidRef
*pidref
, PidRef
*ret
) {
242 _cleanup_(pidref_done
) PidRef copy
= PIDREF_NULL
;
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. */
250 if (pidref_is_remote(pidref
)) /* Propagate remote flag */
252 else if (pidref
->fd
>= 0) {
253 copy
.fd
= fcntl(pidref
->fd
, F_DUPFD_CLOEXEC
, 3);
255 if (!ERRNO_IS_RESOURCE(errno
))
262 copy
.pid
= pidref
->pid
;
263 copy
.fd_id
= pidref
->fd_id
;
266 *ret
= TAKE_PIDREF(copy
);
270 int pidref_dup(const PidRef
*pidref
, PidRef
**ret
) {
271 _cleanup_(pidref_freep
) PidRef
*dup_pidref
= NULL
;
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! */
279 dup_pidref
= newdup(PidRef
, &PIDREF_NULL
, 1);
283 r
= pidref_copy(pidref
, dup_pidref
);
287 *ret
= TAKE_PTR(dup_pidref
);
291 int pidref_new_from_pid(pid_t pid
, PidRef
**ret
) {
292 _cleanup_(pidref_freep
) PidRef
*n
= NULL
;
306 r
= pidref_set_pid(n
, pid
);
314 int pidref_kill(const PidRef
*pidref
, int sig
) {
319 if (pidref_is_remote(pidref
))
323 return RET_NERRNO(pidfd_send_signal(pidref
->fd
, sig
, NULL
, 0));
326 return RET_NERRNO(kill(pidref
->pid
, sig
));
331 int pidref_kill_and_sigcont(const PidRef
*pidref
, int sig
) {
334 r
= pidref_kill(pidref
, sig
);
338 if (!IN_SET(sig
, SIGCONT
, SIGKILL
))
339 (void) pidref_kill(pidref
, SIGCONT
);
344 int pidref_sigqueue(const PidRef
*pidref
, int sig
, int value
) {
349 if (pidref_is_remote(pidref
))
352 if (pidref
->fd
>= 0) {
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 */
360 si
.si_code
= SI_QUEUE
;
361 si
.si_pid
= getpid_cached();
362 si
.si_uid
= getuid();
363 si
.si_value
.sival_int
= value
;
365 return RET_NERRNO(pidfd_send_signal(pidref
->fd
, sig
, &si
, 0));
369 return RET_NERRNO(sigqueue(pidref
->pid
, sig
, (const union sigval
) { .sival_int
= value
}));
374 int pidref_verify(const PidRef
*pidref
) {
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. */
381 if (!pidref_is_set(pidref
))
384 if (pidref_is_remote(pidref
))
387 if (pidref
->pid
== 1)
388 return 1; /* PID 1 can never go away, hence never be recycled to a different process → return 1 */
391 return 0; /* If we don't have a pidfd we cannot validate it, hence we assume it's all OK → return 0 */
393 r
= pidfd_verify_pid(pidref
->fd
, pidref
->pid
);
397 return 1; /* We have a pidfd and it still points to the PID we have, hence all is *really* OK → return 1 */
400 bool pidref_is_self(PidRef
*pidref
) {
401 if (!pidref_is_set(pidref
))
404 if (pidref_is_remote(pidref
))
407 if (pidref
->pid
!= getpid_cached())
410 /* PID1 cannot exit, hence no point in comparing pidfd IDs, they can never change */
411 if (pidref
->pid
== 1)
414 /* Also compare pidfd ID if we can get it */
415 if (pidref_acquire_pidfd_id(pidref
) < 0)
419 if (pidfd_get_inode_id_self_cached(&self_id
) < 0)
422 return pidref
->fd_id
== self_id
;
425 int pidref_wait(PidRef
*pidref
, siginfo_t
*ret
, int options
) {
428 if (!pidref_is_set(pidref
))
431 if (pidref_is_remote(pidref
))
434 if (pidref
->pid
== 1 || pidref_is_self(pidref
))
439 r
= RET_NERRNO(waitid(P_PIDFD
, pidref
->fd
, &si
, options
));
441 r
= RET_NERRNO(waitid(P_PID
, pidref
->pid
, &si
, options
));
451 int pidref_wait_for_terminate(PidRef
*pidref
, siginfo_t
*ret
) {
455 r
= pidref_wait(pidref
, ret
, WEXITED
);
461 bool pidref_is_automatic(const PidRef
*pidref
) {
462 return pidref
&& pid_is_automatic(pidref
->pid
);
465 void pidref_hash_func(const PidRef
*pidref
, struct siphash
*state
) {
466 siphash24_compress_typesafe(pidref
->pid
, state
);
469 int pidref_compare_func(const PidRef
*a
, const PidRef
*b
) {
475 r
= CMP(pidref_is_set(a
), pidref_is_set(b
));
479 r
= CMP(pidref_is_automatic(a
), pidref_is_automatic(b
));
483 r
= CMP(pidref_is_remote(a
), pidref_is_remote(b
));
487 r
= CMP(a
->pid
, b
->pid
);
491 if (a
->fd_id
!= 0 && b
->fd_id
!= 0)
492 return CMP(a
->fd_id
, b
->fd_id
);
497 DEFINE_HASH_OPS(pidref_hash_ops
, PidRef
, pidref_hash_func
, pidref_compare_func
);
499 DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(pidref_hash_ops_free
,
500 PidRef
, pidref_hash_func
, pidref_compare_func
,