diff options
author | Lennart Poettering <lennart@poettering.net> | 2022-09-28 11:39:25 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2022-09-30 14:17:46 +0200 |
commit | 897448bd3753eab2c7b411221cfc33b283ae67a5 (patch) | |
tree | 6cd7bec475dc0ec15430f42dddd7cd0eb6446f4e | |
parent | 0b8218b901a44f3e6a7d76f25038fb0526e8b1d7 (diff) | |
download | systemd-897448bd3753eab2c7b411221cfc33b283ae67a5.tar.gz |
sd-event: if signal nr has high bit set sd_event_add_signal() auto-block it via sigprocmask()
So far we expected callers to block the signals manually. Which is
usually a good idea, since they should do that before forking off
threads and similar. But let's add a mode where we automatically block
it for the caller, to simplify things.
-rw-r--r-- | man/rules/meson.build | 4 | ||||
-rw-r--r-- | man/sd_event_add_signal.xml | 41 | ||||
-rw-r--r-- | src/libsystemd/sd-event/event-source.h | 1 | ||||
-rw-r--r-- | src/libsystemd/sd-event/sd-event.c | 71 | ||||
-rw-r--r-- | src/systemd/sd-event.h | 2 |
5 files changed, 91 insertions, 28 deletions
diff --git a/man/rules/meson.build b/man/rules/meson.build index 2925dadc1e..7f4a42b139 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -555,7 +555,9 @@ manpages = [ ''], ['sd_event_add_signal', '3', - ['sd_event_signal_handler_t', 'sd_event_source_get_signal'], + ['SD_EVENT_SIGNAL_PROCMASK', + 'sd_event_signal_handler_t', + 'sd_event_source_get_signal'], ''], ['sd_event_add_time', '3', diff --git a/man/sd_event_add_signal.xml b/man/sd_event_add_signal.xml index b2aaff87c1..3e8536e961 100644 --- a/man/sd_event_add_signal.xml +++ b/man/sd_event_add_signal.xml @@ -19,6 +19,7 @@ <refname>sd_event_add_signal</refname> <refname>sd_event_source_get_signal</refname> <refname>sd_event_signal_handler_t</refname> + <refname>SD_EVENT_SIGNAL_PROCMASK</refname> <refpurpose>Add a UNIX process signal event source to an event loop</refpurpose> @@ -30,6 +31,8 @@ <funcsynopsisinfo><token>typedef</token> struct sd_event_source sd_event_source;</funcsynopsisinfo> + <funcsynopsisinfo><constant>SD_EVENT_SIGNAL_PROCMASK</constant></funcsynopsisinfo> + <funcprototype> <funcdef>typedef int (*<function>sd_event_signal_handler_t</function>)</funcdef> <paramdef>sd_event_source *<parameter>s</parameter></paramdef> @@ -50,30 +53,26 @@ <funcdef>int <function>sd_event_source_get_signal</function></funcdef> <paramdef>sd_event_source *<parameter>source</parameter></paramdef> </funcprototype> - </funcsynopsis> </refsynopsisdiv> <refsect1> <title>Description</title> - <para><function>sd_event_add_signal()</function> adds a new UNIX - process signal event source to an event loop. The event loop - object is specified in the <parameter>event</parameter> parameter, - and the event source object is returned in the - <parameter>source</parameter> parameter. The - <parameter>signal</parameter> parameter specifies the numeric - signal to be handled (see <citerefentry + <para><function>sd_event_add_signal()</function> adds a new UNIX process signal event source to an event + loop. The event loop object is specified in the <parameter>event</parameter> parameter, and the event + source object is returned in the <parameter>source</parameter> parameter. The + <parameter>signal</parameter> parameter specifies the numeric signal to be handled (see <citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>).</para> <para>The <parameter>handler</parameter> parameter is a function to call when the signal is received or <constant>NULL</constant>. The handler function will be passed the <parameter>userdata</parameter> pointer, which may be chosen freely by the caller. The handler also receives a pointer to a <structname>signalfd_siginfo</structname> structure containing information about the received signal. See - <citerefentry project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry> - for further information. The handler may return negative to signal an error (see below), other return - values are ignored. If <parameter>handler</parameter> is <constant>NULL</constant>, a default handler - that calls + <citerefentry + project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry> for + further information. The handler may return negative to signal an error (see below), other return values + are ignored. If <parameter>handler</parameter> is <constant>NULL</constant>, a default handler that calls <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry> will be used.</para> @@ -81,14 +80,18 @@ threads before this function is called (using <citerefentry project='man-pages'><refentrytitle>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry> or <citerefentry - project='man-pages'><refentrytitle>pthread_sigmask</refentrytitle><manvolnum>3</manvolnum></citerefentry>).</para> - - <para>By default, the event source is enabled permanently - (<constant>SD_EVENT_ON</constant>), but this may be changed with + project='man-pages'><refentrytitle>pthread_sigmask</refentrytitle><manvolnum>3</manvolnum></citerefentry>). For + convenience, if the special flag <constant>SD_EVENT_SIGNAL_PROCMASK</constant> is ORed into the specified + signal the signal will be automatically masked as necessary, for the calling thread. Note that this only + works reliably if the signal is already masked in all other threads of the process, or if there are no + other threads at the moment of invocation.</para> + + <para>By default, the event source is enabled permanently (<constant>SD_EVENT_ON</constant>), but this + may be changed with <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>. - If the handler function returns a negative error code, it will either be disabled after the - invocation, even if the <constant>SD_EVENT_ON</constant> mode was requested before, or it will cause the - loop to terminate, see + If the handler function returns a negative error code, it will either be disabled after the invocation, + even if the <constant>SD_EVENT_ON</constant> mode was requested before, or it will cause the loop to + terminate, see <citerefentry><refentrytitle>sd_event_source_set_exit_on_failure</refentrytitle><manvolnum>3</manvolnum></citerefentry>. </para> diff --git a/src/libsystemd/sd-event/event-source.h b/src/libsystemd/sd-event/event-source.h index 74cbc26962..6092652d0f 100644 --- a/src/libsystemd/sd-event/event-source.h +++ b/src/libsystemd/sd-event/event-source.h @@ -99,6 +99,7 @@ struct sd_event_source { sd_event_signal_handler_t callback; struct signalfd_siginfo siginfo; int sig; + bool unblock; } signal; struct { sd_event_child_handler_t callback; diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 6f99c5f0cd..890b62b1f9 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -813,6 +813,7 @@ static void event_source_time_prioq_remove( static void source_disconnect(sd_event_source *s) { sd_event *event; + int r; assert(s); @@ -853,6 +854,20 @@ static void source_disconnect(sd_event_source *s) { s->event->signal_sources[s->signal.sig] = NULL; event_gc_signal_data(s->event, &s->priority, s->signal.sig); + + if (s->signal.unblock) { + sigset_t new_ss; + + if (sigemptyset(&new_ss) < 0) + log_debug_errno(errno, "Failed to reset signal set, ignoring: %m"); + else if (sigaddset(&new_ss, s->signal.sig) < 0) + log_debug_errno(errno, "Failed to add signal %i to signal mask, ignoring: %m", s->signal.sig); + else { + r = pthread_sigmask(SIG_UNBLOCK, &new_ss, NULL); + if (r != 0) + log_debug_errno(r, "Failed to unblock signal %i, ignoring: %m", s->signal.sig); + } + } } break; @@ -1328,23 +1343,38 @@ _public_ int sd_event_add_signal( _cleanup_(source_freep) sd_event_source *s = NULL; struct signal_data *d; + sigset_t new_ss; + bool block_it; int r; assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(SIGNAL_VALID(sig), -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); + /* Let's make sure our special flag stays outside of the valid signal range */ + assert_cc(_NSIG < SD_EVENT_SIGNAL_PROCMASK); + + if (sig & SD_EVENT_SIGNAL_PROCMASK) { + sig &= ~SD_EVENT_SIGNAL_PROCMASK; + assert_return(SIGNAL_VALID(sig), -EINVAL); + + block_it = true; + } else { + assert_return(SIGNAL_VALID(sig), -EINVAL); + + r = signal_is_blocked(sig); + if (r < 0) + return r; + if (r == 0) + return -EBUSY; + + block_it = false; + } + if (!callback) callback = signal_exit_callback; - r = signal_is_blocked(sig); - if (r < 0) - return r; - if (r == 0) - return -EBUSY; - if (!e->signal_sources) { e->signal_sources = new0(sd_event_source*, _NSIG); if (!e->signal_sources) @@ -1363,9 +1393,34 @@ _public_ int sd_event_add_signal( e->signal_sources[sig] = s; + if (block_it) { + sigset_t old_ss; + + if (sigemptyset(&new_ss) < 0) + return -errno; + + if (sigaddset(&new_ss, sig) < 0) + return -errno; + + r = pthread_sigmask(SIG_BLOCK, &new_ss, &old_ss); + if (r != 0) + return -r; + + r = sigismember(&old_ss, sig); + if (r < 0) + return -errno; + + s->signal.unblock = !r; + } else + s->signal.unblock = false; + r = event_make_signal_data(e, sig, &d); - if (r < 0) + if (r < 0) { + if (s->signal.unblock) + (void) pthread_sigmask(SIG_UNBLOCK, &new_ss, NULL); + return r; + } /* Use the signal name as description for the event source by default */ (void) sd_event_source_set_description(s, signal_to_string(sig)); diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index e782339c4a..d2886b8038 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -68,6 +68,8 @@ enum { SD_EVENT_PRIORITY_IDLE = 100 }; +#define SD_EVENT_SIGNAL_PROCMASK (1 << 30) + typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata); typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata); typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata); |