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
)
51 // List of signals for which we establish the signal handler.
52 static const int sigs
[] = {
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
]);
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
]);
82 // All the signals that we handle we also blocked while the signal
84 sa
.sa_mask
= hooked_signals
;
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.
90 sa
.sa_handler
= &signal_handler
;
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.
96 if (sigaction(sigs
[i
], NULL
, &old
) == 0
97 && old
.sa_handler
== SIG_IGN
)
100 // Establish the signal handler.
101 if (sigaction(sigs
[i
], &sa
, NULL
))
102 message_signal_handler();
105 signals_are_initialized
= true;
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
);
128 signals_unblock(void)
130 if (signals_are_initialized
) {
131 assert(signals_block_count
> 0);
133 if (--signals_block_count
== 0) {
134 const int saved_errno
= errno
;
135 mythread_sigmask(SIG_UNBLOCK
, &hooked_signals
, NULL
);
148 const int sig
= exit_signal
;
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
155 set_exit_status(E_ERROR
);
158 sa
.sa_handler
= SIG_DFL
;
159 sigfillset(&sa
.sa_mask
);
161 sigaction(sig
, &sa
, NULL
);
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.
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
182 signal_handler(DWORD type
lzma_attribute((__unused__
)))
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
);
196 if (!SetConsoleCtrlHandler(&signal_handler
, TRUE
))
197 message_signal_handler();