From: Yu Watanabe Date: Thu, 27 Mar 2025 02:10:15 +0000 (+0900) Subject: notify-recv: introduce notify_socket_prepare() X-Git-Tag: v258-rc1~975^2~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2351bc991e3a4f1fbdaa2d53900980b6703babbd;p=thirdparty%2Fsystemd.git notify-recv: introduce notify_socket_prepare() --- diff --git a/src/shared/notify-recv.c b/src/shared/notify-recv.c index 08719a20bb4..70f4bb8d5c2 100644 --- a/src/shared/notify-recv.c +++ b/src/shared/notify-recv.c @@ -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, diff --git a/src/shared/notify-recv.h b/src/shared/notify-recv.h index f4739d8c120..24482fdf1d4 100644 --- a/src/shared/notify-recv.h +++ b/src/shared/notify-recv.h @@ -3,9 +3,18 @@ #include +#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, diff --git a/src/test/meson.build b/src/test/meson.build index a4c33cb50ca..9db9735a197 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -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 index 00000000000..24690b40a42 --- /dev/null +++ b/src/test/test-notify-recv.c @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#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);