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