home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
mm
/
mm-ccmd-0.91.tar.Z
/
mm-ccmd-0.91.tar
/
work
/
mm
/
signals.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-09-16
|
9KB
|
390 lines
/*
* Copyright (c) 1986, 2002 by The Trustees of Columbia University in
* the City of New York. Permission is granted to any individual or
* institution to use, copy, or redistribute this software so long as it
* is not sold for profit, provided this copyright notice is retained.
*/
#ifndef lint
static char *rcsid = "$Header: /f/src2/encore.bin/cucca/mm/tarring-it-up/RCS/signals.c,v 2.1 90/10/04 18:26:46 melissa Exp $";
#endif
/*
* Signal handling support for MM.
*/
#include "mm.h"
#ifndef NSIG
#define NSIG 32
#endif
mmsigset_t signal_mask;
mmsigset_t deferred_signals;
signalhandler signal_handler();
/*
* Set up signal handlers
*/
int
init_signals ()
{
mmsigfillset(&signal_mask);
mmsigemptyset(&deferred_signals);
(void) mmsignal (SIGHUP, signal_handler);
(void) mmsignal (SIGALRM, signal_handler);
(void) mmsignal (SIGINT, signal_handler);
#ifdef SIGPIPE
(void) mmsignal (SIGPIPE, signal_handler);
#endif
#ifdef SIGTSTP
(void) mmsignal (SIGTSTP, signal_handler);
#endif
#ifdef SIGXCPU
(void) mmsignal (SIGXCPU, signal_handler);
#endif
#ifdef SIGCHLD
(void) mmsignal (SIGCHLD, signal_handler);
#endif
release_signals (&deferred_signals); /* Use empty set */
}
/*
* Block signals we want to lock out. On BSD systems this
* just calls sigblock(); on others, we maintain our own signal mask:
* the signal_handler routine will catch signals and remember they
* happened, so their handlers can be invoked by release_signals.
*/
void
block_signals (oldset)
mmsigset_t *oldset;
{
mmsigset_t osigs, sigs;
sigs = signal_mask;
(void) mmsigaddset(&sigs, SIGHUP);
(void) mmsigaddset(&sigs, SIGINT);
(void) mmsigaddset(&sigs, SIGALRM);
#ifdef SIGPIPE
(void) mmsigaddset(&sigs, SIGPIPE);
#endif
#ifdef SIGTSTP
(void) mmsigaddset(&sigs, SIGTSTP);
#endif
#ifdef SIGCONT
(void) mmsigaddset(&sigs, SIGCONT);
#endif
#ifdef SIGCHLD
(void) mmsigaddset(&sigs, SIGCHLD);
#endif
#ifdef SIGXCPU
(void) mmsigaddset(&sigs, SIGXCPU);
#endif
#if defined(HAVE_SIGSETS)
signal_mask = sigs;
(void) sigprocmask(SIG_BLOCK, &sigs, oldset);
#elif defined(HAVE_BSD_SIGNALS)
signal_mask = sigs;
*oldset = sigblock (signal_mask);
#else
*oldset = signal_mask;
signal_mask = sigs;
#endif
}
/*
* Restore the signal mask, and invoke handlers for any pending signals
*/
release_signals (set)
mmsigset_t *set;
{
#if defined(HAVE_BSD_SIGNALS)
(void) sigsetmask (*set);
#elif defined(HAVE_SIGSETS)
(void) sigprocmask(SIG_SETMASK, set, (sigset_t *)NULL);
#endif
signal_mask = *set;
#if 0 /* No trivial way to check this any more, so just invoke. */
if (deferred_signals)
#endif
run_deferred_signals ();
}
queue_signal (sig)
int sig;
{
(void) mmsigaddset(&deferred_signals, sig);
}
run_deferred_signals ()
{
int i;
for (i = 0; (i < NSIG); i++)
if (mmsigismember(&deferred_signals, i)
&& !mmsigismember(&signal_mask, i)) {
handle_signal (i);
}
}
/*
* This routine is the only one we install as a signal handler using
* signal(2). On BSD systems or those that support sigaction()
* it just calls handle_signal() and assumes that the signal handler is
* NOT reset.
* On other systems it may defer invocation of the handler if the signal
* has been disabled with block_signals, and assumes that the signal
* handler was reset to SIG_DFL (and thus, another mmsignal call is needed).
*/
signalhandler
signal_handler (sig)
int sig;
{
#if defined(HAVE_BSD_SIGNALS) || defined(HAVE_SIGSETS)
handle_signal (sig);
#else
if (mmsigismember(&signal_mask, sig)) {
queue_signal (sig);
if (sig != SIGCHLD)
mmsignal (sig, signal_handler);
return;
}
handle_signal (sig);
mmsignal (sig, signal_handler);
#endif
return;
}
/*
* Dispatch to handle signal SIG.
*/
handle_signal (sig)
int sig;
{
mmsigdelset(&deferred_signals, sig);
switch (sig) {
case SIGALRM:
new_mail (true);
break;
case SIGINT:
sigint_handler ();
break;
case SIGHUP:
sighup_handler ();
break;
#ifdef SIGPIPE
case SIGPIPE: /* ignore */
break;
#endif
#ifdef SIGCHLD
case SIGCHLD:
collect_process (0);
break;
#endif
#ifdef SIGXCPU
case SIGXCPU:
sigxcpu_handler ();
break;
#endif
#ifdef SIGTSTP
case SIGTSTP:
write (1, "\r\n", 2); /* move to new line */
suspend (0); /* suspend ourself */
break;
#endif
}
}
/*
* This routine should be called before and after any synchronous fork/wait
* sequences; arg should be "true" on the first call and "false" on the
* second.
*
* This routine serves to disable the SIGCHLD handler for callers who want
* to wait for children to exit, and saves/restores the tty process group
* for ill-behaved children who may change the tty process group and not
* change it back before suspending themselves or exiting (e.g. ksh).
*
*/
fix_signals_for_fork (before_the_fork)
int before_the_fork;
{
#ifdef SIGCHLD
static signalhandler (*old_chld_handler)() = 0;
#endif
signalhandler (*tmp)() = 0;
static int ttypgrp = -1, nesting = 0;
if (before_the_fork) {
if (nesting++ != 0)
return;
#ifdef TIOCGPGRP
if (isatty(2)) {
tmp = mmsignal (SIGTTIN, SIG_IGN);
(void) ioctl (0, TIOCGPGRP, &ttypgrp);
if (tmp != SIG_IGN)
mmsignal (SIGTTIN, tmp);
}
#endif
#ifdef SIGCHLD
old_chld_handler = mmsignal (SIGCHLD, SIG_DFL);
#endif
}
else {
#ifdef TIOCGPGRP
if (--nesting != 0)
return;
if (isatty(2) && ttypgrp > 0) {
tmp = mmsignal (SIGTTOU, SIG_IGN);
(void) ioctl (2, TIOCSPGRP, &ttypgrp);
if (tmp != SIG_IGN)
mmsignal (SIGTTOU, tmp);
}
ttypgrp = -1;
#endif
#ifdef SIGCHLD
(void) mmsignal (SIGCHLD, old_chld_handler);
#endif
}
}
/*
* SIGINT and SIGQUIT must be ignored around blocking wait calls.
* SIGTSTP should use the default signal handler.
*
* under solaris (maybe elsewhere?) ignore SIGCHLD while we are waiting,
* or the wait gets done by the signal handler, and we lose the return
* status.
*/
fix_signals_for_wait (before_the_wait)
int before_the_wait;
{
static nesting = 0;
static signalhandler (*old_handlers[4]) ();
if (before_the_wait) {
if (++nesting == 1) {
old_handlers[0] = mmsignal (SIGINT, SIG_IGN);
old_handlers[1] = mmsignal (SIGQUIT, SIG_IGN);
#ifdef SIGTSTP
old_handlers[2] = mmsignal (SIGTSTP, SIG_DFL);
#endif
#ifdef MM_OS_SOLARIS
old_handlers[3] = mmsignal (SIGCHLD, SIG_DFL);
#endif
}
}
else {
if (--nesting == 0) {
(void) mmsignal (SIGINT, old_handlers[0]);
(void) mmsignal (SIGQUIT, old_handlers[1]);
#ifdef SIGTSTP
(void) mmsignal (SIGTSTP, old_handlers[2]);
#endif
#ifdef MM_OS_SOLARIS
(void) mmsignal (SIGCHLD, old_handlers[3]);
#endif
}
}
}
/*
* mm_popen and mm_pclose:
* Since MM traps SIGCHLD (to catch whatever sendmails it sent into
* the background), it will wait() on the pipe process. In fact, when
* any process finishes, the SIGCHLD handler catches it. So, when
* pclose() does a wait, it never gets any processes. The wait (in
* pclose()) cannot return until all processes have sent their
* SIGCHLDs and been waited on by the SIGCHLD handler. This means
* that pclose() must wait() until all those really SLOW sendmail
* processes are done, which is pretty slow.
*
* Therefore, in order to guarantee that the pipe process is still
* around for pclose() to wait() on, we block or ignore all SIGCHLD
* signals before we do the popen(), and turn our SIGCHLD handler back
* on after the pclose() is done. (Note that this may allow pclose()
* to catch some of our sendmail processes, if they end before the
* pipe process, but we check for that in maybe_wait_for_process().
*/
static signalhandler (*old_handler)();
FILE *
mm_popen (command, type)
char *command, *type;
{
FILE *stream;
#ifdef SIGCHLD
old_handler = mmsignal (SIGCHLD, SIG_IGN);
#endif
if ((stream = popen(command, type)) == NULL)
#ifdef SIGCHLD
mmsignal (SIGCHLD, old_handler);
#endif
return (stream);
}
mm_pclose (stream)
FILE *stream;
{
int ret;
ret = pclose (stream);
#ifdef SIGCHLD
mmsignal (SIGCHLD, old_handler);
#endif
return (ret);
}
/* Implement our own version of signal().
* Needed because systems vary in how they implement this.
* NetBSD/FreeBSD: SA_RESTART on, SA_RESETHAND off
* Solaris: SA_RESTART off, SA_RESETHAND on
* Specifically:
* Solaris default sigaction flags are x20012:
* x2 = SA_RESETHAND
* x10 = SA_NODEFER
* x20000 = ??
* SunOS default sigaction flags are x0000C:
* x4 = SA_RESTART
* x8 = SA_SIGINFO
* Others: ???
* Ugh.
* MM was written assuming BSD settings that do NOT reset the handler
* when a signal is caught.
*/
signalhandler *
mmsignal(int sig, signalhandler *func)
{
#ifdef HAVE_SIGSETS
struct sigaction act, oact;
#ifndef SA_RESTART /* SunOS 4.1... (fdc) */
#define SA_RESTART 0
#endif /* SA_RESTART */
act.sa_handler = func;
act.sa_flags = SA_RESTART; /* Ensure SA_RESETHAND is off! */
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, sig); /* Suspend this sig during handler */
if (sigaction(sig, &act, &oact) == 0)
return oact.sa_handler;
return (signalhandler *)-1;
#else
return (signalhandler *)signal(sig, func);
#endif
}