chiark / gitweb /
Add global unihash table; use universal hashing instead of CRC.
[mLib] / sig.c
1 /* -*-c-*-
2  *
3  * $Id: sig.c,v 1.2 1999/08/19 18:31:04 mdw Exp $
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 $
33  * Revision 1.2  1999/08/19 18:31:04  mdw
34  * Improve signal handling to prevent signals from being lost.
35  *
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
59 typedef struct sigstate {
60   struct sigaction sa;
61   sig *list;
62 } sigstate;
63
64 /*----- Static variables --------------------------------------------------*/
65
66 static sel_file sigsel;
67 static int sigfd;
68 static sigstate *sigs;
69 static sigset_t ss_all, ss_caught;
70 static unsigned nsig;
71
72 /*----- Main code ---------------------------------------------------------*/
73
74 /* --- @sig_handler@ --- *
75  *
76  * Arguments:   @int n@ = signal number
77  *
78  * Returns:     ---
79  *
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.
84  */
85
86 static void sig_handler(int n)
87 {
88   int e = errno;
89   unsigned char sch = (unsigned char)n;
90   sigprocmask(SIG_BLOCK, &ss_all, 0);
91   sigaddset(&ss_caught, n);
92   write(sigfd, &sch, 1);
93   /* The system should reset the signal mask here. */
94   errno = e;
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
108 static void sig_read(int fd, unsigned mode, void *p)
109 {
110   sigset_t ss;
111
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++) {
134       sig *s;
135       if (!sigismember(&ss, i))
136         continue;
137       s = sigs[i].list;
138       while (s) {
139         sig *ss = s;
140         s = s->next;
141         ss->proc(i, ss->p);
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
159 void 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);
181     sigaddset(&ss_all, n);
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
201 void 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
216   if (!ss->list) {
217     sigaction(s->sig, &ss->sa, 0);
218     sigdelset(&ss_all, s->sig);
219   }
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
231 void sig_init(sel_state *s)
232 {
233   int fd[2];
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);
296   sigemptyset(&ss_all);
297   sigemptyset(&ss_caught);
298 }
299
300 /*----- That's all, folks -------------------------------------------------*/