Merge pull request #33697 from poettering/vmspawn-stream-fix

vmspawn: fix AF_VSOCK sd_notify() reception
This commit is contained in:
Daan De Meyer
2024-07-12 08:33:18 +02:00
committed by GitHub
+100 -49
View File
@@ -582,94 +582,119 @@ static int parse_argv(int argc, char *argv[]) {
}
static int open_vsock(void) {
_cleanup_close_ int vsock_fd = -EBADF;
int r;
static const union sockaddr_union bind_addr = {
.vm.svm_family = AF_VSOCK,
.vm.svm_cid = VMADDR_CID_ANY,
.vm.svm_port = VMADDR_PORT_ANY,
};
vsock_fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
_cleanup_close_ int vsock_fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
if (vsock_fd < 0)
return log_error_errno(errno, "Failed to open AF_VSOCK socket: %m");
r = bind(vsock_fd, &bind_addr.sa, sizeof(bind_addr.vm));
if (r < 0)
if (bind(vsock_fd, &bind_addr.sa, sizeof(bind_addr.vm)) < 0)
return log_error_errno(errno, "Failed to bind to VSOCK address %u:%u: %m", bind_addr.vm.svm_cid, bind_addr.vm.svm_port);
r = listen(vsock_fd, SOMAXCONN_DELUXE);
if (r < 0)
if (listen(vsock_fd, SOMAXCONN_DELUXE) < 0)
return log_error_errno(errno, "Failed to listen on VSOCK: %m");
return TAKE_FD(vsock_fd);
}
static int vmspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
char buf[NOTIFY_BUFFER_MAX+1];
const char *p = NULL;
struct iovec iovec = {
.iov_base = buf,
.iov_len = sizeof(buf)-1,
};
struct msghdr msghdr = {
.msg_iov = &iovec,
.msg_iovlen = 1,
};
ssize_t n;
_cleanup_strv_free_ char **tags = NULL;
int r, *exit_status = ASSERT_PTR(userdata);
typedef struct NotifyConnectionData {
char buffer[NOTIFY_BUFFER_MAX+1];
size_t full;
int *exit_status;
} NotifyConnectionData;
n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT);
if (ERRNO_IS_NEG_TRANSIENT(n))
return 0;
if (n == -EXFULL) {
log_warning_errno(n, "Got message with truncated control data, ignoring: %m");
return 0;
}
if (n < 0)
return log_warning_errno(n, "Couldn't read notification socket: %m");
static int read_vsock_notify(NotifyConnectionData *d, int fd) {
int r;
if ((size_t) n >= sizeof(buf)) {
log_warning("Received notify message exceeded maximum size. Ignoring.");
return 0;
assert(d);
assert(fd >= 0);
for (;;) {
assert(d->full < sizeof(d->buffer));
ssize_t n = read(fd, d->buffer + d->full, sizeof(d->buffer) - d->full);
if (n < 0) {
if (ERRNO_IS_TRANSIENT(errno))
return 0;
return log_error_errno(errno, "Failed to read notification message: %m");
}
if (n == 0) /* We hit EOF! Let's parse this */
break;
d->full += n;
if (d->full >= sizeof(d->buffer))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Received notify message exceeded maximum size.");
}
buf[n] = 0;
tags = strv_split(buf, "\n\r");
/* We reached EOF, now parse the thing */
assert(d->full < sizeof(d->buffer));
d->buffer[d->full] = 0;
_cleanup_strv_free_ char **tags = strv_split(d->buffer, "\n\r");
if (!tags)
return log_oom();
STRV_FOREACH(s, tags)
log_debug("Received tag %s from notify socket", *s);
if (DEBUG_LOGGING) {
_cleanup_free_ char *j = strv_join(tags, " ");
log_debug("Received notification message with tags: %s", strnull(j));
}
if (strv_contains(tags, "READY=1")) {
r = sd_notify(false, "READY=1\n");
r = sd_notify(false, "READY=1");
if (r < 0)
log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
}
p = strv_find_startswith(tags, "STATUS=");
const char *p = strv_find_startswith(tags, "STATUS=");
if (p)
(void) sd_notifyf(false, "STATUS=VM running: %s", p);
p = strv_find_startswith(tags, "EXIT_STATUS=");
if (p) {
r = safe_atoi(p, exit_status);
r = safe_atoi(p, d->exit_status);
if (r < 0)
log_warning_errno(r, "Failed to parse exit status from %s, ignoring: %m", p);
}
/* we will only receive one message from each connection so disable this source once one is received */
source = sd_event_source_disable_unref(source);
return 1; /* done */
}
static int vmspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
NotifyConnectionData *d = ASSERT_PTR(userdata);
int r;
assert(source);
assert(fd >= 0);
r = read_vsock_notify(d, fd);
if (r != 0) {
int q;
/* If we are done or are seeing an error we'll turn off floating mode, which means the event
* loop itself won't keep the event source pinned anymore, and since noone else (hopefully!)
* keeps a reference anymore the whole thing will be released once we exit from this handler
* here. */
q = sd_event_source_set_floating(source, false);
if (q < 0)
log_warning_errno(q, "Failed to disable floating mode of event source, ignoring: %m");
return r;
}
return 0;
}
static int vmspawn_dispatch_vsock_connections(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
int r;
sd_event *event;
_cleanup_close_ int conn_fd = -EBADF;
sd_event *event;
int r;
assert(userdata);
@@ -680,7 +705,10 @@ static int vmspawn_dispatch_vsock_connections(sd_event_source *source, int fd, u
conn_fd = accept4(fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
if (conn_fd < 0) {
log_warning_errno(errno, "Failed to accept connection from VSOCK fd (%m), ignoring...");
if (ERRNO_IS_TRANSIENT(errno))
return 0;
log_warning_errno(errno, "Failed to accept connection from VSOCK connection, ignoring: %m");
return 0;
}
@@ -688,13 +716,36 @@ static int vmspawn_dispatch_vsock_connections(sd_event_source *source, int fd, u
if (!event)
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Failed to retrieve event from event source, exiting task");
_cleanup_free_ NotifyConnectionData *d = new(NotifyConnectionData, 1);
if (!d)
return log_oom();
*d = (NotifyConnectionData) {
.exit_status = userdata,
};
/* add a new floating task to read from the connection */
r = sd_event_add_io(event, NULL, conn_fd, revents, vmspawn_dispatch_notify_fd, userdata);
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
r = sd_event_add_io(event, &s, conn_fd, EPOLLIN, vmspawn_dispatch_notify_fd, d);
if (r < 0)
return log_error_errno(r, "Failed to allocate notify connection event source: %m");
/* conn_fd is now owned by the event loop so don't clean it up */
TAKE_FD(conn_fd);
r = sd_event_source_set_io_fd_own(s, true);
if (r < 0)
return log_error_errno(r, "Failed to pass ownership of notify to event source: %m");
TAKE_FD(conn_fd); /* conn_fd is now owned by the event loop so don't clean it up */
r = sd_event_source_set_destroy_callback(s, free);
if (r < 0)
return log_error_errno(r, "Failed to set destroy callback on event source: %m");
TAKE_PTR(d); /* The data object will now automatically be freed by the event source when it goes away */
/* Finally, make sure the event loop pins the event source */
r = sd_event_source_set_floating(s, true);
if (r < 0)
return log_error_errno(r, "Failed to set event source to floating mode: %m");
(void) sd_event_source_set_description(s, "vmspawn-notify-socket-connection");
return 0;
}
@@ -711,7 +762,7 @@ static int setup_notify_parent(sd_event *event, int fd, int *exit_status, sd_eve
if (r < 0)
return log_error_errno(r, "Failed to allocate notify socket event source: %m");
(void) sd_event_source_set_description(*ret_notify_event_source, "vmspawn-notify-sock");
(void) sd_event_source_set_description(*ret_notify_event_source, "vmspawn-notify-socket-listen");
return 0;
}