diff --git a/man/sd_event_add_child.xml b/man/sd_event_add_child.xml
index 4bf07baf59..28ff624516 100644
--- a/man/sd_event_add_child.xml
+++ b/man/sd_event_add_child.xml
@@ -116,8 +116,9 @@
parameter specifies the PID of the process to watch, which must be a direct child process of the invoking
process. The options parameter determines which state changes will be watched for.
It must contain an OR-ed mask of WEXITED (watch for the child process terminating),
- WSTOPPED (watch for the child process being stopped by a signal), and
- WCONTINUED (watch for the child process being resumed by a signal). See
+ WSTOPPED (watch for the child process being stopped by a signal),
+ WCONTINUED (watch for the child process being resumed by a signal) and
+ WNOWAIT (Do not reap the child process after it exits). See
waitid2
for further information.
diff --git a/man/sd_event_set_exit_on_idle.xml b/man/sd_event_set_exit_on_idle.xml
new file mode 100644
index 0000000000..1dd1042ad4
--- /dev/null
+++ b/man/sd_event_set_exit_on_idle.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+ sd_event_set_exit_on_idle
+ systemd
+
+
+
+ sd_event_set_exit_on_idle
+ 3
+
+
+
+ sd_event_set_exit_on_idle
+ sd_event_get_exit_on_idle
+
+ Enable event loop exit-on-idle support
+
+
+
+
+ #include <systemd/sd-event.h>
+
+
+ int sd_event_set_exit_on_idle
+ sd_event *event
+ int b
+
+
+
+ int sd_event_get_exit_on_idle
+ sd_event *event
+
+
+
+
+
+
+ Description
+
+ sd_event_set_exit_on_idle() may be used to
+ enable or disable the exit-on-idle support in the
+ event loop object specified in the event
+ parameter. If enabled, the event loop will exit with a zero exit code
+ there are no more enabled (SD_EVENT_ON, SD_EVENT_ONESHOT),
+ non-exit, non-post event sources.
+
+ sd_event_get_exit_on_idle() may be used to
+ determine whether exit-on-idle support was previously requested by a
+ call to sd_event_set_exit_on_idle() with a true
+ b parameter and successfully enabled.
+
+
+
+ Return Value
+
+ On success, sd_event_set_exit_on_idle() and
+ sd_event_get_exit_on_idle() return a non-zero positive integer if the exit-on-idle
+ support was successfully enabled. They return zero if the exit-on-idle support was explicitly disabled
+ with a false b parameter. On failure, they return a negative errno-style error
+ code.
+
+
+ Errors
+
+ Returned errors may indicate the following problems:
+
+
+
+
+ -ECHILD
+
+ The event loop has been created in a different process, library or module instance.
+
+
+
+ -EINVAL
+
+ The passed event loop object was invalid.
+
+
+
+
+
+
+
+
+
+ History
+ sd_event_set_exit_on_idle() and
+ sd_event_get_exit_on_idle() were added in version 259.
+
+
+
+ See Also
+
+
+ systemd1
+ sd-event3
+ sd_event_new3
+ sd_event_add_io3
+ sd_event_add_time3
+ sd_event_add_signal3
+ sd_event_add_child3
+ sd_event_add_inotify3
+ sd_event_add_defer3
+ systemd.service5
+
+
+
+
diff --git a/src/basic/basic-forward.h b/src/basic/basic-forward.h
index dae8788140..0f8c3ea06c 100644
--- a/src/basic/basic-forward.h
+++ b/src/basic/basic-forward.h
@@ -94,6 +94,7 @@ typedef enum TimestampStyle TimestampStyle;
typedef enum UnitActiveState UnitActiveState;
typedef enum UnitDependency UnitDependency;
typedef enum UnitType UnitType;
+typedef enum WaitFlags WaitFlags;
typedef struct Hashmap Hashmap;
typedef struct HashmapBase HashmapBase;
diff --git a/src/basic/log-context.c b/src/basic/log-context.c
index 27bce6f1f6..334fe95fc3 100644
--- a/src/basic/log-context.c
+++ b/src/basic/log-context.c
@@ -58,7 +58,9 @@ static LogContext* log_context_detach(LogContext *c) {
LogContext* log_context_new(const char *key, const char *value) {
assert(key);
assert(endswith(key, "="));
- assert(value);
+
+ if (!value)
+ return NULL;
LIST_FOREACH(ll, i, _log_context)
if (i->key == key && i->value == value)
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index d0e000bd0c..a1fa794264 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -1081,5 +1081,7 @@ global:
LIBSYSTEMD_259 {
global:
+ sd_event_set_exit_on_idle;
+ sd_event_get_exit_on_idle;
sd_varlink_is_connected;
} LIBSYSTEMD_258;
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index d8cd1ba7df..94b178eafd 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -48,7 +48,7 @@ static bool EVENT_SOURCE_WATCH_PIDFD(const sd_event_source *s) {
/* Returns true if this is a PID event source and can be implemented by watching EPOLLIN */
return s &&
s->type == SOURCE_CHILD &&
- s->child.options == WEXITED;
+ (s->child.options & ~WNOWAIT) == WEXITED;
}
static bool event_source_is_online(sd_event_source *s) {
@@ -157,6 +157,7 @@ struct sd_event {
bool need_process_child:1;
bool watchdog:1;
bool profile_delays:1;
+ bool exit_on_idle:1;
int exit_code;
@@ -1583,7 +1584,7 @@ _public_ int sd_event_add_child(
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(pid > 1, -EINVAL);
- assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
+ assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED|WNOWAIT)), -EINVAL);
assert_return(options != 0, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_origin_changed(e), -ECHILD);
@@ -1675,7 +1676,7 @@ _public_ int sd_event_add_child_pidfd(
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(pidfd >= 0, -EBADF);
- assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
+ assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED|WNOWAIT)), -EINVAL);
assert_return(options != 0, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_origin_changed(e), -ECHILD);
@@ -2987,9 +2988,11 @@ static int event_source_online(
break;
case SOURCE_MEMORY_PRESSURE:
- r = source_memory_pressure_register(s, enabled);
- if (r < 0)
- return r;
+ if (s->memory_pressure.write_buffer_size == 0) {
+ r = source_memory_pressure_register(s, enabled);
+ if (r < 0)
+ return r;
+ }
break;
@@ -3042,16 +3045,8 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
if (m == SD_EVENT_OFF)
r = event_source_offline(s, m, s->ratelimited);
- else {
- if (s->enabled != SD_EVENT_OFF) {
- /* Switching from "on" to "oneshot" or back? If that's the case, we can take a shortcut, the
- * event source is already enabled after all. */
- s->enabled = m;
- return 0;
- }
-
+ else
r = event_source_online(s, m, s->ratelimited);
- }
if (r < 0)
return r;
@@ -3695,7 +3690,7 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
zero(s->child.siginfo);
if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo,
- WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options) < 0)
+ WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | (s->child.options & ~WNOWAIT)) < 0)
return negative_errno();
if (s->child.siginfo.si_pid != 0) {
@@ -3743,7 +3738,6 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
/* Note that pidfd would also generate EPOLLHUP when the process gets reaped. But at this point we
* only permit EPOLLIN, under the assumption that upon EPOLLHUP the child source should already
* be set to pending, and we would have returned early above. */
- assert(!s->child.exited);
zero(s->child.siginfo);
if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
@@ -4083,6 +4077,22 @@ static int source_memory_pressure_initiate_dispatch(sd_event_source *s) {
return 0; /* go on, dispatch to user callback */
}
+static int mark_post_sources_pending(sd_event *e) {
+ sd_event_source *z;
+ int r;
+
+ SET_FOREACH(z, e->post_sources) {
+ if (event_source_is_offline(z))
+ continue;
+
+ r = source_set_pending(z, true);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int source_dispatch(sd_event_source *s) {
EventSourceType saved_type;
sd_event *saved_event;
@@ -4110,25 +4120,23 @@ static int source_dispatch(sd_event_source *s) {
return 1;
}
- if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) {
+ if (IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) {
+ /* Make sure this event source is moved to the end of the priority list now. We do this here
+ * because defer and exit event sources are always pending from the moment they're added so
+ * the same logic in source_set_pending() is never triggered. */
+ s->pending_iteration = s->event->iteration;
+ event_source_pp_prioq_reshuffle(s);
+ } else {
r = source_set_pending(s, false);
if (r < 0)
return r;
}
if (s->type != SOURCE_POST) {
- sd_event_source *z;
-
/* If we execute a non-post source, let's mark all post sources as pending. */
-
- SET_FOREACH(z, s->event->post_sources) {
- if (event_source_is_offline(z))
- continue;
-
- r = source_set_pending(z, true);
- if (r < 0)
- return r;
- }
+ r = mark_post_sources_pending(s->event);
+ if (r < 0)
+ return r;
}
if (s->type == SOURCE_MEMORY_PRESSURE) {
@@ -4172,10 +4180,11 @@ static int source_dispatch(sd_event_source *s) {
r = s->child.callback(s, &s->child.siginfo, s->userdata);
- /* Now, reap the PID for good. */
+ /* Now, reap the PID for good (unless WNOWAIT was specified by the caller). */
if (zombie) {
- (void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|WEXITED);
- s->child.waited = true;
+ (void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|WEXITED|(s->child.options & WNOWAIT));
+ if (!FLAGS_SET(s->child.options, WNOWAIT))
+ s->child.waited = true;
}
break;
@@ -4237,6 +4246,14 @@ static int source_dispatch(sd_event_source *s) {
s->dispatching = false;
+ if (saved_type != SOURCE_POST) {
+ /* More post sources might have been added while executing the callback, let's make sure
+ * those are marked pending as well. */
+ r = mark_post_sources_pending(saved_event);
+ if (r < 0)
+ return r;
+ }
+
finish:
if (r < 0) {
log_debug_errno(r, "Event source %s (type %s) returned error, %s: %m",
@@ -4412,6 +4429,28 @@ static int event_memory_pressure_write_list(sd_event *e) {
return 0;
}
+static bool event_loop_idle(sd_event *e) {
+ assert(e);
+
+ LIST_FOREACH(sources, s, e->sources) {
+ /* Exit sources only trigger on exit, so whether they're enabled or not doesn't matter when
+ * we're deciding if the event loop is idle or not. */
+ if (s->type == SOURCE_EXIT)
+ continue;
+
+ if (s->enabled == SD_EVENT_OFF)
+ continue;
+
+ /* Post event sources always need another active event source to become pending. */
+ if (s->type == SOURCE_POST && !s->pending)
+ continue;
+
+ return false;
+ }
+
+ return true;
+}
+
_public_ int sd_event_prepare(sd_event *e) {
int r;
@@ -4429,6 +4468,9 @@ _public_ int sd_event_prepare(sd_event *e) {
/* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
PROTECT_EVENT(e);
+ if (!e->exit_requested && e->exit_on_idle && event_loop_idle(e))
+ (void) sd_event_exit(e, 0);
+
if (e->exit_requested)
goto pending;
@@ -5233,6 +5275,22 @@ _public_ int sd_event_set_signal_exit(sd_event *e, int b) {
return change;
}
+_public_ int sd_event_set_exit_on_idle(sd_event *e, int b) {
+ assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
+ assert_return(!event_origin_changed(e), -ECHILD);
+
+ return e->exit_on_idle = b;
+}
+
+_public_ int sd_event_get_exit_on_idle(sd_event *e) {
+ assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
+ assert_return(!event_origin_changed(e), -ECHILD);
+
+ return e->exit_on_idle;
+}
+
_public_ int sd_event_source_set_memory_pressure_type(sd_event_source *s, const char *ty) {
_cleanup_free_ char *b = NULL;
_cleanup_free_ void *w = NULL;
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index 77cf7af541..eb986c9e08 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -946,4 +946,225 @@ TEST(leave_ratelimit) {
ASSERT_TRUE(manually_left_ratelimit);
}
+static int defer_post_handler(sd_event_source *s, void *userdata) {
+ bool *dispatched_post = ASSERT_PTR(userdata);
+
+ *dispatched_post = true;
+
+ return 0;
+}
+
+static int defer_adds_post_handler(sd_event_source *s, void *userdata) {
+ sd_event *e = sd_event_source_get_event(s);
+
+ /* Add a post event source from within the defer handler */
+ ASSERT_OK(sd_event_add_post(e, NULL, defer_post_handler, userdata));
+
+ return 0;
+}
+
+TEST(defer_add_post) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ bool dispatched_post = false;
+
+ ASSERT_OK(sd_event_default(&e));
+
+ /* Add a oneshot defer event source that will add a post event source */
+ ASSERT_OK(sd_event_add_defer(e, NULL, defer_adds_post_handler, &dispatched_post));
+
+ /* Run one iteration - this should dispatch the defer handler */
+ ASSERT_OK_POSITIVE(sd_event_run(e, UINT64_MAX));
+
+ /* The post handler should have been added but not yet dispatched */
+ ASSERT_FALSE(dispatched_post);
+
+ /* Run another iteration - this should dispatch the post handler */
+ ASSERT_OK_POSITIVE(sd_event_run(e, 0));
+
+ /* Now the post handler should have been dispatched */
+ ASSERT_TRUE(dispatched_post);
+}
+
+static int child_handler_wnowait(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ int *counter = ASSERT_PTR(userdata);
+
+ (*counter)++;
+
+ if (*counter == 5)
+ ASSERT_OK(sd_event_exit(sd_event_source_get_event(s), 0));
+
+ return 0;
+}
+
+TEST(child_wnowait) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+
+ ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
+
+ ASSERT_OK(sd_event_default(&e));
+
+ /* Fork a subprocess */
+ pid_t pid;
+ ASSERT_OK_ERRNO(pid = fork());
+
+ if (pid == 0)
+ /* Child process - exit with a specific code */
+ _exit(42);
+
+ /* Add a child source with WNOWAIT flag */
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ int counter = 0;
+ ASSERT_OK(sd_event_add_child(e, &s, pid, WEXITED|WNOWAIT, child_handler_wnowait, &counter));
+ ASSERT_OK(sd_event_source_set_enabled(s, SD_EVENT_ON));
+
+ /* Run the event loop - this should call the handler */
+ ASSERT_OK(sd_event_loop(e));
+ ASSERT_EQ(counter, 5);
+
+ /* Since we used WNOWAIT, the child should still be waitable */
+ siginfo_t si = {};
+ ASSERT_OK_ERRNO(waitid(P_PID, pid, &si, WEXITED|WNOHANG));
+ ASSERT_EQ(si.si_pid, pid);
+ ASSERT_EQ(si.si_code, CLD_EXITED);
+ ASSERT_EQ(si.si_status, 42);
+}
+
+TEST(child_pidfd_wnowait) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+
+ ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
+
+ ASSERT_OK(sd_event_default(&e));
+
+ /* Fork a subprocess */
+ pid_t pid;
+ ASSERT_OK_ERRNO(pid = fork());
+
+ if (pid == 0)
+ /* Child process - exit with a specific code */
+ _exit(42);
+
+ _cleanup_close_ int pidfd = -EBADF;
+ ASSERT_OK_ERRNO(pidfd = pidfd_open(pid, 0));
+
+ /* Add a child source with WNOWAIT flag */
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ int counter = 0;
+ ASSERT_OK(sd_event_add_child_pidfd(e, &s, pidfd, WEXITED|WNOWAIT, child_handler_wnowait, &counter));
+ ASSERT_OK(sd_event_source_set_enabled(s, SD_EVENT_ON));
+
+ /* Run the event loop - this should call the handler */
+ ASSERT_OK(sd_event_loop(e));
+ ASSERT_EQ(counter, 5);
+
+ /* Since we used WNOWAIT, the child should still be waitable */
+ siginfo_t si = {};
+ ASSERT_OK_ERRNO(waitid(P_PIDFD, pidfd, &si, WEXITED|WNOHANG));
+ ASSERT_EQ(si.si_pid, pid);
+ ASSERT_EQ(si.si_code, CLD_EXITED);
+ ASSERT_EQ(si.si_status, 42);
+}
+
+static int exit_on_idle_defer_handler(sd_event_source *s, void *userdata) {
+ unsigned *c = ASSERT_PTR(userdata);
+
+ /* Should not be reached on third call because the event loop should exit before */
+ ASSERT_LT(*c, 2u);
+
+ (*c)++;
+
+ /* Disable ourselves, which should trigger exit-on-idle after the second iteration */
+ if (*c == 2)
+ sd_event_source_set_enabled(s, SD_EVENT_OFF);
+
+ return 0;
+}
+
+static int exit_on_idle_post_handler(sd_event_source *s, void *userdata) {
+ unsigned *c = ASSERT_PTR(userdata);
+
+ /* Should not be reached on third call because the event loop should exit before */
+ ASSERT_LT(*c, 2u);
+
+ (*c)++;
+ return 0;
+}
+
+static int exit_on_idle_exit_handler(sd_event_source *s, void *userdata) {
+ return 0;
+}
+
+TEST(exit_on_idle) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ ASSERT_OK(sd_event_new(&e));
+ ASSERT_OK(sd_event_set_exit_on_idle(e, true));
+ ASSERT_OK_POSITIVE(sd_event_get_exit_on_idle(e));
+
+ /* Create a recurring defer event source. */
+ _cleanup_(sd_event_source_unrefp) sd_event_source *d = NULL;
+ unsigned dc = 0;
+ ASSERT_OK(sd_event_add_defer(e, &d, exit_on_idle_defer_handler, &dc));
+ ASSERT_OK(sd_event_source_set_enabled(d, SD_EVENT_ON));
+
+ /* This post event source should not keep the event loop running after the defer source is disabled. */
+ _cleanup_(sd_event_source_unrefp) sd_event_source *p = NULL;
+ unsigned pc = 0;
+ ASSERT_OK(sd_event_add_post(e, &p, exit_on_idle_post_handler, &pc));
+ ASSERT_OK(sd_event_source_set_enabled(p, SD_EVENT_ON));
+ ASSERT_OK(sd_event_source_set_priority(p, SD_EVENT_PRIORITY_IMPORTANT));
+
+ /* And neither should this exit event source. */
+ ASSERT_OK(sd_event_add_exit(e, NULL, exit_on_idle_exit_handler, NULL));
+
+ /* Run the event loop - it should exit after we disable the event source */
+ ASSERT_OK(sd_event_loop(e));
+ ASSERT_EQ(dc, 2u);
+ ASSERT_EQ(pc, 2u);
+}
+
+TEST(exit_on_idle_no_sources) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ ASSERT_OK(sd_event_new(&e));
+ ASSERT_OK(sd_event_set_exit_on_idle(e, true));
+
+ /* Running loop with no sources should return immediately with success */
+ ASSERT_OK(sd_event_loop(e));
+}
+
+static int defer_fair_handler(sd_event_source *s, void *userdata) {
+ unsigned *counter = ASSERT_PTR(userdata);
+
+ /* If we're about to increment above 5, exit the event loop */
+ if (*counter >= 5)
+ return sd_event_exit(sd_event_source_get_event(s), 0);
+
+ (*counter)++;
+
+ return 0;
+}
+
+TEST(defer_fair_scheduling) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ sd_event_source *sources[5] = {};
+ unsigned counters[5] = {};
+
+ ASSERT_OK(sd_event_new(&e));
+ ASSERT_OK(sd_event_set_exit_on_idle(e, true));
+
+ /* Create 5 defer sources with equal priority */
+ for (unsigned i = 0; i < 5; i++) {
+ ASSERT_OK(sd_event_add_defer(e, &sources[i], defer_fair_handler, &counters[i]));
+ ASSERT_OK(sd_event_source_set_enabled(sources[i], SD_EVENT_ON));
+ }
+
+ /* Run the event loop until one of the handlers exits */
+ ASSERT_OK(sd_event_loop(e));
+
+ /* All counters should be equal to 5, demonstrating fair scheduling */
+ for (unsigned i = 0; i < 5; i++) {
+ ASSERT_EQ(counters[i], 5u);
+ sd_event_source_unref(sources[i]);
+ }
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h
index ac98b67da4..7f0c444cfa 100644
--- a/src/systemd/sd-event.h
+++ b/src/systemd/sd-event.h
@@ -116,6 +116,8 @@ int sd_event_set_watchdog(sd_event *e, int b);
int sd_event_get_watchdog(sd_event *e);
int sd_event_get_iteration(sd_event *e, uint64_t *ret);
int sd_event_set_signal_exit(sd_event *e, int b);
+int sd_event_set_exit_on_idle(sd_event *e, int b);
+int sd_event_get_exit_on_idle(sd_event *e);
sd_event_source* sd_event_source_ref(sd_event_source *s);
sd_event_source* sd_event_source_unref(sd_event_source *s);
diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c
index fea0733173..997bcf2215 100644
--- a/src/test/test-cgroup-util.c
+++ b/src/test/test-cgroup-util.c
@@ -527,7 +527,9 @@ TEST(cgroupid) {
fd2 = cg_cgroupid_open(fd, id);
- if (ERRNO_IS_NEG_PRIVILEGE(fd2))
+ /* The kernel converts a bunch of errors to ESTALE in the open_by_handle_at() codepath so we treat
+ * it as missing privs but it could be absolutely anything really. */
+ if (ERRNO_IS_NEG_PRIVILEGE(fd2) || fd2 == -ESTALE)
log_notice("Skipping open-by-cgroup-id test because lacking privs.");
else if (ERRNO_IS_NEG_NOT_SUPPORTED(fd2))
log_notice("Skipping open-by-cgroup-id test because syscall is missing or blocked.");
diff --git a/src/test/test-id128.c b/src/test/test-id128.c
index 0ecdf76e80..9c28e82f5c 100644
--- a/src/test/test-id128.c
+++ b/src/test/test-id128.c
@@ -8,6 +8,7 @@
#include "sd-id128.h"
#include "alloc-util.h"
+#include "capability-util.h"
#include "fd-util.h"
#include "id128-util.h"
#include "path-util.h"
@@ -278,7 +279,7 @@ TEST(id128_at) {
ASSERT_OK(sd_id128_randomize(&id));
ASSERT_OK(id128_write_at(tfd, "etc/machine-id", ID128_FORMAT_PLAIN, id));
- if (geteuid() == 0)
+ if (have_effective_cap(CAP_DAC_OVERRIDE))
ASSERT_OK(id128_write_at(tfd, "etc/machine-id", ID128_FORMAT_PLAIN, id));
else
ASSERT_ERROR(id128_write_at(tfd, "etc/machine-id", ID128_FORMAT_PLAIN, id), EACCES);
diff --git a/src/test/test-rm-rf.c b/src/test/test-rm-rf.c
index af369d81f7..870a8b9a4e 100644
--- a/src/test/test-rm-rf.c
+++ b/src/test/test-rm-rf.c
@@ -3,6 +3,7 @@
#include
#include
+#include "capability-util.h"
#include "process-util.h"
#include "rm-rf.h"
#include "string-util.h"
@@ -29,7 +30,8 @@ static void test_rm_rf_chmod_inner(void) {
ASSERT_OK_ERRNO(chmod(x, 0500));
ASSERT_OK_ERRNO(chmod(d, 0500));
- ASSERT_ERROR(rm_rf(d, REMOVE_PHYSICAL), EACCES);
+ if (!have_effective_cap(CAP_DAC_OVERRIDE))
+ ASSERT_ERROR(rm_rf(d, REMOVE_PHYSICAL), EACCES);
ASSERT_OK_ERRNO(access(d, F_OK));
ASSERT_OK_ERRNO(access(x, F_OK));