]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/xz/src/xz/signals.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / xz / src / xz / signals.c
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 have 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         return;
45 }
46
47
48 extern void
49 signals_init(void)
50 {
51         // List of signals for which we establish the signal handler.
52         static const int sigs[] = {
53                 SIGINT,
54                 SIGTERM,
55 #ifdef SIGHUP
56                 SIGHUP,
57 #endif
58 #ifdef SIGPIPE
59                 SIGPIPE,
60 #endif
61 #ifdef SIGXCPU
62                 SIGXCPU,
63 #endif
64 #ifdef SIGXFSZ
65                 SIGXFSZ,
66 #endif
67         };
68
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]);
73
74 #ifdef SIGALRM
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]);
78 #endif
79
80         // Using "my_sa" because "sa" may conflict with a sockaddr variable
81         // from system headers on Solaris.
82         struct sigaction my_sa;
83
84         // All the signals that we handle we also blocked while the signal
85         // handler runs.
86         my_sa.sa_mask = hooked_signals;
87
88         // Don't set SA_RESTART, because we want EINTR so that we can check
89         // for user_abort and cleanup before exiting. We block the signals
90         // for which we have established a handler when we don't want EINTR.
91         my_sa.sa_flags = 0;
92         my_sa.sa_handler = &signal_handler;
93
94         for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) {
95                 // If the parent process has left some signals ignored,
96                 // we don't unignore them.
97                 struct sigaction old;
98                 if (sigaction(sigs[i], NULL, &old) == 0
99                                 && old.sa_handler == SIG_IGN)
100                         continue;
101
102                 // Establish the signal handler.
103                 if (sigaction(sigs[i], &my_sa, NULL))
104                         message_signal_handler();
105         }
106
107         signals_are_initialized = true;
108
109         return;
110 }
111
112
113 #ifndef __VMS
114 extern void
115 signals_block(void)
116 {
117         if (signals_are_initialized) {
118                 if (signals_block_count++ == 0) {
119                         const int saved_errno = errno;
120                         mythread_sigmask(SIG_BLOCK, &hooked_signals, NULL);
121                         errno = saved_errno;
122                 }
123         }
124
125         return;
126 }
127
128
129 extern void
130 signals_unblock(void)
131 {
132         if (signals_are_initialized) {
133                 assert(signals_block_count > 0);
134
135                 if (--signals_block_count == 0) {
136                         const int saved_errno = errno;
137                         mythread_sigmask(SIG_UNBLOCK, &hooked_signals, NULL);
138                         errno = saved_errno;
139                 }
140         }
141
142         return;
143 }
144 #endif
145
146
147 extern void
148 signals_exit(void)
149 {
150         const int sig = exit_signal;
151
152         if (sig != 0) {
153 #if defined(TUKLIB_DOSLIKE) || defined(__VMS)
154                 // Don't raise(), set only exit status. This avoids
155                 // printing unwanted message about SIGINT when the user
156                 // presses C-c.
157                 set_exit_status(E_ERROR);
158 #else
159                 struct sigaction sa;
160                 sa.sa_handler = SIG_DFL;
161                 sigfillset(&sa.sa_mask);
162                 sa.sa_flags = 0;
163                 sigaction(sig, &sa, NULL);
164                 raise(exit_signal);
165 #endif
166         }
167
168         return;
169 }
170
171 #else
172
173 // While Windows has some very basic signal handling functions as required
174 // by C89, they are not really used, and e.g. SIGINT doesn't work exactly
175 // the way it does on POSIX (Windows creates a new thread for the signal
176 // handler). Instead, we use SetConsoleCtrlHandler() to catch user
177 // pressing C-c, because that seems to be the recommended way to do it.
178 //
179 // NOTE: This doesn't work under MSYS. Trying with SIGINT doesn't work
180 // either even if it appeared to work at first. So test using Windows
181 // console window.
182
183 static BOOL WINAPI
184 signal_handler(DWORD type lzma_attribute((__unused__)))
185 {
186         // Since we don't get a signal number which we could raise() at
187         // signals_exit() like on POSIX, just set the exit status to
188         // indicate an error, so that we cannot return with zero exit status.
189         set_exit_status(E_ERROR);
190         user_abort = true;
191         return TRUE;
192 }
193
194
195 extern void
196 signals_init(void)
197 {
198         if (!SetConsoleCtrlHandler(&signal_handler, TRUE))
199                 message_signal_handler();
200
201         return;
202 }
203
204 #endif