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 we 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 45 #ifndef TUKLIB_DOSLIKE 46 io_write_to_user_abort_pipe(); 47 #endif 48 49 return; 50 } 51 52 53 extern void 54 signals_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 119 extern void 120 signals_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 134 extern void 135 signals_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 152 extern void 153 signals_exit(void) 154 { 155 const int sig = (int)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(sig); 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 188 static BOOL WINAPI 189 signal_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 200 extern void 201 signals_init(void) 202 { 203 if (!SetConsoleCtrlHandler(&signal_handler, TRUE)) 204 message_signal_handler(); 205 206 return; 207 } 208 209 #endif 210