]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core/manager: port to notify_recv_with_fds() 36492/head
authorMike Yuan <me@yhndnzj.com>
Wed, 19 Feb 2025 22:06:38 +0000 (23:06 +0100)
committerMike Yuan <me@yhndnzj.com>
Wed, 26 Feb 2025 12:27:39 +0000 (13:27 +0100)
src/core/manager.c
src/shared/notify-recv.c
src/shared/notify-recv.h

index c5fd03e42b7a397aaac15f81616c4443362f2dc0..90d741e563cec921bcbbf3a9d5d1e08105e7df28 100644 (file)
@@ -68,6 +68,7 @@
 #include "memory-util.h"
 #include "mkdir-label.h"
 #include "mount-util.h"
+#include "notify-recv.h"
 #include "os-util.h"
 #include "parse-util.h"
 #include "path-lookup.h"
@@ -2795,20 +2796,10 @@ static int manager_get_units_for_pidref(Manager *m, const PidRef *pidref, Unit *
 
 static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
         Manager *m = ASSERT_PTR(userdata);
-        char buf[NOTIFY_BUFFER_MAX+1];
-        struct iovec iovec = {
-                .iov_base = buf,
-                .iov_len = sizeof(buf)-1,
-        };
-        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int)) /* SCM_PIDFD */ +
-                         CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)) control;
-        struct msghdr msghdr = {
-                .msg_iov = &iovec,
-                .msg_iovlen = 1,
-                .msg_control = &control,
-                .msg_controllen = sizeof(control),
-        };
-        ssize_t n;
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+        struct ucred ucred;
+        _cleanup_free_ char *buf = NULL;
+        _cleanup_(fdset_free_asyncp) FDSet *fds = NULL;
         int r;
 
         assert(m->notify_fd == fd);
@@ -2818,98 +2809,15 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
                 return 0;
         }
 
