]>
Commit | Line | Data |
---|---|---|
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, ¤tState) < 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 | } |