]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-event: if signal nr has high bit set sd_event_add_signal() auto-block it via sigpr...
authorLennart Poettering <lennart@poettering.net>
Wed, 28 Sep 2022 09:39:25 +0000 (11:39 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 30 Sep 2022 12:17:46 +0000 (14:17 +0200)
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.

man/rules/meson.build
man/sd_event_add_signal.xml
src/libsystemd/sd-event/event-source.h
src/libsystemd/sd-event/sd-event.c
src/systemd/sd-event.h

index 2925dadc1e1cee752895bdf98e6d22def3302842..7f4a42b1396ad51c7218e64cb953e30152ffc5db 100644 (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',
index b2aaff87c10b739fcd02605f14fdc268b8abe34f..3e8536e96112bdcd60a6c1ff22cdd49860050ec3 100644 (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>
         <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>
 
     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>
 
index 74cbc269626eb36fddbb140dbd1453d369737783..6092652d0fa99a33e7c1dabfd59bbd9c38378917 100644 (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;
index 6f99c5f0cd57bcd3a46ad7ddc4d8223c40d43219..890b62b1f962a59d7277a3d7f12ddb9fae3cb079 100644 (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));
index e782339c4acc565ceae6989c26754f1117f66386..d2886b8038505dd639b816172d2b45c77ec0a353 100644 (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);