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