Restore signal handler after uloop_run()
authorKristian Evensen <kristian.evensen@gmail.com>
Tue, 22 Oct 2013 08:24:15 +0000 (10:24 +0200)
committerFelix Fietkau <nbd@openwrt.org>
Tue, 22 Oct 2013 15:26:05 +0000 (17:26 +0200)
uloop_run calls uloop_setup_signals() to set up signal handling before the while
loop, but does not remove the signal handling after the loop has ended. This can
cause problems for for example applications using the ubus file descriptor in
their own event loops, and perhaps with their own signal handling.

This patch stores the signal handle that was in place when the initial
uloop_run() call was made, and restores the handle when this call returns.
For recursive calls, the signal handler is not updated.

One use-case I experienced was an application that subscribed to several ubus
objects and used the ubus file descriptor in its own event loop. Even though
ubus_register_subscriber() (which calls uloop_run()) had returned, the signal
handler was not removed. This caused SIGINT not to be caught by the application.

Signed-off-by: Kristian Evensen <kristian.evensen@gmail.com>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
uloop.c

diff --git a/uloop.c b/uloop.c
index 9a0590f..0566d80 100644 (file)
--- a/uloop.c
+++ b/uloop.c
@@ -556,19 +556,31 @@ static void uloop_sigchld(int signo)
        do_sigchld = true;
 }
 
-static void uloop_setup_signals(void)
+static void uloop_setup_signals(bool add)
 {
+       static struct sigaction old_sigint, old_sigchld;
        struct sigaction s;
 
        memset(&s, 0, sizeof(struct sigaction));
-       s.sa_handler = uloop_handle_sigint;
-       s.sa_flags = 0;
-       sigaction(SIGINT, &s, NULL);
 
-       if (uloop_handle_sigchld) {
-               s.sa_handler = uloop_sigchld;
-               sigaction(SIGCHLD, &s, NULL);
+       if (add) {
+               s.sa_handler = uloop_handle_sigint;
+               s.sa_flags = 0;
+       } else {
+               s = old_sigint;
        }
+
+       sigaction(SIGINT, &s, &old_sigint);
+
+       if (!uloop_handle_sigchld)
+               return;
+
+       if (add)
+               s.sa_handler = uloop_sigchld;
+       else
+               s = old_sigchld;
+
+       sigaction(SIGCHLD, &s, &old_sigchld);
 }
 
 static int uloop_get_next_timeout(struct timeval *tv)
@@ -621,9 +633,16 @@ static void uloop_clear_processes(void)
 
 void uloop_run(void)
 {
+       static int recursive_calls = 0;
        struct timeval tv;
 
-       uloop_setup_signals();
+       /*
+        * Handlers are only updated for the first call to uloop_run() (and restored
+        * when this call is done).
+        */
+       if (!recursive_calls++)
+               uloop_setup_signals(true);
+
        while(!uloop_cancelled)
        {
                uloop_gettime(&tv);
@@ -636,6 +655,9 @@ void uloop_run(void)
                uloop_gettime(&tv);
                uloop_run_events(uloop_get_next_timeout(&tv));
        }
+
+       if (!--recursive_calls)
+               uloop_setup_signals(false);
 }
 
 void uloop_done(void)