1 ///////////////////////////////////////////////////////////////////////////////
4 /// \brief Handling signals to abort operation
6 // Author: Lasse Collin
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
11 ///////////////////////////////////////////////////////////////////////////////
16 volatile sig_atomic_t user_abort = false;
19 #if !(defined(_WIN32) && !defined(__CYGWIN__))
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
24 static volatile sig_atomic_t exit_signal = 0;
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;
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;
35 /// signals_block() and signals_unblock() can be called recursively.
36 static size_t signals_block_count = 0;
40 signal_handler(int sig)
45 #ifndef TUKLIB_DOSLIKE
46 io_write_to_user_abort_pipe();
56 // List of signals for which we establish the signal handler.
57 static const int sigs[] = {
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]);
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]);
85 // Using "my_sa" because "sa" may conflict with a sockaddr variable
86 // from system headers on Solaris.
87 struct sigaction my_sa;
89 // All the signals that we handle we also blocked while the signal
91 my_sa.sa_mask = hooked_signals;
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.
97 my_sa.sa_handler = &signal_handler;
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)
107 // Establish the signal handler.
108 if (sigaction(sigs[i], &my_sa, NULL))
109 message_signal_handler();
112 signals_are_initialized = true;
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);
135 signals_unblock(void)
137 if (signals_are_initialized) {
138 assert(signals_block_count > 0);
140 if (--signals_block_count == 0) {
141 const int saved_errno = errno;
142 mythread_sigmask(SIG_UNBLOCK, &hooked_signals, NULL);
155 const int sig = exit_signal;
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
162 set_exit_status(E_ERROR);
165 sa.sa_handler = SIG_DFL;
166 sigfillset(&sa.sa_mask);
168 sigaction(sig, &sa, NULL);
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.
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
189 signal_handler(DWORD type lzma_attribute((__unused__)))
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);
203 if (!SetConsoleCtrlHandler(&signal_handler, TRUE))
204 message_signal_handler();