diff --git a/src/basic/meson.build b/src/basic/meson.build index 77ce2cf262..0e4c5584da 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -65,6 +65,7 @@ basic_sources = files( 'path-lookup.c', 'path-util.c', 'percent-util.c', + 'pidref.c', 'prioq.c', 'proc-cmdline.c', 'process-util.c', diff --git a/src/basic/pidref.c b/src/basic/pidref.c new file mode 100644 index 0000000000..f41460938c --- /dev/null +++ b/src/basic/pidref.c @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "errno-util.h" +#include "fd-util.h" +#include "missing_syscall.h" +#include "parse-util.h" +#include "pidref.h" +#include "process-util.h" + +int pidref_set_pid(PidRef *pidref, pid_t pid) { + int fd; + + assert(pidref); + + if (pid < 0) + return -ESRCH; + if (pid == 0) + pid = getpid_cached(); + + fd = pidfd_open(pid, 0); + if (fd < 0) { + /* Graceful fallback in case the kernel doesn't support pidfds or is out of fds */ + if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno) && !ERRNO_IS_RESOURCE(errno)) + return -errno; + + fd = -EBADF; + } + + *pidref = (PidRef) { + .fd = fd, + .pid = pid, + }; + + return 0; +} + +int pidref_set_pidstr(PidRef *pidref, const char *pid) { + pid_t nr; + int r; + + assert(pidref); + + r = parse_pid(pid, &nr); + if (r < 0) + return r; + + return pidref_set_pid(pidref, nr); +} + +int pidref_set_pidfd(PidRef *pidref, int fd) { + int r; + + assert(pidref); + + if (fd < 0) + return -EBADF; + + int fd_copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (fd_copy < 0) { + pid_t pid; + + if (!ERRNO_IS_RESOURCE(errno)) + return -errno; + + /* Graceful fallback if we are out of fds */ + r = pidfd_get_pid(fd, &pid); + if (r < 0) + return r; + + *pidref = (PidRef) { + .fd = -EBADF, + .pid = pid, + }; + + return 0; + } + + return pidref_set_pidfd_consume(pidref, fd_copy); +} + +int pidref_set_pidfd_take(PidRef *pidref, int fd) { + pid_t pid; + int r; + + assert(pidref); + + if (fd < 0) + return -EBADF; + + r = pidfd_get_pid(fd, &pid); + if (r < 0) + return r; + + *pidref = (PidRef) { + .fd = fd, + .pid = pid, + }; + + return 0; +} + +int pidref_set_pidfd_consume(PidRef *pidref, int fd) { + int r; + + r = pidref_set_pidfd_take(pidref, fd); + if (r < 0) + safe_close(fd); + + return r; +} + +void pidref_done(PidRef *pidref) { + assert(pidref); + + *pidref = (PidRef) { + .fd = safe_close(pidref->fd), + }; +} + +int pidref_kill(PidRef *pidref, int sig) { + + if (!pidref) + return -ESRCH; + + if (pidref->fd >= 0) + return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0)); + + if (pidref->pid > 0) + return RET_NERRNO(kill(pidref->pid, sig)); + + return -ESRCH; +} + +int pidref_kill_and_sigcont(PidRef *pidref, int sig) { + int r; + + r = pidref_kill(pidref, sig); + if (r < 0) + return r; + + if (!IN_SET(sig, SIGCONT, SIGKILL)) + (void) pidref_kill(pidref, SIGCONT); + + return 0; +} diff --git a/src/basic/pidref.h b/src/basic/pidref.h new file mode 100644 index 0000000000..2411e510f1 --- /dev/null +++ b/src/basic/pidref.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "macro.h" + +/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes continously. */ +typedef struct PidRef { + pid_t pid; /* always valid */ + int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd */ +} PidRef; + +#define PIDREF_NULL (PidRef) { .fd = -EBADF } + +static inline bool pidref_is_set(const PidRef *pidref) { + return pidref && pidref->pid > 0; +} + +int pidref_set_pid(PidRef *pidref, pid_t pid); +int pidref_set_pidstr(PidRef *pidref, const char *pid); +int pidref_set_pidfd(PidRef *pidref, int fd); +int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/ +int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */ + +void pidref_done(PidRef *pidref); + +int pidref_kill(PidRef *pidref, int sig); +int pidref_kill_and_sigcont(PidRef *pidref, int sig); + +#define TAKE_PIDREF(p) TAKE_GENERIC((p), PidRef, PIDREF_NULL)