1207753Smm/////////////////////////////////////////////////////////////////////////////// 2207753Smm// 3207753Smm/// \file signals.c 4207753Smm/// \brief Handling signals to abort operation 5207753Smm// 6207753Smm// Author: Lasse Collin 7207753Smm// 8207753Smm// This file has been put into the public domain. 9207753Smm// You can do whatever you want with this file. 10207753Smm// 11207753Smm/////////////////////////////////////////////////////////////////////////////// 12207753Smm 13207753Smm#include "private.h" 14207753Smm 15207753Smm 16207753Smmvolatile sig_atomic_t user_abort = false; 17207753Smm 18207753Smm 19213700Smm#if !(defined(_WIN32) && !defined(__CYGWIN__)) 20207753Smm 21207753Smm/// If we were interrupted by a signal, we store the signal number so that 22207753Smm/// we can raise that signal to kill the program when all cleanups have 23207753Smm/// been done. 24207753Smmstatic volatile sig_atomic_t exit_signal = 0; 25207753Smm 26207753Smm/// Mask of signals for which have have established a signal handler to set 27207753Smm/// user_abort to true. 28207753Smmstatic sigset_t hooked_signals; 29207753Smm 30207753Smm/// True once signals_init() has finished. This is used to skip blocking 31207753Smm/// signals (with uninitialized hooked_signals) if signals_block() and 32207753Smm/// signals_unblock() are called before signals_init() has been called. 33207753Smmstatic bool signals_are_initialized = false; 34207753Smm 35207753Smm/// signals_block() and signals_unblock() can be called recursively. 36207753Smmstatic size_t signals_block_count = 0; 37207753Smm 38207753Smm 39207753Smmstatic void 40207753Smmsignal_handler(int sig) 41207753Smm{ 42207753Smm exit_signal = sig; 43207753Smm user_abort = true; 44292588Sdelphij 45292588Sdelphij#ifndef TUKLIB_DOSLIKE 46292588Sdelphij io_write_to_user_abort_pipe(); 47292588Sdelphij#endif 48292588Sdelphij 49207753Smm return; 50207753Smm} 51207753Smm 52207753Smm 53207753Smmextern void 54207753Smmsignals_init(void) 55207753Smm{ 56207753Smm // List of signals for which we establish the signal handler. 57207753Smm static const int sigs[] = { 58207753Smm SIGINT, 59207753Smm SIGTERM, 60207753Smm#ifdef SIGHUP 61207753Smm SIGHUP, 62207753Smm#endif 63207753Smm#ifdef SIGPIPE 64207753Smm SIGPIPE, 65207753Smm#endif 66207753Smm#ifdef SIGXCPU 67207753Smm SIGXCPU, 68207753Smm#endif 69207753Smm#ifdef SIGXFSZ 70207753Smm SIGXFSZ, 71207753Smm#endif 72207753Smm }; 73207753Smm 74207753Smm // Mask of the signals for which we have established a signal handler. 75207753Smm sigemptyset(&hooked_signals); 76207753Smm for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) 77207753Smm sigaddset(&hooked_signals, sigs[i]); 78207753Smm 79215187Smm#ifdef SIGALRM 80215187Smm // Add also the signals from message.c to hooked_signals. 81215187Smm for (size_t i = 0; message_progress_sigs[i] != 0; ++i) 82215187Smm sigaddset(&hooked_signals, message_progress_sigs[i]); 83215187Smm#endif 84215187Smm 85274261Sdelphij // Using "my_sa" because "sa" may conflict with a sockaddr variable 86274261Sdelphij // from system headers on Solaris. 87274261Sdelphij struct sigaction my_sa; 88207753Smm 89207753Smm // All the signals that we handle we also blocked while the signal 90207753Smm // handler runs. 91274261Sdelphij my_sa.sa_mask = hooked_signals; 92207753Smm 93207753Smm // Don't set SA_RESTART, because we want EINTR so that we can check 94207753Smm // for user_abort and cleanup before exiting. We block the signals 95207753Smm // for which we have established a handler when we don't want EINTR. 96274261Sdelphij my_sa.sa_flags = 0; 97274261Sdelphij my_sa.sa_handler = &signal_handler; 98207753Smm 99207753Smm for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) { 100207753Smm // If the parent process has left some signals ignored, 101207753Smm // we don't unignore them. 102207753Smm struct sigaction old; 103207753Smm if (sigaction(sigs[i], NULL, &old) == 0 104207753Smm && old.sa_handler == SIG_IGN) 105207753Smm continue; 106207753Smm 107207753Smm // Establish the signal handler. 108274261Sdelphij if (sigaction(sigs[i], &my_sa, NULL)) 109207753Smm message_signal_handler(); 110207753Smm } 111207753Smm 112207753Smm signals_are_initialized = true; 113207753Smm 114207753Smm return; 115207753Smm} 116207753Smm 117207753Smm 118207753Smm#ifndef __VMS 119207753Smmextern void 120207753Smmsignals_block(void) 121207753Smm{ 122207753Smm if (signals_are_initialized) { 123207753Smm if (signals_block_count++ == 0) { 124207753Smm const int saved_errno = errno; 125207753Smm mythread_sigmask(SIG_BLOCK, &hooked_signals, NULL); 126207753Smm errno = saved_errno; 127207753Smm } 128207753Smm } 129207753Smm 130207753Smm return; 131207753Smm} 132207753Smm 133207753Smm 134207753Smmextern void 135207753Smmsignals_unblock(void) 136207753Smm{ 137207753Smm if (signals_are_initialized) { 138207753Smm assert(signals_block_count > 0); 139207753Smm 140207753Smm if (--signals_block_count == 0) { 141207753Smm const int saved_errno = errno; 142207753Smm mythread_sigmask(SIG_UNBLOCK, &hooked_signals, NULL); 143207753Smm errno = saved_errno; 144207753Smm } 145207753Smm } 146207753Smm 147207753Smm return; 148207753Smm} 149207753Smm#endif 150207753Smm 151207753Smm 152207753Smmextern void 153207753Smmsignals_exit(void) 154207753Smm{ 155207753Smm const int sig = exit_signal; 156207753Smm 157207753Smm if (sig != 0) { 158215187Smm#if defined(TUKLIB_DOSLIKE) || defined(__VMS) 159213700Smm // Don't raise(), set only exit status. This avoids 160213700Smm // printing unwanted message about SIGINT when the user 161213700Smm // presses C-c. 162213700Smm set_exit_status(E_ERROR); 163213700Smm#else 164207753Smm struct sigaction sa; 165207753Smm sa.sa_handler = SIG_DFL; 166207753Smm sigfillset(&sa.sa_mask); 167207753Smm sa.sa_flags = 0; 168207753Smm sigaction(sig, &sa, NULL); 169207753Smm raise(exit_signal); 170213700Smm#endif 171207753Smm } 172207753Smm 173207753Smm return; 174207753Smm} 175207753Smm 176207753Smm#else 177207753Smm 178207753Smm// While Windows has some very basic signal handling functions as required 179213700Smm// by C89, they are not really used, and e.g. SIGINT doesn't work exactly 180213700Smm// the way it does on POSIX (Windows creates a new thread for the signal 181213700Smm// handler). Instead, we use SetConsoleCtrlHandler() to catch user 182213700Smm// pressing C-c, because that seems to be the recommended way to do it. 183213700Smm// 184213700Smm// NOTE: This doesn't work under MSYS. Trying with SIGINT doesn't work 185213700Smm// either even if it appeared to work at first. So test using Windows 186213700Smm// console window. 187207753Smm 188207753Smmstatic BOOL WINAPI 189223935Smmsignal_handler(DWORD type lzma_attribute((__unused__))) 190207753Smm{ 191207753Smm // Since we don't get a signal number which we could raise() at 192207753Smm // signals_exit() like on POSIX, just set the exit status to 193207753Smm // indicate an error, so that we cannot return with zero exit status. 194207753Smm set_exit_status(E_ERROR); 195207753Smm user_abort = true; 196207753Smm return TRUE; 197207753Smm} 198207753Smm 199207753Smm 200207753Smmextern void 201207753Smmsignals_init(void) 202207753Smm{ 203207753Smm if (!SetConsoleCtrlHandler(&signal_handler, TRUE)) 204207753Smm message_signal_handler(); 205207753Smm 206207753Smm return; 207207753Smm} 208207753Smm 209207753Smm#endif 210