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: event_sig.c,v 1.1.1.1 2008/10/15 03:31:00 james26_jang Exp $";
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