-        n = recvmsg_safe(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
-        if (ERRNO_IS_NEG_TRANSIENT(n))
-                return 0; /* Spurious wakeup, try again */
-        if (n == -ECHRNG) {
-                log_warning_errno(n, "Got message with truncated control data (too many fds sent?), ignoring.");
-                return 0;
-        }
-        if (n == -EXFULL) {
-                log_warning_errno(n, "Got message with truncated payload data, ignoring.");
+        r = notify_recv_with_fds(m->notify_fd, &buf, &ucred, &pidref, &fds);
+        if (r == -EAGAIN)
                 return 0;
-        }
-        if (n < 0)
+        if (r < 0)
                 /* If this is any other, real error, then stop processing this socket. This of course means
                  * we won't take notification messages anymore, but that's still better than busy looping:
                  * being woken up over and over again, but being unable to actually read the message from the
                  * socket. */
-                return log_error_errno(n, "Failed to receive notification message: %m");
-
-        _cleanup_close_ int pidfd = -EBADF;
-        struct ucred *ucred = NULL;
-        int *fd_array = NULL;
-        size_t n_fds = 0;
-
-        struct cmsghdr *cmsg;
-        CMSG_FOREACH(cmsg, &msghdr) {
-                if (cmsg->cmsg_level != SOL_SOCKET)
-                        continue;
-
-                if (cmsg->cmsg_type == SCM_RIGHTS) {
-                        assert(!fd_array);
-                        fd_array = CMSG_TYPED_DATA(cmsg, int);
-                        n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
-                } else if (cmsg->cmsg_type == SCM_CREDENTIALS &&
-                           cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
-
-                        assert(!ucred);
-                        ucred = CMSG_TYPED_DATA(cmsg, struct ucred);
-                } else if (cmsg->cmsg_type == SCM_PIDFD) {
-                        assert(pidfd < 0);
-                        pidfd = *CMSG_TYPED_DATA(cmsg, int);
-                }
-        }
-
-        _cleanup_(fdset_free_asyncp) FDSet *fds = NULL;
-
-        if (n_fds > 0) {
-                assert(fd_array);
-
-                r = fdset_new_array(&fds, fd_array, n_fds);
-                if (r < 0) {
-                        close_many(fd_array, n_fds);
-                        log_oom_warning();
-                        return 0;
-                }
-        }
-
-        if (!ucred || !pid_is_valid(ucred->pid)) {
-                log_warning("Received notify message without valid credentials. Ignoring.");
-                return 0;
-        }
-
-        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
-
-        if (pidfd >= 0)
-                r = pidref_set_pidfd_consume(&pidref, TAKE_FD(pidfd));
-        else
-                r = pidref_set_pid(&pidref, ucred->pid);
-        if (r < 0) {
-                if (r == -ESRCH)
-                        log_debug_errno(r, "Notify sender died before message is processed. Ignoring.");
-                else
-                        log_warning_errno(r, "Failed to pin notify sender, ignoring message: %m");
-                return 0;
-        }
-
-        if (pidref.pid != ucred->pid) {
-                assert(pidref.fd >= 0);
-
-                log_warning("Got SCM_PIDFD for process " PID_FMT " but SCM_CREDENTIALS for process " PID_FMT ". Ignoring.",
-                            pidref.pid, ucred->pid);
-                return 0;
-        }
-
-        /* As extra safety check, let's make sure the string we get doesn't contain embedded NUL bytes.
-         * We permit one trailing NUL byte in the message, but don't expect it. */
-        if (n > 1 && memchr(buf, 0, n-1)) {
-                log_warning("Received notify message with embedded NUL bytes. Ignoring.");
-                return 0;
-        }
-
-        /* Make sure it's NUL-terminated, then parse it to obtain the tags list. */
-        buf[n] = 0;
+                return r;
 
         _cleanup_strv_free_ char **tags = strv_split_newlines(buf);
         if (!tags) {
@@ -2919,7 +2827,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
 
         /* Possibly a barrier fd, let's see. */
         if (manager_process_barrier_fd(tags, fds)) {
-                log_debug("Received barrier notification message from PID " PID_FMT ".", ucred->pid);
+                log_debug("Received barrier notification message from PID " PID_FMT ".", pidref.pid);
                 return 0;
         }
 
@@ -2931,16 +2839,16 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
 
         int n_array = manager_get_units_for_pidref(m, &pidref, &array);
         if (n_array < 0) {
-                log_warning_errno(n_array, "Failed to determine units for PID " PID_FMT ", ignoring: %m", ucred->pid);
+                log_warning_errno(n_array, "Failed to determine units for PID " PID_FMT ", ignoring: %m", pidref.pid);
                 return 0;
         }
         if (n_array == 0)
-                log_debug("Cannot find unit for notify message of PID "PID_FMT", ignoring.", ucred->pid);
+                log_debug("Cannot find unit for notify message of PID "PID_FMT", ignoring.", pidref.pid);
         else
                 /* And now invoke the per-unit callbacks. Note that manager_invoke_notify_message() will handle
                  * duplicate units – making sure we only invoke each unit's handler once. */
                 FOREACH_ARRAY(u, array, n_array)
-                        manager_invoke_notify_message(m, *u, &pidref, ucred, tags, fds);
+                        manager_invoke_notify_message(m, *u, &pidref, &ucred, tags, fds);
 
         if (!fdset_isempty(fds))
                 log_warning("Got extra auxiliary fds with notification message, closing them.");
index 8730e88080c16281745a681cfb609372f6609c76..ce12ec8e01c3e322cc2ba5019409549a34c37365 100644 (file)
@@ -1,14 +1,16 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include "async.h"
 #include "fd-util.h"
 #include "notify-recv.h"
 #include "socket-util.h"
 
-int notify_recv(
+int notify_recv_with_fds(
                 int fd,
                 char **ret_text,
                 struct ucred *ret_ucred,
-                PidRef *ret_pidref) {
+                PidRef *ret_pidref,
+                FDSet **ret_fds) {
 
         char buf[NOTIFY_BUFFER_MAX];
         struct iovec iovec = {
@@ -38,7 +40,8 @@ int notify_recv(
         if (ERRNO_IS_NEG_TRANSIENT(n))
                 return -EAGAIN;
         if (n == -ECHRNG) {
-                log_warning_errno(n, "Got message with truncated control data (unexpected fds sent?), ignoring.");
+                log_warning_errno(n, "Got message with truncated control data (%s fds sent?), ignoring.",
+                                  ret_fds ? "too many" : "unexpected");
                 return -EAGAIN;
         }
         if (n == -EXFULL) {
@@ -50,6 +53,8 @@ int notify_recv(
 
         const struct ucred *ucred = NULL;
         _cleanup_close_ int pidfd = -EBADF;
+        int *fd_array = NULL;
+        size_t n_fds = 0;
 
         struct cmsghdr *cmsg;
         CMSG_FOREACH(cmsg, &msghdr) {
@@ -59,9 +64,10 @@ int notify_recv(
                 switch (cmsg->cmsg_type) {
 
                 case SCM_RIGHTS:
-                        /* For now, just close every fd */
-                        close_many(CMSG_TYPED_DATA(cmsg, int),
-                                   (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+                        assert(!fd_array && n_fds == 0);
+
+                        fd_array = CMSG_TYPED_DATA(cmsg, int);
+                        n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
                         break;
 
                 case SCM_PIDFD:
@@ -80,6 +86,23 @@ int notify_recv(
                 }
         }
 
+        _cleanup_(fdset_free_asyncp) FDSet *fds = NULL;
+        if (n_fds > 0) {
+                assert(fd_array);
+
+                if (!ret_fds) {
+                        log_debug("Received fds via notification while none is expected, closing all.");
+                        asynchronous_close_many(fd_array, n_fds);
+                } else {
+                        r = fdset_new_array(&fds, fd_array, n_fds);
+                        if (r < 0) {
+                                asynchronous_close_many(fd_array, n_fds);
+                                log_warning_errno(r, "Failed to collect fds from notification, ignoring message: %m");
+                                return -EAGAIN;
+                        }
+                }
+        }
+
         if ((ret_ucred || ret_pidref) && (!ucred || !pid_is_valid(ucred->pid)))
                 return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN),
                                          "Got notification datagram lacking valid credential information, ignoring.");
@@ -112,5 +135,8 @@ int notify_recv(
                         *ret_pidref = PIDREF_MAKE_FROM_PID(ucred->pid);
         }
 
+        if (ret_fds)
+                *ret_fds = TAKE_PTR(fds);
+
         return 0;
 }
index 5cd5062b35da675c85f124a80fd58f25bf9ad9ce..0a38fd28b4e0d56720cf5cbcfcbc45e9bed872dc 100644 (file)
@@ -3,6 +3,16 @@
 
 #include <sys/socket.h>
 
+#include "fdset.h"
 #include "pidref.h"
 
-int notify_recv(int fd, char **ret_text, struct ucred *ret_ucred, PidRef *ret_pidref);
+int notify_recv_with_fds(
+                int fd,
+                char **ret_text,
+                struct ucred *ret_ucred,
+                PidRef *ret_pidref,
+                FDSet **ret_fds);
+
+static inline int notify_recv(int fd, char **ret_text, struct ucred *ret_ucred, PidRef *ret_pidref) {
+        return notify_recv_with_fds(fd, ret_text, ret_ucred, ret_pidref, NULL);
+}