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 >
C/C++ Source or Header  |  2002-09-16  |  9KB  |  390 lines

  1. /*
  2.  * Copyright (c) 1986, 2002 by The Trustees of Columbia University in
  3.  * the City of New York.  Permission is granted to any individual or
  4.  * institution to use, copy, or redistribute this software so long as it
  5.  * is not sold for profit, provided this copyright notice is retained.
  6.  */
  7.  
  8. #ifndef lint
  9. 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 $";
  10. #endif
  11.  
  12. /*
  13.  * Signal handling support for MM.
  14.  */
  15.  
  16. #include "mm.h"
  17.  
  18. #ifndef NSIG
  19. #define NSIG 32
  20. #endif
  21.  
  22. mmsigset_t signal_mask;
  23. mmsigset_t deferred_signals;
  24. signalhandler signal_handler();
  25.  
  26. /*
  27.  * Set up signal handlers
  28.  */
  29.  
  30. int
  31. init_signals ()
  32. {
  33.     mmsigfillset(&signal_mask);
  34.     mmsigemptyset(&deferred_signals);
  35.  
  36.     (void) mmsignal (SIGHUP, signal_handler);
  37.     (void) mmsignal (SIGALRM, signal_handler);
  38.     (void) mmsignal (SIGINT, signal_handler);
  39. #ifdef SIGPIPE
  40.     (void) mmsignal (SIGPIPE, signal_handler);
  41. #endif
  42. #ifdef SIGTSTP
  43.     (void) mmsignal (SIGTSTP, signal_handler);
  44. #endif
  45. #ifdef SIGXCPU
  46.     (void) mmsignal (SIGXCPU, signal_handler);
  47. #endif
  48. #ifdef SIGCHLD
  49.     (void) mmsignal (SIGCHLD, signal_handler);
  50. #endif
  51.     release_signals (&deferred_signals);    /* Use empty set */
  52. }
  53.  
  54. /*
  55.  * Block signals we want to lock out.  On BSD systems this
  56.  * just calls sigblock(); on others, we maintain our own signal mask:
  57.  * the signal_handler routine will catch signals and remember they
  58.  * happened, so their handlers can be invoked by release_signals.
  59.  */
  60.  
  61. void
  62. block_signals (oldset)
  63. mmsigset_t *oldset;
  64. {
  65.     mmsigset_t osigs, sigs;
  66.  
  67.     sigs = signal_mask;
  68.     (void) mmsigaddset(&sigs, SIGHUP);
  69.     (void) mmsigaddset(&sigs, SIGINT);
  70.     (void) mmsigaddset(&sigs, SIGALRM);
  71. #ifdef SIGPIPE
  72.     (void) mmsigaddset(&sigs, SIGPIPE);
  73. #endif
  74. #ifdef SIGTSTP
  75.     (void) mmsigaddset(&sigs, SIGTSTP);
  76. #endif
  77. #ifdef SIGCONT
  78.     (void) mmsigaddset(&sigs, SIGCONT);
  79. #endif
  80. #ifdef SIGCHLD
  81.     (void) mmsigaddset(&sigs, SIGCHLD);
  82. #endif
  83. #ifdef SIGXCPU
  84.     (void) mmsigaddset(&sigs, SIGXCPU);
  85. #endif
  86.  
  87. #if defined(HAVE_SIGSETS)
  88.     signal_mask = sigs;
  89.     (void) sigprocmask(SIG_BLOCK, &sigs, oldset);
  90. #elif defined(HAVE_BSD_SIGNALS)
  91.     signal_mask = sigs;
  92.     *oldset = sigblock (signal_mask);
  93. #else
  94.     *oldset = signal_mask;
  95.     signal_mask = sigs;
  96. #endif
  97. }
  98.  
  99. /*
  100.  * Restore the signal mask, and invoke handlers for any pending signals
  101.  */
  102. release_signals (set)
  103. mmsigset_t *set;
  104. {
  105. #if defined(HAVE_BSD_SIGNALS)
  106.     (void) sigsetmask (*set);
  107. #elif defined(HAVE_SIGSETS)
  108.     (void) sigprocmask(SIG_SETMASK, set, (sigset_t *)NULL);
  109. #endif
  110.     signal_mask = *set;
  111. #if 0    /* No trivial way to check this any more, so just invoke. */
  112.     if (deferred_signals)
  113. #endif
  114.     run_deferred_signals ();
  115. }
  116.  
  117. queue_signal (sig)
  118. int sig;
  119. {
  120.     (void) mmsigaddset(&deferred_signals, sig);
  121. }
  122.  
  123. run_deferred_signals ()
  124. {
  125.     int i;
  126.  
  127.     for (i = 0; (i < NSIG); i++)
  128.     if (mmsigismember(&deferred_signals, i)
  129.       && !mmsigismember(&signal_mask, i)) {
  130.         handle_signal (i);
  131.     }
  132. }
  133.  
  134. /*
  135.  * This routine is the only one we install as a signal handler using
  136.  * signal(2).  On BSD systems or those that support sigaction()
  137.  * it just calls handle_signal() and assumes that the signal handler is
  138.  * NOT reset.
  139.  * On other systems it may defer invocation of the handler if the signal
  140.  * has been disabled with block_signals, and assumes that the signal
  141.  * handler was reset to SIG_DFL (and thus, another mmsignal call is needed).
  142.  */
  143.  
  144. signalhandler
  145. signal_handler (sig)
  146. int sig;
  147. {
  148. #if defined(HAVE_BSD_SIGNALS) || defined(HAVE_SIGSETS)
  149.     handle_signal (sig);
  150. #else
  151.  
  152.     if (mmsigismember(&signal_mask, sig)) {
  153.     queue_signal (sig);
  154.     if (sig != SIGCHLD)
  155.         mmsignal (sig, signal_handler);
  156.     return;
  157.     }
  158.  
  159.     handle_signal (sig);
  160.     mmsignal (sig, signal_handler);
  161. #endif
  162.     return;
  163. }
  164.  
  165. /*
  166.  * Dispatch to handle signal SIG.
  167.  */
  168.  
  169. handle_signal (sig)
  170. int sig;
  171. {
  172.     mmsigdelset(&deferred_signals, sig);
  173.  
  174.     switch (sig) {
  175.       case SIGALRM:
  176.     new_mail (true);
  177.     break;
  178.       case SIGINT:
  179.     sigint_handler ();
  180.     break;
  181.       case SIGHUP:
  182.     sighup_handler ();
  183.     break;
  184. #ifdef SIGPIPE
  185.       case SIGPIPE:            /* ignore */
  186.     break;
  187. #endif
  188. #ifdef SIGCHLD
  189.       case SIGCHLD:
  190.     collect_process (0);
  191.     break;
  192. #endif
  193. #ifdef SIGXCPU
  194.       case SIGXCPU:
  195.     sigxcpu_handler ();
  196.     break;
  197. #endif
  198. #ifdef SIGTSTP
  199.       case SIGTSTP:
  200.     write (1, "\r\n", 2);        /* move to new line */
  201.     suspend (0);            /* suspend ourself */
  202.     break;
  203. #endif
  204.     }
  205. }
  206.  
  207. /*
  208.  * This routine should be called before and after any synchronous fork/wait
  209.  * sequences; arg should be "true" on the first call and "false" on the
  210.  * second.
  211.  *
  212.  * This routine serves to disable the SIGCHLD handler for callers who want
  213.  * to wait for children to exit, and saves/restores the tty process group
  214.  * for ill-behaved children who may change the tty process group and not
  215.  * change it back before suspending themselves or exiting (e.g. ksh).
  216.  *
  217.  */
  218.  
  219. fix_signals_for_fork (before_the_fork)
  220. int before_the_fork;
  221. {
  222. #ifdef SIGCHLD
  223.     static signalhandler (*old_chld_handler)() = 0;
  224. #endif
  225.     signalhandler (*tmp)() = 0;
  226.     static int ttypgrp = -1, nesting = 0;
  227.     if (before_the_fork) {
  228.     if (nesting++ != 0)
  229.         return;
  230. #ifdef TIOCGPGRP
  231.     if (isatty(2)) {
  232.         tmp = mmsignal (SIGTTIN, SIG_IGN);
  233.         (void) ioctl (0, TIOCGPGRP, &ttypgrp);
  234.         if (tmp != SIG_IGN)
  235.         mmsignal (SIGTTIN, tmp);
  236.     }
  237. #endif
  238. #ifdef SIGCHLD
  239.     old_chld_handler = mmsignal (SIGCHLD, SIG_DFL);
  240. #endif
  241.     }
  242.     else {
  243. #ifdef TIOCGPGRP
  244.     if (--nesting != 0)
  245.         return;
  246.     if (isatty(2) && ttypgrp > 0) {
  247.         tmp = mmsignal (SIGTTOU, SIG_IGN);
  248.         (void) ioctl (2, TIOCSPGRP, &ttypgrp);
  249.         if (tmp != SIG_IGN)
  250.         mmsignal (SIGTTOU, tmp);
  251.     }
  252.     ttypgrp = -1;
  253. #endif
  254. #ifdef SIGCHLD
  255.     (void) mmsignal (SIGCHLD, old_chld_handler);
  256. #endif
  257.     }
  258. }
  259.  
  260. /*
  261.  * SIGINT and SIGQUIT must be ignored around blocking wait calls.
  262.  * SIGTSTP should use the default signal handler.
  263.  *
  264.  * under solaris (maybe elsewhere?) ignore SIGCHLD while we are waiting,
  265.  * or the wait gets done by the signal handler, and we lose the return
  266.  * status.
  267.  */
  268.  
  269. fix_signals_for_wait (before_the_wait)
  270. int before_the_wait;
  271. {
  272.     static nesting = 0;
  273.     static signalhandler (*old_handlers[4]) ();
  274.     
  275.     if (before_the_wait) {
  276.     if (++nesting == 1) {
  277.         old_handlers[0] = mmsignal (SIGINT, SIG_IGN);
  278.         old_handlers[1] = mmsignal (SIGQUIT, SIG_IGN);
  279. #ifdef SIGTSTP
  280.         old_handlers[2] = mmsignal (SIGTSTP, SIG_DFL);
  281. #endif
  282. #ifdef MM_OS_SOLARIS
  283.         old_handlers[3] = mmsignal (SIGCHLD, SIG_DFL);
  284. #endif
  285.     }
  286.     }
  287.     else {
  288.     if (--nesting == 0) {
  289.         (void) mmsignal (SIGINT, old_handlers[0]);
  290.         (void) mmsignal (SIGQUIT, old_handlers[1]);
  291. #ifdef SIGTSTP
  292.         (void) mmsignal (SIGTSTP, old_handlers[2]);
  293. #endif
  294. #ifdef MM_OS_SOLARIS
  295.         (void) mmsignal (SIGCHLD, old_handlers[3]);
  296. #endif
  297.     }
  298.     }
  299. }
  300.  
  301. /*
  302.  * mm_popen and mm_pclose:
  303.  
  304.  * Since MM traps SIGCHLD (to catch whatever sendmails it sent into
  305.  * the background), it will wait() on the pipe process.  In fact, when
  306.  * any process finishes, the SIGCHLD handler catches it.  So, when
  307.  * pclose() does a wait, it never gets any processes.  The wait (in
  308.  * pclose()) cannot return until all processes have sent their
  309.  * SIGCHLDs and been waited on by the SIGCHLD handler.  This means
  310.  * that pclose() must wait() until all those really SLOW sendmail
  311.  * processes are done, which is pretty slow.
  312.  * 
  313.  * Therefore, in order to guarantee that the pipe process is still
  314.  * around for pclose() to wait() on, we block or ignore all SIGCHLD
  315.  * signals before we do the popen(), and turn our SIGCHLD handler back
  316.  * on after the pclose() is done.  (Note that this may allow pclose()
  317.  * to catch some of our sendmail processes, if they end before the
  318.  * pipe process, but we check for that in maybe_wait_for_process().
  319.  */
  320.  
  321. static signalhandler (*old_handler)();
  322.  
  323. FILE *
  324. mm_popen (command, type)
  325. char *command, *type;
  326. {
  327.     FILE *stream;
  328.  
  329. #ifdef SIGCHLD
  330.     old_handler = mmsignal (SIGCHLD, SIG_IGN);
  331. #endif
  332.     if ((stream = popen(command, type)) == NULL)
  333. #ifdef SIGCHLD
  334.     mmsignal (SIGCHLD, old_handler);
  335. #endif
  336.     return (stream);
  337. }
  338.  
  339. mm_pclose (stream)
  340. FILE *stream;
  341. {
  342.     int ret;
  343.  
  344.     ret = pclose (stream);
  345. #ifdef SIGCHLD
  346.     mmsignal (SIGCHLD, old_handler);
  347. #endif
  348.     return (ret);
  349. }
  350.  
  351.  
  352. /* Implement our own version of signal().
  353.  * Needed because systems vary in how they implement this.
  354.  * NetBSD/FreeBSD: SA_RESTART on, SA_RESETHAND off
  355.  *      Solaris: SA_RESTART off, SA_RESETHAND on
  356.  * Specifically:
  357.  *    Solaris default sigaction flags are x20012:
  358.  *        x2 = SA_RESETHAND
  359.  *        x10 = SA_NODEFER
  360.  *        x20000 = ??
  361.  *    SunOS default sigaction flags are x0000C:
  362.  *        x4 = SA_RESTART
  363.  *        x8 = SA_SIGINFO
  364.  *     Others: ???
  365.  * Ugh.
  366.  * MM was written assuming BSD settings that do NOT reset the handler
  367.  * when a signal is caught.
  368.  */
  369. signalhandler *
  370. mmsignal(int sig, signalhandler *func)
  371. {
  372. #ifdef HAVE_SIGSETS
  373.   struct sigaction act, oact;
  374.  
  375. #ifndef SA_RESTART            /* SunOS 4.1... (fdc) */
  376. #define SA_RESTART 0
  377. #endif /* SA_RESTART */
  378.  
  379.   act.sa_handler = func;
  380.   act.sa_flags = SA_RESTART;        /* Ensure SA_RESETHAND is off! */
  381.   sigemptyset(&act.sa_mask);
  382.   sigaddset(&act.sa_mask, sig);        /* Suspend this sig during handler */
  383.   if (sigaction(sig, &act, &oact) == 0)
  384.     return oact.sa_handler;
  385.   return (signalhandler *)-1;
  386. #else
  387.   return (signalhandler *)signal(sig, func);
  388. #endif
  389. }
  390.