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