فهرست منبع

BUnixSignal: add self-pipe backend. Use it on Linux when signalfd() is not available.

ambrop7 15 سال پیش
والد
کامیت
32da4a98cb
3فایلهای تغییر یافته به همراه198 افزوده شده و 8 حذف شده
  1. 4 3
      CMakeLists.txt
  2. 179 4
      system/BUnixSignal.c
  3. 15 1
      system/BUnixSignal.h

+ 4 - 3
CMakeLists.txt

@@ -62,10 +62,11 @@ else ()
         add_definitions(-DBADVPN_LINUX)
 
         check_include_files(sys/signalfd.h HAVE_SYS_SIGNALFD_H)
-        if (NOT HAVE_SYS_SIGNALFD_H)
-            message(FATAL_ERROR "signalfd is required")
+        if (HAVE_SYS_SIGNALFD_H)
+            add_definitions(-DBADVPN_USE_SIGNALFD)
+        else ()
+            add_definitions(-DBADVPN_USE_SELFPIPE)
         endif ()
-        add_definitions(-DBADVPN_USE_SIGNALFD)
 
         check_include_files(sys/epoll.h HAVE_SYS_EPOLL_H)
         if (HAVE_SYS_EPOLL_H)

+ 179 - 4
system/BUnixSignal.c

@@ -25,12 +25,14 @@
 #include <limits.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <string.h>
 
 #ifdef BADVPN_USE_SIGNALFD
 #include <sys/signalfd.h>
 #endif
 
 #include <misc/balloc.h>
+#include <misc/nonblocking.h>
 #include <system/BLog.h>
 
 #include <system/BUnixSignal.h>
@@ -92,6 +94,63 @@ static void kevent_handler (struct BUnixSignal_kevent_entry *entry, u_int fflags
 
 #endif
 
+#ifdef BADVPN_USE_SELFPIPE
+
+struct BUnixSignal_selfpipe_entry *bunixsignal_selfpipe_entries[BUNIXSIGNAL_MAX_SIGNALS];
+
+static void free_selfpipe_entry (struct BUnixSignal_selfpipe_entry *entry)
+{
+    BUnixSignal *o = entry->parent;
+    
+    // uninstall signal handler
+    struct sigaction act;
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = SIG_DFL;
+    sigemptyset(&act.sa_mask);
+    ASSERT_FORCE(sigaction(entry->signo, &act, NULL) == 0)
+    
+    // free BFileDescriptor
+    BReactor_RemoveFileDescriptor(o->reactor, &entry->pipe_read_bfd);
+    
+    // close pipe
+    ASSERT_FORCE(close(entry->pipefds[0]) == 0)
+    ASSERT_FORCE(close(entry->pipefds[1]) == 0)
+}
+
+static void pipe_read_fd_handler (struct BUnixSignal_selfpipe_entry *entry, int events)
+{
+    BUnixSignal *o = entry->parent;
+    DebugObject_Access(&o->d_obj);
+    
+    // read a byte
+    uint8_t b;
+    if (read(entry->pipefds[0], &b, sizeof(b)) < 0) {
+        int error = errno;
+        if (error == EAGAIN || error == EWOULDBLOCK) {
+            return;
+        }
+        BLog(BLOG_ERROR, "read failed (%d)", error);
+        return;
+    }
+    
+    // call handler
+    o->handler(o->user, entry->signo);
+    return;
+}
+
+static void signal_handler (int signo)
+{
+    ASSERT(signo >= 0)
+    ASSERT(signo < BUNIXSIGNAL_MAX_SIGNALS)
+    
+    struct BUnixSignal_selfpipe_entry *entry = bunixsignal_selfpipe_entries[signo];
+    
+    uint8_t b = 0;
+    write(entry->pipefds[1], &b, sizeof(b));
+}
+
+#endif
+
 int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnixSignal_handler handler, void *user)
 {
     // init arguments
@@ -122,6 +181,12 @@ int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnix
     }
     BReactor_SetFileDescriptorEvents(o->reactor, &o->signalfd_bfd, BREACTOR_READ);
     
+    // block signals
+    if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) {
+        BLog(BLOG_ERROR, "sigprocmask block failed");
+        goto fail2;
+    }
+    
     #endif
     
     #ifdef BADVPN_USE_KEVENT
@@ -157,14 +222,90 @@ int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnix
         o->num_entries++;
     }
     
-    #endif
-    
     // block signals
     if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) {
         BLog(BLOG_ERROR, "sigprocmask block failed");
         goto fail2;
     }
     
