blob: e3b4e4b41945ef3b03ba48e2d7bbe0287e0d425b
1 | /*********************************************************************** |
2 | * |
3 | * event_sig.c |
4 | * |
5 | * Code for handling signals nicely (synchronously) and for dealing |
6 | * with reaping child processes. |
7 | * |
8 | * Copyright (C) 2002 by Roaring Penguin Software Inc. |
9 | * |
10 | * This software may be distributed under the terms of the GNU General |
11 | * Public License, Version 2, or (at your option) any later version. |
12 | * |
13 | * LIC: GPL |
14 | * |
15 | ***********************************************************************/ |
16 | |
17 | static char const RCSID[] = |
18 | "$Id$"; |
19 | |
20 | #define _POSIX_SOURCE 1 /* For sigaction defines */ |
21 | #define _BSD_SOURCE 1 /* For SA_RESTART */ |
22 | |
23 | #include <signal.h> |
24 | #include <sys/types.h> |
25 | #include <sys/wait.h> |
26 | #include <errno.h> |
27 | #include <stddef.h> |
28 | |
29 | #include "event.h" |
30 | #include "hash.h" |
31 | |
32 | /* Kludge for figuring out NSIG */ |
33 | #ifdef NSIG |
34 | #define MAX_SIGNALS NSIG |
35 | #elif defined(_NSIG) |
36 | #define MAX_SIGNALS _NSIG |
37 | #else |
38 | #define MAX_SIGNALS 256 /* Should be safe... */ |
39 | #endif |
40 | |
41 | /* A structure for a "synchronous" signal handler */ |
42 | struct SynchronousSignalHandler { |
43 | int fired; /* Have we received this signal? */ |
44 | void (*handler)(int sig); /* Handler function */ |
45 | }; |
46 | |
47 | /* A structure for calling back when a child dies */ |
48 | struct ChildEntry { |
49 | hash_bucket hash; |
50 | void (*handler)(pid_t pid, int status, void *data); |
51 | pid_t pid; |
52 | void *data; |
53 | }; |
54 | |
55 | static struct SynchronousSignalHandler SignalHandlers[MAX_SIGNALS]; |
56 | static int Pipe[2] = {-1, -1}; |
57 | static EventHandler *PipeHandler = NULL; |
58 | static sig_atomic_t PipeFull = 0; |
59 | static hash_table child_process_table; |
60 | |
61 | static unsigned int child_hash(void *data) |
62 | { |
63 | return (unsigned int) ((struct ChildEntry *) data)->pid; |
64 | } |
65 | |
66 | static int child_compare(void *d1, void *d2) |
67 | { |
68 | return ((struct ChildEntry *)d1)->pid != ((struct ChildEntry *)d2)->pid; |
69 | } |
70 | |
71 | /********************************************************************** |
72 | * %FUNCTION: DoPipe |
73 | * %ARGUMENTS: |
74 | * es -- event selector |
75 | * fd -- readable file descriptor |
76 | * flags -- flags from event system |
77 | * data -- ignored |
78 | * %RETURNS: |
79 | * Nothing |
80 | * %DESCRIPTION: |
81 | * Called when an async signal handler wants attention. This function |
82 | * fires all "synchronous" signal handlers. |
83 | ***********************************************************************/ |
84 | static void |
85 | DoPipe(EventSelector *es, |
86 | int fd, |
87 | unsigned int flags, |
88 | void *data) |
89 | { |
90 | char buf[64]; |
91 | int i; |
92 | |
93 | /* Clear buffer */ |
94 | read(fd, buf, 64); |
95 | PipeFull = 0; |
96 | |
97 | /* Fire handlers */ |
98 | for (i=0; i<MAX_SIGNALS; i++) { |
99 | if (SignalHandlers[i].fired && |
100 | SignalHandlers[i].handler) { |
101 | SignalHandlers[i].handler(i); |
102 | } |
103 | SignalHandlers[i].fired = 0; |
104 | } |
105 | } |
106 | |
107 | /********************************************************************** |
108 | * %FUNCTION: sig_handler |
109 | * %ARGUMENTS: |
110 | * sig -- signal number |
111 | * %RETURNS: |
112 | * Nothing |
113 | * %DESCRIPTION: |
114 | * Marks a signal as having "fired"; fills IPC pipe. |
115 | ***********************************************************************/ |
116 | static void |
117 | sig_handler(int sig) |
118 | { |
119 | if (sig <0 || sig > MAX_SIGNALS) { |
120 | /* Ooops... */ |
121 | return; |
122 | } |
123 | SignalHandlers[sig].fired = 1; |
124 | if (!PipeFull) { |
125 | write(Pipe[1], &sig, 1); |
126 | PipeFull = 1; |
127 | } |
128 | } |
129 | |
130 | /********************************************************************** |
131 | * %FUNCTION: child_handler |
132 | * %ARGUMENTS: |
133 | * sig -- signal number (whoop-dee-doo) |
134 | * %RETURNS: |
135 | * Nothing |
136 | * %DESCRIPTION: |
137 | * Called *SYNCHRONOUSLY* to reap dead children. |
138 | ***********************************************************************/ |
139 | static void |
140 | child_handler(int sig) |
141 | { |
142 | int status; |
143 | int pid; |
144 | struct ChildEntry *ce; |
145 | struct ChildEntry candidate; |
146 | |
147 | while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { |
148 | candidate.pid = (pid_t) pid; |
149 | ce = hash_find(&child_process_table, &candidate); |
150 | if (ce) { |
151 | if (ce->handler) { |
152 | ce->handler(pid, status, ce->data); |
153 | } |
154 | hash_remove(&child_process_table, ce); |
155 | free(ce); |
156 | } |
157 | } |
158 | } |
159 | |
160 | /********************************************************************** |
161 | * %FUNCTION: SetupPipes (static) |
162 | * %ARGUMENTS: |
163 | * es -- event selector |
164 | * %RETURNS: |
165 | * 0 on success; -1 on failure |
166 | * %DESCRIPTION: |
167 | * Sets up pipes with an event handler to handle IPC from a signal handler |
168 | ***********************************************************************/ |
169 | static int |
170 | SetupPipes(EventSelector *es) |
171 | { |
172 | /* If already done, do nothing */ |
173 | if (PipeHandler) return 0; |
174 | |
175 | /* Initialize the child-process hash table */ |
176 | hash_init(&child_process_table, |
177 | offsetof(struct ChildEntry, hash), |
178 | child_hash, |
179 | child_compare); |
180 | |
181 | /* Open pipe to self */ |
182 | if (pipe(Pipe) < 0) { |
183 | return -1; |
184 | } |
185 | |
186 | PipeHandler = Event_AddHandler(es, Pipe[0], |
187 | EVENT_FLAG_READABLE, DoPipe, NULL); |
188 | if (!PipeHandler) { |
189 | int old_errno = errno; |
190 | close(Pipe[0]); |
191 | close(Pipe[1]); |
192 | errno = old_errno; |
193 | return -1; |
194 | } |
195 | return 0; |
196 | } |
197 | |
198 | /********************************************************************** |
199 | * %FUNCTION: Event_HandleSignal |
200 | * %ARGUMENTS: |
201 | * es -- event selector |
202 | * sig -- signal number |
203 | * handler -- handler to call when signal is raised. Handler is called |
204 | * "synchronously" as events are processed by event loop. |
205 | * %RETURNS: |
206 | * 0 on success, -1 on error. |
207 | * %DESCRIPTION: |
208 | * Sets up a "synchronous" signal handler. |
209 | ***********************************************************************/ |
210 | int |
211 | Event_HandleSignal(EventSelector *es, |
212 | int sig, |
213 | void (*handler)(int sig)) |
214 | { |
215 | struct sigaction act; |
216 | |
217 | if (SetupPipes(es) < 0) return -1; |
218 | |
219 | act.sa_handler = sig_handler; |
220 | sigemptyset(&act.sa_mask); |
221 | act.sa_flags = 0; |
222 | #ifdef SA_RESTART |
223 | act.sa_flags |= SA_RESTART; |
224 | #endif |
225 | if (sig == SIGCHLD) { |
226 | act.sa_flags |= SA_NOCLDSTOP; |
227 | } |
228 | if (sigaction(sig, &act, NULL) < 0) return -1; |
229 | |
230 | SignalHandlers[sig].handler = handler; |
231 | |
232 | return 0; |
233 | } |
234 | |
235 | /********************************************************************** |
236 | * %FUNCTION: Event_HandleChildExit |
237 | * %ARGUMENTS: |
238 | * es -- event selector |
239 | * pid -- process-ID of child to wait for |
240 | * handler -- function to call when child exits |
241 | * data -- data to pass to handler when child exits |
242 | * %RETURNS: |
243 | * 0 on success, -1 on failure. |
244 | * %DESCRIPTION: |
245 | * Sets things up so that when a child exits, handler() will be called |
246 | * with the pid of the child and "data" as arguments. The call will |
247 | * be synchronous (part of the normal event loop on es). |
248 | ***********************************************************************/ |
249 | int |
250 | Event_HandleChildExit(EventSelector *es, |
251 | pid_t pid, |
252 | void (*handler)(pid_t, int, void *), |
253 | void *data) |
254 | { |
255 | struct ChildEntry *ce; |
256 | |
257 | if (Event_HandleSignal(es, SIGCHLD, child_handler) < 0) return -1; |
258 | ce = malloc(sizeof(struct ChildEntry)); |
259 | if (!ce) return -1; |
260 | ce->pid = pid; |
261 | ce->data = data; |
262 | ce->handler = handler; |
263 | hash_insert(&child_process_table, ce); |
264 | return 0; |
265 | } |
266 |