1 /*-
2 signals.c -- signal handling
3
4 Copyright (C) 2011, 2012, 2013 Mikolaj Izdebski
5 Copyright (C) 2008, 2009, 2010 Laszlo Ersek
6
7 This file is part of lbzip2.
8
9 lbzip2 is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 lbzip2 is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with lbzip2. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "common.h"
24
25 #include <signal.h> /* kill() */
26 #include <pthread.h> /* pthread_sigmask() */
27 #include <unistd.h> /* getpid() */
28
29 #include "signals.h"
30
31
32 #define EX_FAIL 1
33
34 static void
xempty(sigset_t * set)35 xempty(sigset_t *set)
36 {
37 if (sigemptyset(set) != 0)
38 abort();
39 }
40
41 static void
xadd(sigset_t * set,int sig)42 xadd(sigset_t *set, int sig)
43 {
44 if (sigaddset(set, sig) != 0)
45 abort();
46 }
47
48 static void
xmask(int how,const sigset_t * set,sigset_t * oset)49 xmask(int how, const sigset_t *set, sigset_t *oset)
50 {
51 if (pthread_sigmask(how, set, oset) != 0)
52 abort();
53 }
54
55 static void
xpending(sigset_t * set)56 xpending(sigset_t *set)
57 {
58 if (sigpending(set) != 0)
59 abort();
60 }
61
62 static bool
xmember(const sigset_t * set,int sig)63 xmember(const sigset_t *set, int sig)
64 {
65 unsigned rv;
66
67 /* Cast return value to unsigned to save one comparison. */
68 rv = sigismember(set, sig);
69 if (rv > 1)
70 abort();
71
72 return rv;
73 }
74
75 static void
xaction(int sig,void (* handler)(int))76 xaction(int sig, void (*handler)(int))
77 {
78 struct sigaction act;
79
80 act.sa_handler = handler;
81 xempty(&act.sa_mask);
82 act.sa_flags = 0;
83
84 if (sigaction(sig, &act, NULL) != 0)
85 abort();
86 }
87
88
89 /*
90 SIGPIPE and SIGXFSZ will be blocked in all sub-threads during the entire
91 lifetime of the process. Any EPIPE or EFBIG write() condition will be
92 handled just as before lbzip2-0.23, when these signals were ignored.
93 However, starting with lbzip2-0.23, SIGPIPE and/or SIGXFSZ will be
94 generated for the offending thread(s) in addition. bailout(), when called
95 by such sub-threads in response to EPIPE or EFBIG, or when called by other
96 sub-threads failing concurrently (but a bit later) for any other reason,
97 will forward (regenerate) the pending signal(s) for the whole process. The
98 main thread will unblock these signals right before exiting with EX_FAIL in
99 bailout(). 2010-03-03 lacos
100 */
101 static const int blocked_signals[] = {
102 SIGPIPE,
103 SIGXFSZ,
104 };
105
106 static const int handled_signals[] = {
107 SIGUSR1,
108 SIGUSR2,
109 SIGINT,
110 SIGTERM,
111 };
112
113 /* Iterate over signal table. */
114 #define foreach(p,tab) for ((p) = (tab); \
115 (p) < (tab) + sizeof(tab) / sizeof(*(tab)); \
116 (p)++)
117
118 /* sig_atomic_t is nowhere required to be able to hold signal values. */
119 static volatile sig_atomic_t caught_index;
120
121 static void
signal_handler(int caught)122 signal_handler(int caught)
123 {
124 const int *sig;
125
126 foreach (sig, handled_signals) {
127 if (*sig == caught) {
128 caught_index = sig - handled_signals;
129 return;
130 }
131 }
132
133 abort();
134 }
135
136
137 static pid_t pid;
138 static pthread_t main_thread;
139 static sigset_t blocked;
140 static sigset_t handled;
141 static sigset_t saved;
142
143
144 void
setup_signals(void)145 setup_signals(void)
146 {
147 const int *sig;
148
149 pid = getpid();
150 main_thread = pthread_self();
151
152 xempty(&blocked);
153 foreach (sig, blocked_signals)
154 xadd(&blocked, *sig);
155
156 xempty(&handled);
157 foreach (sig, handled_signals)
158 xadd(&handled, *sig);
159
160 xmask(SIG_BLOCK, &blocked, 0);
161 }
162
163
164 /* Block signals. */
165 void
cli(void)166 cli(void)
167 {
168 const int *sig;
169
170 xmask(SIG_BLOCK, &handled, &saved);
171
172 foreach (sig, handled_signals)
173 xaction(*sig, signal_handler);
174 }
175
176
177 /* Unblock signals. */
178 void
sti(void)179 sti(void)
180 {
181 const int *sig;
182
183 foreach (sig, handled_signals)
184 xaction(*sig, SIG_DFL);
185
186 xmask(SIG_UNBLOCK, &handled, NULL);
187 }
188
189
190 /* Terminate the process with a signal. */
191 static void _Noreturn
terminate(int sig)192 terminate(int sig)
193 {
194 sigset_t set;
195
196 /* We might have inherited a SIG_IGN from the parent, but that would make no
197 sense here. 24-OCT-2009 lacos */
198 xaction(sig, SIG_DFL);
199 xraise(sig);
200
201 xempty(&set);
202 xadd(&set, sig);
203 xmask(SIG_UNBLOCK, &set, NULL);
204
205 _exit(EX_FAIL);
206 }
207
208
209 /* Unblock signals, wait for them, then block them again. */
210 void
halt(void)211 halt(void)
212 {
213 int sig;
214 int ret;
215
216 /*
217 We could wait for signals with either sigwait() or sigsuspend(). SUSv2
218 states about sigwait() that its effect on signal actions is unspecified.
219 SUSv3 still claims the same.
220
221 The SUSv2 description of sigsuspend() talks about both the thread and the
222 whole process being suspended until a signal arrives, although thread
223 suspension seems much more likely from the wording. They note that they
224 filed a clarification request for this. SUSv3 cleans this up and chooses
225 thread suspension which was more logical anyway.
226
227 I favor sigsuspend() because I need to re-raise SIGTERM and SIGINT, and
228 unspecified action behavior with sigwait() seems messy.
229
230 13-OCT-2009 lacos
231 */
232 ret = sigsuspend(&saved);
233 assert(-1 == ret && EINTR == errno);
234
235 sig = handled_signals[caught_index];
236 assert(xmember(&handled, sig));
237
238 switch (sig) {
239 default:
240 cleanup();
241 terminate(sig);
242
243 case SIGUSR1:
244 /* Error from a non-main thread via bailout(). */
245 bailout();
246
247 case SIGUSR2:
248 /* Muxer thread joined other sub-threads and finished successfully. */
249 break;
250 }
251 }
252
253
254 void
xraise(int sig)255 xraise(int sig)
256 {
257 if (kill(pid, sig) != 0)
258 abort();
259 }
260
261
262 /* Promote signals pending on current thread to the process level. */
263 static void
promote(void)264 promote(void)
265 {
266 const int *sig;
267 sigset_t pending;
268
269 xempty(&pending);
270 xpending(&pending);
271
272 foreach (sig, blocked_signals)
273 if (xmember(&pending, *sig))
274 xraise(*sig);
275 }
276
277
278 /*
279 The treatment for fatal errors.
280
281 If called from the main thread, remove any current output file and bail out.
282 Primarily by unblocking any pending SIGPIPE/SIGXFSZ signals; both those
283 forwarded by any sub-thread to the process level, and those generated for the
284 main thread specifically, in response to an EPIPE/EFBIG write() condition. If
285 no such signal is pending, or SIG_IGN was inherited through exec() as their
286 actions, then bail out with the failure exit status.
287
288 If called (indirectly) from any other thread, resignal the process with any
289 pending SIGPIPE/SIGXFSZ. This will promote any such signal to the process
290 level, if it was originally generated for the calling thread, accompanying an
291 EPIPE/EFBIG write() condition. If the pending signal was already pending on
292 the whole process, this will result in an idempotent kill(). Thereafter, send
293 SIGUSR1 to the process, in order to signal the fatal error in the sub-thread.
294 Finally, terminate the thread.
295 */
296 void
bailout(void)297 bailout(void)
298 {
299 if (pthread_equal(pthread_self(), main_thread)) {
300 cleanup();
301 xmask(SIG_UNBLOCK, &blocked, NULL);
302 _exit(EX_FAIL);
303 }
304
305 promote();
306 xraise(SIGUSR1);
307 pthread_exit(NULL);
308 }
309