diff --git a/src/core/manager.c b/src/core/manager.c index 7536a9ca84de5..17ec9f1469c07 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -2284,6 +2284,32 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui return 0; } +static bool manager_process_barrier_fd(const char *buf, FDSet *fds) { + assert(buf); + assert(fds); + + _cleanup_strv_free_ char **tags = NULL; + + tags = strv_split(buf, NEWLINE); + if (!tags) { + log_oom(); + return false; + } + + if (strv_find(tags, "BARRIER=1")) { + /* when we process BARRIER=1, we process nothing else */ + if (strv_length(tags) > 1) + log_warning("Extra notification messages passed with BARRER=1, ignoring."); + + if (fdset_size(fds) > 1) + log_warning("Got extra fds with BARRIER=1, closing them."); + + return true; + } + + return false; +} + static void manager_invoke_notify_message( Manager *m, Unit *u, @@ -2416,6 +2442,12 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t /* Make sure it's NUL-terminated. */ buf[n] = 0; + if (fds) { + /* possibly a barrier fd, let's see */ + if (manager_process_barrier_fd(buf, fds)) + return 0; + } + /* Increase the generation counter used for filtering out duplicate unit invocations. */ m->notifygen++; diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 4cd71cb2d3a02..92560a356ff08 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -551,6 +552,41 @@ _public_ int sd_pid_notify_with_fds( return r; } +_public_ int sd_notify_barrier(int unset_environment, uint64_t timeout) { + _cleanup_close_pair_ int pipe_fd[2] = { -1, -1 }; + int r; + + if (pipe2(pipe_fd, O_CLOEXEC) < 0) { + r = -errno; + goto finish; + } + + struct pollfd pfd = { + .fd = pipe_fd[0], + /* POLLHUP is implicit */ + .events = 0, + }; + + r = sd_pid_notify_with_fds(0, unset_environment, "BARRIER=1", &pipe_fd[1], 1); + if (r <= 0) + goto finish; + + pipe_fd[1] = safe_close(pipe_fd[1]); + + /* roughly convert to milliseconds, then clamp down */ + timeout = timeout/1000 < INT_MAX ? timeout/1000 : INT_MAX; + + if (poll(&pfd, 1, (int) timeout) >= 0) { + r = 1; + goto finish; + } + + r = -errno; + +finish: + return r; +} + _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) { return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0); } diff --git a/src/notify/notify.c b/src/notify/notify.c index a76f3376951f8..05b3fdd14c01b 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -213,6 +213,15 @@ static int run(int argc, char* argv[]) { if (r == 0) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "No status data could be sent: $NOTIFY_SOCKET was not set"); + + /* 5,000,000 µs is 5s */ + r = sd_notify_barrier(0, 5000000); + if (r < 0) + return log_error_errno(r, "Failed to create barrier: %m"); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "No status data could be sent: $NOTIFY_SOCKET was not set"); + return 0; } diff --git a/src/systemd/sd-daemon.h b/src/systemd/sd-daemon.h index 62b0f723c7a1a..63999f9703c01 100644 --- a/src/systemd/sd-daemon.h +++ b/src/systemd/sd-daemon.h @@ -286,6 +286,19 @@ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) _s */ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds); +/* + Returns > 0 if synchronization with systemd succeeded. Returns < 0 + on error. Returns 0 if $NOTIFY_SOCKET was not set. Note that the + timeout parameter of this function call takes the timeout in µs, and + will be passed to poll(2), hence the behaviour will be similar to + poll(2). This function can be called after sending a status message + to systemd, if one needs to synchronize against reception of the + status messages sent before this call is made. Therefore, this + cannot be used to know if the status message was processed + successfully, but to only synchronize against its consumption. +*/ +int sd_notify_barrier(int unset_environment, uint64_t timeout); + /* Returns > 0 if the system was booted with systemd. Returns < 0 on error. Returns 0 if the system was not booted with systemd. Note