]> icculus.org git repositories - icculus/xz.git/blob - src/xz/signals.c
Put the interesting parts of XZ Utils into the public domain.
[icculus/xz.git] / 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 #ifndef _WIN32
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 /// signals_block() and signals_unblock() can be called recursively.
31 static size_t signals_block_count = 0;
32
33
34 static void
35 signal_handler(int sig)
36 {
37         exit_signal = sig;
38         user_abort = true;
39         return;
40 }
41
42
43 extern void
44 signals_init(void)
45 {
46         // List of signals for which we establish the signal handler.
47         static const int sigs[] = {
48                 SIGINT,
49                 SIGTERM,
50 #ifdef SIGHUP
51                 SIGHUP,
52 #endif
53 #ifdef SIGPIPE
54                 SIGPIPE,
55 #endif
56 #ifdef SIGXCPU
57                 SIGXCPU,
58 #endif
59 #ifdef SIGXFSZ
60                 SIGXFSZ,
61 #endif
62         };
63
64         // Mask of the signals for which we have established a signal handler.
65         sigemptyset(&hooked_signals);
66         for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i)
67                 sigaddset(&hooked_signals, sigs[i]);
68
69         struct sigaction sa;
70
71         // All the signals that we handle we also blocked while the signal
72         // handler runs.
73         sa.sa_mask = hooked_signals;
74
75         // Don't set SA_RESTART, because we want EINTR so that we can check
76         // for user_abort and cleanup before exiting. We block the signals
77         // for which we have established a handler when we don't want EINTR.
78         sa.sa_flags = 0;
79         sa.sa_handler = &signal_handler;
80
81         for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) {
82                 // If the parent process has left some signals ignored,
83                 // we don't unignore them.
84                 struct sigaction old;
85                 if (sigaction(sigs[i], NULL, &old) == 0
86                                 && old.sa_handler == SIG_IGN)
87                         continue;
88
89                 // Establish the signal handler.
90                 if (sigaction(sigs[i], &sa, NULL))
91                         message_signal_handler();
92         }
93
94         return;
95 }
96
97
98 extern void
99 signals_block(void)
100 {
101         if (signals_block_count++ == 0) {
102                 const int saved_errno = errno;
103                 mythread_sigmask(SIG_BLOCK, &hooked_signals, NULL);
104                 errno = saved_errno;
105         }
106
107         return;
108 }
109
110
111 extern void
112 signals_unblock(void)
113 {
114         assert(signals_block_count > 0);
115
116         if (--signals_block_count == 0) {
117                 const int saved_errno = errno;
118                 mythread_sigmask(SIG_UNBLOCK, &hooked_signals, NULL);
119                 errno = saved_errno;
120         }
121
122         return;
123 }
124
125
126 extern void
127 signals_exit(void)
128 {
129         const int sig = exit_signal;
130
131         if (sig != 0) {
132                 struct sigaction sa;
133                 sa.sa_handler = SIG_DFL;
134                 sigfillset(&sa.sa_mask);
135                 sa.sa_flags = 0;
136                 sigaction(sig, &sa, NULL);
137                 raise(exit_signal);
138         }
139
140         return;
141 }
142
143 #else
144
145 // While Windows has some very basic signal handling functions as required
146 // by C89, they are not really used, or so I understood. Instead, we use
147 // SetConsoleCtrlHandler() to catch user pressing C-c.
148
149 #include <windows.h>
150
151
152 static BOOL WINAPI
153 signal_handler(DWORD type lzma_attribute((unused)))
154 {
155         // Since we don't get a signal number which we could raise() at
156         // signals_exit() like on POSIX, just set the exit status to
157         // indicate an error, so that we cannot return with zero exit status.
158         set_exit_status(E_ERROR);
159         user_abort = true;
160         return TRUE;
161 }
162
163
164 extern void
165 signals_init(void)
166 {
167         if (!SetConsoleCtrlHandler(&signal_handler, TRUE))
168                 message_signal_handler();
169
170         return;
171 }
172
173 #endif