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 16 volatile sig_atomic_t user_abort = false; 17 18 19 #ifndef _WIN32 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. 24 static 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. 28 static 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. 33 static bool signals_are_initialized = false; 34 35 /// signals_block() and signals_unblock() can be called recursively. 36 static size_t signals_block_count = 0; 37 38 39 static void 40 signal_handler(int sig) 41 { 42 exit_signal = sig; 43 user_abort = true; 44 return; 45 } 46 47 48 extern void 49 signals_init(void) 50 { 51 // List of signals for which we establish the signal handler. 52 static const int sigs[] = { 53 SIGINT, 54 SIGTERM, 55 #ifdef SIGHUP 56 SIGHUP, 57 #endif 58 #ifdef SIGPIPE 59 SIGPIPE, 60 #endif 61 #ifdef SIGXCPU 62 SIGXCPU, 63 #endif 64 #ifdef SIGXFSZ 65 SIGXFSZ, 66 #endif 67 }; 68 69 // Mask of the signals for which we have established a signal handler. 70 sigemptyset(&hooked_signals); 71 for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) 72 sigaddset(&hooked_signals, sigs[i]); 73 74 struct sigaction sa; 75 76 // All the signals that we handle we also blocked while the signal 77 // handler runs. 78 sa.sa_mask = hooked_signals; 79 80 // Don't set SA_RESTART, because we want EINTR so that we can check 81 // for user_abort and cleanup before exiting. We block the signals 82 // for which we have established a handler when we don't want EINTR. 83 sa.sa_flags = 0; 84 sa.sa_handler = &signal_handler; 85 86 for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) { 87 // If the parent process has left some signals ignored, 88 // we don't unignore them. 89 struct sigaction old; 90 if (sigaction(sigs[i], NULL, &old) == 0 91 && old.sa_handler == SIG_IGN) 92 continue; 93 94 // Establish the signal handler. 95 if (sigaction(sigs[i], &sa, NULL)) 96 message_signal_handler(); 97 } 98 99 signals_are_initialized = true; 100 101 return; 102 } 103 104 105 #ifndef __VMS 106 extern void 107 signals_block(void) 108 { 109 if (signals_are_initialized) { 110 if (signals_block_count++ == 0) { 111 const int saved_errno = errno; 112 mythread_sigmask(SIG_BLOCK, &hooked_signals, NULL); 113 errno = saved_errno; 114 } 115 } 116 117 return; 118 } 119 120 121 extern void 122 signals_unblock(void) 123 { 124 if (signals_are_initialized) { 125 assert(signals_block_count > 0); 126 127 if (--signals_block_count == 0) { 128 const int saved_errno = errno; 129 mythread_sigmask(SIG_UNBLOCK, &hooked_signals, NULL); 130 errno = saved_errno; 131 } 132 } 133 134 return; 135 } 136 #endif 137 138 139 extern void 140 signals_exit(void) 141 { 142 const int sig = exit_signal; 143 144 if (sig != 0) { 145 struct sigaction sa; 146 sa.sa_handler = SIG_DFL; 147 sigfillset(&sa.sa_mask); 148 sa.sa_flags = 0; 149 sigaction(sig, &sa, NULL); 150 raise(exit_signal); 151 } 152 153 return; 154 } 155 156 #else 157 158 // While Windows has some very basic signal handling functions as required 159 // by C89, they are not really used, or so I understood. Instead, we use 160 // SetConsoleCtrlHandler() to catch user pressing C-c. 161 162 #include <windows.h> 163 164 165 static BOOL WINAPI 166 signal_handler(DWORD type lzma_attribute((unused))) 167 { 168 // Since we don't get a signal number which we could raise() at 169 // signals_exit() like on POSIX, just set the exit status to 170 // indicate an error, so that we cannot return with zero exit status. 171 // 172 // FIXME: Since this function runs in its own thread, 173 // set_exit_status() should have a mutex. 174 set_exit_status(E_ERROR); 175 user_abort = true; 176 return TRUE; 177 } 178 179 180 extern void 181 signals_init(void) 182 { 183 if (!SetConsoleCtrlHandler(&signal_handler, TRUE)) 184 message_signal_handler(); 185 186 return; 187 } 188 189 #endif 190