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 #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. 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 #ifdef SIGALRM 75 // Add also the signals from message.c to hooked_signals. 76 for (size_t i = 0; message_progress_sigs[i] != 0; ++i) 77 sigaddset(&hooked_signals, message_progress_sigs[i]); 78 #endif 79 80 struct sigaction sa; 81 82 // All the signals that we handle we also blocked while the signal 83 // handler runs. 84 sa.sa_mask = hooked_signals; 85 86 // Don't set SA_RESTART, because we want EINTR so that we can check 87 // for user_abort and cleanup before exiting. We block the signals 88 // for which we have established a handler when we don't want EINTR. 89 sa.sa_flags = 0; 90 sa.sa_handler = &signal_handler; 91 92 for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) { 93 // If the parent process has left some signals ignored, 94 // we don't unignore them. 95 struct sigaction old; 96 if (sigaction(sigs[i], NULL, &old) == 0 97 && old.sa_handler == SIG_IGN) 98 continue; 99 100 // Establish the signal handler. 101 if (sigaction(sigs[i], &sa, NULL)) 102 message_signal_handler(); 103 } 104 105 signals_are_initialized = true; 106 107 return; 108 } 109 110 111 #ifndef __VMS 112 extern void 113 signals_block(void) 114 { 115 if (signals_are_initialized) { 116 if (signals_block_count++ == 0) { 117 const int saved_errno = errno; 118 mythread_sigmask(SIG_BLOCK, &hooked_signals, NULL); 119 errno = saved_errno; 120 } 121 } 122 123 return; 124 } 125 126 127 extern void 128 signals_unblock(void) 129 { 130 if (signals_are_initialized) { 131 assert(signals_block_count > 0); 132 133 if (--signals_block_count == 0) { 134 const int saved_errno = errno; 135 mythread_sigmask(SIG_UNBLOCK, &hooked_signals, NULL); 136 errno = saved_errno; 137 } 138 } 139 140 return; 141 } 142 #endif 143 144 145 extern void 146 signals_exit(void) 147 { 148 const int sig = exit_signal; 149 150 if (sig != 0) { 151 #if defined(TUKLIB_DOSLIKE) || defined(__VMS) 152 // Don't raise(), set only exit status. This avoids 153 // printing unwanted message about SIGINT when the user 154 // presses C-c. 155 set_exit_status(E_ERROR); 156 #else 157 struct sigaction sa; 158 sa.sa_handler = SIG_DFL; 159 sigfillset(&sa.sa_mask); 160 sa.sa_flags = 0; 161 sigaction(sig, &sa, NULL); 162 raise(exit_signal); 163 #endif 164 } 165 166 return; 167 } 168 169 #else 170 171 // While Windows has some very basic signal handling functions as required 172 // by C89, they are not really used, and e.g. SIGINT doesn't work exactly 173 // the way it does on POSIX (Windows creates a new thread for the signal 174 // handler). Instead, we use SetConsoleCtrlHandler() to catch user 175 // pressing C-c, because that seems to be the recommended way to do it. 176 // 177 // NOTE: This doesn't work under MSYS. Trying with SIGINT doesn't work 178 // either even if it appeared to work at first. So test using Windows 179 // console window. 180 181 static BOOL WINAPI 182 signal_handler(DWORD type lzma_attribute((__unused__))) 183 { 184 // Since we don't get a signal number which we could raise() at 185 // signals_exit() like on POSIX, just set the exit status to 186 // indicate an error, so that we cannot return with zero exit status. 187 set_exit_status(E_ERROR); 188 user_abort = true; 189 return TRUE; 190 } 191 192 193 extern void 194 signals_init(void) 195 { 196 if (!SetConsoleCtrlHandler(&signal_handler, TRUE)) 197 message_signal_handler(); 198 199 return; 200 } 201 202 #endif 203