chiark / gitweb /
87dbb3334890ef23fe610d97394dfcc7072d04c6
[mLib] / sig.c
1 /* -*-c-*-
2  *
3  * Signal handling
4  *
5  * (c) 1999 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
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.
16  *
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.
21  *
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
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
46 typedef struct sigstate {
47   struct sigaction sa;
48   sig *list;
49 } sigstate;
50
51 /*----- Static variables --------------------------------------------------*/
52
53 static sel_file sigsel;
54 static int sigfd;
55 static sigstate *sigs;
56 static sigset_t ss_all, ss_caught;
57 static unsigned nsig;
58
59 /*----- Main code ---------------------------------------------------------*/
60
61 /* --- @sig_handler@ --- *
62  *
63  * Arguments:   @int n@ = signal number
64  *
65  * Returns:     ---
66  *
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.
71  */
72
73 static void sig_handler(int n)
74 {
75   int e = errno;
76   unsigned char sch = (unsigned char)n;
77   sigprocmask(SIG_BLOCK, &ss_all, 0);
78   sigaddset(&ss_caught, n);
79   write(sigfd, &sch, 1);
80   /* The system should reset the signal mask here. */
81   errno = e;
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
95 static void sig_read(int fd, unsigned mode, void *p)
96 {
97   sigset_t ss;
98
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++) {
121       sig *s;
122       if (!sigismember(&ss, i))
123         continue;
124       s = sigs[i].list;
125       while (s) {
126         sig *ss = s;
127         s = s->next;
128         ss->proc(i, ss->p);
129       }
130     }
131   }
132 }
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
146 void 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);
168     sigaddset(&ss_all, n);
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
188 void 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
203   if (!ss->list) {
204     sigaction(s->sig, &ss->sa, 0);
205     sigdelset(&ss_all, s->sig);
206   }
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
218 void sig_init(sel_state *s)
219 {
220   int fd[2];
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 --- */
279
280   sigfd = fd[1];
281   sel_initfile(s, &sigsel, fd[0], SEL_READ, sig_read, 0);
282   sel_addfile(&sigsel);
283   sigemptyset(&ss_all);
284   sigemptyset(&ss_caught);
285 }
286
287 /*----- That's all, folks -------------------------------------------------*/