/* ***************************************************************************** * * $RCSfile: sigstuff.c,v $ * $Date: 1998/07/29 13:02:50 $ * $Source: /home/richard/Xml/RCS/sigstuff.c,v $ * $Revision: 1.6 $ * $Author: richard $ * ***************************************************************************** * * Copyright 1998, Brown University and Richard Goerwitz * ***************************************************************************** * * Signal-handling utilities. Work with POSIX systems; also work, * kind of, with SYSV and BSD systems. * * catch_sigchld (handler) installs handler for SIGCHLD * catch_sigint_and_sigterm(handler) installs handler for SIGINT and SIGTERM * * Note that these routines do some extra setup on POSIX systems. In * particular, catch_sigchld() blocks SIGTERM, SIGINT, and SIGHUP * while SIGCHLD is being handled. Also, catch_sigint_and_sigterm() * sets up the same handler for SIGINT and SIGTERM, but blocks the * one signal while the other is being handled; it also blocks SIGHUP * for both. Both catch_sigchld() and catch_sigint_and_sigterm() set * set system calls to be interruptible. catch_sigchld() sets them * to be re-startable, if that facility is available. * * The following routine is a general-purpose signal-catcher * installer: * * sig_catch (sig, handler) - installs handler for sig * * For those who don't want "slow" system calls restarted (they'll * abort and errno will be set to EINTR), there is: * * sig_catch_no_restart (sig, handler) - installs handler for sig * * The function of the following routines should be obvious: * * sig_ignore (sig) * sig_hold (sig) * sig_release (sig) * sig_default (sig) * * The only proviso here is that sig_ignore() treats SIGCHLD * specially. Normally if somebody is catching SIGCHLD, what it * means is that they don't want zombie processes created, and aren't * interested in the exit status of child processes. The trouble is * that ignoring SIGCHLD on some systems does not have this effect. * If sig_ignore (SIGCHLD) is called on such a system, an innocuous * handler will be installed for SIGCHLD, whose effect is to ignore * children's exit status and prevent zombies from being created. * * Error numbers from 90 to 99. * ***************************************************************************** */ #include "sigstuff.h" #include "errabort.h" #ifdef HAVE_SYS_TIME_H # include # include #else # include #endif #ifndef WUNTRACED # define WUNTRACED 0 #endif #ifndef SA_NOCLDSTOP # define SA_NOCLDSTOP 0 #endif #ifndef HAVE_WAITPID # define waitpid(a,b,c) wait3(b,c,NULL) #endif RETSIGTYPE wait_for_children (int); void catch_sigchld (RETSIGTYPE (*handler)(int)) { #ifdef HAVE_SIGACTION /* POSIX; this is the best code */ struct sigaction act; act.sa_handler = handler; sigemptyset (&act.sa_mask); /* while handling SIGCHLD, block SIGINT, etc. */ sigaddset (&act.sa_mask, SIGTERM); sigaddset (&act.sa_mask, SIGINT); sigaddset (&act.sa_mask, SIGHUP); act.sa_flags = 0; #ifdef SA_INTERRUPT /* ensure system call is interrupted */ act.sa_flags |= SA_INTERRUPT; #endif #ifdef SA_RESTART /* ensure system call is re-started */ act.sa_flags |= SA_RESTART; #endif reinstall_handler: errno = 0; if (sigaction (SIGCHLD, &act, NULL) == -1) { if (errno == EINTR) goto reinstall_handler; xwrap (errdebug (1, "sigaction() failed to install SIGCHLD handler!\n")); errabort (80, "%s failed\n", "sigaction()"); } #else # ifdef HAVE_SIGSET /* SYSV */ reinstall_sigchld_handler: errno = 0; if (sigset (SIGCHLD, handler) == -1) { if (errno == EINTR) goto reinstall_sigchld_handler; xwrap (errdebug (1, "sigset() failed to install SIGCHLD handler!\n")); errabort (80, "%s failed\n", "sigset()"); } # else /* BSD or other; don't try very hard */ signal (SIGCHLD, handler); # endif /* not HAVE_SIGSET */ #endif /* not HAVE_SIGACTION */ xwrap (errdebug (5, "installed SIGCHLD handler; returning\n")); } void catch_sigint_and_sigterm (RETSIGTYPE (*handler)(int)) { #ifdef HAVE_SIGACTION /* POSIX; this is the best code */ struct sigaction act; act.sa_handler = handler; /* set up SIGTERM handler */ sigemptyset (&act.sa_mask); /* while handling SIGTERM, block SIGINT */ sigaddset (&act.sa_mask, SIGINT); sigaddset (&act.sa_mask, SIGHUP); act.sa_flags = 0; #ifdef SA_INTERRUPT /* ensure system call is interrupted */ act.sa_flags |= SA_INTERRUPT; #endif sigaction (SIGTERM, &act, NULL); /* set up SIGINT handler */ sigemptyset (&act.sa_mask); /* while handling SIGINT, block SIGTERM */ sigaddset (&act.sa_mask, SIGTERM); sigaddset (&act.sa_mask, SIGHUP); retry_sigaction: errno = 0; if (sigaction (SIGINT, &act, NULL) == -1) { if (errno == EINTR) goto retry_sigaction; xwrap (errdebug (1, "sigaction() failed to install SIGINT handler!\n")); errabort (80, "%s failed\n", "sigaction()"); } #else # ifdef HAVE_SIGSET /* SYSV */ sigset (SIGTERM, handler); retry_sigset: errno = 0; if (sigset (SIGINT, handler) == -1) { if (errno == EINTR) goto retry_sigset; xwrap (errdebug (1, "sigset() failed to install SIGINT handler!\n")); errabort (80, "%s failed\n", "sigset()"); } # else /* BSD or other; don't try very hard */ signal (SIGTERM, handler); signal (SIGINT, handler); # endif /* not HAVE_SIGSET */ #endif /* not HAVE_SIGACTION */ xwrap (errdebug (5, "installed SIGINT, SIGTERM handler; returning\n")); } void sig_catch (int sig, RETSIGTYPE (*handler)(int)) { #ifdef HAVE_SIGACTION /* POSIX; this is the best code */ struct sigaction act; act.sa_handler = handler; sigemptyset (&act.sa_mask); act.sa_flags = 0; #ifdef SA_INTERRUPT /* ensure system call is interrupted */ act.sa_flags |= SA_INTERRUPT; #endif #ifdef SA_RESTART /* ensure system call is re-started */ act.sa_flags |= SA_RESTART; #endif try_again: errno = 0; if (sigaction (sig, &act, NULL) == -1) { if (errno == EINTR) goto try_again; xwrap (errdebug (1, "sigaction() failed to install signal #%d handler!\n", sig)); errabort (80, "%s failed\n", "sigaction()"); } #else # ifdef HAVE_SIGSET /* SYSV */ try_sigset_again: errno = 0; if (sigset (sig, handler) == -1) { if (errno == EINTR) goto try_sigset_again; xwrap (errdebug (1, "sigset() failed to install signal #%d handler!\n", sig)); errabort (80, "%s failed\n", "sigset()"); } # else /* BSD or other; don't try very hard */ signal (sig, handler); # endif /* not HAVE_SIGSET */ #endif /* not HAVE_SIGACTION */ xwrap (errdebug (5, "installed handler for signal #%d; returning\n", sig)); } #ifdef SA_RESTART void sig_catch_no_restart (int sig, RETSIGTYPE (*handler)(int)) { #ifdef HAVE_SIGACTION /* POSIX; this is the best code */ struct sigaction act; act.sa_handler = handler; sigemptyset (&act.sa_mask); act.sa_flags = 0; #ifdef SA_INTERRUPT /* ensure system call is interrupted */ act.sa_flags |= SA_INTERRUPT; #else # ifdef HAVE_SIGINTERRUPT siginterrupt (sig, 1); # else # ifdef siginterrupt siginterrupt (sig, 1); # endif # endif #endif try_again: errno = 0; if (sigaction (sig, &act, NULL) == -1) { if (errno == EINTR) goto try_again; xwrap (errdebug (1, "sigaction() failed to install signal #%d handler!\n", sig)); errabort (80, "%s failed\n", "sigaction()"); } #else # ifdef HAVE_SIGSET /* SYSV */ try_sigset_again: errno = 0; if (sigset (sig, handler) == -1) { if (errno == EINTR) goto try_sigset_again; xwrap (errdebug (1, "sigset() failed to install signal #%d handler!\n", sig)); errabort (80, "%s failed\n", "sigset()"); } # else /* BSD or other; don't try very hard */ signal (sig, handler); # endif /* not HAVE_SIGSET */ #endif /* not HAVE_SIGACTION */ xwrap (errdebug (5, "installed handler for signal #%d; returning\n", sig)); } #endif /* SA_RESTART */ void sig_ignore (int sig) { #ifdef HAVE_SIGACTION /* POSIX; this is the best code */ struct sigaction act; xwrap (errdebug (5, "ignoring signal #%d\n", sig)); # ifdef SA_NOCLDWAIT /* SYSV SIGCHLD behavior is available */ act.sa_handler = SIG_IGN; if (sig != SIGCHLD) act.sa_flags = 0; else { act.sa_flags = SA_NOCLDWAIT | SA_NOCLDSTOP; xwrap (errdebug (5, "ignoring SIGCHLD; reaper unnecessary\n", sig)); } # else if (sig != SIGCHLD) { act.sa_handler = SIG_IGN; act.sa_flags = 0; } else { /* SYSV SIGCHLD behavior is NOT available; must catch SIGCHLD */ act.sa_handler = wait_for_children; act.sa_flags = SA_NOCLDSTOP; sigemptyset (&act.sa_mask); sigaddset (&act.sa_mask, SIGTERM); sigaddset (&act.sa_mask, SIGINT); sigaddset (&act.sa_mask, SIGHUP); xwrap (errdebug (5, "treating SIGCHLD specially; installing handler\n", sig)); } # endif /* not SA_NOCLDWAIT */ try_sigaction_again: errno = 0; if (sigaction (sig, &act, NULL) == -1) { if (errno == EINTR) goto try_sigaction_again; xwrap (errdebug (1, "sigaction() failed!\n")); errabort (80, "%s failed\n", "sigaction()"); } #else # ifdef HAVE_SIGSET /* SYSV */ xwrap (errdebug (5, "ignoring signal #%d\n", sig)); if (sigignore (sig) == -1) { xwrap (errdebug (1, "sigignore() failed!\n")); errabort (80, "%s failed\n", "sigignore()"); } # else /* BSD or other; don't try very hard */ xwrap (errdebug (5, "ignoring signal #%d\n", sig)); if (sig == SIGCHLD) signal (sig, wait_for_children); else signal (sig, SIG_IGN); # endif /* not HAVE_SIGSET */ #endif /* not HAVE_SIGACTION */ } RETSIGTYPE wait_for_children (int sig) { pid_t pid; int wstatus, our_errno; /* errno is often declared volatile, and may get reset * asynchronously; so it's never a bad idea to save and restore it * in signal handlers */ our_errno = errno; xwrap (errdebug (7, "holding SIGTERM, SIGINT, SIGHUP while running waitpid()\n", sig)); #ifndef HAVE_SIGACTION /* let the process clean up its zombies; don't interrupt */ sig_hold (SIGTERM); sig_hold (SIGINT); sig_hold (SIGHUP); #endif /* WNOHANG means "return immediately if no child has exited" */ wait_again: errno = 0; while ((pid = waitpid (-1, &wstatus, WNOHANG | WUNTRACED)) > 0) xwrap (errdebug (5, "child process #%d died\n", pid)); if (pid == -1 && errno == EINTR) goto wait_again; xwrap (errdebug (7, "releasing SIGTERM, SIGINT, SIGHUP\n", sig)); #ifndef HAVE_SIGACTION sig_release (SIGHUP); sig_release (SIGINT); sig_release (SIGTERM); #endif /* Linux resets to default every time signal is caught. Note well: * calling sig_ignore() with SIGCHLD as its argument resets the * default handler to wait_for_children(). I.e., it's correct! */ xwrap (errdebug (7, "reinstalling SIGCHLD handler (needed on some systems)\n")); sig_ignore (SIGCHLD); errno = our_errno; } void sig_default (int sig) { #ifdef HAVE_SIGACTION /* POSIX; this is the best code */ struct sigaction act; xwrap (errdebug (7, "resetting disposition of signal %d to default\n", sig)); act.sa_handler = SIG_DFL; act.sa_flags = 0; reinstall_default_handler: errno = 0; if (sigaction (sig, &act, NULL) == -1) { if (errno == EINTR) goto reinstall_default_handler; xwrap (errdebug (1, "sigaction() failed to reinstall default handler for signal %d!\n", sig)); errabort (80, "%s failed\n", "sigaction()"); } #else # ifdef HAVE_SIGSET /* SYSV */ if (sigset (sig, SIG_DFL) == -1) { xwrap (errdebug (1, "sigset() failed!\n")); errabort (80, "%s failed\n", "sigset()"); } # else /* BSD or other; don't try very hard */ signal (sig, SIG_DFL); # endif /* not HAVE_SIGSET */ #endif /* not HAVE_SIGACTION */ xwrap (errdebug (5, "reset signal %d to default\n", sig)); } void sig_hold (int sig) { #ifdef HAVE_SIGACTION /* POSIX; this is the best code */ sigset_t block; sigemptyset (&block); sigaddset (&block, sig); if (sigprocmask (SIG_BLOCK, &block, NULL) == -1) { xwrap (errdebug (1, "sigprocmask() failed!\n")); errabort (80, "%s failed\n", "sigprocmask()"); } #else # ifdef HAVE_SIGSET /* SYSV */ if (sighold (sig) == -1) { xwrap (errdebug (1, "sighold() failed!\n")); errabort (80, "%s failed\n", "sighold()"); } # else /* BSD or other; don't try very hard */ sigblock (sigmask (sig)); # endif /* not HAVE_SIGSET */ #endif /* not HAVE_SIGACTION */ xwrap (errdebug (5, "now holding signal #%d\n", sig)); } void sig_release (int sig) { #ifdef HAVE_SIGACTION /* POSIX; this is the best code */ sigset_t unblock; sigemptyset (&unblock); sigaddset (&unblock, sig); if (sigprocmask (SIG_UNBLOCK, &unblock, NULL) == -1) { xwrap (errdebug (1, "sigprocmask() failed!\n")); errabort (80, "%s failed\n", "sigprocmask()"); } #else # ifdef HAVE_SIGSET /* SYSV */ if (sigrelse (sig) == -1) { xwrap (errdebug (1, "sigrelse() failed!\n")); errabort (80, "%s failed\n", "sigrelse()"); } # else /* BSD or other; don't try very hard */ int unmask; unmask = sigblock (sigmask (sig)) & ~(sigmask (sig)); sigsetmask (unmask); # endif /* not HAVE_SIGSET */ #endif /* not HAVE_SIGACTION */ xwrap (errdebug (5, "released signal #%d\n", sig)); }