+    #endif
+    
+    #ifdef BADVPN_USE_SELFPIPE
+    
+    // count signals
+    int num_signals = 0;
+    for (int i = 1; i < BUNIXSIGNAL_MAX_SIGNALS; i++) {
+        if (!sigismember(&o->signals, i)) {
+            continue;
+        }
+        num_signals++;
+    }
+    
+    // allocate array
+    if (!(o->entries = BAllocArray(num_signals, sizeof(o->entries[0])))) {
+        BLog(BLOG_ERROR, "BAllocArray failed");
+        goto fail0;
+    }
+    
+    // init entries
+    o->num_entries = 0;
+    for (int i = 1; i < BUNIXSIGNAL_MAX_SIGNALS; i++) {
+        if (!sigismember(&o->signals, i)) {
+            continue;
+        }
+        
+        struct BUnixSignal_selfpipe_entry *entry = &o->entries[o->num_entries];
+        entry->parent = o;
+        entry->signo = i;
+        
+        // init pipe
+        if (pipe(entry->pipefds) < 0) {
+            BLog(BLOG_ERROR, "pipe failed");
+            goto loop_fail0;
+        }
+        
+        // set pipe ends non-blocking
+        if (!badvpn_set_nonblocking(entry->pipefds[0]) || !badvpn_set_nonblocking(entry->pipefds[1])) {
+            BLog(BLOG_ERROR, "set nonblocking failed");
+            goto loop_fail1;
+        }
+        
+        // init read end BFileDescriptor
+        BFileDescriptor_Init(&entry->pipe_read_bfd, entry->pipefds[0], (BFileDescriptor_handler)pipe_read_fd_handler, entry);
+        if (!BReactor_AddFileDescriptor(o->reactor, &entry->pipe_read_bfd)) {
+            BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
+            goto loop_fail1;
+        }
+        BReactor_SetFileDescriptorEvents(o->reactor, &entry->pipe_read_bfd, BREACTOR_READ);
+        
+        // set global entry pointer
+        bunixsignal_selfpipe_entries[entry->signo] = entry;
+        
+        // install signal handler
+        struct sigaction act;
+        memset(&act, 0, sizeof(act));
+        act.sa_handler = signal_handler;
+        sigemptyset(&act.sa_mask);
+        if (sigaction(entry->signo, &act, NULL) < 0) {
+            BLog(BLOG_ERROR, "sigaction failed");
+            goto loop_fail2;
+        }
+        
+        o->num_entries++;
+        
+        continue;
+        
+    loop_fail2:
+        BReactor_RemoveFileDescriptor(o->reactor, &entry->pipe_read_bfd);
+    loop_fail1:
+        ASSERT_FORCE(close(entry->pipefds[0]) == 0)
+        ASSERT_FORCE(close(entry->pipefds[1]) == 0)
+    loop_fail0:
+        goto fail2;
+    }
+    
+    #endif
+    
     DebugObject_Init(&o->d_obj);
     
     return 1;
@@ -185,6 +326,15 @@ fail2:
     BFree(o->entries);
     #endif
     
+    #ifdef BADVPN_USE_SELFPIPE
+fail2:
+    while (o->num_entries > 0) {
+        free_selfpipe_entry(&o->entries[o->num_entries - 1]);
+        o->num_entries--;
+    }
+    BFree(o->entries);
+    #endif
+    
 fail0:
     return 0;
 }
@@ -194,13 +344,13 @@ void BUnixSignal_Free (BUnixSignal *o, int unblock)
     ASSERT(unblock == 0 || unblock == 1)
     DebugObject_Free(&o->d_obj);
     
+    #ifdef BADVPN_USE_SIGNALFD
+    
     if (unblock) {
         // unblock signals
         ASSERT_FORCE(sigprocmask(SIG_UNBLOCK, &o->signals, 0) == 0)
     }
     
-    #ifdef BADVPN_USE_SIGNALFD
-    
     // free signalfd BFileDescriptor
     BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd);
     
@@ -211,6 +361,11 @@ void BUnixSignal_Free (BUnixSignal *o, int unblock)
     
     #ifdef BADVPN_USE_KEVENT
     
+    if (unblock) {
+        // unblock signals
+        ASSERT_FORCE(sigprocmask(SIG_UNBLOCK, &o->signals, 0) == 0)
+    }
+    
     // free kevents
     while (o->num_entries > 0) {
         BReactorKEvent_Free(&o->entries[o->num_entries - 1].kevent);
@@ -221,4 +376,24 @@ void BUnixSignal_Free (BUnixSignal *o, int unblock)
     BFree(o->entries);
     
     #endif
+    
+    #ifdef BADVPN_USE_SELFPIPE
+    
+    if (!unblock) {
+        // block signals
+        if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) {
+            BLog(BLOG_ERROR, "sigprocmask block failed");
+        }
+    }
+    
+    // free entries
+    while (o->num_entries > 0) {
+        free_selfpipe_entry(&o->entries[o->num_entries - 1]);
+        o->num_entries--;
+    }
+    
+    // free array
+    BFree(o->entries);
+    
+    #endif
 }

+ 15 - 1
system/BUnixSignal.h

@@ -27,7 +27,7 @@
 #ifndef BADVPN_SYSTEM_BUNIXSIGNAL_H
 #define BADVPN_SYSTEM_BUNIXSIGNAL_H
 
-#if (defined(BADVPN_USE_SIGNALFD) + defined(BADVPN_USE_KEVENT)) != 1
+#if (defined(BADVPN_USE_SIGNALFD) + defined(BADVPN_USE_KEVENT) + defined(BADVPN_USE_SELFPIPE)) != 1
 #error Unknown signal backend or too many signal backends
 #endif
 
@@ -56,6 +56,15 @@ struct BUnixSignal_kevent_entry {
 };
 #endif
 
+#ifdef BADVPN_USE_SELFPIPE
+struct BUnixSignal_selfpipe_entry {
+    struct BUnixSignal_s *parent;
+    int signo;
+    int pipefds[2];
+    BFileDescriptor pipe_read_bfd;
+};
+#endif
+
 /**
  * Object for catching unix signals.
  */
@@ -75,6 +84,11 @@ typedef struct BUnixSignal_s {
     int num_entries;
     #endif
     
+    #ifdef BADVPN_USE_SELFPIPE
+    struct BUnixSignal_selfpipe_entry *entries;
+    int num_entries;
+    #endif
+    
     DebugObject d_obj;
 } BUnixSignal;