1
0
mirror of https://github.com/systemd/systemd synced 2026-03-31 12:14:57 +02:00

Compare commits

..

13 Commits

Author SHA1 Message Date
Daan De Meyer
10c79431a0
Three new sd-event features + various fixes (#39608) 2025-11-14 12:05:12 +01:00
Daan De Meyer
bc8aebdce9 log-context: Don't add log context if value is NULL
This avoids if conditions at the callsite which mess up stack based
lifetimes.
2025-11-14 11:38:59 +01:00
Daan De Meyer
5cf894ad58 basic-forward: Add WaitFlags forward decl 2025-11-14 11:38:59 +01:00
Daan De Meyer
a3dd54c097 sd-event: Make sure iterations of defer and exit sources are updated
Defer and exit event sources are marked pending once when they are added
and never again afterwards. This means their pending_iteration is never
incremented after they are initially added, which breaks fairness among
event sources with equal priority which depend on the pending_iteration
variable getting updated in source_set_pending(). To fix this, let's assign
iterations for defer and exit sources in source_dispatch() instead so that
those get their pending_iteration updated as well.
2025-11-14 11:38:59 +01:00
Daan De Meyer
5a5cb6ba50 sd-event: Add exit-on-idle support
Sometimes it's hard to assign responsibility to a specific event source
for exiting when there's no more work to be done. So let's add exit-on-idle
support where we exit when there are no more event sources.
2025-11-14 11:38:56 +01:00
Daan De Meyer
475729b805 run0: Make --same-root-dir available for run0
This enables running something like
"mkosi box -- run0 --empower --same-root-dir -E PATH" to get an
empowered session as the current user within the "mkosi box" environment.
2025-11-14 10:36:53 +01:00
Daan De Meyer
c11e1001db sd-event: Allow passing WNOWAIT to sd_event_add_child()
This allows doing the reaping outside of the callback, we'll use this
when adding fibers in a later commit.
2025-11-14 10:34:32 +01:00
Daan De Meyer
0a79791d0a sd-event: Drop faulty shortcut
We can't take this shortcut, as we might have to drop the
EPOLLONESHOT flag for some event sources, so drop the shortcut.
2025-11-14 10:34:32 +01:00
Daan De Meyer
d0ddb0aafb sd-event: Only register memory presure if write buffer size is zero
As documented in sd_event_add_memory_pressure(), we can only add
the memory pressure fd to epoll once we've written the watch string,
so make sure we don't register the memory pressure in
event_source_online() until we've written the watch string.
2025-11-14 10:34:32 +01:00
Daan De Meyer
4c8b6d636c sd-event: Mark post sources as pending after dispatching
More post event sources might get added during dispatching, we want
to make sure those become pending as well if we're dispatching a non-post
event source.
2025-11-14 10:34:30 +01:00
Daan De Meyer
6403a81b28 test-cgroup-util: Skip test on ESTALE
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.
2025-11-14 09:19:01 +01:00
Daan De Meyer
1312353fdd test-rm-rf: Check capabilities
The assertion will succeed if we have the right capability, causing the
test to fail.
2025-11-14 09:19:01 +01:00
Daan De Meyer
a0fb74153d test-id128: Check capability instead of uid 2025-11-14 09:19:01 +01:00
13 changed files with 464 additions and 38 deletions

View File

@ -302,6 +302,15 @@
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--same-root-dir</option></term>
<listitem><para>Execute the <command>run0</command> session in the same root directory that the
<command>run0</command> command is executed in.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--machine=</option></term>

View File

@ -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 <parameter>options</parameter> parameter determines which state changes will be watched for.
It must contain an OR-ed mask of <constant>WEXITED</constant> (watch for the child process terminating),
<constant>WSTOPPED</constant> (watch for the child process being stopped by a signal), and
<constant>WCONTINUED</constant> (watch for the child process being resumed by a signal). See
<constant>WSTOPPED</constant> (watch for the child process being stopped by a signal),
<constant>WCONTINUED</constant> (watch for the child process being resumed by a signal) and
<constant>WNOWAIT</constant> (Do not reap the child process after it exits). See
<citerefentry project='man-pages'><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry>
for further information.</para>

View File

@ -0,0 +1,116 @@
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="sd_event_set_exit_on_idle" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_event_set_exit_on_idle</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>sd_event_set_exit_on_idle</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>sd_event_set_exit_on_idle</refname>
<refname>sd_event_get_exit_on_idle</refname>
<refpurpose>Enable event loop exit-on-idle support</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>sd_event_set_exit_on_idle</function></funcdef>
<paramdef>sd_event *<parameter>event</parameter></paramdef>
<paramdef>int b</paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_get_exit_on_idle</function></funcdef>
<paramdef>sd_event *<parameter>event</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><function>sd_event_set_exit_on_idle()</function> may be used to
enable or disable the exit-on-idle support in the
event loop object specified in the <parameter>event</parameter>
parameter. If enabled, the event loop will exit with a zero exit code
there are no more enabled (<constant>SD_EVENT_ON</constant>, <constant>SD_EVENT_ONESHOT</constant>),
non-exit, non-post event sources.</para>
<para><function>sd_event_get_exit_on_idle()</function> may be used to
determine whether exit-on-idle support was previously requested by a
call to <function>sd_event_set_exit_on_idle()</function> with a true
<parameter>b</parameter> parameter and successfully enabled.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>On success, <function>sd_event_set_exit_on_idle()</function> and
<function>sd_event_get_exit_on_idle()</function> 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 <parameter>b</parameter> parameter. On failure, they return a negative errno-style error
code.</para>
<refsect2>
<title>Errors</title>
<para>Returned errors may indicate the following problems:</para>
<variablelist>
<varlistentry>
<term><constant>-ECHILD</constant></term>
<listitem><para>The event loop has been created in a different process, library or module instance.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-EINVAL</constant></term>
<listitem><para>The passed event loop object was invalid.</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>History</title>
<para><function>sd_event_set_exit_on_idle()</function> and
<function>sd_event_get_exit_on_idle()</function> were added in version 259.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -899,6 +899,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
ARG_AREA,
ARG_VIA_SHELL,
ARG_EMPOWER,
ARG_SAME_ROOT_DIR,
};
/* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
@ -929,6 +930,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
{ "lightweight", required_argument, NULL, ARG_LIGHTWEIGHT },
{ "area", required_argument, NULL, ARG_AREA },
{ "empower", no_argument, NULL, ARG_EMPOWER },
{ "same-root-dir", no_argument, NULL, ARG_SAME_ROOT_DIR },
{},
};
@ -1072,6 +1074,13 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
arg_empower = true;
break;
case ARG_SAME_ROOT_DIR:
r = free_and_strdup_warn(&arg_root_directory, "/");
if (r < 0)
return r;
break;
case '?':
return -EINVAL;

View File

@ -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);

View File

@ -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.");

View File

@ -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);

View File

@ -3,6 +3,7 @@
#include <sys/stat.h>
#include <unistd.h>
#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));