chiark / gitweb /
buf: Fix two embarassing bugs found while writing Lisp bindings.
[mLib] / sig.c
1 /* -*-c-*-
2  *
3  * $Id: sig.c,v 1.3 2004/04/08 01:36:13 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 /*----- 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
48 typedef struct sigstate {
49   struct sigaction sa;
50   sig *list;
51 } sigstate;
52
53 /*----- Static variables --------------------------------------------------*/
54
55 static sel_file sigsel;
56 static int sigfd;
57 static sigstate *sigs;
58 static sigset_t ss_all, ss_caught;
59 static unsigned nsig;
60
61 /*----- Main code ---------------------------------------------------------*/
62
63 /* --- @sig_handler@ --- *
64  *
65  * Arguments:   @int n@ = signal number
66  *
67  * Returns:     ---
68  *
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.
73  */
74
75 static void sig_handler(int n)
76 {
77   int e = errno;
78   unsigned char sch = (unsigned char)n;
79   sigprocmask(SIG_BLOCK, &ss_all, 0);
80   sigaddset(&ss_caught, n);
81   write(sigfd, &sch, 1);
82   /* The system should reset the signal mask here. */
83   errno = e;
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
97 static void sig_read(int fd, unsigned mode, void *p)
98 {
99   sigset_t ss;
100
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++) {
123       sig *s;
124       if (!sigismember(&ss, i))
125         continue;
126       s = sigs[i].list;
127       while (s) {
128         sig *ss = s;
129         s = s->next;
130         ss->proc(i, ss->p);
131       }
132     }
133   }
134 }  
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
148 void 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);
170     sigaddset(&ss_all, n);
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
190 void 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
205   if (!ss->list) {
206     sigaction(s->sig, &ss->sa, 0);
207     sigdelset(&ss_all, s->sig);
208   }
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
220 void sig_init(sel_state *s)
221 {
222   int fd[2];
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 --- */
281     
282   sigfd = fd[1];
283   sel_initfile(s, &sigsel, fd[0], SEL_READ, sig_read, 0);
284   sel_addfile(&sigsel);
285   sigemptyset(&ss_all);
286   sigemptyset(&ss_caught);
287 }
288
289 /*----- That's all, folks -------------------------------------------------*/