diff --git a/src/home/homed-home.c b/src/home/homed-home.c index 6785472351..0f970ac2d0 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -1308,21 +1308,9 @@ static int home_start_work( if (r < 0) return r; if (r == 0) { - _cleanup_free_ char *joined = NULL; - const char *suffix, *unix_path; - /* Child */ - suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX"); - if (suffix) { - joined = strjoin("/run/systemd/home/notify.", suffix); - if (!joined) - return log_oom(); - unix_path = joined; - } else - unix_path = "/run/systemd/home/notify"; - - if (setenv("NOTIFY_SOCKET", unix_path, 1) < 0) { + if (setenv("NOTIFY_SOCKET", h->manager->notify_socket_path, /* overwrite = */ true) < 0) { log_error_errno(errno, "Failed to set $NOTIFY_SOCKET: %m"); _exit(EXIT_FAILURE); } diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index 5a56b3de23..9723725e43 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -291,7 +291,7 @@ Manager* manager_free(Manager *m) { m->device_monitor = sd_device_monitor_unref(m->device_monitor); m->inotify_event_source = sd_event_source_unref(m->inotify_event_source); - m->notify_socket_event_source = sd_event_source_unref(m->notify_socket_event_source); + m->notify_socket_path = mfree(m->notify_socket_path); m->deferred_rescan_event_source = sd_event_source_unref(m->deferred_rescan_event_source); m->deferred_gc_event_source = sd_event_source_unref(m->deferred_gc_event_source); m->deferred_auto_login_event_source = sd_event_source_unref(m->deferred_auto_login_event_source); @@ -1142,64 +1142,23 @@ static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void * } static int manager_listen_notify(Manager *m) { - _cleanup_close_ int fd = -EBADF; - union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/home/notify", - }; - const char *suffix; int r; assert(m); - assert(!m->notify_socket_event_source); + assert(!m->notify_socket_path); - suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX"); - if (suffix) { - _cleanup_free_ char *unix_path = NULL; - - unix_path = strjoin("/run/systemd/home/notify.", suffix); - if (!unix_path) - return log_oom(); - r = sockaddr_un_set_path(&sa.un, unix_path); - if (r < 0) - return log_error_errno(r, "Socket path %s does not fit in sockaddr_un: %m", unix_path); - } - - fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) - return log_error_errno(errno, "Failed to create listening socket: %m"); - - (void) mkdir_parents(sa.un.sun_path, 0755); - (void) sockaddr_un_unlink(&sa.un); - - if (bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return log_error_errno(errno, "Failed to bind to socket: %m"); - - r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true); + r = notify_socket_prepare( + m->event, + SD_EVENT_PRIORITY_NORMAL - 5, /* Make sure we process sd_notify() before SIGCHLD for + * any worker, so that we always know the error number + * of a client before it exits. */ + on_notify_socket, + m, + &m->notify_socket_path); if (r < 0) - return log_error_errno(r, "Failed to enable SO_PASSCRED on notify socket: %m"); + return log_error_errno(r, "Failed to prepare notify socket: %m"); - r = setsockopt_int(fd, SOL_SOCKET, SO_PASSPIDFD, true); - if (r < 0) - log_warning_errno(r, "Failed to enable SO_PASSPIDFD on notify socket, ignoring: %m"); - - r = sd_event_add_io(m->event, &m->notify_socket_event_source, fd, EPOLLIN, on_notify_socket, m); - if (r < 0) - return log_error_errno(r, "Failed to allocate event source for notify socket: %m"); - - (void) sd_event_source_set_description(m->notify_socket_event_source, "notify-socket"); - - /* Make sure we process sd_notify() before SIGCHLD for any worker, so that we always know the error - * number of a client before it exits. */ - r = sd_event_source_set_priority(m->notify_socket_event_source, SD_EVENT_PRIORITY_NORMAL - 5); - if (r < 0) - return log_error_errno(r, "Failed to alter priority of NOTIFY_SOCKET event source: %m"); - - r = sd_event_source_set_io_fd_own(m->notify_socket_event_source, true); - if (r < 0) - return log_error_errno(r, "Failed to pass ownership of notify socket: %m"); - - return TAKE_FD(fd); + return 0; } static int manager_add_device(Manager *m, sd_device *d) { diff --git a/src/home/homed-manager.h b/src/home/homed-manager.h index b780094d0d..2b4bd38adb 100644 --- a/src/home/homed-manager.h +++ b/src/home/homed-manager.h @@ -42,8 +42,7 @@ struct Manager { sd_event_source *inotify_event_source; - /* An event source we receive sd_notify() messages from our worker from */ - sd_event_source *notify_socket_event_source; + char *notify_socket_path; sd_device_monitor *device_monitor; diff --git a/src/import/importd.c b/src/import/importd.c index ad1cc93b76..b584a0e685 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -107,9 +107,7 @@ struct Manager { Hashmap *polkit_registry; - int notify_fd; - - sd_event_source *notify_event_source; + char *notify_socket_path; bool use_btrfs_subvol; bool use_btrfs_quota; @@ -467,7 +465,7 @@ static int transfer_start(Transfer *t) { /* Child */ if (setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1) < 0 || - setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1) < 0) { + setenv("NOTIFY_SOCKET", t->manager->notify_socket_path, 1) < 0) { log_error_errno(errno, "setenv() failed: %m"); _exit(EXIT_FAILURE); } @@ -624,8 +622,7 @@ static Manager *manager_unref(Manager *m) { if (!m) return NULL; - sd_event_source_unref(m->notify_event_source); - safe_close(m->notify_fd); + free(m->notify_socket_path); while ((t = hashmap_first(m->transfers))) transfer_unref(t); @@ -687,10 +684,6 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void static int manager_new(Manager **ret) { _cleanup_(manager_unrefp) Manager *m = NULL; - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/import/notify", - }; int r; assert(ret); @@ -725,26 +718,12 @@ static int manager_new(Manager **ret) { if (r < 0) log_debug_errno(r, "Failed to enable watchdog logic, ignoring: %m"); - m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->notify_fd < 0) - return -errno; - - (void) mkdir_parents_label(sa.un.sun_path, 0755); - (void) sockaddr_un_unlink(&sa.un); - - if (bind(m->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return -errno; - - r = setsockopt_int(m->notify_fd, SOL_SOCKET, SO_PASSCRED, true); - if (r < 0) - return r; - - r = setsockopt_int(m->notify_fd, SOL_SOCKET, SO_PASSPIDFD, true); - if (r < 0) - log_debug_errno(r, "Failed to enable SO_PASSPIDFD on notification socket, ignoring. %m"); - - r = sd_event_add_io(m->event, &m->notify_event_source, - m->notify_fd, EPOLLIN, manager_on_notify, m); + r = notify_socket_prepare( + m->event, + SD_EVENT_PRIORITY_NORMAL, + manager_on_notify, + m, + &m->notify_socket_path); if (r < 0) return r; diff --git a/src/notify/notify.c b/src/notify/notify.c index 2b7f1c4177..acc8bc72af 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -494,28 +494,28 @@ static int action_fork(char *const *_command) { if (!command) return log_oom(); - _cleanup_close_ int socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (socket_fd < 0) - return log_error_errno(errno, "Failed to allocate AF_UNIX socket for notifications: %m"); - - r = setsockopt_int(socket_fd, SOL_SOCKET, SO_PASSCRED, true); - if (r < 0) - return log_error_errno(r, "Failed to enable SO_PASSCRED: %m"); - - r = setsockopt_int(socket_fd, SOL_SOCKET, SO_PASSPIDFD, true); - if (r < 0) - log_debug_errno(r, "Failed to enable SO_PASSPIDFD, ignoring: %m"); - - _cleanup_free_ char *addr_string = NULL; - r = socket_autobind(socket_fd, &addr_string); - if (r < 0) - return log_error_errno(r, "Failed to bind notify socket: %m"); - _cleanup_free_ char *c = strv_join(command, " "); if (!c) return log_oom(); + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + r = sd_event_new(&event); + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); + _cleanup_(pidref_done) PidRef child = PIDREF_NULL; + _cleanup_free_ char *addr_string = NULL; + r = notify_socket_prepare( + event, + SD_EVENT_PRIORITY_NORMAL - 10, /* If we receive both the sd_notify() message and a + * SIGCHLD always process sd_notify() first, it's the + * more interesting, "positive" information. */ + on_notify_socket, + &child, + &addr_string); + if (r < 0) + return log_error_errno(r, "Failed to prepare notify socket: %m"); + r = pidref_safe_fork_full( "(notify)", /* stdio_fds= */ (const int[]) { -EBADF, -EBADF, STDERR_FILENO }, @@ -526,11 +526,6 @@ static int action_fork(char *const *_command) { if (r < 0) return log_error_errno(r, "Failed to fork child in order to execute '%s': %m", c); if (r == 0) { - /* Let's explicitly close the fds we just opened. Not because it was necessary (we should be - * setting O_CLOEXEC after all on all of them), but mostly to make debugging nice */ - socket_fd = safe_close(socket_fd); - pidref_done(&child); - if (setenv("NOTIFY_SOCKET", addr_string, /* overwrite= */ true) < 0) { log_error_errno(errno, "Failed to set $NOTIFY_SOCKET: %m"); _exit(EXIT_MEMORY); @@ -549,22 +544,6 @@ static int action_fork(char *const *_command) { BLOCK_SIGNALS(SIGCHLD); - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - r = sd_event_new(&event); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - _cleanup_(sd_event_source_disable_unrefp) sd_event_source *socket_event_source = NULL; - r = sd_event_add_io(event, &socket_event_source, socket_fd, EPOLLIN, on_notify_socket, &child); - if (r < 0) - return log_error_errno(r, "Failed to allocate IO source: %m"); - - /* If we receive both the sd_notify() message and a SIGCHLD always process sd_notify() first, it's - * the more interesting, "positive" information. */ - r = sd_event_source_set_priority(socket_event_source, SD_EVENT_PRIORITY_NORMAL - 10); - if (r < 0) - return log_error_errno(r, "Failed to change child event source priority: %m"); - _cleanup_(sd_event_source_disable_unrefp) sd_event_source *child_event_source = NULL; r = event_add_child_pidref(event, &child_event_source, &child, WEXITED, on_child, /* userdata= */ NULL); if (r < 0) diff --git a/src/shared/notify-recv.c b/src/shared/notify-recv.c index 08719a20bb..70f4bb8d5c 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 f4739d8c12..24482fdf1d 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/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c index 26d65d4078..2bdf8c6c3a 100644 --- a/src/sysupdate/sysupdate-transfer.c +++ b/src/sysupdate/sysupdate-transfer.c @@ -945,15 +945,13 @@ static int callout_context_new(const Transfer *t, const Instance *i, TransferPro } static int helper_on_exit(sd_event_source *s, const siginfo_t *si, void *userdata) { - _cleanup_(callout_context_freep) CalloutContext *ctx = ASSERT_PTR(userdata); + CalloutContext *ctx = ASSERT_PTR(userdata); int r; assert(s); assert(si); assert(ctx); - pidref_done(&ctx->pid); - if (si->si_code == CLD_EXITED) { if (si->si_status == EXIT_SUCCESS) { r = 0; @@ -1031,11 +1029,7 @@ static int run_callout( const Instance *instance, TransferProgress callback, void *userdata) { - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - _cleanup_(sd_event_source_unrefp) sd_event_source *exit_source = NULL, *notify_source = NULL; - _cleanup_close_ int fd = -EBADF; - _cleanup_free_ char *bind_name = NULL; - union sockaddr_union bsa; + int r; assert(name); @@ -1043,11 +1037,11 @@ static int run_callout( assert(cmdline[0]); _cleanup_(callout_context_freep) CalloutContext *ctx = NULL; - r = callout_context_new(transfer, instance, callback, name, userdata, &ctx); if (r < 0) return log_oom(); + _cleanup_(sd_event_unrefp) sd_event *event = NULL; r = sd_event_new(&event); if (r < 0) return log_error_errno(r, "Failed to create event: %m"); @@ -1060,27 +1054,15 @@ static int run_callout( if (r < 0) return log_error_errno(r, "Failed to register signal to event: %m"); - fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) - return log_error_errno(errno, "Failed to create UNIX socket for notification: %m"); - - if (asprintf(&bind_name, "@%" PRIx64 "/sysupdate/" PID_FMT "/notify", random_u64(), getpid_cached()) < 0) - return log_oom(); - - r = sockaddr_un_set_path(&bsa.un, bind_name); + _cleanup_free_ char *bind_name = NULL; + r = notify_socket_prepare( + event, + SD_EVENT_PRIORITY_NORMAL - 5, + helper_on_notify, + ctx, + &bind_name); if (r < 0) - return log_error_errno(r, "Failed to set socket path: %m"); - - if (bind(fd, &bsa.sa, r) < 0) - return log_error_errno(errno, "Failed to bind to notification socket: %m"); - - r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true); - if (r < 0) - return log_error_errno(r, "Failed to enable SO_PASSCRED: %m"); - - r = setsockopt_int(fd, SOL_SOCKET, SO_PASSPIDFD, true); - if (r < 0) - log_debug_errno(r, "Failed to enable SO_PASSPIDFD, ignoring: %m"); + return log_error_errno(r, "Failed to prepare notify socket: %m"); r = pidref_safe_fork(ctx->name, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_LOG, &ctx->pid); if (r < 0) @@ -1097,7 +1079,8 @@ static int run_callout( } /* Quit the loop w/ when child process exits */ - r = event_add_child_pidref(event, &exit_source, &ctx->pid, WEXITED, helper_on_exit, (void*) ctx); + _cleanup_(sd_event_source_unrefp) sd_event_source *exit_source = NULL; + r = event_add_child_pidref(event, &exit_source, &ctx->pid, WEXITED, helper_on_exit, ctx); if (r < 0) return log_error_errno(r, "Failed to add child process to event loop: %m"); @@ -1105,20 +1088,6 @@ static int run_callout( if (r < 0) return log_error_errno(r, "Failed to take ownership of child process: %m"); - /* Propagate sd_notify calls */ - r = sd_event_add_io(event, ¬ify_source, fd, EPOLLIN, helper_on_notify, TAKE_PTR(ctx)); - if (r < 0) - return log_error_errno(r, "Failed to add notification propagation to event loop: %m"); - - (void) sd_event_source_set_description(notify_source, "notify-socket"); - - (void) sd_event_source_set_priority(notify_source, SD_EVENT_PRIORITY_NORMAL - 5); - - r = sd_event_source_set_io_fd_own(notify_source, true); - if (r < 0) - return log_error_errno(r, "Event loop failed to take ownership of notification source: %m"); - TAKE_FD(fd); - /* Process events until the helper quits */ return sd_event_loop(event); } diff --git a/src/sysupdate/sysupdated.c b/src/sysupdate/sysupdated.c index db11c30ac4..fcb949fc0f 100644 --- a/src/sysupdate/sysupdated.c +++ b/src/sysupdate/sysupdated.c @@ -46,7 +46,7 @@ typedef struct Manager { Hashmap *polkit_registry; - sd_event_source *notify_event; + char *notify_socket_path; RuntimeScope runtime_scope; /* For now only RUNTIME_SCOPE_SYSTEM */ } Manager; @@ -456,7 +456,7 @@ static int job_start(Job *j) { }; size_t k = 2; - if (setenv("NOTIFY_SOCKET", "/run/systemd/sysupdate/notify", /* overwrite= */ 1) < 0) { + if (setenv("NOTIFY_SOCKET", j->manager->notify_socket_path, /* overwrite= */ 1) < 0) { log_error_errno(errno, "setenv() failed: %m"); _exit(EXIT_FAILURE); } @@ -1637,7 +1637,7 @@ static Manager *manager_free(Manager *m) { hashmap_free(m->jobs); m->bus = sd_bus_flush_close_unref(m->bus); - sd_event_source_unref(m->notify_event); + free(m->notify_socket_path); sd_event_unref(m->event); return mfree(m); @@ -1698,11 +1698,6 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void static int manager_new(Manager **ret) { _cleanup_(manager_freep) Manager *m = NULL; - _cleanup_close_ int notify_fd = -EBADF; - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/sysupdate/notify", - }; int r; assert(ret); @@ -1738,35 +1733,15 @@ static int manager_new(Manager **ret) { if (r < 0) return r; - notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (notify_fd < 0) - return -errno; - - (void) mkdir_parents_label(sa.un.sun_path, 0755); - (void) sockaddr_un_unlink(&sa.un); - - if (bind(notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return -errno; - - r = setsockopt_int(notify_fd, SOL_SOCKET, SO_PASSCRED, true); + r = notify_socket_prepare( + m->event, + SD_EVENT_PRIORITY_NORMAL, + manager_on_notify, + m, + &m->notify_socket_path); if (r < 0) return r; - r = setsockopt_int(notify_fd, SOL_SOCKET, SO_PASSPIDFD, true); - if (r < 0) - log_debug_errno(r, "Failed to enable SO_PASSPIDFD, ignoring: %m"); - - r = sd_event_add_io(m->event, &m->notify_event, notify_fd, EPOLLIN, manager_on_notify, m); - if (r < 0) - return r; - - (void) sd_event_source_set_description(m->notify_event, "notify-socket"); - - r = sd_event_source_set_io_fd_own(m->notify_event, true); - if (r < 0) - return r; - TAKE_FD(notify_fd); - *ret = TAKE_PTR(m); return 0; } diff --git a/src/test/meson.build b/src/test/meson.build index a4c33cb50c..9db9735a19 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 0000000000..24690b40a4 --- /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);