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