407 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			407 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
| /**
 | |
|  * @file BUnixSignal.c
 | |
|  * @author Ambroz Bizjak <ambrop7@gmail.com>
 | |
|  * 
 | |
|  * @section LICENSE
 | |
|  * 
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  * 3. Neither the name of the author nor the
 | |
|  *    names of its contributors may be used to endorse or promote products
 | |
|  *    derived from this software without specific prior written permission.
 | |
|  * 
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 | |
|  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | |
|  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | |
|  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 | |
|  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | |
|  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | |
|  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | |
|  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | |
|  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include <inttypes.h>
 | |
| #include <stdlib.h>
 | |
| #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 <base/BLog.h>
 | |
| 
 | |
| #include <system/BUnixSignal.h>
 | |
| 
 | |
| #include <generated/blog_channel_BUnixSignal.h>
 | |
| 
 | |
| #define BUNIXSIGNAL_MAX_SIGNALS 64
 | |
| 
 | |
| #ifdef BADVPN_USE_SIGNALFD
 | |
| 
 | |
| 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
 | |
|     o->handler(o->user, signo);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| #ifdef BADVPN_USE_KEVENT
 | |
| 
 | |
| static void kevent_handler (struct BUnixSignal_kevent_entry *entry, u_int fflags, intptr_t data)
 | |
| {
 | |
|     BUnixSignal *o = entry->parent;
 | |
|     DebugObject_Access(&o->d_obj);
 | |
|     
 | |
|     // call signal
 | |
|     o->handler(o->user, entry->signo);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| #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
 | |
|     o->reactor = reactor;
 | |
|     o->signals = signals;
 | |
|     o->handler = handler;
 | |
|     o->user = user;
 | |
|     
 | |
|     #ifdef BADVPN_USE_SIGNALFD
 | |
|     
 | |
|     // 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) {
 | |
|         BLog(BLOG_ERROR, "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;
 | |
|     }
 | |
|     
 | |
|     #endif
 | |
|     
 | |
|     #ifdef BADVPN_USE_KEVENT
 | |
|     
 | |
|     // count signals
 | |
|     int num_signals = 0;
 | |
|     for (int i = 0; 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 kevents
 | |
|     o->num_entries = 0;
 | |
|     for (int i = 0; i < BUNIXSIGNAL_MAX_SIGNALS; i++) {
 | |
|         if (!sigismember(&o->signals, i)) {
 | |
|             continue;
 | |
|         }
 | |
|         struct BUnixSignal_kevent_entry *entry = &o->entries[o->num_entries];
 | |
|         entry->parent = o;
 | |
|         entry->signo = i;
 | |
|         if (!BReactorKEvent_Init(&entry->kevent, o->reactor, (BReactorKEvent_handler)kevent_handler, entry, entry->signo, EVFILT_SIGNAL, 0, 0)) {
 | |
|             BLog(BLOG_ERROR, "BReactorKEvent_Init failed");
 | |
|             goto fail2;
 | |
|         }
 | |
|         o->num_entries++;
 | |
|     }
 | |
|     
 | |
|     // 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;
 | |
|     
 | |
|     #ifdef BADVPN_USE_SIGNALFD
 | |
| fail2:
 | |
|     BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd);
 | |
| fail1:
 | |
|     ASSERT_FORCE(close(o->signalfd_fd) == 0)
 | |
|     #endif
 | |
|     
 | |
|     #ifdef BADVPN_USE_KEVENT
 | |
| fail2:
 | |
|     while (o->num_entries > 0) {
 | |
|         BReactorKEvent_Free(&o->entries[o->num_entries - 1].kevent);
 | |
|         o->num_entries--;
 | |
|     }
 | |
|     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;
 | |
| }
 | |
| 
 | |
| 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)
 | |
|     }
 | |
|     
 | |
|     // free signalfd BFileDescriptor
 | |
|     BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd);
 | |
|     
 | |
|     // free signalfd fd
 | |
|     ASSERT_FORCE(close(o->signalfd_fd) == 0)
 | |
|     
 | |
|     #endif
 | |
|     
 | |
|     #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);
 | |
|         o->num_entries--;
 | |
|     }
 | |
|     
 | |
|     // free array
 | |
|     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
 | |
| }
 |