]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
notify-recv: introduce notify_socket_prepare()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 27 Mar 2025 02:10:15 +0000 (11:10 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 31 Mar 2025 18:26:22 +0000 (03:26 +0900)
src/shared/notify-recv.c
src/shared/notify-recv.h
src/test/meson.build
src/test/test-notify-recv.c [new file with mode: 0644]

index 08719a20bb45f92f61509443f6ec992f684d8224..70f4bb8d5c220a3a1064856e00aeb7102fc18d69 100644 (file)
@@ -7,6 +7,61 @@
 #include "strv.h"
 #include "user-util.h"
 
+int notify_socket_prepare(
+                sd_event *event,
+                int64_t priority,
+                sd_event_io_handler_t handler,
+                void *userdata,
+                char **ret_path) {
+
+        int r;
+
+        assert(event);
+
+        _cleanup_close_ int fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (fd < 0)
+                return log_debug_errno(errno, "Failed to create notification socket: %m");
+
+        _cleanup_free_ char *path = NULL;
+        r = socket_autobind(fd, &path);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to bind notification socket: %m");
+
+        r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to enable SO_PASSCRED on notification socket: %m");
+
+        /* SO_PASSPIDFD is supported since kernel v6.5. */
+        r = setsockopt_int(fd, SOL_SOCKET, SO_PASSPIDFD, true);
+        if (r < 0)
+                log_debug_errno(r, "Failed to enable SO_PASSPIDFD on notification socket, ignoring. %m");
+
+        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+        r = sd_event_add_io(event, &s, fd, EPOLLIN, handler, userdata);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to create notification event source: %m");
+
+        r = sd_event_source_set_priority(s, priority);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to set priority to notification event source: %m");
+
+        r = sd_event_source_set_io_fd_own(s, true);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to make notification event source own file descriptor: %m");
+        TAKE_FD(fd);
+
+        (void) sd_event_source_set_description(s, "notify-socket");
+
+        r = sd_event_source_set_floating(s, true);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to make notification event source floating: %m");
+
+        if (ret_path)
+                *ret_path = TAKE_PTR(path);
+
+        return 0;
+}
+
 int notify_recv_with_fds(
                 int fd,
                 char **ret_text,
index f4739d8c120c7720cce35012b8903e50b6a9063b..24482fdf1d4e74835f1219e4c85a58e7be6b341d 100644 (file)
@@ -3,9 +3,18 @@
 
 #include <sys/socket.h>
 
+#include "sd-event.h"
+
 #include "fdset.h"
 #include "pidref.h"
 
+int notify_socket_prepare(
+                sd_event *event,
+                int64_t priority,
+                sd_event_io_handler_t handler,
+                void *userdata,
+                char **ret_path);
+
 int notify_recv_with_fds(
                 int fd,
                 char **ret_text,
index a4c33cb50ca5442cb840f870d06b5226e512e9d5..9db9735a197ebf8fdfb19612457d3bb5de48bd1f 100644 (file)
@@ -132,6 +132,7 @@ simple_tests += files(
         'test-modhex.c',
         'test-mountpoint-util.c',
         'test-net-naming-scheme.c',
+        'test-notify-recv.c',
         'test-nsresource.c',
         'test-nulstr-util.c',
         'test-open-file.c',
diff --git a/src/test/test-notify-recv.c b/src/test/test-notify-recv.c
new file mode 100644 (file)
index 0000000..24690b4
--- /dev/null
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <unistd.h>
+
+#include "sd-daemon.h"
+
+#include "event-util.h"
+#include "notify-recv.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "rm-rf.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+
+typedef struct Context {
+        unsigned data;
+        PidRef pidref;
+} Context;
+
+static void context_done(Context *c) {
+        assert(c);
+
+        pidref_done(&c->pidref);
+}
+
+static int on_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        Context *c = ASSERT_PTR(userdata);
+
+        _cleanup_(pidref_done) PidRef sender = PIDREF_NULL;
+        _cleanup_strv_free_ char **l = NULL;
+        ASSERT_OK(notify_recv_strv(fd, &l, /* ret_ucred= */ NULL, &sender));
+
+        ASSERT_TRUE(pidref_equal(&c->pidref, &sender));
+
+        _cleanup_free_ char *joined = strv_join(l, ", ");
+        ASSERT_NOT_NULL(joined);
+        log_info("Received message: %s", joined);
+
+        if (strv_contains(l, "FIRST_MESSAGE=1")) {
+                ASSERT_STREQ(l[0], "FIRST_MESSAGE=1");
+                ASSERT_NULL(l[1]);
+                ASSERT_EQ(c->data, 0u);
+        } else if (strv_contains(l, "SECOND_MESSAGE=1")) {
+                ASSERT_STREQ(l[0], "SECOND_MESSAGE=1");
+                ASSERT_STREQ(l[1], "ADDITIONAL_DATA=hoge");
+                ASSERT_EQ(c->data, 1u);
+        }
+
+        c->data++;
+
+        return 0;
+}
+
+static int on_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
+        Context *c = ASSERT_PTR(userdata);
+
+        ASSERT_EQ(si->si_code, CLD_EXITED);
+        ASSERT_EQ(si->si_status, EXIT_SUCCESS);
+
+        ASSERT_EQ(c->data, 2u);
+
+        return sd_event_exit(sd_event_source_get_event(s), 0);
+}
+
+TEST(notify_socket_prepare) {
+        int r;
+
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        ASSERT_OK(sd_event_new(&e));
+
+        _cleanup_(context_done) Context c = {
+                .pidref = PIDREF_NULL,
+        };
+        _cleanup_free_ char *path = NULL;
+        ASSERT_OK(notify_socket_prepare(e, SD_EVENT_PRIORITY_NORMAL - 10, on_recv, &c, &path) >= 0);
+
+        ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
+
+        ASSERT_OK(r = pidref_safe_fork("(test-notify-recv-child)", FORK_DEATHSIG_SIGTERM|FORK_LOG, &c.pidref));
+        if (r == 0) {
+                ASSERT_OK_ERRNO(setenv("NOTIFY_SOCKET", path, /* overwrite = */ true));
+                ASSERT_OK_POSITIVE(sd_notify(/* unset_environment = */ false, "FIRST_MESSAGE=1"));
+                ASSERT_OK_POSITIVE(sd_notify(/* unset_environment = */ false, "FIRST_MESSAGE=2\nADDITIONAL_DATA=hoge"));
+                _exit(EXIT_SUCCESS);
+        }
+
+        ASSERT_OK(event_add_child_pidref(e, NULL, &c.pidref, WEXITED, on_sigchld, &c));
+        ASSERT_OK(sd_event_loop(e));
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);