ambrop7 15 лет назад
Родитель
Сommit
baba7254fb

+ 1 - 0
blog_channels.txt

@@ -17,3 +17,4 @@ BSocksClient 4
 BDHCPClientCore 4
 BDHCPClient 4
 NCDIfConfig 4
+BUnixSignal 4

+ 4 - 0
generated/blog_channel_BUnixSignal.h

@@ -0,0 +1,4 @@
+#ifdef BLOG_CURRENT_CHANNEL
+#undef BLOG_CURRENT_CHANNEL
+#endif
+#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BUnixSignal

+ 2 - 1
generated/blog_channels_defines.h

@@ -17,4 +17,5 @@
 #define BLOG_CHANNEL_BDHCPClientCore 16
 #define BLOG_CHANNEL_BDHCPClient 17
 #define BLOG_CHANNEL_NCDIfConfig 18
-#define BLOG_NUM_CHANNELS 19
+#define BLOG_CHANNEL_BUnixSignal 19
+#define BLOG_NUM_CHANNELS 20

+ 1 - 0
generated/blog_channels_list.h

@@ -17,3 +17,4 @@
 {.name = "BDHCPClientCore", .loglevel = 4},
 {.name = "BDHCPClient", .loglevel = 4},
 {.name = "NCDIfConfig", .loglevel = 4},
+{.name = "BUnixSignal", .loglevel = 4},

+ 133 - 0
system/BUnixSignal.c

@@ -0,0 +1,133 @@
+/**
+ * @file BUnixSignal.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/signalfd.h>
+#include <fcntl.h>
+
+#include <system/BLog.h>
+
+#include <system/BUnixSignal.h>
+
+#include <generated/blog_channel_BUnixSignal.h>
+
+static void signalfd_handler (BUnixSignal *o, int events)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    // read a signal
+    struct signalfd_siginfo siginfo;
+    int bytes = read(o->signalfd_fd, &siginfo, sizeof(siginfo));
+    if (bytes < 0) {
+        int error = errno;
+        if (error == EAGAIN || error == EWOULDBLOCK) {
+            return;
+        }
+        BLog(BLOG_ERROR, "read failed (%d)", error);
+        return;
+    }
+    ASSERT_FORCE(bytes == sizeof(siginfo))
+    
+    // check signal
+    if (siginfo.ssi_signo > INT_MAX) {
+        BLog(BLOG_ERROR, "read returned out of int range signo (%"PRIu32")", siginfo.ssi_signo);
+        return;
+    }
+    int signo = siginfo.ssi_signo;
+    if (sigismember(&o->signals, signo) <= 0) {
+        BLog(BLOG_ERROR, "read returned wrong signo (%d)", signo);
+        return;
+    }
+    
+    BLog(BLOG_DEBUG, "dispatching signal %d", signo);
+    
+    // call handler
+    struct BUnixSignal_siginfo dispatch_siginfo = { .signo = signo, .pid = siginfo.ssi_pid };
+    o->handler(o->user, dispatch_siginfo);
+    return;
+}
+
+int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnixSignal_handler handler, void *user)
+{
+    // init arguments
+    o->reactor = reactor;
+    o->signals = signals;
+    o->handler = handler;
+    o->user = user;
+    
+    // init signalfd fd
+    if ((o->signalfd_fd = signalfd(-1, &o->signals, 0)) < 0) {
+        BLog(BLOG_ERROR, "signalfd failed");
+        goto fail0;
+    }
+    
+    // set non-blocking
+    if (fcntl(o->signalfd_fd, F_SETFL, O_NONBLOCK) < 0) {
+        DEBUG("cannot set non-blocking");
+        goto fail1;
+    }
+    
+    // init signalfd BFileDescriptor
+    BFileDescriptor_Init(&o->signalfd_bfd, o->signalfd_fd, (BFileDescriptor_handler)signalfd_handler, o);
+    if (!BReactor_AddFileDescriptor(o->reactor, &o->signalfd_bfd)) {
+        BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
+        goto fail1;
+    }
+    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;
+    }
+    
+    DebugObject_Init(&o->d_obj);
+    
+    return 1;
+    
+fail2:
+    BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd);
+fail1:
+    ASSERT_FORCE(close(o->signalfd_fd) == 0)
+fail0:
+    return 0;
+}
+
+void BUnixSignal_Free (BUnixSignal *o, int unblock)
+{
+    ASSERT(unblock == 0 || unblock == 1)
+    DebugObject_Free(&o->d_obj);
+    
+    if (unblock) {
+        // unblock signals
+        ASSERT_FORCE(sigprocmask(SIG_UNBLOCK, &o->signals, 0) == 0)
+    }
+    
+    // free signalfd BFileDescriptor
+    BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd);
+    
+    // free signalfd fd
+    ASSERT_FORCE(close(o->signalfd_fd) == 0)
+}

+ 84 - 0
system/BUnixSignal.h

@@ -0,0 +1,84 @@
+/**
+ * @file BUnixSignal.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * @section DESCRIPTION
+ * 
+ * Object for catching unix signals.
+ */
+
+#ifndef BADVPN_SYSTEM_BUNIXSIGNAL_H
+#define BADVPN_SYSTEM_BUNIXSIGNAL_H
+
+#include <unistd.h>
+#include <signal.h>
+
+#include <misc/debug.h>
+#include <system/BReactor.h>
+#include <system/DebugObject.h>
+
+struct BUnixSignal_siginfo {
+    int signo;
+    pid_t pid;
+};
+
+typedef void (*BUnixSignal_handler) (void *user, struct BUnixSignal_siginfo siginfo);
+
+/**
+ * Object for catching unix signals.
+ */
+typedef struct {
+    BReactor *reactor;
+    sigset_t signals;
+    BUnixSignal_handler handler;
+    void *user;
+    int signalfd_fd;
+    BFileDescriptor signalfd_bfd;
+    DebugObject d_obj;
+} BUnixSignal;
+
+/**
+ * Initializes the object.
+ * {@link BLog_Init} must have been done.
+ * 
+ * This blocks the signal using sigprocmask() and sets up signalfd() for receiving
+ * signals.
+ *
+ * @param o the object
+ * @param reactor reactor we live in
+ * @param signals signals to handle. See man 3 sigsetops.
+ * @param handler handler function to call when a signal is received
+ * @param user value passed to callback function
+ * @return 1 on success, 0 on failure
+ */
+int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnixSignal_handler handler, void *user) WARN_UNUSED;
+
+/**
+ * Frees the object.
+ * 
+ * @param o the object
+ * @param unblock whether to unblock the signals using sigprocmask(). Not unblocking it
+ *                can be used while the program is exiting gracefully to prevent the
+ *                signals from being handled handled according to its default disposition
+ *                after this function is called. Must be 0 or 1.
+ */
+void BUnixSignal_Free (BUnixSignal *o, int unblock);
+
+#endif

+ 1 - 0
system/CMakeLists.txt

@@ -7,6 +7,7 @@ set(BSYSTEM_ADDITIONAL_SOURCES)
 if (NOT WIN32)
     list(APPEND BSYSTEM_ADDITIONAL_SOURCES
         BLog_syslog.c
+        BUnixSignal.c
     )
 endif ()