1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
6 /// \brief Handling signals to abort operation
8 // Author: Lasse Collin
10 ///////////////////////////////////////////////////////////////////////////////
15 volatile sig_atomic_t user_abort
= false;
18 #if !(defined(_WIN32) && !defined(__CYGWIN__))
20 /// If we were interrupted by a signal, we store the signal number so that
21 /// we can raise that signal to kill the program when all cleanups have
23 static volatile sig_atomic_t exit_signal
= 0;
25 /// Mask of signals for which we have established a signal handler to set
26 /// user_abort to true.
27 static sigset_t hooked_signals
;
29 /// True once signals_init() has finished. This is used to skip blocking
30 /// signals (with uninitialized hooked_signals) if signals_block() and
31 /// signals_unblock() are called before signals_init() has been called.
32 static bool signals_are_initialized
= false;
34 /// signals_block() and signals_unblock() can be called recursively.
35 static size_t signals_block_count
= 0;
39 signal_handler(int sig
)
44 #ifndef TUKLIB_DOSLIKE
45 io_write_to_user_abort_pipe();
55 // List of signals for which we establish the signal handler.
56 static const int sigs
[] = {
73 // Mask of the signals for which we have established a signal handler.
74 sigemptyset(&hooked_signals
);
75 for (size_t i
= 0; i
< ARRAY_SIZE(sigs
); ++i
)
76 sigaddset(&hooked_signals
, sigs
[i
]);
79 // Add also the signals from message.c to hooked_signals.
80 for (size_t i
= 0; message_progress_sigs
[i
] != 0; ++i
)
81 sigaddset(&hooked_signals
, message_progress_sigs
[i
]);
84 #ifdef USE_SIGTSTP_HANDLER
85 // Add the SIGTSTP handler from mytime.c to hooked_signals.
86 sigaddset(&hooked_signals
, SIGTSTP
);
89 // Using "my_sa" because "sa" may conflict with a sockaddr variable
90 // from system headers on Solaris.
91 struct sigaction my_sa
;
93 // All the signals that we handle we also blocked while the signal
95 my_sa
.sa_mask
= hooked_signals
;
97 // Don't set SA_RESTART, because we want EINTR so that we can check
98 // for user_abort and cleanup before exiting. We block the signals
99 // for which we have established a handler when we don't want EINTR.
101 my_sa
.sa_handler
= &signal_handler
;
103 struct sigaction old
;
105 for (size_t i
= 0; i
< ARRAY_SIZE(sigs
); ++i
) {
106 // If the parent process has left some signals ignored,
107 // we don't unignore them.
108 if (sigaction(sigs
[i
], NULL
, &old
) == 0
109 && old
.sa_handler
== SIG_IGN
)
112 // Establish the signal handler.
113 if (sigaction(sigs
[i
], &my_sa
, NULL
))
114 message_signal_handler();
117 #ifdef USE_SIGTSTP_HANDLER
118 if (!(sigaction(SIGTSTP
, NULL
, &old
) == 0
119 && old
.sa_handler
== SIG_IGN
)) {
120 my_sa
.sa_handler
= &mytime_sigtstp_handler
;
121 if (sigaction(SIGTSTP
, &my_sa
, NULL
))
122 message_signal_handler();
126 signals_are_initialized
= true;
136 if (signals_are_initialized
) {
137 if (signals_block_count
++ == 0) {
138 const int saved_errno
= errno
;
139 mythread_sigmask(SIG_BLOCK
, &hooked_signals
, NULL
);
149 signals_unblock(void)
151 if (signals_are_initialized
) {
152 assert(signals_block_count
> 0);
154 if (--signals_block_count
== 0) {
155 const int saved_errno
= errno
;
156 mythread_sigmask(SIG_UNBLOCK
, &hooked_signals
, NULL
);
169 const int sig
= (int)exit_signal
;
172 #if defined(TUKLIB_DOSLIKE) || defined(__VMS)
173 // Don't raise(), set only exit status. This avoids
174 // printing unwanted message about SIGINT when the user
176 set_exit_status(E_ERROR
);
179 sa
.sa_handler
= SIG_DFL
;
180 sigfillset(&sa
.sa_mask
);
182 sigaction(sig
, &sa
, NULL
);
192 // While Windows has some very basic signal handling functions as required
193 // by C89, they are not really used, and e.g. SIGINT doesn't work exactly
194 // the way it does on POSIX (Windows creates a new thread for the signal
195 // handler). Instead, we use SetConsoleCtrlHandler() to catch user
196 // pressing C-c, because that seems to be the recommended way to do it.
198 // NOTE: This doesn't work under MSYS. Trying with SIGINT doesn't work
199 // either even if it appeared to work at first. So test using Windows
203 signal_handler(DWORD type
lzma_attribute((__unused__
)))
205 // Since we don't get a signal number which we could raise() at
206 // signals_exit() like on POSIX, just set the exit status to
207 // indicate an error, so that we cannot return with zero exit status.
208 set_exit_status(E_ERROR
);
217 if (!SetConsoleCtrlHandler(&signal_handler
, TRUE
))
218 message_signal_handler();