]> git.zerfleddert.de Git - micropolis/blob - src/tclx/src/tclxsig.c
Import Micropolis from http://www.donhopkins.com/home/micropolis/
[micropolis] / src / tclx / src / tclxsig.c
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
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 #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
97 typedef 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 */
118 static 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 */
124 int tclReceivedSignal = FALSE; /* A signal was received */
125 static 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 */
131 static char *signalTrapCmds [MAXSIG];
132
133 /*
134 * Prototypes of internal functions.
135 */
136
137 static int
138 SigNameToNum _ANSI_ARGS_((char *sigName));
139
140 static signalProcPtr_t
141 GetSignalState _ANSI_ARGS_((int signalNum));
142
143 static int
144 SetSignalAction _ANSI_ARGS_((int signalNum,
145 signalProcPtr_t sigFunc));
146
147 static SIG_PROC_RET_TYPE
148 TclSignalTrap _ANSI_ARGS_((int signalNum));
149
150 static int
151 EvalTrapCode _ANSI_ARGS_((Tcl_Interp *interp,
152 int signalNum,
153 char *command));
154
155 static int
156 ParseSignalList _ANSI_ARGS_((Tcl_Interp *interp,
157 char *signalListStr,
158 int signalList []));
159
160 static char *
161 SignalBlocked _ANSI_ARGS_((Tcl_Interp *interp,
162 int signalNum));
163
164 static int
165 GetSignalStates _ANSI_ARGS_((Tcl_Interp *interp,
166 int signalListSize,
167 int signalList [MAXSIG]));
168
169 static int
170 SetSignalStates _ANSI_ARGS_((Tcl_Interp *interp,
171 int signalListSize,
172 int signalList [MAXSIG],
173 signalProcPtr_t actionFunc,
174 char *command));
175
176 static int
177 BlockSignals _ANSI_ARGS_((Tcl_Interp *interp,
178 int action,
179 int signalListSize,
180 int signalList [MAXSIG]));
181
182 static void
183 SignalCmdCleanUp _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 */
196 static int
197 SigNameToNum (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 */
237 int
238 Tcl_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;
282 exitPoint:
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 */
299 static signalProcPtr_t
300 GetSignalState (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 */
336 static int
337 SetSignalAction (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 */
372 static SIG_PROC_RET_TYPE
373 TclSignalTrap (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 */
412 static int
413 EvalTrapCode (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 */
480 void
481 Tcl_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 */
525 int
526 Tcl_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
582 exitPoint:
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 */
609 static int
610 ParseSignalList (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;
662 exitPoint:
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 */
684 static char *
685 SignalBlocked (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 */
719 static int
720 GetSignalStates (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
773 unixSigError:
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 */
799 static int
800 SetSignalStates (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
831 unixSigError:
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 */
853 static int
854 BlockSignals (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 */
895 static int
896 Tcl_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 */
988 static void
989 SignalCmdCleanUp (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 */
1013 void
1014 Tcl_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