1*4d1abfb2Sjoerg /////////////////////////////////////////////////////////////////////////////// 2*4d1abfb2Sjoerg // 3*4d1abfb2Sjoerg /// \file signals.c 4*4d1abfb2Sjoerg /// \brief Handling signals to abort operation 5*4d1abfb2Sjoerg // 6*4d1abfb2Sjoerg // Author: Lasse Collin 7*4d1abfb2Sjoerg // 8*4d1abfb2Sjoerg // This file has been put into the public domain. 9*4d1abfb2Sjoerg // You can do whatever you want with this file. 10*4d1abfb2Sjoerg // 11*4d1abfb2Sjoerg /////////////////////////////////////////////////////////////////////////////// 12*4d1abfb2Sjoerg 13*4d1abfb2Sjoerg #include "private.h" 14*4d1abfb2Sjoerg 15*4d1abfb2Sjoerg 16*4d1abfb2Sjoerg volatile sig_atomic_t user_abort = false; 17*4d1abfb2Sjoerg 18*4d1abfb2Sjoerg 19*4d1abfb2Sjoerg #if !(defined(_WIN32) && !defined(__CYGWIN__)) 20*4d1abfb2Sjoerg 21*4d1abfb2Sjoerg /// If we were interrupted by a signal, we store the signal number so that 22*4d1abfb2Sjoerg /// we can raise that signal to kill the program when all cleanups have 23*4d1abfb2Sjoerg /// been done. 24*4d1abfb2Sjoerg static volatile sig_atomic_t exit_signal = 0; 25*4d1abfb2Sjoerg 26*4d1abfb2Sjoerg /// Mask of signals for which have have established a signal handler to set 27*4d1abfb2Sjoerg /// user_abort to true. 28*4d1abfb2Sjoerg static sigset_t hooked_signals; 29*4d1abfb2Sjoerg 30*4d1abfb2Sjoerg /// True once signals_init() has finished. This is used to skip blocking 31*4d1abfb2Sjoerg /// signals (with uninitialized hooked_signals) if signals_block() and 32*4d1abfb2Sjoerg /// signals_unblock() are called before signals_init() has been called. 33*4d1abfb2Sjoerg static bool signals_are_initialized = false; 34*4d1abfb2Sjoerg 35*4d1abfb2Sjoerg /// signals_block() and signals_unblock() can be called recursively. 36*4d1abfb2Sjoerg static size_t signals_block_count = 0; 37*4d1abfb2Sjoerg 38*4d1abfb2Sjoerg 39*4d1abfb2Sjoerg static void 40*4d1abfb2Sjoerg signal_handler(int sig) 41*4d1abfb2Sjoerg { 42*4d1abfb2Sjoerg exit_signal = sig; 43*4d1abfb2Sjoerg user_abort = true; 44*4d1abfb2Sjoerg return; 45*4d1abfb2Sjoerg } 46*4d1abfb2Sjoerg 47*4d1abfb2Sjoerg 48*4d1abfb2Sjoerg extern void 49*4d1abfb2Sjoerg signals_init(void) 50*4d1abfb2Sjoerg { 51*4d1abfb2Sjoerg // List of signals for which we establish the signal handler. 52*4d1abfb2Sjoerg static const int sigs[] = { 53*4d1abfb2Sjoerg SIGINT, 54*4d1abfb2Sjoerg SIGTERM, 55*4d1abfb2Sjoerg #ifdef SIGHUP 56*4d1abfb2Sjoerg SIGHUP, 57*4d1abfb2Sjoerg #endif 58*4d1abfb2Sjoerg #ifdef SIGPIPE 59*4d1abfb2Sjoerg SIGPIPE, 60*4d1abfb2Sjoerg #endif 61*4d1abfb2Sjoerg #ifdef SIGXCPU 62*4d1abfb2Sjoerg SIGXCPU, 63*4d1abfb2Sjoerg #endif 64*4d1abfb2Sjoerg #ifdef SIGXFSZ 65*4d1abfb2Sjoerg SIGXFSZ, 66*4d1abfb2Sjoerg #endif 67*4d1abfb2Sjoerg }; 68*4d1abfb2Sjoerg 69*4d1abfb2Sjoerg // Mask of the signals for which we have established a signal handler. 70*4d1abfb2Sjoerg sigemptyset(&hooked_signals); 71*4d1abfb2Sjoerg for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) 72*4d1abfb2Sjoerg sigaddset(&hooked_signals, sigs[i]); 73*4d1abfb2Sjoerg 74*4d1abfb2Sjoerg #ifdef SIGALRM 75*4d1abfb2Sjoerg // Add also the signals from message.c to hooked_signals. 76*4d1abfb2Sjoerg for (size_t i = 0; message_progress_sigs[i] != 0; ++i) 77*4d1abfb2Sjoerg sigaddset(&hooked_signals, message_progress_sigs[i]); 78*4d1abfb2Sjoerg #endif 79*4d1abfb2Sjoerg 80*4d1abfb2Sjoerg struct sigaction sa; 81*4d1abfb2Sjoerg 82*4d1abfb2Sjoerg // All the signals that we handle we also blocked while the signal 83*4d1abfb2Sjoerg // handler runs. 84*4d1abfb2Sjoerg sa.sa_mask = hooked_signals; 85*4d1abfb2Sjoerg 86*4d1abfb2Sjoerg // Don't set SA_RESTART, because we want EINTR so that we can check 87*4d1abfb2Sjoerg // for user_abort and cleanup before exiting. We block the signals 88*4d1abfb2Sjoerg // for which we have established a handler when we don't want EINTR. 89*4d1abfb2Sjoerg sa.sa_flags = 0; 90*4d1abfb2Sjoerg sa.sa_handler = &signal_handler; 91*4d1abfb2Sjoerg 92*4d1abfb2Sjoerg for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) { 93*4d1abfb2Sjoerg // If the parent process has left some signals ignored, 94*4d1abfb2Sjoerg // we don't unignore them. 95*4d1abfb2Sjoerg struct sigaction old; 96*4d1abfb2Sjoerg if (sigaction(sigs[i], NULL, &old) == 0 97*4d1abfb2Sjoerg && old.sa_handler == SIG_IGN) 98*4d1abfb2Sjoerg continue; 99*4d1abfb2Sjoerg 100*4d1abfb2Sjoerg // Establish the signal handler. 101*4d1abfb2Sjoerg if (sigaction(sigs[i], &sa, NULL)) 102*4d1abfb2Sjoerg message_signal_handler(); 103*4d1abfb2Sjoerg } 104*4d1abfb2Sjoerg 105*4d1abfb2Sjoerg signals_are_initialized = true; 106*4d1abfb2Sjoerg 107*4d1abfb2Sjoerg return; 108*4d1abfb2Sjoerg } 109*4d1abfb2Sjoerg 110*4d1abfb2Sjoerg 111*4d1abfb2Sjoerg #ifndef __VMS 112*4d1abfb2Sjoerg extern void 113*4d1abfb2Sjoerg signals_block(void) 114*4d1abfb2Sjoerg { 115*4d1abfb2Sjoerg if (signals_are_initialized) { 116*4d1abfb2Sjoerg if (signals_block_count++ == 0) { 117*4d1abfb2Sjoerg const int saved_errno = errno; 118*4d1abfb2Sjoerg mythread_sigmask(SIG_BLOCK, &hooked_signals, NULL); 119*4d1abfb2Sjoerg errno = saved_errno; 120*4d1abfb2Sjoerg } 121*4d1abfb2Sjoerg } 122*4d1abfb2Sjoerg 123*4d1abfb2Sjoerg return; 124*4d1abfb2Sjoerg } 125*4d1abfb2Sjoerg 126*4d1abfb2Sjoerg 127*4d1abfb2Sjoerg extern void 128*4d1abfb2Sjoerg signals_unblock(void) 129*4d1abfb2Sjoerg { 130*4d1abfb2Sjoerg if (signals_are_initialized) { 131*4d1abfb2Sjoerg assert(signals_block_count > 0); 132*4d1abfb2Sjoerg 133*4d1abfb2Sjoerg if (--signals_block_count == 0) { 134*4d1abfb2Sjoerg const int saved_errno = errno; 135*4d1abfb2Sjoerg mythread_sigmask(SIG_UNBLOCK, &hooked_signals, NULL); 136*4d1abfb2Sjoerg errno = saved_errno; 137*4d1abfb2Sjoerg } 138*4d1abfb2Sjoerg } 139*4d1abfb2Sjoerg 140*4d1abfb2Sjoerg return; 141*4d1abfb2Sjoerg } 142*4d1abfb2Sjoerg #endif 143*4d1abfb2Sjoerg 144*4d1abfb2Sjoerg 145*4d1abfb2Sjoerg extern void 146*4d1abfb2Sjoerg signals_exit(void) 147*4d1abfb2Sjoerg { 148*4d1abfb2Sjoerg const int sig = exit_signal; 149*4d1abfb2Sjoerg 150*4d1abfb2Sjoerg if (sig != 0) { 151*4d1abfb2Sjoerg #if defined(TUKLIB_DOSLIKE) || defined(__VMS) 152*4d1abfb2Sjoerg // Don't raise(), set only exit status. This avoids 153*4d1abfb2Sjoerg // printing unwanted message about SIGINT when the user 154*4d1abfb2Sjoerg // presses C-c. 155*4d1abfb2Sjoerg set_exit_status(E_ERROR); 156*4d1abfb2Sjoerg #else 157*4d1abfb2Sjoerg struct sigaction sa; 158*4d1abfb2Sjoerg sa.sa_handler = SIG_DFL; 159*4d1abfb2Sjoerg sigfillset(&sa.sa_mask); 160*4d1abfb2Sjoerg sa.sa_flags = 0; 161*4d1abfb2Sjoerg sigaction(sig, &sa, NULL); 162*4d1abfb2Sjoerg raise(exit_signal); 163*4d1abfb2Sjoerg #endif 164*4d1abfb2Sjoerg } 165*4d1abfb2Sjoerg 166*4d1abfb2Sjoerg return; 167*4d1abfb2Sjoerg } 168*4d1abfb2Sjoerg 169*4d1abfb2Sjoerg #else 170*4d1abfb2Sjoerg 171*4d1abfb2Sjoerg // While Windows has some very basic signal handling functions as required 172*4d1abfb2Sjoerg // by C89, they are not really used, and e.g. SIGINT doesn't work exactly 173*4d1abfb2Sjoerg // the way it does on POSIX (Windows creates a new thread for the signal 174*4d1abfb2Sjoerg // handler). Instead, we use SetConsoleCtrlHandler() to catch user 175*4d1abfb2Sjoerg // pressing C-c, because that seems to be the recommended way to do it. 176*4d1abfb2Sjoerg // 177*4d1abfb2Sjoerg // NOTE: This doesn't work under MSYS. Trying with SIGINT doesn't work 178*4d1abfb2Sjoerg // either even if it appeared to work at first. So test using Windows 179*4d1abfb2Sjoerg // console window. 180*4d1abfb2Sjoerg 181*4d1abfb2Sjoerg static BOOL WINAPI 182*4d1abfb2Sjoerg signal_handler(DWORD type lzma_attribute((unused))) 183*4d1abfb2Sjoerg { 184*4d1abfb2Sjoerg // Since we don't get a signal number which we could raise() at 185*4d1abfb2Sjoerg // signals_exit() like on POSIX, just set the exit status to 186*4d1abfb2Sjoerg // indicate an error, so that we cannot return with zero exit status. 187*4d1abfb2Sjoerg set_exit_status(E_ERROR); 188*4d1abfb2Sjoerg user_abort = true; 189*4d1abfb2Sjoerg return TRUE; 190*4d1abfb2Sjoerg } 191*4d1abfb2Sjoerg 192*4d1abfb2Sjoerg 193*4d1abfb2Sjoerg extern void 194*4d1abfb2Sjoerg signals_init(void) 195*4d1abfb2Sjoerg { 196*4d1abfb2Sjoerg if (!SetConsoleCtrlHandler(&signal_handler, TRUE)) 197*4d1abfb2Sjoerg message_signal_handler(); 198*4d1abfb2Sjoerg 199*4d1abfb2Sjoerg return; 200*4d1abfb2Sjoerg } 201*4d1abfb2Sjoerg 202*4d1abfb2Sjoerg #endif 203