home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tclX6.5c / src / tclXsignal.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-12-19  |  30.5 KB  |  1,052 lines

  1. /*
  2.  * tclXsignal.c --
  3.  *
  4.  * Tcl Unix signal support routines and the signal and commands.
  5.  *-----------------------------------------------------------------------------
  6.  * Copyright 1992 Karl Lehenbauer and Mark Diekhans.
  7.  *
  8.  * Permission to use, copy, modify, and distribute this software and its
  9.  * documentation for any purpose and without fee is hereby granted, provided
  10.  * that the above copyright notice appear in all copies.  Karl Lehenbauer and
  11.  * Mark Diekhans make no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without express or
  13.  * implied warranty.
  14.  *-----------------------------------------------------------------------------
  15.  * $Id: tclXsignal.c,v 2.2 1992/12/19 21:21:14 markd Exp $
  16.  *-----------------------------------------------------------------------------
  17.  */
  18.  
  19. #include "tclExtdInt.h"
  20.  
  21.  
  22. #ifndef SIGCLD
  23. #   define SIGCLD SIGCHLD
  24. #endif
  25. #ifndef SIGCHLD
  26. #   define SIGCHLD SIGCLD
  27. #endif
  28.  
  29. #ifndef MAXSIG
  30. #    define MAXSIG 32
  31. #endif
  32.  
  33. /*
  34.  * Signal name table maps name to number.
  35.  */
  36.  
  37. #define SIG_NAME_MAX 7
  38.  
  39. static struct {char *name;
  40.         short num;
  41.        } sigNameTable [] = {
  42.     "HUP",     SIGHUP,
  43.     "INT",     SIGINT,
  44.     "QUIT",    SIGQUIT,
  45.     "ILL",     SIGILL,
  46.     "TRAP",    SIGTRAP,
  47.     "IOT",     SIGIOT,
  48. #ifdef SIGABRT
  49.     "ABRT",    SIGABRT,
  50. #endif
  51.     "EMT",     SIGEMT,
  52.     "FPE",     SIGFPE,
  53.     "KILL",    SIGKILL,
  54.     "BUS",     SIGBUS,
  55.     "SEGV",    SIGSEGV,
  56.     "SYS",     SIGSYS,
  57.     "PIPE",    SIGPIPE,
  58.     "ALRM",    SIGALRM,
  59.     "TERM",    SIGTERM,
  60.     "USR1",    SIGUSR1,
  61.     "USR2",    SIGUSR2,
  62.     "CLD",     SIGCLD,
  63.     "CHLD",    SIGCHLD,
  64. #ifdef SIGPWR
  65.     "PWR",     SIGPWR,
  66. #endif
  67. #ifdef SIGPOLL
  68.     "POLL",    SIGPOLL,
  69. #endif
  70. #ifdef SIGSTOP
  71.     "STOP",    SIGSTOP,
  72. #endif
  73. #ifdef SIGTSTP
  74.     "TSTP",    SIGTSTP,
  75. #endif
  76. #ifdef SIGCONT
  77.     "CONT",    SIGCONT,
  78. #endif
  79. #ifdef SIGTTIN
  80.     "TTIN",    SIGTTIN,
  81. #endif
  82. #ifdef SIGTTOU
  83.     "TTOU",    SIGTTOU,
  84. #endif
  85.     NULL,         -1};
  86.  
  87. #ifdef TCL_SIG_PROC_INT
  88. #   define SIG_PROC_RET_TYPE int
  89. #else
  90. #   define SIG_PROC_RET_TYPE void
  91. #endif
  92.  
  93. typedef SIG_PROC_RET_TYPE (*signalProcPtr_t) _ANSI_ARGS_((int));
  94.  
  95. /*
  96.  * Include for 386BSD, since they don't have SIG_ERR right now.
  97.  */
  98.  
  99. #if defined(BADSIG) && !defined(SIG_ERR)
  100. #    define SIG_ERR BADSIG
  101. #endif
  102.  
  103.  
  104. /*
  105.  * Class of actions that can be set by the signal command.
  106.  */
  107. #define SIGACT_SET     1   /* Set the signal     */
  108. #define SIGACT_GET     2   /* Get the signal     */
  109. #define SIGACT_BLOCK   3   /* Block the signal   */
  110. #define SIGACT_UNBLOCK 4   /* Unblock the signal */
  111.  
  112. /*
  113.  * Defines if this is not Posix.
  114.  */
  115. #ifndef SIG_BLOCK
  116. #   define SIG_BLOCK       1
  117. #   define SIG_UNBLOCK     2
  118. #endif
  119.  
  120. /*
  121.  * Messages.
  122.  */
  123. static char *noPosix = "Posix signals are not available on this system";
  124.  
  125. /*
  126.  * Globals that indicate that some signal was received and how many of each
  127.  * signal type has not yet been processed.
  128.  */
  129. int             tclReceivedSignal = FALSE;    /* A signal was received */ 
  130. static unsigned signalsReceived [MAXSIG];     /* Counters of signals   */
  131.  
  132. /*
  133.  * Table of commands to evaluate when a signal occurs.  If the command is
  134.  * NULL and the signal is received, an error is returned.
  135.  */
  136. static char *signalTrapCmds [MAXSIG];
  137.  
  138. /*
  139.  * Prototypes of internal functions.
  140.  */
  141.  
  142. static int
  143. SigNameToNum _ANSI_ARGS_((char *sigName));
  144.  
  145. static signalProcPtr_t
  146. GetSignalState _ANSI_ARGS_((int signalNum));
  147.  
  148. static int
  149. SetSignalAction _ANSI_ARGS_((int             signalNum,
  150.                              signalProcPtr_t sigFunc));
  151.  
  152. static SIG_PROC_RET_TYPE
  153. TclSignalTrap _ANSI_ARGS_((int signalNum));
  154.  
  155. static int
  156. EvalTrapCode _ANSI_ARGS_((Tcl_Interp *interp,
  157.                           int         signalNum,
  158.                           char       *command));
  159.  
  160. static int
  161. ParseSignalList _ANSI_ARGS_((Tcl_Interp *interp,
  162.                              char       *signalListStr,
  163.                              int         signalList []));
  164.  
  165. static char *
  166. SignalBlocked _ANSI_ARGS_((Tcl_Interp  *interp,
  167.                            int          signalNum));
  168.  
  169. static int
  170. GetSignalStates  _ANSI_ARGS_((Tcl_Interp *interp,
  171.                               int         signalListSize,
  172.                               int         signalList [MAXSIG]));
  173.  
  174. static int
  175. SetSignalStates  _ANSI_ARGS_((Tcl_Interp      *interp,
  176.                               int              signalListSize,
  177.                               int              signalList [MAXSIG],
  178.                               signalProcPtr_t  actionFunc,
  179.                               char            *command));
  180.  
  181. static int
  182. BlockSignals _ANSI_ARGS_((Tcl_Interp  *interp,
  183.                           int          action,
  184.                           int          signalListSize,
  185.                           int          signalList [MAXSIG]));
  186.  
  187. static void
  188. SignalCmdCleanUp _ANSI_ARGS_((ClientData clientData));
  189.  
  190.  
  191. /*
  192.  *-----------------------------------------------------------------------------
  193.  *
  194.  * SigNameToNum --
  195.  *     Converts a UNIX signal name to its number, returns -1 if not found.
  196.  *     the name may be upper or lower case and may optionally have the 
  197.  *     leading "SIG" omitted.
  198.  *
  199.  *-----------------------------------------------------------------------------
  200.  */
  201. static int
  202. SigNameToNum (sigName)
  203.     char *sigName;
  204. {
  205.     char  sigNameUp [SIG_NAME_MAX+1];  /* Upshifted signal name */
  206.     char *sigNamePtr; 
  207.     int   idx;
  208.  
  209.     /*
  210.      * Copy and upshift requested name.
  211.      */
  212.  
  213.     if (strlen (sigName) > SIG_NAME_MAX)
  214.         return -1;   /* Name too long */
  215.  
  216.     Tcl_UpShift (sigNameUp, sigName);
  217.  
  218.     if (STRNEQU (sigNameUp, "SIG", 3))
  219.         sigNamePtr = &sigNameUp [3];
  220.     else
  221.         sigNamePtr = sigNameUp;
  222.  
  223.     for (idx = 0; sigNameTable [idx].num != -1; idx++)
  224.         if (STREQU (sigNamePtr, sigNameTable [idx].name))
  225.             break;
  226.  
  227.     return sigNameTable [idx].num;
  228. }
  229.  
  230. /*
  231.  *-----------------------------------------------------------------------------
  232.  *
  233.  * Tcl_KillCmd --
  234.  *     Implements the TCL kill command:
  235.  *        kill [signal] proclist
  236.  *
  237.  * Results:
  238.  *  Standard TCL results, may return the UNIX system error message.
  239.  *
  240.  *-----------------------------------------------------------------------------
  241.  */
  242. int
  243. Tcl_KillCmd (clientData, interp, argc, argv)
  244.     ClientData  clientData;
  245.     Tcl_Interp *interp;
  246.     int     argc;
  247.     char      **argv;
  248. {
  249.     int    signalNum, idx, procId, procArgc, result = TCL_ERROR;
  250.     char **procArgv;
  251.  
  252.     if ((argc < 2) || (argc > 3)) {
  253.         Tcl_AppendResult (interp, tclXWrongArgs, argv [0], 
  254.                           " [signal] processlist", (char *) NULL);
  255.         return TCL_ERROR;
  256.     }
  257.  
  258.     if (argc == 2)
  259.         signalNum = SIGTERM;
  260.     else {
  261.         if (!Tcl_StrToInt (argv[1], 0, &signalNum)) {
  262.             signalNum = SigNameToNum (argv[1]);
  263.         }
  264.         if ((signalNum < 0) || (signalNum > NSIG)) {
  265.             Tcl_AppendResult (interp, "invalid signal", (char *) NULL);
  266.             return TCL_ERROR;
  267.         }
  268.     }
  269.  
  270.     if (Tcl_SplitList (interp, argv [argc - 1], &procArgc, 
  271.                        &procArgv) != TCL_OK)
  272.         return TCL_ERROR;
  273.  
  274.     for (idx = 0; idx < procArgc; idx++) {
  275.  
  276.         if (Tcl_GetInt (interp, procArgv [idx], &procId) != TCL_OK)
  277.             goto exitPoint;
  278.  
  279.         if (kill ((pid_t) procId, signalNum) < 0) {
  280.             Tcl_AppendResult (interp, "pid ", procArgv [idx],
  281.                               ": ", Tcl_UnixError (interp), (char *) NULL);
  282.             goto exitPoint;
  283.         }
  284.      }
  285.  
  286.     result = TCL_OK;
  287. exitPoint:
  288.     ckfree ((char *) procArgv);
  289.     return result;
  290. }
  291.  
  292. /*
  293.  *-----------------------------------------------------------------------------
  294.  *
  295.  * GetSignalState --
  296.  *     Get the current state of the specified signal.
  297.  * Parameters:
  298.  *   o signalNum (I) - Signal number to query.
  299.  * Results
  300.  *   The signal function or SIG_DFL or SIG_IGN.  If an error occures,
  301.  *   SIG_ERR is returned (check errno);
  302.  *-----------------------------------------------------------------------------
  303.  */
  304. static signalProcPtr_t
  305. GetSignalState (signalNum)
  306.     int signalNum;
  307. {
  308. #ifdef TCL_POSIX_SIG
  309.     struct sigaction currentState;
  310.  
  311.     if (sigaction (signalNum, NULL, ¤tState) < 0)
  312.         return SIG_ERR;
  313.     return currentState.sa_handler;
  314. #else
  315.     signalProcPtr_t  actionFunc;
  316.  
  317.     if (signalNum == SIGKILL)
  318.         return SIG_DFL;
  319.  
  320.     actionFunc = signal (signalNum, SIG_DFL);
  321.     if (actionFunc == SIG_ERR)
  322.         return SIG_ERR;
  323.     if (actionFunc != SIG_DFL)
  324.         signal (signalNum, actionFunc);  /* reset */
  325.     return actionFunc;
  326. #endif
  327. }
  328.  
  329. /*
  330.  *-----------------------------------------------------------------------------
  331.  *
  332.  * SetSignalAction --
  333.  *     Set the action to occur when a signal is received.
  334.  * Parameters:
  335.  *   o signalNum (I) - Signal number to query.
  336.  *   o sigFunc (O) - The signal function or SIG_DFL or SIG_IGN.
  337.  * Results
  338.  *   TRUE if ok,  FALSE if an error (check errno).
  339.  *-----------------------------------------------------------------------------
  340.  */
  341. static int
  342. SetSignalAction (signalNum, sigFunc)
  343.     int             signalNum;
  344.     signalProcPtr_t sigFunc;
  345. {
  346. #ifdef TCL_POSIX_SIG
  347.     struct sigaction newState;
  348.     
  349.     newState.sa_handler = sigFunc;
  350.     sigfillset (&newState.sa_mask);
  351.     newState.sa_flags = 0;
  352.  
  353.     if (sigaction (signalNum, &newState, NULL) < 0)
  354.         return FALSE;
  355.  
  356.     return TRUE;
  357. #else
  358.     if (signal (signalNum, sigFunc) == SIG_ERR)
  359.         return FALSE;
  360.     else
  361.         return TRUE;
  362. #endif
  363. }
  364.  
  365. /*
  366.  *-----------------------------------------------------------------------------
  367.  *
  368.  * TclSignalTrap --
  369.  *     Trap handler for UNIX signals.  Sets a flag indicating that the
  370.  *     trap has occured, saves the name and rearms the trap.  The flag
  371.  *     will be seen by the interpreter when its safe to trap.
  372.  * Globals:
  373.  *   o tclReceivedSignal (O) - Set to TRUE, to indicate a signal was received.
  374.  *   o signalsReceived (O) - The count of each signal that was received.
  375.  *-----------------------------------------------------------------------------
  376.  */
  377. static SIG_PROC_RET_TYPE
  378. TclSignalTrap (signalNum)
  379.     int signalNum;
  380. {
  381.     /*
  382.      * Set flags that are checked by the eval loop.
  383.      */
  384.     signalsReceived [signalNum]++;
  385.     tclReceivedSignal = TRUE;
  386.  
  387. #ifndef TCL_POSIX_SIG
  388.     /*
  389.      * For old-style Unix signals, the signal must be explictly re-enabled.
  390.      * Not done for SIGCHLD, as we would continue to the signal until the
  391.      * wait is done.  This is fixed by Posix signals and is not necessary under
  392.      * BSD, but it done this way for consistency.
  393.      */
  394.     if (signalNum != SIGCHLD) {
  395.         if (SetSignalAction (signalNum, TclSignalTrap) < 0)
  396.             panic ("TclSignalTrap bug");
  397.     }
  398. #endif
  399. }
  400.  
  401. /*
  402.  *-----------------------------------------------------------------------------
  403.  *
  404.  * EvalTrapCode --
  405.  *     Run code as the result of a signal.  The code will be run in the
  406.  *     global context, with the symbolic signal name in a global variable.
  407.  *     signalReceived.  If an error occured, then the result will be
  408.  *     left in the interp, if no error occured, the result will be reset.
  409.  * Parameters:
  410.  *   o interp (I/O) - The interpreter to run the signal in.
  411.  *   o signalNum (I) - The signal number of the signal that occured.
  412.  *   o command (I) - The command string to execute.
  413.  * Return:
  414.  *   TCL_OK or TCL_ERROR.
  415.  *-----------------------------------------------------------------------------
  416.  */
  417. static int
  418. EvalTrapCode (interp, signalNum, command)
  419.     Tcl_Interp *interp;
  420.     int         signalNum;
  421.     char       *command;
  422. {
  423.     Interp        *iPtr = (Interp *) interp;
  424.     char          *signalName;
  425.     int            result;
  426.     CallFrame     *savedVarFramePtr;
  427.  
  428.     Tcl_ResetResult (interp);
  429.  
  430.     /*
  431.      * Modify the interpreter state to execute in the global frame.
  432.      */
  433.     savedVarFramePtr = iPtr->varFramePtr;
  434.     iPtr->varFramePtr = NULL;
  435.  
  436.     /*
  437.      * Force name to always be SIGCHLD, even if system defines only SIGCLD.
  438.      */
  439.     if (signalNum == SIGCHLD)
  440.         signalName = "SIGCHLD";
  441.     else
  442.         signalName = Tcl_SignalId (signalNum);
  443.  
  444.     if (Tcl_SetVar (interp, "signalReceived", signalName,
  445.                     TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL)
  446.         result = TCL_ERROR;
  447.     else
  448.         result = TCL_OK;
  449.     if (result == TCL_OK);
  450.         result = Tcl_Eval (interp, signalTrapCmds [signalNum], 0, NULL);
  451.  
  452.     /*
  453.      * Restore the frame pointer and return the result (only OK or ERROR).
  454.      */
  455.     iPtr->varFramePtr = savedVarFramePtr;
  456.  
  457.     if (result == TCL_ERROR) {
  458.         char errorInfo [TCL_RESULT_SIZE];
  459.  
  460.         sprintf (errorInfo, "\n    while executing signal trap code for %s%s",
  461.                  signalName, " signal");
  462.         Tcl_AddErrorInfo (interp, errorInfo);
  463.  
  464.         return TCL_ERROR;
  465.     } else {
  466.         Tcl_ResetResult (interp);
  467.         return TCL_OK;
  468.     }
  469. }
  470.  
  471. /*
  472.  *-----------------------------------------------------------------------------
  473.  *
  474.  * Tcl_ResetSignals --
  475.  *  
  476.  *   Reset all of the signal flags to indicate that no signals have 
  477.  * occured.  This is used by the shell at the beginning of each interactive
  478.  * command
  479.  *
  480.  * Globals:
  481.  *   o tclReceivedSignal (O) - Will be cleared.
  482.  *   o signalsReceived (O) - The count of each signal that was received.
  483.  *-----------------------------------------------------------------------------
  484.  */
  485. void
  486. Tcl_ResetSignals ()
  487. {
  488.     int  signalNum;
  489.  
  490.     tclReceivedSignal = 0;
  491.     for (signalNum = 0; signalNum < MAXSIG; signalNum++) 
  492.         signalsReceived [signalNum] = 0;
  493.  
  494. }
  495.  
  496. /*
  497.  *-----------------------------------------------------------------------------
  498.  *
  499.  * Tcl_CheckForSignal --
  500.  *  
  501.  *   Called by Tcl_Eval to check if a signal was received when Tcl_Eval is in
  502.  * a safe state.  If the signal was received, this handles processing the
  503.  * signal prehaps recursively eval-ing some code.  This is called just after a
  504.  * command completes.  The results of the command are passed to this procedure
  505.  * and may be altered by it.  If trap code is specified for the signal that
  506.  * was received, then the trap will be executed, otherwise an error result
  507.  * will be returned indicating that the signal occured.  If an error is
  508.  * returned, clear the errorInfo variable.  This makes sure it exists and
  509.  * that it is empty, otherwise bogus or non-existant information will be
  510.  * returned if this routine was called somewhere besides Tcl_Eval.  If a
  511.  * signal was received multiple times and a trap is set on it, then that
  512.  * trap will be executed for each time the signal was received.
  513.  * 
  514.  * Parameters:
  515.  *   o interp (I/O) - interp->result should contain the result for
  516.  *     the command that just executed.  This will either be restored or
  517.  *     replaced with a new result.
  518.  *   o cmdResultCode (I) - The integer result returned by the command that
  519.  *     Tcl_Eval just completed.  Should be TCL_OK if not called from
  520.  *     Tcl_Eval.
  521.  * Globals:
  522.  *   o tclReceivedSignal (I/O) - Will be cleared.
  523.  *   o signalsReceived (I/O) - The count of each signal that was received.
  524.  * Returns:
  525.  *   Either the original result code, an error result if one of the
  526.  *   trap commands returned an error, or an error indicating the
  527.  *   a signal occured.
  528.  *-----------------------------------------------------------------------------
  529.  */
  530. int
  531. Tcl_CheckForSignal (interp, cmdResultCode)
  532.     Tcl_Interp *interp;
  533.     int         cmdResultCode;
  534. {
  535.     char   *savedResult;
  536.     int     signalNum, result, sigCnt, retErrorForSignal = -1;
  537.  
  538.     if (!tclReceivedSignal)
  539.         return cmdResultCode;  /* No signal received */
  540.  
  541.     savedResult = ckalloc (strlen (interp->result) + 1);
  542.     strcpy (savedResult, interp->result);
  543.     Tcl_ResetResult (interp);
  544.  
  545.     for (signalNum = 1; signalNum < MAXSIG; signalNum++) {
  546.         if (signalsReceived [signalNum] == 0)
  547.             continue;
  548.         
  549.         if (signalTrapCmds [signalNum] == NULL) {
  550.             retErrorForSignal = signalNum;
  551.             signalsReceived [signalNum] = 0;
  552.         } else {
  553.             sigCnt = signalsReceived [signalNum];
  554.             signalsReceived [signalNum] = 0;
  555.             
  556.             while (sigCnt-- > 0) {
  557.                 result = EvalTrapCode (interp, signalNum,
  558.                                        signalTrapCmds [signalNum]);
  559.                 if (result == TCL_ERROR)
  560.                     goto exitPoint;
  561.             }
  562.         }
  563.     }
  564.  
  565.     if (retErrorForSignal >= 0) {
  566.         char *signalName;
  567.  
  568.         /*
  569.          * Force name to always be SIGCHLD, even if system defines only SIGCLD.
  570.          */
  571.         if (retErrorForSignal == SIGCHLD)
  572.             signalName = "SIGCHLD";
  573.         else
  574.             signalName = Tcl_SignalId (retErrorForSignal);
  575.  
  576.         Tcl_SetErrorCode (interp, "UNIX", "SIG", signalName, (char*) NULL);
  577.         Tcl_AppendResult (interp, signalName, " signal received", 
  578.                           (char *)NULL);
  579.         Tcl_SetVar (interp, "errorInfo", "", TCL_GLOBAL_ONLY);
  580.         result = TCL_ERROR;
  581.     } else {
  582.         Tcl_SetResult (interp, savedResult, TCL_DYNAMIC);
  583.         savedResult = NULL;
  584.         result = cmdResultCode;
  585.     }
  586.  
  587. exitPoint:
  588.     if (savedResult != NULL)
  589.         ckfree (savedResult);
  590.     /*
  591.      * An error might have caused clearing of some signal flags to be missed.
  592.      */
  593.     Tcl_ResetSignals ();
  594.     return result;
  595. }
  596.  
  597. /*
  598.  *-----------------------------------------------------------------------------
  599.  *
  600.  * ParseSignalList --
  601.  *  
  602.  *   Parse a list of signal names or numbers.
  603.  * 
  604.  * Parameters:
  605.  *   o interp (O) - Interpreter for returning errors.
  606.  *   o signalListStr (I) - The Tcl list of signals to convert.
  607.  *   o signalList (O) - The list of converted signal numbers, must be
  608.  *     big enough to hold MAXSIG signals.
  609.  *     Tcl_Eval just completed.
  610.  * Returns:
  611.  *   The number of signals converted, or -1 if an error occures.
  612.  *-----------------------------------------------------------------------------
  613.  */
  614. static int
  615. ParseSignalList (interp, signalListStr, signalList)
  616.     Tcl_Interp *interp;
  617.     char       *signalListStr;
  618.     int         signalList [];
  619. {
  620.     char         **signalListArgv;
  621.     int            signalListSize, signalNum, idx;
  622.     int            result = -1;
  623.     char          *signalName;
  624.  
  625.     if (Tcl_SplitList (interp, signalListStr, &signalListSize, 
  626.                        &signalListArgv) != TCL_OK)
  627.         return -1;
  628.  
  629.     if (signalListSize > MAXSIG) {
  630.         Tcl_AppendResult (interp, "too many signals supplied in list",
  631.                           (char *) NULL);
  632.         goto exitPoint;
  633.     }
  634.  
  635.     if (signalListSize == 0) {
  636.         Tcl_AppendResult (interp, "signal list may not be empty",
  637.                           (char *) NULL);
  638.         goto exitPoint;
  639.     }
  640.  
  641.     for (idx = 0; idx < signalListSize; idx++) {
  642.         signalName = signalListArgv [idx];
  643.  
  644.         if (Tcl_StrToInt (signalName, 0, &signalNum))
  645.             signalName = Tcl_SignalId (signalNum);
  646.         else
  647.             signalNum = SigNameToNum (signalName);
  648.  
  649.         if (signalName == NULL) {
  650.             char numBuf [20];
  651.  
  652.             sprintf (numBuf, "%d", signalNum);
  653.             Tcl_AppendResult (interp, "invalid signal number: ",
  654.                               numBuf, (char *) NULL);
  655.             goto exitPoint;
  656.         }
  657.  
  658.         if ((signalNum < 1) || (signalNum > NSIG)) {
  659.             Tcl_AppendResult (interp, "invalid signal name: ",
  660.                               signalName, (char *) NULL);
  661.             goto exitPoint;
  662.         }
  663.         signalList [idx] = signalNum;
  664.     }
  665.  
  666.     result = signalListSize;
  667. exitPoint:
  668.     ckfree ((char *) signalListArgv);
  669.     return result;
  670.  
  671. }
  672.  
  673. /*
  674.  *-----------------------------------------------------------------------------
  675.  *
  676.  * SignalBlocked --
  677.  *     
  678.  *    Determine if a signal is blocked.  On non-Posix systems, always returns
  679.  * "0".
  680.  *
  681.  * Parameters::
  682.  *   o interp (O) - Error messages are returned in result.
  683.  *   o signalNum (I) - The signal to determine the state for.
  684.  * Returns:
  685.  *   NULL if an error occured, or a pointer to a static string of "1" if the
  686.  * signal is block, and a static string of "0" if it is not blocked.
  687.  *-----------------------------------------------------------------------------
  688.  */
  689. static char *
  690. SignalBlocked (interp, signalNum)
  691.     Tcl_Interp  *interp;
  692.     int          signalNum;
  693. {
  694. #ifdef TCL_POSIX_SIG
  695.     int      idx;
  696.     sigset_t sigBlockSet;
  697.  
  698.     if (sigprocmask (SIG_BLOCK, NULL, &sigBlockSet)) {
  699.         interp->result = Tcl_UnixError (interp);
  700.         return NULL;
  701.     }
  702.     return sigismember (&sigBlockSet, signalNum) ? "1" : "0";
  703. #else
  704.     return "0";
  705. #endif
  706. }
  707.  
  708. /*
  709.  *-----------------------------------------------------------------------------
  710.  *
  711.  * GetSignalStates --
  712.  *     
  713.  *    Return a keyed list containing the signal states for the specified
  714.  * signals.
  715.  *
  716.  * Parameters::
  717.  *   o interp (O) - The list is returned in the result.
  718.  *   o signalListSize (I) - Number of signals in the signal list.
  719.  *   o signalList (I) - List of signals of requested signals.
  720.  * Returns:
  721.  *   TCL_OK or TCL_ERROR, with error message in interp.
  722.  *-----------------------------------------------------------------------------
  723.  */
  724. static int
  725. GetSignalStates (interp, signalListSize, signalList)
  726.     Tcl_Interp *interp;
  727.     int         signalListSize;
  728.     int         signalList [MAXSIG];
  729. {
  730.     int              idx, signalNum, actuallyDone = -1;
  731.     char            *stateKeyedList [MAXSIG];
  732.     char            *sigState [3], *sigEntry [2];
  733.     signalProcPtr_t  actionFunc;
  734.  
  735.     for (idx = 0; idx < signalListSize; idx ++) {
  736.         signalNum = signalList [idx];
  737.  
  738.         actionFunc = GetSignalState (signalNum);
  739.         if (actionFunc == SIG_ERR)
  740.             goto unixSigError;
  741.         
  742.         sigState [2] = NULL;
  743.         if (actionFunc == SIG_DFL)
  744.             sigState [0]  = "default";
  745.         else if (actionFunc == SIG_IGN)
  746.             sigState [0] = "ignore";
  747.         else if (actionFunc == TclSignalTrap) {
  748.             if (signalTrapCmds [signalNum] == NULL)
  749.                 sigState [0] = "error";
  750.             else {
  751.                 sigState [0] = "trap";
  752.                 sigState [2] = signalTrapCmds [signalNum];
  753.             }
  754.         } else {
  755.             sigState [0] = "unknown";
  756.         }
  757.  
  758.         sigState [1] = SignalBlocked (interp, signalNum);
  759.         if (sigState [1] == NULL)
  760.             goto unixSigError;
  761.  
  762.         sigEntry [0] = Tcl_SignalId (signalNum);
  763.         sigEntry [1] = Tcl_Merge ((sigState [2] == NULL) ? 2 : 3,
  764.                                   sigState);
  765.  
  766.         stateKeyedList [idx] = Tcl_Merge (2, sigEntry);
  767.         ckfree (sigEntry [1]);
  768.  
  769.         actuallyDone = idx;
  770.  
  771.     }
  772.     Tcl_SetResult (interp, Tcl_Merge (signalListSize, stateKeyedList),
  773.                    TCL_DYNAMIC);
  774.  
  775.     for (idx = 0; idx <= actuallyDone; idx++)
  776.         ckfree (stateKeyedList [idx]);
  777.  
  778.     return TCL_OK;
  779.  
  780. unixSigError:
  781.     for (idx = 0; idx <= actuallyDone; idx++)
  782.         ckfree (stateKeyedList [idx]);
  783.  
  784.     interp->result = Tcl_UnixError (interp);
  785.     return TCL_ERROR;
  786. }
  787.  
  788. /*
  789.  *-----------------------------------------------------------------------------
  790.  *
  791.  * SetSignalStates --
  792.  *     
  793.  *    Set the signal state for the specified signals.  
  794.  *
  795.  * Parameters::
  796.  *   o interp (O) - The list is returned in the result.
  797.  *   o signalListSize (I) - Number of signals in the signal list.
  798.  *   o signalList (I) - List of signals of requested signals.
  799.  *   o actionFunc (I) - The function to run when the signal is received.
  800.  *   o command (I) - If the function is the "trap" function, this is the
  801.  *     Tcl command to run when the trap occurs.  Otherwise, NULL.
  802.  * Returns:
  803.  *   TCL_OK or TCL_ERROR, with error message in interp.
  804.  *-----------------------------------------------------------------------------
  805.  */
  806. static int
  807. SetSignalStates (interp, signalListSize, signalList, actionFunc, command)
  808.     Tcl_Interp      *interp;
  809.     int              signalListSize;
  810.     int              signalList [MAXSIG];
  811.     signalProcPtr_t  actionFunc;
  812.     char            *command;
  813.  
  814. {
  815.     int idx, signalNum, commandLen;
  816.  
  817.     if (command != NULL)
  818.         commandLen = strlen (command);
  819.  
  820.     for (idx = 0; idx < signalListSize; idx ++) {
  821.         signalNum = signalList [idx];
  822.  
  823.         if (signalTrapCmds [signalNum] != NULL) {
  824.             ckfree (signalTrapCmds [signalNum]);
  825.             signalTrapCmds [signalNum] = NULL;
  826.         }
  827.         if (!SetSignalAction (signalNum, actionFunc))
  828.             goto unixSigError;
  829.  
  830.         if (command != NULL) {
  831.             signalTrapCmds [signalNum] = ckalloc (commandLen + 1);
  832.             strcpy (signalTrapCmds [signalNum], command);
  833.         }
  834.     }
  835.  
  836.     return TCL_OK;
  837.  
  838. unixSigError:
  839.     interp->result = Tcl_UnixError (interp);
  840.     return TCL_ERROR;
  841. }
  842.  
  843. /*
  844.  *-----------------------------------------------------------------------------
  845.  *
  846.  * BlockSignals --
  847.  *     
  848.  *    Block or unblock the specified signals.  Returns an error if not a Posix
  849.  * system.
  850.  *
  851.  * Parameters::
  852.  *   o interp (O) - Error messages are returned in result.
  853.  *   o action (I) - SIG_BLOCK or SIG_UNBLOCK.
  854.  *   o signalListSize (I) - Number of signals in the signal list.
  855.  *   o signalList (I) - List of signals of requested signals.
  856.  * Returns:
  857.  *   TCL_OK or TCL_ERROR, with error message in interp.
  858.  *-----------------------------------------------------------------------------
  859.  */
  860. static int
  861. BlockSignals (interp, action, signalListSize, signalList)
  862.     Tcl_Interp  *interp;
  863.     int          action;
  864.     int          signalListSize;
  865.     int          signalList [MAXSIG];
  866. {
  867. #ifdef TCL_POSIX_SIG
  868.     int      idx;
  869.     sigset_t sigBlockSet;
  870.  
  871.     sigemptyset (&sigBlockSet);
  872.  
  873.     for (idx = 0; idx < signalListSize; idx ++)
  874.         sigaddset (&sigBlockSet, signalList [idx]);
  875.  
  876.     if (sigprocmask (action, &sigBlockSet, NULL)) {
  877.         interp->result = Tcl_UnixError (interp);
  878.         return TCL_ERROR;
  879.     }
  880.  
  881.     return TCL_OK;
  882. #else
  883.     interp->result = noPosix;
  884.     return TCL_ERROR;
  885. #endif
  886. }
  887.  
  888. /*
  889.  *-----------------------------------------------------------------------------
  890.  *
  891.  * Tcl_SignalCmd --
  892.  *     Implements the TCL signal command:
  893.  *         signal action siglist [command]
  894.  *
  895.  * Results:
  896.  *      Standard TCL results, may return the UNIX system error message.
  897.  *
  898.  * Side effects:
  899.  *    Signal handling states may be changed.
  900.  *-----------------------------------------------------------------------------
  901.  */
  902. static int
  903. Tcl_SignalCmd (clientData, interp, argc, argv)
  904.     char       *clientData;
  905.     Tcl_Interp *interp;
  906.     int         argc;
  907.     char      **argv;
  908. {
  909.     int                  signalListSize, signalNum, idx;
  910.     int                  signalList [MAXSIG], actionClass;
  911.     char                *signalName;
  912.     signalProcPtr_t      actionFunc;
  913.     char                *command = NULL;
  914.  
  915.     if ((argc < 3) || (argc > 4)) {
  916.         Tcl_AppendResult (interp, tclXWrongArgs, argv [0], 
  917.                           " action signalList [commands]", (char *) NULL);
  918.         return TCL_ERROR;
  919.     }
  920.  
  921.     signalListSize = ParseSignalList (interp, argv [2], signalList);
  922.     if (signalListSize < 0)    
  923.         return TCL_ERROR;
  924.  
  925.     /*
  926.      * Determine the action to take on all of the signals.
  927.      */
  928.     if (STREQU (argv [1], "trap")) {
  929.         actionFunc = TclSignalTrap;
  930.         actionClass = SIGACT_SET;
  931.         if (argc != 4) {
  932.             Tcl_AppendResult (interp, "command required for ",
  933.                              "trapping signals", (char *) NULL);
  934.             return TCL_ERROR;
  935.         }
  936.         command = argv [3];
  937.     } else {
  938.         if (STREQU (argv [1], "default")) {
  939.             actionFunc  = SIG_DFL;
  940.             actionClass = SIGACT_SET;
  941.         } else if (STREQU (argv [1], "ignore")) {
  942.             actionFunc = SIG_IGN;
  943.             actionClass = SIGACT_SET;
  944.         } else if (STREQU (argv [1], "error")) {
  945.             actionFunc = TclSignalTrap;
  946.             actionClass = SIGACT_SET;
  947.         } else if (STREQU (argv [1], "get")) {
  948.             actionClass = SIGACT_GET;
  949.         } else if (STREQU (argv [1], "block")) {
  950.             actionClass = SIGACT_BLOCK;
  951.         } else if (STREQU (argv [1], "unblock")) {
  952.             actionClass = SIGACT_UNBLOCK;
  953.         } else {
  954.             Tcl_AppendResult (interp, "invalid signal action specified: ", 
  955.                               argv [1], ": expected one of \"default\", ",
  956.                               "\"ignore\", \"error\", \"trap\", or \"get\", ",
  957.                               "\"block\", \"unblock\"", (char *) NULL);
  958.             return TCL_ERROR;
  959.         }
  960.         if (argc != 3) {
  961.             Tcl_AppendResult (interp, "command may not be ",
  962.                               "specified for \"", argv [1], "\" action",
  963.                               (char *) NULL);
  964.             return TCL_ERROR;
  965.         }
  966.     }
  967.  
  968.     /*
  969.      * Process the specified action class.
  970.      */
  971.     switch (actionClass) {
  972.       case SIGACT_SET:
  973.         return SetSignalStates (interp, signalListSize, signalList,
  974.                                 actionFunc, command);
  975.       case SIGACT_GET:
  976.         return GetSignalStates (interp, signalListSize, signalList);
  977.       case SIGACT_BLOCK:
  978.         return BlockSignals (interp, SIG_BLOCK, signalListSize, signalList);
  979.       case SIGACT_UNBLOCK:
  980.         return BlockSignals (interp, SIG_UNBLOCK, signalListSize, signalList);
  981.     }
  982.  
  983. }
  984.  
  985. /*
  986.  *-----------------------------------------------------------------------------
  987.  *
  988.  *  SignalCmdCleanUp --
  989.  *      Clean up the signal table when the interpreter is deleted.  This
  990.  *      is actually when the signal command is deleted.  It releases the
  991.  *      all signal commands that have been allocated.
  992.  *
  993.  *-----------------------------------------------------------------------------
  994.  */
  995. static void
  996. SignalCmdCleanUp (clientData)
  997.     ClientData clientData;
  998. {
  999.     int idx;
  1000.  
  1001.     for (idx = 0; idx < MAXSIG; idx++)
  1002.         if (signalTrapCmds [idx] != NULL) {
  1003.             ckfree (signalTrapCmds [idx]);
  1004.             signalTrapCmds [idx] = NULL;
  1005.         }
  1006.  
  1007. }
  1008.  
  1009. /*
  1010.  *-----------------------------------------------------------------------------
  1011.  *
  1012.  * Tcl_SetupSigInt --
  1013.  *    Set up SIGINT to the "error" state if the current state is default.
  1014.  * This is done because shells set SIGINT to ignore for background processes
  1015.  * so that they don't die on signals generated by the user at the keyboard.
  1016.  * Tcl only enables SIGINT catching if it is an interactive session.
  1017.  *-----------------------------------------------------------------------------
  1018.  */
  1019. void
  1020. Tcl_SetupSigInt ()
  1021. {
  1022.     if (GetSignalState (SIGINT) == SIG_DFL)
  1023.         SetSignalAction (SIGINT, TclSignalTrap);
  1024. }
  1025.  
  1026. /*
  1027.  *-----------------------------------------------------------------------------
  1028.  *
  1029.  * Tcl_InitSignalHandling --
  1030.  *      Initializes the TCL unix commands.
  1031.  *
  1032.  * Side effects:
  1033.  *    A catch trap is armed for the SIGINT signal.
  1034.  *
  1035.  *-----------------------------------------------------------------------------
  1036.  */
  1037. void
  1038. Tcl_InitSignalHandling (interp)
  1039.     Tcl_Interp *interp;
  1040. {
  1041.     int idx;
  1042.  
  1043.     for (idx = 0; idx < MAXSIG; idx++) {
  1044.         signalsReceived [idx] = 0;
  1045.         signalTrapCmds [idx] = NULL;
  1046.     }
  1047.     Tcl_CreateCommand (interp, "kill", Tcl_KillCmd, (ClientData)NULL,
  1048.                       (void (*)())NULL);
  1049.     Tcl_CreateCommand (interp, "signal", Tcl_SignalCmd, (ClientData)NULL,
  1050.                       SignalCmdCleanUp);
  1051. }
  1052.