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