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#include <fcntl.h> 29 30#include "event.h" 31#include "hash.h" 32 33/* Kludge for figuring out NSIG */ 34#ifdef NSIG 35#define MAX_SIGNALS NSIG 36#elif defined(_NSIG) 37#define MAX_SIGNALS _NSIG 38#else 39#define MAX_SIGNALS 256 /* Should be safe... */ 40#endif 41 42/* A structure for a "synchronous" signal handler */ 43struct SynchronousSignalHandler { 44 int fired; /* Have we received this signal? */ 45 void (*handler)(int sig); /* Handler function */ 46}; 47 48/* A structure for calling back when a child dies */ 49struct ChildEntry { 50 hash_bucket hash; 51 void (*handler)(pid_t pid, int status, void *data); 52 pid_t pid; 53 void *data; 54}; 55 56static struct SynchronousSignalHandler SignalHandlers[MAX_SIGNALS]; 57static int Pipe[2] = {-1, -1}; 58static pid_t MyPid = (pid_t) -1; 59 60static EventHandler *PipeHandler = NULL; 61static hash_table child_process_table; 62 63static unsigned int child_hash(void *data) 64{ 65 return (unsigned int) ((struct ChildEntry *) data)->pid; 66} 67 68static int child_compare(void *d1, void *d2) 69{ 70 return ((struct ChildEntry *)d1)->pid != ((struct ChildEntry *)d2)->pid; 71} 72 73/********************************************************************** 74* %FUNCTION: DoPipe 75* %ARGUMENTS: 76* es -- event selector 77* fd -- readable file descriptor 78* flags -- flags from event system 79* data -- ignored 80* %RETURNS: 81* Nothing 82* %DESCRIPTION: 83* Called when an async signal handler wants attention. This function 84* fires all "synchronous" signal handlers. 85***********************************************************************/ 86static void 87DoPipe(EventSelector *es, 88 int fd, 89 unsigned int flags, 90 void *data) 91{ 92 char buf[64]; 93 int i; 94 sigset_t set; 95 96 /* Clear pipe */ 97 while (read(fd, buf, 64) == 64) { 98 ; 99 } 100 101 /* Fire handlers */ 102 for (i=0; i<MAX_SIGNALS; i++) { 103 if (SignalHandlers[i].fired && 104 SignalHandlers[i].handler) { 105 106 /* Block signal while we call its handler */ 107 sigemptyset(&set); 108 sigaddset(&set, i); 109 sigprocmask(SIG_BLOCK, &set, NULL); 110 111 SignalHandlers[i].handler(i); 112 113 SignalHandlers[i].fired = 0; 114 115 /* Unblock signal */ 116 sigprocmask(SIG_UNBLOCK, &set, NULL); 117 } 118 } 119} 120 121/********************************************************************** 122* %FUNCTION: sig_handler 123* %ARGUMENTS: 124* sig -- signal number 125* %RETURNS: 126* Nothing 127* %DESCRIPTION: 128* Marks a signal as having "fired"; fills IPC pipe. 129***********************************************************************/ 130static void 131sig_handler(int sig) 132{ 133 if (sig <0 || sig > MAX_SIGNALS) { 134 /* Ooops... */ 135 return; 136 } 137 if (getpid() != MyPid) { 138 /* Spuriously-caught signal caught in child! */ 139 return; 140 } 141 142 /* If there's no handler, ignore it */ 143 if (!SignalHandlers[sig].handler) { 144 return; 145 } 146 147 SignalHandlers[sig].fired = 1; 148 int errno_save = errno; 149 write(Pipe[1], &sig, 1); 150 errno = errno_save; 151} 152 153/********************************************************************** 154* %FUNCTION: child_handler 155* %ARGUMENTS: 156* sig -- signal number (whoop-dee-doo) 157* %RETURNS: 158* Nothing 159* %DESCRIPTION: 160* Called *SYNCHRONOUSLY* to reap dead children. SIGCHLD is blocked 161* during the execution of this function. 162***********************************************************************/ 163static void 164child_handler(int sig) 165{ 166 int status; 167 int pid; 168 struct ChildEntry *ce; 169 struct ChildEntry candidate; 170 171 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { 172 candidate.pid = (pid_t) pid; 173 174 ce = hash_find(&child_process_table, &candidate); 175 if (ce) { 176 hash_remove(&child_process_table, ce); 177 if (ce->handler) { 178 ce->handler(pid, status, ce->data); 179 } 180 free(ce); 181 } 182 } 183} 184 185/********************************************************************** 186* %FUNCTION: SetupPipes (static) 187* %ARGUMENTS: 188* es -- event selector 189* %RETURNS: 190* 0 on success; -1 on failure 191* %DESCRIPTION: 192* Sets up pipes with an event handler to handle IPC from a signal handler 193***********************************************************************/ 194static int 195SetupPipes(EventSelector *es) 196{ 197 int flags; 198 int i; 199 200 /* If already done, do nothing */ 201 if (PipeHandler) return 0; 202 203 MyPid = getpid(); 204 205 /* Initialize the child-process hash table */ 206 hash_init(&child_process_table, 207 offsetof(struct ChildEntry, hash), 208 child_hash, 209 child_compare); 210 211 /* Open pipe to self */ 212 if (pipe(Pipe) < 0) { 213 return -1; 214 } 215 216 /* Make pipes non-blocking */ 217 for (i=0; i<=1; i++) { 218 flags = fcntl(Pipe[i], F_GETFL, 0); 219 if (flags != -1) { 220 flags = fcntl(Pipe[i], F_SETFL, flags | O_NONBLOCK); 221 } 222 if (flags == -1) { 223 close(Pipe[0]); 224 close(Pipe[1]); 225 return -1; 226 } 227 } 228 229 PipeHandler = Event_AddHandler(es, Pipe[0], 230 EVENT_FLAG_READABLE, DoPipe, NULL); 231 if (!PipeHandler) { 232 int old_errno = errno; 233 close(Pipe[0]); 234 close(Pipe[1]); 235 errno = old_errno; 236 return -1; 237 } 238 return 0; 239} 240 241/********************************************************************** 242* %FUNCTION: Event_HandleSignal 243* %ARGUMENTS: 244* es -- event selector 245* sig -- signal number 246* handler -- handler to call when signal is raised. Handler is called 247* "synchronously" as events are processed by event loop. 248* %RETURNS: 249* 0 on success, -1 on error. 250* %DESCRIPTION: 251* Sets up a "synchronous" signal handler. 252***********************************************************************/ 253int 254Event_HandleSignal(EventSelector *es, 255 int sig, 256 void (*handler)(int sig)) 257{ 258 struct sigaction act; 259 260 if (SetupPipes(es) < 0) return -1; 261 262 act.sa_handler = sig_handler; 263 sigemptyset(&act.sa_mask); 264 act.sa_flags = 0; 265#ifdef SA_RESTART 266 act.sa_flags |= SA_RESTART; 267#endif 268 if (sig == SIGCHLD) { 269 act.sa_flags |= SA_NOCLDSTOP; 270 } 271 if (sigaction(sig, &act, NULL) < 0) return -1; 272 273 SignalHandlers[sig].handler = handler; 274 275 return 0; 276} 277 278/********************************************************************** 279* %FUNCTION: Event_HandleChildExit 280* %ARGUMENTS: 281* es -- event selector 282* pid -- process-ID of child to wait for 283* handler -- function to call when child exits 284* data -- data to pass to handler when child exits 285* %RETURNS: 286* 0 on success, -1 on failure. 287* %DESCRIPTION: 288* Sets things up so that when a child exits, handler() will be called 289* with the pid of the child and "data" as arguments. The call will 290* be synchronous (part of the normal event loop on es). 291***********************************************************************/ 292int 293Event_HandleChildExit(EventSelector *es, 294 pid_t pid, 295 void (*handler)(pid_t, int, void *), 296 void *data) 297{ 298 struct ChildEntry *ce; 299 sigset_t set; 300 301 if (Event_HandleSignal(es, SIGCHLD, child_handler) < 0) return -1; 302 ce = malloc(sizeof(struct ChildEntry)); 303 if (!ce) return -1; 304 ce->pid = pid; 305 ce->data = data; 306 ce->handler = handler; 307 308 /* Critical section: Don't let SIGCHLD mess hash_insert */ 309 sigemptyset(&set); 310 sigaddset(&set, SIGCHLD); 311 sigprocmask(SIG_BLOCK, &set, NULL); 312 313 hash_insert(&child_process_table, ce); 314 315 sigprocmask(SIG_UNBLOCK, &set, NULL); 316 317 return 0; 318} 319