From: Lennart Poettering Date: Fri, 14 Feb 2025 13:40:31 +0000 (+0100) Subject: notify-recv: add generic implementation of sd_notify() server side dgram recv code X-Git-Tag: v258-rc1~1283^2~9 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7f6af95dab037e7d15591a924dbf256460bbf069;p=thirdparty%2Fsystemd.git notify-recv: add generic implementation of sd_notify() server side dgram recv code The code is not trivial, and we implemented this at various places already, introduce a common implementation for this we can reuse. --- diff --git a/src/shared/meson.build b/src/shared/meson.build index ed7cad7c883..0459975005e 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -126,6 +126,7 @@ shared_sources = files( 'netif-naming-scheme.c', 'netif-sriov.c', 'netif-util.c', + 'notify-recv.c', 'nsflags.c', 'nsresource.c', 'numa-util.c', diff --git a/src/shared/notify-recv.c b/src/shared/notify-recv.c new file mode 100644 index 00000000000..c3596d0258a --- /dev/null +++ b/src/shared/notify-recv.c @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "fd-util.h" +#include "notify-recv.h" +#include "socket-util.h" + +int notify_recv(int fd, char **ret_text, struct ucred *ret_ucred, PidRef *ret_pidref) { + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(int)) + /* SCM_PIDFD */ + CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)) control; + struct iovec iovec; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + ssize_t n; + + assert(fd >= 0); + + /* Receives a $NOTIFY_SOCKET message (aka sd_notify()). Does various validations. Returns -EAGAIN in + * case an invalid message is received (following the logic that an invalid message shall be ignored, + * and treated like no message at all). */ + + _cleanup_free_ char *buf = new(char, NOTIFY_BUFFER_MAX+1); + if (!buf) + return log_oom_debug(); + + iovec = (struct iovec) { + .iov_base = buf, + .iov_len = NOTIFY_BUFFER_MAX, + }; + + n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); + if (ERRNO_IS_NEG_TRANSIENT(n)) + return -EAGAIN; + if (n == -ECHRNG) { + log_debug_errno(n, "Got message with truncated control data (unexpected fds sent?), ignoring."); + return -EAGAIN; + } + if (n == -EXFULL) { + log_debug_errno(n, "Got message with truncated payload data, ignoring."); + return -EAGAIN; + } + if (n < 0) + return (int) n; + + const struct ucred *ucred = NULL; + _cleanup_close_ int pidfd = -EBADF; + struct cmsghdr *cmsg; + CMSG_FOREACH(cmsg, &msghdr) { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + + 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)); + break; + + case SCM_PIDFD: + assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int))); + pidfd = *CMSG_TYPED_DATA(cmsg, int); + break; + + case SCM_CREDENTIALS: + assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))); + ucred = CMSG_TYPED_DATA(cmsg, struct ucred); + break; + } + } + + if ((ret_ucred || ret_pidref) && (!ucred || ucred->pid <= 0)) + return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "Got notification datagram lacking valid credential information, ignoring."); + + if (n == 0) + return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "Got empty notification message, ignoring."); + if (memchr(buf, 0, n - 1)) + return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "Got notification message with embedded NUL, ignoring."); + + if (ret_pidref) { + assert(ucred); + assert(ucred->pid > 0); + + if (pidfd >= 0) + *ret_pidref = (PidRef) { + .pid = ucred->pid, + .fd = TAKE_FD(pidfd), + }; + else + *ret_pidref = PIDREF_MAKE_FROM_PID(ucred->pid); + } + + if (ret_text) { + buf[n] = 0; + *ret_text = TAKE_PTR(buf); + } + + if (ret_ucred) + *ret_ucred = *ucred; + + return 0; +} diff --git a/src/shared/notify-recv.h b/src/shared/notify-recv.h new file mode 100644 index 00000000000..5cd5062b35d --- /dev/null +++ b/src/shared/notify-recv.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "pidref.h" + +int notify_recv(int fd, char **ret_text, struct ucred *ret_ucred, PidRef *ret_pidref);