]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
coredump: split out process_socket() to coredump-receive.[ch]
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 8 Oct 2025 01:01:06 +0000 (10:01 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 19 Oct 2025 01:01:38 +0000 (10:01 +0900)
Then, rename to coredump_receive().

src/coredump/coredump-context.c
src/coredump/coredump-receive.c [new file with mode: 0644]
src/coredump/coredump-receive.h [new file with mode: 0644]
src/coredump/coredump.c
src/coredump/meson.build

index 8134d75cbc4aeee5e40f14067dab6d48249ab492..6e62c4615f4eea13478fa0ed0d6fc82e1493763a 100644 (file)
@@ -275,7 +275,7 @@ int context_parse_iovw(Context *context, struct iovec_wrapper *iovw) {
         bool have_signal_name = false;
         FOREACH_ARRAY(iovec, iovw->iovec, iovw->count) {
                 /* Note that these strings are NUL-terminated, because we made sure that a trailing NUL byte
-                 * is in the buffer, though not included in the iov_len count. See process_socket() and
+                 * is in the buffer, though not included in the iov_len count. See coredump_receive() and
                  * gather_pid_metadata_*(). */
                 assert(((char*) iovec->iov_base)[iovec->iov_len] == 0);
 
diff --git a/src/coredump/coredump-receive.c b/src/coredump/coredump-receive.c
new file mode 100644 (file)
index 0000000..db9987b
--- /dev/null
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stdlib.h>
+
+#include "coredump-context.h"
+#include "coredump-receive.h"
+#include "coredump-submit.h"
+#include "iovec-util.h"
+#include "iovec-wrapper.h"
+#include "fd-util.h"
+#include "log.h"
+#include "socket-util.h"
+
+int coredump_receive(int fd) {
+        _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
+        _cleanup_(context_done) Context context = CONTEXT_NULL;
+        _cleanup_close_ int input_fd = -EBADF;
+        enum {
+                STATE_PAYLOAD,
+                STATE_INPUT_FD_DONE,
+                STATE_PID_FD_DONE,
+        } state = STATE_PAYLOAD;
+        int r;
+
+        assert(fd >= 0);
+
+        log_setup();
+
+        log_debug("Processing coredump received via socket...");
+
+        for (;;) {
+                CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control;
+                struct msghdr mh = {
+                        .msg_control = &control,
+                        .msg_controllen = sizeof(control),
+                        .msg_iovlen = 1,
+                };
+                ssize_t n, l;
+
+                l = next_datagram_size_fd(fd);
+                if (l < 0)
+                        return log_error_errno(l, "Failed to determine datagram size to read: %m");
+
+                _cleanup_(iovec_done) struct iovec iovec = {
+                        .iov_len = l,
+                        .iov_base = malloc(l + 1),
+                };
+                if (!iovec.iov_base)
+                        return log_oom();
+
+                mh.msg_iov = &iovec;
+
+                n = recvmsg_safe(fd, &mh, MSG_CMSG_CLOEXEC);
+                if (n < 0)
+                        return log_error_errno(n, "Failed to receive datagram: %m");
+
+                /* The final zero-length datagrams ("sentinels") carry file descriptors and tell us that
+                 * we're done. There are three sentinels: one with just the coredump fd, followed by one with
+                 * the pidfd, and finally one with the mount tree fd. The latter two or the last one may be
+                 * omitted (which is supported for compatibility with older systemd version, in particular to
+                 * facilitate cross-container coredumping). */
+                if (n == 0) {
+                        struct cmsghdr *found;
+
+                        found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
+                        if (!found) {
+                                /* This is zero length message but it either doesn't carry a single
+                                 * descriptor, or it has more than one. This is a protocol violation so let's
+                                 * bail out.
+                                 *
+                                 * Well, not quite! In practice there's one more complication: EOF on
+                                 * SOCK_SEQPACKET is not distinguishable from a zero length datagram. Hence
+                                 * if we get a zero length datagram without fds we consider it EOF, and
+                                 * that's permissible for the final two fds. Hence let's be strict on the
+                                 * first fd, but lenient on the other two. */
+
+                                if (!cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1) && state != STATE_PAYLOAD)
+                                        /* No fds, and already got the first fd → we are done. */
+                                        break;
+
+                                cmsg_close_all(&mh);
+                                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                                       "Received zero length message with zero or more than one file descriptor(s), expected one.");
+                        }
+
+                        switch (state) {
+
+                        case STATE_PAYLOAD:
+                                assert(input_fd < 0);
+                                input_fd = *CMSG_TYPED_DATA(found, int);
+                                state = STATE_INPUT_FD_DONE;
+                                continue;
+
+                        case STATE_INPUT_FD_DONE:
+                                assert(!pidref_is_set(&context.pidref));
+
+                                r = pidref_set_pidfd_consume(&context.pidref, *CMSG_TYPED_DATA(found, int));
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to initialize pidref: %m");
+
+                                state = STATE_PID_FD_DONE;
+                                continue;
+
+                        case STATE_PID_FD_DONE:
+                                assert(context.mount_tree_fd < 0);
+                                context.mount_tree_fd = *CMSG_TYPED_DATA(found, int);
+                                /* We have all FDs we need so we are done. */
+                                break;
+                        }
+
+                        break;
+                }
+
+                cmsg_close_all(&mh);
+
+                /* Only zero length messages are allowed after the first message that carried a file descriptor. */
+                if (state != STATE_PAYLOAD)
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Received unexpected message with non-zero length.");
+
+                /* Payload messages should not carry fds */
+                if (cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1))
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "Received payload message with file descriptor(s), expected none.");
+
+                /* Add trailing NUL byte, in case these are strings */
+                ((char*) iovec.iov_base)[n] = 0;
+                iovec.iov_len = (size_t) n;
+
+                if (iovw_put(&iovw, iovec.iov_base, iovec.iov_len) < 0)
+                        return log_oom();
+
+                TAKE_STRUCT(iovec);
+        }
+
+        /* Make sure we got all data we really need */
+        assert(input_fd >= 0);
+
+        r = context_parse_iovw(&context, &iovw);
+        if (r < 0)
+                return r;
+
+        /* Make sure we received all the expected fields. We support being called by an *older*
+         * systemd-coredump from the outside, so we require only the basic set of fields that
+         * was being sent when the support for sending to containers over a socket was added
+         * in a108c43e36d3ceb6e34efe37c014fc2cda856000. */
+        meta_argv_t i;
+        FOREACH_ARGUMENT(i,
+                         META_ARGV_PID,
+                         META_ARGV_UID,
+                         META_ARGV_GID,
+                         META_ARGV_SIGNAL,
+                         META_ARGV_TIMESTAMP,
+                         META_ARGV_RLIMIT,
+                         META_ARGV_HOSTNAME,
+                         META_COMM)
+                if (!context.meta[i])
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Mandatory argument %s not received on socket, aborting.",
+                                               meta_field_names[i]);
+
+        return coredump_submit(&context, &iovw, input_fd);
+}
diff --git a/src/coredump/coredump-receive.h b/src/coredump/coredump-receive.h
new file mode 100644 (file)
index 0000000..2476887
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int coredump_receive(int fd);
index e34b9a8d85b47bd6138e6c0e14c9d7142a919922..df85ef447bb45e7642beca07c8cd084bf26f6972 100644 (file)
@@ -25,6 +25,7 @@
 #include "coredump-backtrace.h"
 #include "coredump-config.h"
 #include "coredump-context.h"
