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.
This commit is contained in:
Lennart Poettering 2022-09-28 11:39:25 +02:00
parent 0b8218b901
commit 897448bd37
5 changed files with 90 additions and 27 deletions

View File

@ -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',

View File

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

View File

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

View File

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

View File

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