mirror of
https://github.com/systemd/systemd.git
synced 2024-11-24 10:43:35 +08:00
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:
parent
0b8218b901
commit
897448bd37
@ -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',
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user