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