coredump: split out process_socket() to coredump-receive.[ch]
Then, rename to coredump_receive().
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int coredump_receive(int fd);
|
||||
+2
-151
@@ -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"
|
||||
@@ -60,156 +61,6 @@
|
||||
#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.");
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user