sel/sig.c: Use `DISCARD' to ignore errors from the signal-pipe `write'.
[mLib] / sel / sig.c
CommitLineData
9f8886c7 1/* -*-c-*-
2 *
9f8886c7 3 * Signal handling
4 *
5 * (c) 1999 Straylight/Edgeware
6 */
7
d4efbcd9 8/*----- Licensing notice --------------------------------------------------*
9f8886c7 9 *
10 * This file is part of the mLib utilities library.
11 *
12 * mLib is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
d4efbcd9 16 *
9f8886c7 17 * mLib is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
d4efbcd9 21 *
9f8886c7 22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
26 */
27
9f8886c7 28/*----- Header files ------------------------------------------------------*/
29
30#include <errno.h>
31#include <signal.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35
36#include <fcntl.h>
37
38#include "alloc.h"
39#include "fdflags.h"
b05d9857 40#include "macros.h"
9f8886c7 41#include "report.h"
42#include "sel.h"
43#include "sig.h"
44
45/*----- Data structures ---------------------------------------------------*/
46
47typedef struct sigstate {
48 struct sigaction sa;
49 sig *list;
50} sigstate;
51
52/*----- Static variables --------------------------------------------------*/
53
54static sel_file sigsel;
55static int sigfd;
56static sigstate *sigs;
f9adeb47 57static sigset_t ss_all, ss_caught;
58static unsigned nsig;
9f8886c7 59
60/*----- Main code ---------------------------------------------------------*/
61
62/* --- @sig_handler@ --- *
63 *
64 * Arguments: @int n@ = signal number
65 *
66 * Returns: ---
67 *
f9adeb47 68 * Use: Generic signal handler. Writes a single byte to the
69 * signal pipe. The byte contains the signal number. Also
70 * sets the appropriate bit in @ss_caught@ to indicate which
71 * signals are pending.
9f8886c7 72 */
73
74static void sig_handler(int n)
75{
f9adeb47 76 int e = errno;
9f8886c7 77 unsigned char sch = (unsigned char)n;
f9adeb47 78 sigprocmask(SIG_BLOCK, &ss_all, 0);
79 sigaddset(&ss_caught, n);
b05d9857 80 DISCARD(write(sigfd, &sch, 1));
f9adeb47 81 /* The system should reset the signal mask here. */
82 errno = e;
9f8886c7 83}
84
85/* --- @sig_read@ --- *
86 *
87 * Arguments: @int fd@ = file descriptor to read
88 * @unsigned mode@ = thing to do with descriptor
89 * @void *p@ = uninteresting argument
90 *
91 * Returns: ---
92 *
93 * Use: Dispatches signals to their handlers safely.
94 */
95
96static void sig_read(int fd, unsigned mode, void *p)
97{
98 sigset_t ss;
9f8886c7 99
f9adeb47 100 /* --- Read the currently caught signals --- *
101 *
102 * Block signals while the mask is being copied.
103 */
104
105 {
106 sigset_t oss;
107 unsigned char buf[256];
108
109 sigprocmask(SIG_BLOCK, &ss_all, &oss);
110 ss = ss_caught;
111 sigemptyset(&ss_caught);
112 while (read(fd, buf, sizeof(buf)) > 0)
113 /* Do nothing */;
114 sigprocmask(SIG_SETMASK, &oss, 0);
115 }
116
117 /* --- Process the caught signals --- */
118
119 {
120 int i;
121 for (i = 0; i < nsig; i++) {
9f8886c7 122 sig *s;
f9adeb47 123 if (!sigismember(&ss, i))
9f8886c7 124 continue;
f9adeb47 125 s = sigs[i].list;
9f8886c7 126 while (s) {
127 sig *ss = s;
128 s = s->next;
f9adeb47 129 ss->proc(i, ss->p);
9f8886c7 130 }
131 }
132 }
d4efbcd9 133}
9f8886c7 134
135/* --- @sig_add@ --- *
136 *
137 * Arguments: @sig *s@ = pointer to signal handler block
138 * @int n@ = number of the signal
139 * @void (*proc)(int n, void *p)@ = signal handler function
140 * @void *p@ = argument to pass to handler
141 *
142 * Returns: ---
143 *
144 * Use: Adds a signal handler.
145 */
146
147void sig_add(sig *s, int n, void (*proc)(int /*n*/, void */*p*/), void *p)
148{
149 sigstate *ss = &sigs[n];
150
151 /* --- Initialize the block --- */
152
153 s->proc = proc;
154 s->p = p;
155 s->sig = n;
156 s->next = ss->list;
157 s->prev = 0;
158
159 /* --- Engage a signal handler, maybe --- */
160
161 if (!ss->list) {
162 struct sigaction sa;
163 sa.sa_handler = sig_handler;
164 sa.sa_flags = SA_NOCLDSTOP;
165#ifdef SA_RESTART
166 sa.sa_flags |= SA_RESTART;
167#endif
168 sigemptyset(&sa.sa_mask);
f9adeb47 169 sigaddset(&ss_all, n);
9f8886c7 170 sigaction(n, &sa, &ss->sa);
171 }
172
173 /* --- Link the block into the list --- */
174
175 if (ss->list)
176 ss->list->prev = s;
177 ss->list = s;
178}
179
180/* --- @sig_remove@ --- *
181 *
182 * Arguments: @sig *s@ = pointer to signal handler block
183 *
184 * Returns: ---
185 *
186 * Use: Removes the signal handler from the list.
187 */
188
189void sig_remove(sig *s)
190{
191 sigstate *ss = &sigs[s->sig];
192
193 /* --- Unlink the handler block --- */
194
195 if (s->next)
196 s->next->prev = s->prev;
197 if (s->prev)
198 s->prev->next = s->next;
199 else
200 ss->list = s->next;
201
202 /* --- Maybe remove the handler --- */
203
f9adeb47 204 if (!ss->list) {
9f8886c7 205 sigaction(s->sig, &ss->sa, 0);
f9adeb47 206 sigdelset(&ss_all, s->sig);
207 }
9f8886c7 208}
209
210/* --- @sig_init@ --- *
211 *
212 * Arguments: @sel_state *s@ = pointer to select state
213 *
214 * Returns: ---
215 *
216 * Use: Initializes the signal handling system ready for use.
217 */
218
219void sig_init(sel_state *s)
220{
221 int fd[2];
9f8886c7 222
223 /* --- Work out how many signals there are --- */
224
225 {
226 sigset_t ss;
227 unsigned min = 0, max = 0;
228
229 sigemptyset(&ss);
230
231 /* --- Get a good upper bound --- *
232 *
233 * Cap the search at 256. I'll be sending signal numbers as bytes.
234 */
235
236 nsig = 1;
237 while (sigaddset(&ss, nsig) == 0) {
238 if (nsig == 256)
239 goto counted;
240 nsig <<= 1;
241 }
242
243 /* --- Now binary search until I find the actual limit --- */
244
245 min = nsig >> 1;
246 max = nsig;
247 for (;;) {
248 nsig = (min + max) >> 1;
249 if (nsig == min)
250 break;
251 if (sigaddset(&ss, nsig))
252 max = nsig;
253 else
254 min = nsig;
255 }
256 counted:;
257 }
258
259 /* --- Initialize the signal state table --- */
260
261 {
262 unsigned i;
263
264 sigs = xmalloc(nsig * sizeof(*sigs));
265 for (i = 0; i < nsig; i++)
266 sigs[i].list = 0;
267 }
268
269 /* --- Create the signal pipe --- */
270
271 if (pipe(fd))
272 die(1, "couldn't create pipe for signal handling");
273
274 /* --- Set both ends to nonblocking and close-on-exec --- */
275
276 fdflags(fd[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
277 fdflags(fd[1], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
278
279 /* --- Set everything up for the future --- */
d4efbcd9 280
9f8886c7 281 sigfd = fd[1];
282 sel_initfile(s, &sigsel, fd[0], SEL_READ, sig_read, 0);
283 sel_addfile(&sigsel);
f9adeb47 284 sigemptyset(&ss_all);
285 sigemptyset(&ss_caught);
9f8886c7 286}
287
288/*----- That's all, folks -------------------------------------------------*/