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