]> icculus.org git repositories - icculus/xz.git/blob - src/xz/signals.c
Cleanups to the code that detects the amount of RAM and
[icculus/xz.git] / src / xz / signals.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       signals.c
4 /// \brief      Handling signals to abort operation
5 //
6 //  Copyright (C) 2007-2009 Lasse Collin
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU Lesser General Public
10 //  License as published by the Free Software Foundation; either
11 //  version 2.1 of the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 //  Lesser General Public License for more details.
17 //
18 ///////////////////////////////////////////////////////////////////////////////
19
20 #include "private.h"
21
22
23 volatile sig_atomic_t user_abort = false;
24
25
26 #ifndef _WIN32
27
28 /// If we were interrupted by a signal, we store the signal number so that
29 /// we can raise that signal to kill the program when all cleanups have
30 /// been done.
31 static volatile sig_atomic_t exit_signal = 0;
32
33 /// Mask of signals for which have have established a signal handler to set
34 /// user_abort to true.
35 static sigset_t hooked_signals;
36
37 /// signals_block() and signals_unblock() can be called recursively.
38 static size_t signals_block_count = 0;
39
40
41 static void
42 signal_handler(int sig)
43 {
44         exit_signal = sig;
45         user_abort = true;
46         return;
47 }
48
49
50 extern void
51 signals_init(void)
52 {
53         // List of signals for which we establish the signal handler.
54         static const int sigs[] = {
55                 SIGINT,
56                 SIGTERM,
57 #ifdef SIGHUP
58                 SIGHUP,
59 #endif
60 #ifdef SIGPIPE
61                 SIGPIPE,
62 #endif
63 #ifdef SIGXCPU
64                 SIGXCPU,
65 #endif
66 #ifdef SIGXFSZ
67                 SIGXFSZ,
68 #endif
69         };
70
71         // Mask of the signals for which we have established a signal handler.
72         sigemptyset(&hooked_signals);
73         for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i)
74                 sigaddset(&hooked_signals, sigs[i]);
75
76         struct sigaction sa;
77
78         // All the signals that we handle we also blocked while the signal
79         // handler runs.
80         sa.sa_mask = hooked_signals;
81
82         // Don't set SA_RESTART, because we want EINTR so that we can check
83         // for user_abort and cleanup before exiting. We block the signals
84         // for which we have established a handler when we don't want EINTR.
85         sa.sa_flags = 0;
86         sa.sa_handler = &signal_handler;
87
88         for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) {
89                 // If the parent process has left some signals ignored,
90                 // we don't unignore them.
91                 struct sigaction old;
92                 if (sigaction(sigs[i], NULL, &old) == 0
93                                 && old.sa_handler == SIG_IGN)
94                         continue;
95
96                 // Establish the signal handler.
97                 if (sigaction(sigs[i], &sa, NULL))
98                         message_signal_handler();
99         }
100
101         return;
102 }
103
104
105 extern void
106 signals_block(void)
107 {
108         if (signals_block_count++ == 0) {
109                 const int saved_errno = errno;
110                 mythread_sigmask(SIG_BLOCK, &hooked_signals, NULL);
111                 errno = saved_errno;
112         }
113
114         return;
115 }
116
117
118 extern void
119 signals_unblock(void)
120 {
121         assert(signals_block_count > 0);
122
123         if (--signals_block_count == 0) {
124                 const int saved_errno = errno;
125                 mythread_sigmask(SIG_UNBLOCK, &hooked_signals, NULL);
126                 errno = saved_errno;
127         }
128
129         return;
130 }
131
132
133 extern void
134 signals_exit(void)
135 {
136         const int sig = exit_signal;
137
138         if (sig != 0) {
139                 struct sigaction sa;
140                 sa.sa_handler = SIG_DFL;
141                 sigfillset(&sa.sa_mask);
142                 sa.sa_flags = 0;
143                 sigaction(sig, &sa, NULL);
144                 raise(exit_signal);
145         }
146
147         return;
148 }
149
150 #else
151
152 // While Windows has some very basic signal handling functions as required
153 // by C89, they are not really used, or so I understood. Instead, we use
154 // SetConsoleCtrlHandler() to catch user pressing C-c.
155
156 #include <windows.h>
157
158
159 static BOOL WINAPI
160 signal_handler(DWORD type lzma_attribute((unused)))
161 {
162         // Since we don't get a signal number which we could raise() at
163         // signals_exit() like on POSIX, just set the exit status to
164         // indicate an error, so that we cannot return with zero exit status.
165         set_exit_status(E_ERROR);
166         user_abort = true;
167         return TRUE;
168 }
169
170
171 extern void
172 signals_init(void)
173 {
174         if (!SetConsoleCtrlHandler(&signal_handler, TRUE))
175                 message_signal_handler();
176
177         return;
178 }
179
180 #endif