/** * @file BSignal.c * @author Ambroz Bizjak * * @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. */ #ifdef BADVPN_USE_WINAPI #include #else #include #include #endif #include #include #include #include struct { int initialized; int finished; BReactor *reactor; BSignal_handler handler; void *user; #ifdef BADVPN_USE_WINAPI CRITICAL_SECTION handler_mutex; // mutex to make sure only one handler is working at a time HANDLE signal_sem1; HANDLE signal_sem2; BHandle bhandle; #else BUnixSignal signal; #endif } bsignal_global = { .initialized = 0, }; #ifdef BADVPN_USE_WINAPI static void signal_handle_handler (void *user) { ASSERT(bsignal_global.initialized) ASSERT(!bsignal_global.finished) ASSERT_FORCE(ReleaseSemaphore(bsignal_global.signal_sem2, 1, NULL)) BLog(BLOG_DEBUG, "Dispatching signal"); // call handler bsignal_global.handler(bsignal_global.user); return; } static BOOL WINAPI ctrl_handler (DWORD type) { // don't check bsignal_global.initialized to avoid a race EnterCriticalSection(&bsignal_global.handler_mutex); ASSERT_FORCE(ReleaseSemaphore(bsignal_global.signal_sem1, 1, NULL)) ASSERT_FORCE(WaitForSingleObject(bsignal_global.signal_sem2, INFINITE) == WAIT_OBJECT_0) LeaveCriticalSection(&bsignal_global.handler_mutex); return TRUE; } #else static void unix_signal_handler (void *user, int signo) { ASSERT(signo == SIGTERM || signo == SIGINT) ASSERT(bsignal_global.initialized) ASSERT(!bsignal_global.finished) BLog(BLOG_DEBUG, "Dispatching signal"); // call handler bsignal_global.handler(bsignal_global.user); return; } #endif int BSignal_Init (BReactor *reactor, BSignal_handler handler, void *user) { ASSERT(!bsignal_global.initialized) // init arguments bsignal_global.reactor = reactor; bsignal_global.handler = handler; bsignal_global.user = user; BLog(BLOG_DEBUG, "BSignal initializing"); #ifdef BADVPN_USE_WINAPI InitializeCriticalSection(&bsignal_global.handler_mutex); if (!(bsignal_global.signal_sem1 = CreateSemaphore(NULL, 0, 1, NULL))) { BLog(BLOG_ERROR, "CreateSemaphore failed"); goto fail1; } if (!(bsignal_global.signal_sem2 = CreateSemaphore(NULL, 0, 1, NULL))) { BLog(BLOG_ERROR, "CreateSemaphore failed"); goto fail2; } // init BHandle BHandle_Init(&bsignal_global.bhandle, bsignal_global.signal_sem1, signal_handle_handler, NULL); if (!BReactor_AddHandle(bsignal_global.reactor, &bsignal_global.bhandle)) { BLog(BLOG_ERROR, "BReactor_AddHandle failed"); goto fail3; } BReactor_EnableHandle(bsignal_global.reactor, &bsignal_global.bhandle); // configure ctrl handler if (!SetConsoleCtrlHandler(ctrl_handler, TRUE)) { BLog(BLOG_ERROR, "SetConsoleCtrlHandler failed"); goto fail4; } #else sigset_t sset; ASSERT_FORCE(sigemptyset(&sset) == 0) ASSERT_FORCE(sigaddset(&sset, SIGTERM) == 0) ASSERT_FORCE(sigaddset(&sset, SIGINT) == 0) // init BUnixSignal if (!BUnixSignal_Init(&bsignal_global.signal, bsignal_global.reactor, sset, unix_signal_handler, NULL)) { BLog(BLOG_ERROR, "BUnixSignal_Init failed"); goto fail0; } #endif bsignal_global.initialized = 1; bsignal_global.finished = 0; return 1; #ifdef BADVPN_USE_WINAPI fail4: BReactor_RemoveHandle(bsignal_global.reactor, &bsignal_global.bhandle); fail3: ASSERT_FORCE(CloseHandle(bsignal_global.signal_sem2)) fail2: ASSERT_FORCE(CloseHandle(bsignal_global.signal_sem1)) fail1: DeleteCriticalSection(&bsignal_global.handler_mutex); #endif fail0: return 0; } void BSignal_Finish (void) { ASSERT(bsignal_global.initialized) ASSERT(!bsignal_global.finished) #ifdef BADVPN_USE_WINAPI // free BHandle BReactor_RemoveHandle(bsignal_global.reactor, &bsignal_global.bhandle); #else // free BUnixSignal BUnixSignal_Free(&bsignal_global.signal, 0); #endif bsignal_global.finished = 1; }