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