don't crash
[metze/samba/wip.git] / lib / tevent / tevent_signal.c
index 27e862453ae3996ddb00a3ee741e079d956f817e..46bcfca5e90681472745608994343ad9848f34d3 100644 (file)
 
 #define NUM_SIGNALS 64
 
-/* maximum number of SA_SIGINFO signals to hold in the queue */
-#define SA_INFO_QUEUE_COUNT 100
+/* maximum number of SA_SIGINFO signals to hold in the queue.
+  NB. This *MUST* be a power of 2, in order for the ring buffer
+  wrap to work correctly. Thanks to Petr Vandrovec <petr@vandrovec.name>
+  for this. */
+
+#define SA_INFO_QUEUE_COUNT 64
 
 struct sigcounter {
        uint32_t count;
@@ -57,7 +61,6 @@ static struct sig_state {
        struct sigaction *oldact[NUM_SIGNALS+1];
        struct sigcounter signal_count[NUM_SIGNALS+1];
        struct sigcounter got_signal;
-       int pipe_hack[2];
 #ifdef SA_SIGINFO
        /* with SA_SIGINFO we get quite a lot of info per signal */
        siginfo_t *sig_info[NUM_SIGNALS+1];
@@ -70,10 +73,7 @@ static struct sig_state {
 */
 static uint32_t sig_count(struct sigcounter s)
 {
-       if (s.count >= s.seen) {
-               return s.count - s.seen;
-       }
-       return 1 + (0xFFFFFFFF & ~(s.seen - s.count));
+       return s.count - s.seen;
 }
 
 /*
@@ -83,10 +83,25 @@ static void tevent_common_signal_handler(int signum)
 {
        char c = 0;
        ssize_t res;
+       struct tevent_common_signal_list *sl;
+       struct tevent_context *ev = NULL;
+       int saved_errno = errno;
+
        SIG_INCREMENT(sig_state->signal_count[signum]);
        SIG_INCREMENT(sig_state->got_signal);
-       /* doesn't matter if this pipe overflows */
-       res = write(sig_state->pipe_hack[1], &c, 1);
+
+       /* Write to each unique event context. */
+       for (sl = sig_state->sig_handlers[signum]; sl; sl = sl->next) {
+               if (sl->se->event_ctx != ev) {
+                       ev = sl->se->event_ctx;
+                       if (!ev) continue;
+                       if (!ev->pipe_fds) continue;
+                       /* doesn't matter if this pipe overflows */
+                       res = write(ev->pipe_fds[1], &c, 1);
+               }
+       }
+
+       errno = saved_errno;
 }
 
 #ifdef SA_SIGINFO
@@ -158,12 +173,12 @@ static int tevent_signal_destructor(struct tevent_signal *se)
   this is part of the pipe hack needed to avoid the signal race condition
 */
 static void signal_pipe_handler(struct tevent_context *ev, struct tevent_fd *fde, 
-                               uint16_t flags, void *private)
+                               uint16_t flags, void *_private)
 {
        char c[16];
        ssize_t res;
        /* its non-blocking, doesn't matter if we read too much */
-       res = read(sig_state->pipe_hack[0], c, sizeof(c));
+       res = read(fde->fd, c, sizeof(c));
 }
 
 /*
@@ -181,6 +196,7 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev,
 {
        struct tevent_signal *se;
        struct tevent_common_signal_list *sl;
+       sigset_t set, oldset;
 
        if (signum >= NUM_SIGNALS) {
                errno = EINVAL;
@@ -222,6 +238,26 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev,
                return NULL;
        }
 
+       /* we need to setup the pipe hack handler if not already
+          setup */
+       if (ev->pipe_fde == NULL) {
+               if (pipe(ev->pipe_fds) == -1) {
+                       talloc_free(se);
+                       return NULL;
+               }
+               ev_set_blocking(ev->pipe_fds[0], false);
+               ev_set_blocking(ev->pipe_fds[1], false);
+               ev->pipe_fde = tevent_add_fd(ev, ev, ev->pipe_fds[0],
+                                            TEVENT_FD_READ,
+                                            signal_pipe_handler, NULL);
+               if (!ev->pipe_fde) {
+                       close(ev->pipe_fds[0]);
+                       close(ev->pipe_fds[1]);
+                       talloc_free(se);
+                       return NULL;
+               }
+       }
+
        /* only install a signal handler if not already installed */
        if (sig_state->sig_handlers[signum] == NULL) {
                struct sigaction act;
@@ -253,31 +289,17 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev,
        }
 
        DLIST_ADD(se->event_ctx->signal_events, se);
+
+       /* Make sure the signal doesn't come in while we're mangling list. */
+       sigemptyset(&set);
+       sigaddset(&set, signum);
+       sigprocmask(SIG_BLOCK, &set, &oldset);
        DLIST_ADD(sig_state->sig_handlers[signum], sl);
+       sigprocmask(SIG_SETMASK, &oldset, NULL);
 
        talloc_set_destructor(se, tevent_signal_destructor);
        talloc_set_destructor(sl, tevent_common_signal_list_destructor);
 
-       /* we need to setup the pipe hack handler if not already
-          setup */
-       if (ev->pipe_fde == NULL) {
-               if (sig_state->pipe_hack[0] == 0 && 
-                   sig_state->pipe_hack[1] == 0) {
-                       if (pipe(sig_state->pipe_hack) == -1) {
-                               talloc_free(se);
-                               return NULL;
-                       }
-                       ev_set_blocking(sig_state->pipe_hack[0], false);
-                       ev_set_blocking(sig_state->pipe_hack[1], false);
-               }
-               ev->pipe_fde = tevent_add_fd(ev, ev, sig_state->pipe_hack[0],
-                                            TEVENT_FD_READ, signal_pipe_handler, NULL);
-               if (!ev->pipe_fde) {
-                       talloc_free(se);
-                       return NULL;
-               }
-       }
-
        return se;
 }