+#include "coredump-receive.h"
 #include "coredump-submit.h"
 #include "coredump-util.h"
 #include "coredump-vacuum.h"
 #include "uid-classification.h"
 #include "user-util.h"
 
-static int process_socket(int fd) {
-        _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
-        _cleanup_(context_done) Context context = CONTEXT_NULL;
-        _cleanup_close_ int input_fd = -EBADF;
-        enum {
-                STATE_PAYLOAD,
-                STATE_INPUT_FD_DONE,
-                STATE_PID_FD_DONE,
-        } state = STATE_PAYLOAD;
-        int r;
-
-        assert(fd >= 0);
-
-        log_setup();
-
-        log_debug("Processing coredump received via socket...");
-
-        for (;;) {
-                CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control;
-                struct msghdr mh = {
-                        .msg_control = &control,
-                        .msg_controllen = sizeof(control),
-                        .msg_iovlen = 1,
-                };
-                ssize_t n, l;
-
-                l = next_datagram_size_fd(fd);
-                if (l < 0)
-                        return log_error_errno(l, "Failed to determine datagram size to read: %m");
-
-                _cleanup_(iovec_done) struct iovec iovec = {
-                        .iov_len = l,
-                        .iov_base = malloc(l + 1),
-                };
-                if (!iovec.iov_base)
-                        return log_oom();
-
-                mh.msg_iov = &iovec;
-
-                n = recvmsg_safe(fd, &mh, MSG_CMSG_CLOEXEC);
-                if (n < 0)
-                        return log_error_errno(n, "Failed to receive datagram: %m");
-
-                /* The final zero-length datagrams ("sentinels") carry file descriptors and tell us that
-                 * we're done. There are three sentinels: one with just the coredump fd, followed by one with
-                 * the pidfd, and finally one with the mount tree fd. The latter two or the last one may be
-                 * omitted (which is supported for compatibility with older systemd version, in particular to
-                 * facilitate cross-container coredumping). */
-                if (n == 0) {
-                        struct cmsghdr *found;
-
-                        found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
-                        if (!found) {
-                                /* This is zero length message but it either doesn't carry a single
-                                 * descriptor, or it has more than one. This is a protocol violation so let's
-                                 * bail out.
-                                 *
-                                 * Well, not quite! In practice there's one more complication: EOF on
-                                 * SOCK_SEQPACKET is not distinguishable from a zero length datagram. Hence
-                                 * if we get a zero length datagram without fds we consider it EOF, and
-                                 * that's permissible for the final two fds. Hence let's be strict on the
-                                 * first fd, but lenient on the other two. */
-
-                                if (!cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1) && state != STATE_PAYLOAD)
-                                        /* No fds, and already got the first fd → we are done. */
-                                        break;
-
-                                cmsg_close_all(&mh);
-                                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                                       "Received zero length message with zero or more than one file descriptor(s), expected one.");
-                        }
-
-                        switch (state) {
-
-                        case STATE_PAYLOAD:
-                                assert(input_fd < 0);
-                                input_fd = *CMSG_TYPED_DATA(found, int);
-                                state = STATE_INPUT_FD_DONE;
-                                continue;
-
-                        case STATE_INPUT_FD_DONE:
-                                assert(!pidref_is_set(&context.pidref));
-
-                                r = pidref_set_pidfd_consume(&context.pidref, *CMSG_TYPED_DATA(found, int));
-                                if (r < 0)
-                                        return log_error_errno(r, "Failed to initialize pidref: %m");
-
-                                state = STATE_PID_FD_DONE;
-                                continue;
-
-                        case STATE_PID_FD_DONE:
-                                assert(context.mount_tree_fd < 0);
-                                context.mount_tree_fd = *CMSG_TYPED_DATA(found, int);
-                                /* We have all FDs we need so we are done. */
-                                break;
-                        }
-
-                        break;
-                }
-
-                cmsg_close_all(&mh);
-
-                /* Only zero length messages are allowed after the first message that carried a file descriptor. */
-                if (state != STATE_PAYLOAD)
-                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Received unexpected message with non-zero length.");
-
-                /* Payload messages should not carry fds */
-                if (cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1))
-                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                            "Received payload message with file descriptor(s), expected none.");
-
-                /* Add trailing NUL byte, in case these are strings */
-                ((char*) iovec.iov_base)[n] = 0;
-                iovec.iov_len = (size_t) n;
-
-                if (iovw_put(&iovw, iovec.iov_base, iovec.iov_len) < 0)
-                        return log_oom();
-
-                TAKE_STRUCT(iovec);
-        }
-
-        /* Make sure we got all data we really need */
-        assert(input_fd >= 0);
-
-        r = context_parse_iovw(&context, &iovw);
-        if (r < 0)
-                return r;
-
-        /* Make sure we received all the expected fields. We support being called by an *older*
-         * systemd-coredump from the outside, so we require only the basic set of fields that
-         * was being sent when the support for sending to containers over a socket was added
-         * in a108c43e36d3ceb6e34efe37c014fc2cda856000. */
-        meta_argv_t i;
-        FOREACH_ARGUMENT(i,
-                         META_ARGV_PID,
-                         META_ARGV_UID,
-                         META_ARGV_GID,
-                         META_ARGV_SIGNAL,
-                         META_ARGV_TIMESTAMP,
-                         META_ARGV_RLIMIT,
-                         META_ARGV_HOSTNAME,
-                         META_COMM)
-                if (!context.meta[i])
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Mandatory argument %s not received on socket, aborting.",
-                                               meta_field_names[i]);
-
-        return coredump_submit(&context, &iovw, input_fd);
-}
-
 static int send_iovec(const struct iovec_wrapper *iovw, int input_fd, PidRef *pidref, int mount_tree_fd) {
         _cleanup_close_ int fd = -EBADF;
         int r;
@@ -624,7 +475,7 @@ static int run(int argc, char *argv[]) {
                 else
                         return process_kernel(argc, argv);
         } else if (r == 1)
-                return process_socket(SD_LISTEN_FDS_START);
+                return coredump_receive(SD_LISTEN_FDS_START);
 
         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                "Received unexpected number of file descriptors.");
index c1d26b4ac626ec0236c0054371ca89496e226ce1..a14a4464de36c2f1b397f46179249f64d0a076f3 100644 (file)
@@ -9,6 +9,7 @@ systemd_coredump_sources = files(
         'coredump-backtrace.c',
         'coredump-config.c',
         'coredump-context.c',
+        'coredump-receive.c',
         'coredump-submit.c',
 )
 systemd_coredump_extract_sources = files(