Tests: Add a few test files.
[xz.git] / src / xz / signals.c
blob13cc4c2b0c2f8c8dee50d56ac5571d980c05caf8
1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file signals.c
6 /// \brief Handling signals to abort operation
7 //
8 // Author: Lasse Collin
9 //
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "private.h"
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
22 /// been done.
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;
38 static void
39 signal_handler(int sig)
41 exit_signal = sig;
42 user_abort = true;
44 #ifndef TUKLIB_DOSLIKE
45 io_write_to_user_abort_pipe();
46 #endif
48 return;
52 extern void
53 signals_init(void)
55 // List of signals for which we establish the signal handler.
56 static const int sigs[] = {
57 SIGINT,
58 SIGTERM,
59 #ifdef SIGHUP
60 SIGHUP,
61 #endif
62 #ifdef SIGPIPE
63 SIGPIPE,
64 #endif
65 #ifdef SIGXCPU
66 SIGXCPU,
67 #endif
68 #ifdef SIGXFSZ
69 SIGXFSZ,
70 #endif
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]);
78 #ifdef SIGALRM
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]);
82 #endif
84 #ifdef USE_SIGTSTP_HANDLER
85 // Add the SIGTSTP handler from mytime.c to hooked_signals.
86 sigaddset(&hooked_signals, SIGTSTP);
87 #endif
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
94 // handler runs.
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.
100 my_sa.sa_flags = 0;
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)
110 continue;
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();
124 #endif
126 signals_are_initialized = true;
128 return;
132 #ifndef __VMS
133 extern void
134 signals_block(void)
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);
140 errno = saved_errno;
144 return;
148 extern void
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);
157 errno = saved_errno;
161 return;
163 #endif
166 extern void
167 signals_exit(void)
169 const int sig = (int)exit_signal;
171 if (sig != 0) {
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
175 // presses C-c.
176 set_exit_status(E_ERROR);
177 #else
178 struct sigaction sa;
179 sa.sa_handler = SIG_DFL;
180 sigfillset(&sa.sa_mask);
181 sa.sa_flags = 0;
182 sigaction(sig, &sa, NULL);
183 raise(sig);
184 #endif
187 return;
190 #else
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
200 // console window.
202 static BOOL WINAPI
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);
209 user_abort = true;
210 return TRUE;
214 extern void
215 signals_init(void)
217 if (!SetConsoleCtrlHandler(&signal_handler, TRUE))
218 message_signal_handler();
220 return;
223 #endif