chiark / gitweb /
Infrastructure: Strip away crufty CVS $Id$ tags.
[mLib] / sig.c
CommitLineData
9f8886c7 1/* -*-c-*-
9f8886c7 2 *
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"
40#include "report.h"
41#include "sel.h"
42#include "sig.h"
43
44/*----- Data structures ---------------------------------------------------*/
45
46typedef struct sigstate {
47 struct sigaction sa;
48 sig *list;
49} sigstate;
50
51/*----- Static variables --------------------------------------------------*/
52
53static sel_file sigsel;
54static int sigfd;
55static sigstate *sigs;
f9adeb47 56static sigset_t ss_all, ss_caught;
57static unsigned nsig;
9f8886c7 58
59/*----- Main code ---------------------------------------------------------*/
60
61/* --- @sig_handler@ --- *
62 *
63 * Arguments: @int n@ = signal number
64 *
65 * Returns: ---
66 *
f9adeb47 67 * Use: Generic signal handler. Writes a single byte to the
68 * signal pipe. The byte contains the signal number. Also
69 * sets the appropriate bit in @ss_caught@ to indicate which
70 * signals are pending.
9f8886c7 71 */
72
73static void sig_handler(int n)
74{
f9adeb47 75 int e = errno;
9f8886c7 76 unsigned char sch = (unsigned char)n;
f9adeb47 77 sigprocmask(SIG_BLOCK, &ss_all, 0);
78 sigaddset(&ss_caught, n);
9f8886c7 79 write(sigfd, &sch, 1);
f9adeb47 80 /* The system should reset the signal mask here. */
81 errno = e;
9f8886c7 82}
83
84/* --- @sig_read@ --- *
85 *
86 * Arguments: @int fd@ = file descriptor to read
87 * @unsigned mode@ = thing to do with descriptor
88 * @void *p@ = uninteresting argument
89 *
90 * Returns: ---
91 *
92 * Use: Dispatches signals to their handlers safely.
93 */
94
95static void sig_read(int fd, unsigned mode, void *p)
96{
97 sigset_t ss;
9f8886c7 98
f9adeb47 99 /* --- Read the currently caught signals --- *
100 *
101 * Block signals while the mask is being copied.
102 */
103
104 {
105 sigset_t oss;
106 unsigned char buf[256];
107
108 sigprocmask(SIG_BLOCK, &ss_all, &oss);
109 ss = ss_caught;
110 sigemptyset(&ss_caught);
111 while (read(fd, buf, sizeof(buf)) > 0)
112 /* Do nothing */;
113 sigprocmask(SIG_SETMASK, &oss, 0);
114 }
115
116 /* --- Process the caught signals --- */
117
118 {
119 int i;
120 for (i = 0; i < nsig; i++) {
9f8886c7 121 sig *s;
f9adeb47 122 if (!sigismember(&ss, i))
9f8886c7 123 continue;
f9adeb47 124 s = sigs[i].list;
9f8886c7 125 while (s) {
126 sig *ss = s;
127 s = s->next;
f9adeb47 128 ss->proc(i, ss->p);
9f8886c7 129 }
130 }
131 }
d4efbcd9 132}
9f8886c7 133
134/* --- @sig_add@ --- *
135 *
136 * Arguments: @sig *s@ = pointer to signal handler block
137 * @int n@ = number of the signal
138 * @void (*proc)(int n, void *p)@ = signal handler function
139 * @void *p@ = argument to pass to handler
140 *
141 * Returns: ---
142 *
143 * Use: Adds a signal handler.
144 */
145
146void sig_add(sig *s, int n, void (*proc)(int /*n*/, void */*p*/), void *p)
147{
148 sigstate *ss = &sigs[n];
149
150 /* --- Initialize the block --- */
151
152 s->proc = proc;
153 s->p = p;
154 s->sig = n;
155 s->next = ss->list;
156 s->prev = 0;
157
158 /* --- Engage a signal handler, maybe --- */
159
160 if (!ss->list) {
161 struct sigaction sa;
162 sa.sa_handler = sig_handler;
163 sa.sa_flags = SA_NOCLDSTOP;
164#ifdef SA_RESTART
165 sa.sa_flags |= SA_RESTART;
166#endif
167 sigemptyset(&sa.sa_mask);
f9adeb47 168 sigaddset(&ss_all, n);
9f8886c7 169 sigaction(n, &sa, &ss->sa);
170 }
171
172 /* --- Link the block into the list --- */
173
174 if (ss->list)
175 ss->list->prev = s;
176 ss->list = s;
177}
178
179/* --- @sig_remove@ --- *
180 *
181 * Arguments: @sig *s@ = pointer to signal handler block
182 *
183 * Returns: ---
184 *
185 * Use: Removes the signal handler from the list.
186 */
187
188void sig_remove(sig *s)
189{
190 sigstate *ss = &sigs[s->sig];
191
192 /* --- Unlink the handler block --- */
193
194 if (s->next)
195 s->next->prev = s->prev;
196 if (s->prev)
197 s->prev->next = s->next;
198 else
199 ss->list = s->next;
200
201 /* --- Maybe remove the handler --- */
202
f9adeb47 203 if (!ss->list) {
9f8886c7 204 sigaction(s->sig, &ss->sa, 0);
f9adeb47 205 sigdelset(&ss_all, s->sig);
206 }
9f8886c7 207}
208
209/* --- @sig_init@ --- *
210 *
211 * Arguments: @sel_state *s@ = pointer to select state
212 *
213 * Returns: ---
214 *
215 * Use: Initializes the signal handling system ready for use.
216 */
217
218void sig_init(sel_state *s)
219{
220 int fd[2];
9f8886c7 221
222 /* --- Work out how many signals there are --- */
223
224 {
225 sigset_t ss;
226 unsigned min = 0, max = 0;
227
228 sigemptyset(&ss);
229
230 /* --- Get a good upper bound --- *
231 *
232 * Cap the search at 256. I'll be sending signal numbers as bytes.
233 */
234
235 nsig = 1;
236 while (sigaddset(&ss, nsig) == 0) {
237 if (nsig == 256)
238 goto counted;
239 nsig <<= 1;
240 }
241
242 /* --- Now binary search until I find the actual limit --- */
243
244 min = nsig >> 1;
245 max = nsig;
246 for (;;) {
247 nsig = (min + max) >> 1;
248 if (nsig == min)
249 break;
250 if (sigaddset(&ss, nsig))
251 max = nsig;
252 else
253 min = nsig;
254 }
255 counted:;
256 }
257
258 /* --- Initialize the signal state table --- */
259
260 {
261 unsigned i;
262
263 sigs = xmalloc(nsig * sizeof(*sigs));
264 for (i = 0; i < nsig; i++)
265 sigs[i].list = 0;
266 }
267
268 /* --- Create the signal pipe --- */
269
270 if (pipe(fd))
271 die(1, "couldn't create pipe for signal handling");
272
273 /* --- Set both ends to nonblocking and close-on-exec --- */
274
275 fdflags(fd[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
276 fdflags(fd[1], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
277
278 /* --- Set everything up for the future --- */
d4efbcd9 279
9f8886c7 280 sigfd = fd[1];
281 sel_initfile(s, &sigsel, fd[0], SEL_READ, sig_read, 0);
282 sel_addfile(&sigsel);
f9adeb47 283 sigemptyset(&ss_all);
284 sigemptyset(&ss_caught);
9f8886c7 285}
286
287/*----- That's all, folks -------------------------------------------------*/