]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pidref: add structure that can reference a pid via both pidfd and pid_t
authorLennart Poettering <lennart@poettering.net>
Sat, 9 Sep 2023 07:29:27 +0000 (09:29 +0200)
committerLennart Poettering <lennart@poettering.net>
Sat, 9 Sep 2023 12:03:31 +0000 (14:03 +0200)
Let's start with the conversion of PID 1 to pidfds. Let's add a simple
structure with just two fields that can be used to maintain a reference
to arbitrary processes via both pid_t and pidfd.

This is an embeddable struct, to keep it in line with where we
previously used a pid_t directly to track a process.

Of course, since this might contain an fd on systems where we have pidfd
this structure has a proper lifecycle.

(Note that this is quite different from sd_event_add_child() event
source objects as that one is only for child processes and collects
process results, while this infra is much simpler and more generic and
can be used to reference any process, anywhere in the tree.)

src/basic/meson.build
src/basic/pidref.c [new file with mode: 0644]
src/basic/pidref.h [new file with mode: 0644]

index 77ce2cf26219be83006b9d1fc087d4082851f914..0e4c5584da134ce39bfb82934f80705fada0fa32 100644 (file)
@@ -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 (file)
index 0000000..f414609
--- /dev/null
@@ -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 (file)
index 0000000..2411e51
--- /dev/null
@@ -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)