mirror of
https://github.com/systemd/systemd
synced 2026-03-31 20:24:50 +02:00
Compare commits
13 Commits
98ae19d9fe
...
10c79431a0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10c79431a0 | ||
|
|
bc8aebdce9 | ||
|
|
5cf894ad58 | ||
|
|
a3dd54c097 | ||
|
|
5a5cb6ba50 | ||
|
|
475729b805 | ||
|
|
c11e1001db | ||
|
|
0a79791d0a | ||
|
|
d0ddb0aafb | ||
|
|
4c8b6d636c | ||
|
|
6403a81b28 | ||
|
|
1312353fdd | ||
|
|
a0fb74153d |
@ -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>
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
116
man/sd_event_set_exit_on_idle.xml
Normal file
116
man/sd_event_set_exit_on_idle.xml
Normal 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 <systemd/sd-event.h></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>
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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:
|
||||
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,26 +4120,24 @@ 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);
|
||||
r = mark_post_sources_pending(s->event);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->type == SOURCE_MEMORY_PRESSURE) {
|
||||
r = source_memory_pressure_initiate_dispatch(s);
|
||||
@ -4172,9 +4180,10 @@ 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);
|
||||
(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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.");
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,6 +30,7 @@ static void test_rm_rf_chmod_inner(void) {
|
||||
ASSERT_OK_ERRNO(chmod(x, 0500));
|
||||
ASSERT_OK_ERRNO(chmod(d, 0500));
|
||||
|
||||
if (!have_effective_cap(CAP_DAC_OVERRIDE))
|
||||
ASSERT_ERROR(rm_rf(d, REMOVE_PHYSICAL), EACCES);
|
||||
|
||||
ASSERT_OK_ERRNO(access(d, F_OK));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user