summaryrefslogtreecommitdiff
path: root/src/libevent/event_sig.c (plain)
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
17static 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 */
42struct 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 */
48struct ChildEntry {
49 hash_bucket hash;
50 void (*handler)(pid_t pid, int status, void *data);
51 pid_t pid;
52 void *data;
53};
54
55static struct SynchronousSignalHandler SignalHandlers[MAX_SIGNALS];
56static int Pipe[2] = {-1, -1};
57static EventHandler *PipeHandler = NULL;
58static sig_atomic_t PipeFull = 0;
59static hash_table child_process_table;
60
61static unsigned int child_hash(void *data)
62{
63 return (unsigned int) ((struct ChildEntry *) data)->pid;
64}
65
66static 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***********************************************************************/
84static void
85DoPipe(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***********************************************************************/
116static void
117sig_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***********************************************************************/
139static void
140child_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***********************************************************************/
169static int
170SetupPipes(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***********************************************************************/
210int
211Event_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***********************************************************************/
249int
250Event_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