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