1 /* $LAAS: signal.c,v 1.4 2003/08/13 11:47:50 mallet Exp $ */
4 * Copyright (c) 2001 LAAS/CNRS -- Wed Oct 10 2001
5 * All rights reserved. Anthony Mallet
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
28 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
42 /* should be large enough */
43 #define ELTCL_MAXSIG 64
45 /* these two macros correspond to SIG_IGN and SIG_DFL and are used to
46 * distinguish between a valid Tcl script and the particular behavior
48 #define ELTCL_SIGIGN ((void *)-1)
49 #define ELTCL_SIGDFL ((void *)0)
52 const char *signalNames
[ELTCL_MAXSIG
];
54 /* Context definition for signal handler's execution: it's an array
55 * indexed by signal number. */
56 typedef struct ElTclSignalContext
{
57 Tcl_Obj
*script
; /* tcl script, or one of ELTCL_SIGDFL or
58 * ELTCL_SIGIGN macros */
59 ElTclInterpInfo
*iinfo
; /* tcl interpreter in which to execute
61 Tcl_AsyncHandler asyncH
; /* acync handler for the interpreter */
63 struct ElTclSignalContext
*next
;
64 /* in a multi-threaded context, this
65 * points to the next handler for this
66 * signal (possibly in another
67 * interpreter). This does not apply for
68 VxWorks since there is at most one
72 /* Since there is no obvious way to make that context non-global, this
73 * array is TaskVarAdd'ed under VxWorks. */
74 static ElTclSignalContext
*signalContext
[ELTCL_MAXSIG
];
76 /* VxWorks doesn't have SIGWINCH, so we don't have to bother with
79 static ElTclSignalContext
*sigWinchContext
;
83 /* local prototypes */
84 static int elTclSignal(ClientData data
,
85 Tcl_Interp
*interp
, int objc
,
86 Tcl_Obj
*const objv
[]);
87 static ElTclSignalContext
* getSignalContext(int signum
,
88 ElTclInterpInfo
*iinfo
);
89 static ElTclSignalContext
* createSignalContext(int signum
,
90 ElTclInterpInfo
*iinfo
);
91 static int asyncSignalHandler(ClientData data
,
92 Tcl_Interp
*interp
, int code
);
93 static void signalHandler(int signal
);
94 static void initSigNames(const char **signalNames
);
99 * elTclHandlersInit ----------------------------------------------------
101 * Intialize signal handlers: create the list of signal names and the
106 elTclHandlersInit(ElTclInterpInfo
*iinfo
)
109 /* we must keep track of that one (for libedit and completion engine) */
112 ElTclSignalContext
*ctx
= malloc(sizeof(*ctx
));
116 sigaddset(&set
, SIGWINCH
);
117 sigprocmask(SIG_BLOCK
, &set
, &oset
);
120 ctx
->next
= sigWinchContext
;
121 /* XXX this might not be atomic */
122 sigWinchContext
= ctx
;
124 signal(SIGWINCH
, signalHandler
);
126 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
131 # error TaskVarAdd signalContext
134 initSigNames(signalNames
);
135 Tcl_CreateObjCommand(iinfo
->interp
, "signal", elTclSignal
, iinfo
, NULL
);
141 * elTclHandlersExit ----------------------------------------------------
147 elTclHandlersExit(ElTclInterpInfo
*iinfo
)
149 ElTclSignalContext
*ctx
, *swp
, *prev
;
153 /* block all signals */
155 sigprocmask(SIG_BLOCK
, &set
, &oset
);
157 /* XXX must be protected... */
160 for(ctx
= sigWinchContext
; ctx
!= NULL
; ) {
161 if (ctx
->iinfo
== iinfo
) {
166 sigWinchContext
= ctx
;
178 for(i
=0; i
<ELTCL_MAXSIG
; i
++) {
180 for(ctx
= signalContext
[i
]; ctx
!= NULL
; ) {
181 /* delete the handlers that are for us */
182 if (ctx
->iinfo
== iinfo
) {
187 signalContext
[i
] = ctx
;
191 if (swp
->script
!= ELTCL_SIGDFL
&&
192 swp
->script
!= ELTCL_SIGIGN
) {
193 Tcl_DecrRefCount(swp
->script
);
194 Tcl_AsyncDelete(swp
->asyncH
);
205 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
210 * elTclSignal ----------------------------------------------------------
212 * Implements the "signal" procedure.
216 elTclSignal(ClientData data
, Tcl_Interp
*interp
, int objc
,
217 Tcl_Obj
*const objv
[])
219 ElTclInterpInfo
*iinfo
= data
;
220 ElTclSignalContext
*ctx
;
225 if (objc
< 2 || objc
> 3) {
226 Tcl_WrongNumArgs(interp
, 1, objv
,
227 "signal ?script|-ignore|-default|-block|-unblock?");
232 !strcmp(Tcl_GetStringFromObj(objv
[1], NULL
), "names")) {
236 Tcl_DStringInit(&dstring
);
237 for(i
=0; i
<ELTCL_MAXSIG
; i
++) if (signalNames
[i
] != NULL
) {
238 Tcl_DStringAppendElement(&dstring
, signalNames
[i
]);
240 Tcl_DStringResult(interp
, &dstring
);
244 /* objv[1] must be a signal name */
246 for(i
=0; i
<ELTCL_MAXSIG
; i
++) if (signalNames
[i
] != NULL
)
247 if (!strcmp(Tcl_GetStringFromObj(objv
[1], NULL
), signalNames
[i
])) {
254 if (Tcl_GetIntFromObj(interp
, objv
[1], &signum
) == TCL_ERROR
)
258 /* prepare the interpreter result so that this command returns the
259 * previous action for that signal */
260 ctx
= getSignalContext(signum
, iinfo
);
261 if (ctx
== NULL
|| ctx
->script
== ELTCL_SIGDFL
) {
262 Tcl_SetResult(interp
, "-default", TCL_STATIC
);
263 } else if (ctx
->script
== ELTCL_SIGIGN
) {
264 Tcl_SetResult(interp
, "-ignore", TCL_STATIC
);
266 Tcl_SetObjResult(interp
, ctx
->script
);
269 /* if no action given, return current script associated with
271 if (objc
== 2) { return TCL_OK
; }
273 /* get the given action */
274 action
= Tcl_GetStringFromObj(objv
[2], NULL
);
276 /* check if signal should be reset to default */
277 if (!strcmp(action
, "-default")) {
278 /* special case of SIGWINCH, which we must keep processing */
280 if (signum
!= SIGWINCH
)
282 if (signal(signum
, SIG_DFL
) == (void *)-1) goto error
;
284 if (ctx
== NULL
) return TCL_OK
;
286 if (ctx
->script
!= ELTCL_SIGDFL
&& ctx
->script
!= ELTCL_SIGIGN
) {
287 Tcl_DecrRefCount(ctx
->script
);
288 Tcl_AsyncDelete(ctx
->asyncH
);
290 ctx
->script
= ELTCL_SIGDFL
;
294 /* check if signal should be ignored */
295 if (!strcmp(action
, "-ignore")) {
297 ctx
= createSignalContext(signum
, iinfo
);
298 if (ctx
== NULL
) goto error
;
300 /* special case of SIGWINCH, which we must keep processing */
302 if (signum
!= SIGWINCH
)
304 if (signal(signum
, SIG_IGN
) == (void *)-1) goto error
;
306 if (ctx
->script
!= ELTCL_SIGDFL
&& ctx
->script
!= ELTCL_SIGIGN
) {
307 Tcl_DecrRefCount(ctx
->script
);
308 Tcl_AsyncDelete(ctx
->asyncH
);
310 ctx
->script
= ELTCL_SIGIGN
;
314 /* check if signal should be (un)blocked */
315 if (!strcmp(action
, "-block") || !strcmp(action
, "-unblock")) {
321 sigaddset(&set
, signum
);
323 if (!strcmp(action
, "-block"))
324 code
= sigprocmask(SIG_BLOCK
, &set
, &oset
);
326 code
= sigprocmask(SIG_UNBLOCK
, &set
, &oset
);
328 if (code
) goto error
;
330 /* return the previous mask */
331 Tcl_DStringInit(&dstring
);
332 for(i
=0; i
<ELTCL_MAXSIG
; i
++) if (signalNames
[i
] != NULL
) {
333 if (sigismember(&oset
, i
))
334 Tcl_DStringAppendElement(&dstring
, signalNames
[i
]);
336 Tcl_DStringResult(interp
, &dstring
);
340 /* a script was given: create async handler and register signal */
343 ctx
= createSignalContext(signum
, iinfo
);
344 if (ctx
== NULL
) goto error
;
347 /* block signal while installing handler */
349 sigaddset(&set
, signum
);
350 if (sigprocmask(SIG_BLOCK
, &set
, &oset
)) goto error
;
353 if (signum
!= SIGWINCH
)
355 if (signal(signum
, signalHandler
) == (void *)-1) {
356 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
360 if (ctx
->script
!= ELTCL_SIGDFL
&& ctx
->script
!= ELTCL_SIGIGN
) {
361 Tcl_DecrRefCount(ctx
->script
);
362 Tcl_AsyncDelete(ctx
->asyncH
);
365 ctx
->script
= objv
[2];
366 Tcl_IncrRefCount(ctx
->script
);
367 ctx
->asyncH
= Tcl_AsyncCreate(asyncSignalHandler
, ctx
);
369 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
373 Tcl_SetResult(interp
, (char *)Tcl_ErrnoMsg(errno
), TCL_VOLATILE
);
375 Tcl_PosixError(interp
);
381 * getSignalContext -----------------------------------------------------
383 * Return the signal context for the given signal and interpreter, or one
384 * of the ELTCL_SIGIGN or ELTCL_SIGDFL macros.
387 static ElTclSignalContext
*
388 getSignalContext(int signum
, ElTclInterpInfo
*iinfo
)
390 ElTclSignalContext
*ctx
;
392 for(ctx
= signalContext
[signum
]; ctx
!= NULL
; ctx
=ctx
->next
)
393 if (ctx
->iinfo
== iinfo
) return ctx
;
400 * createSignalContext --------------------------------------------------
402 * Create the signal context for the given signal and interpreter.
405 static ElTclSignalContext
*
406 createSignalContext(int signum
, ElTclInterpInfo
*iinfo
)
408 ElTclSignalContext
*ctx
;
410 for(ctx
= signalContext
[signum
]; ctx
!= NULL
; ctx
=ctx
->next
)
411 if (ctx
->iinfo
== iinfo
) return ctx
;
413 ctx
= malloc(sizeof(*ctx
));
414 if (ctx
== NULL
) return NULL
;
416 ctx
->script
= ELTCL_SIGDFL
;
418 ctx
->next
= signalContext
[signum
];
420 /* XXX this might not be atomic */
421 signalContext
[signum
] = ctx
;
428 * asyncSignalHandler ---------------------------------------------------
430 * Tcl asynchronous signal handler. The ClientData is a pointer to the
431 * eltcl signal context struture.
435 asyncSignalHandler(ClientData data
, Tcl_Interp
*interp
, int code
)
437 ElTclSignalContext
*ctx
= data
;
438 Tcl_Obj
*result
, *errorInfo
, *errorCode
;
440 if (ctx
->script
== ELTCL_SIGDFL
|| ctx
->script
== ELTCL_SIGIGN
) {
441 fputs("Warning: wrong signal delivered for Tcl\n", stdout
);
445 /* save interpreter state */
446 result
= Tcl_GetObjResult(ctx
->iinfo
->interp
);
447 if (result
!= NULL
) Tcl_IncrRefCount(result
);
448 errorInfo
= Tcl_GetVar2Ex(ctx
->iinfo
->interp
, "errorInfo", NULL
,
450 if (errorInfo
!= NULL
) Tcl_IncrRefCount(errorInfo
);
451 errorCode
= Tcl_GetVar2Ex(ctx
->iinfo
->interp
, "errorCode", NULL
,
453 if (errorCode
!= NULL
) Tcl_IncrRefCount(errorCode
);
456 if (Tcl_EvalObjEx(ctx
->iinfo
->interp
,
457 ctx
->script
, TCL_EVAL_GLOBAL
) != TCL_OK
)
458 Tcl_BackgroundError(ctx
->iinfo
->interp
);
461 /* restore interpreter state */
462 if (errorInfo
!= NULL
) {
463 Tcl_SetVar2Ex(ctx
->iinfo
->interp
, "errorInfo", NULL
, errorInfo
,
465 Tcl_DecrRefCount(errorInfo
);
467 if (errorCode
!= NULL
) {
468 Tcl_SetVar2Ex(ctx
->iinfo
->interp
, "errorCode", NULL
, errorCode
,
470 Tcl_DecrRefCount(errorCode
);
472 if (result
!= NULL
) {
473 Tcl_SetObjResult(ctx
->iinfo
->interp
, result
);
474 Tcl_DecrRefCount(result
);
482 * signalHandler --------------------------------------------------------
484 * Simply mark the corresponding Tcl async handlers.
488 signalHandler(int signum
)
490 ElTclSignalContext
*ctx
;
493 for(ctx
= sigWinchContext
; ctx
!= NULL
; ctx
=ctx
->next
) {
494 el_resize(ctx
->iinfo
->el
);
495 elTclGetWindowSize(1, NULL
, &ctx
->iinfo
->windowSize
);
499 for(ctx
= signalContext
[signum
]; ctx
!= NULL
; ctx
=ctx
->next
)
500 if (ctx
->script
!= ELTCL_SIGDFL
&& ctx
->script
!= ELTCL_SIGIGN
)
501 Tcl_AsyncMark(ctx
->asyncH
);
503 #ifdef __svr4__ /* solaris 2 */
504 signal(signum
, signalHandler
);
510 * initSigNames ---------------------------------------------------------
512 * Fill signalNames array with existing signals
516 initSigNames(const char **signalNames
)
519 signalNames
[SIGHUP
] = "HUP";
522 signalNames
[SIGINT
] = "INT";
525 signalNames
[SIGQUIT
] = "QUIT";
528 signalNames
[SIGILL
] = "ILL";
531 signalNames
[SIGTRAP
] = "TRAP";
534 signalNames
[SIGABRT
] = "ABRT";
537 signalNames
[SIGEMT
] = "EMT";
540 signalNames
[SIGFPE
] = "FPE";
543 signalNames
[SIGKILL
] = "KILL";
546 signalNames
[SIGBUS
] = "BUS";
549 signalNames
[SIGSEGV
] = "SEGV";
552 signalNames
[SIGSYS
] = "SYS";
555 signalNames
[SIGPIPE
] = "PIPE";
558 signalNames
[SIGALRM
] = "ALRM";
561 signalNames
[SIGTERM
] = "TERM";
564 signalNames
[SIGURG
] = "URG";
567 signalNames
[SIGSTOP
] = "STOP";
570 signalNames
[SIGTSTP
] = "TSTP";
573 signalNames
[SIGCONT
] = "CONT";
576 signalNames
[SIGCHLD
] = "CHLD";
579 signalNames
[SIGTTIN
] = "TTIN";
582 signalNames
[SIGTTOU
] = "TTOU";
585 signalNames
[SIGIO
] = "IO";
588 signalNames
[SIGXCPU
] = "XCPU";
591 signalNames
[SIGXFSZ
] = "XFSZ";
594 signalNames
[SIGVTALRM
] = "VTALRM";
597 signalNames
[SIGPROF
] = "PROF";
600 signalNames
[SIGWINCH
] = "WINCH";
603 signalNames
[SIGINFO
] = "INFO";
606 signalNames
[SIGUSR1
] = "USR1";
609 signalNames
[SIGUSR2
] = "USR2";
612 signalNames
[SIGPWR
] = "PWR";
615 signalNames
[SIGPOLL
] = "POLL";
618 signalNames
[SIGSTP
] = "STP";
621 signalNames
[SIGWAITING
] = "WAITING";
624 signalNames
[SIGLWP
] = "LWP";
627 signalNames
[SIGFREEZE
] = "FREEZE";
630 signalNames
[SIGTHAW
] = "THAW";
633 signalNames
[SIGCANCEL
] = "CANCEL";