Initial version of donated sources by Avertec, 3.4p5.
[tas-yagle.git] / distrib / sources / api / tcl / signal.c
1 /* $LAAS: signal.c,v 1.4 2003/08/13 11:47:50 mallet Exp $ */
2
3 /*
4 * Copyright (c) 2001 LAAS/CNRS -- Wed Oct 10 2001
5 * All rights reserved. Anthony Mallet
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
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
16 * distribution.
17 *
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
29 * DAMAGE.
30 */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <sys/wait.h>
37 #include <signal.h>
38 #include <unistd.h>
39
40 #include "eltclsh.h"
41
42 /* should be large enough */
43 #define ELTCL_MAXSIG 64
44
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
47 * they represent */
48 #define ELTCL_SIGIGN ((void *)-1)
49 #define ELTCL_SIGDFL ((void *)0)
50
51 /* signal names */
52 const char *signalNames[ELTCL_MAXSIG];
53
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
60 * script */
61 Tcl_AsyncHandler asyncH; /* acync handler for the interpreter */
62
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
69 handler per task. */
70 } ElTclSignalContext;
71
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];
75
76 /* VxWorks doesn't have SIGWINCH, so we don't have to bother with
77 * taskVars... */
78 #ifdef SIGWINCH
79 static ElTclSignalContext *sigWinchContext;
80 #endif
81
82
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);
95
96
97
98 /*
99 * elTclHandlersInit ----------------------------------------------------
100 *
101 * Intialize signal handlers: create the list of signal names and the
102 * signal procedure.
103 */
104
105 int
106 elTclHandlersInit(ElTclInterpInfo *iinfo)
107 {
108 #ifdef SIGWINCH
109 /* we must keep track of that one (for libedit and completion engine) */
110
111 sigset_t set, oset;
112 ElTclSignalContext *ctx = malloc(sizeof(*ctx));
113
114 if (ctx != NULL) {
115 sigemptyset(&set);
116 sigaddset(&set, SIGWINCH);
117 sigprocmask(SIG_BLOCK, &set, &oset);
118
119 ctx->iinfo = iinfo;
120 ctx->next = sigWinchContext;
121 /* XXX this might not be atomic */
122 sigWinchContext = ctx;
123
124 signal(SIGWINCH, signalHandler);
125
126 sigprocmask(SIG_SETMASK, &oset, NULL);
127 }
128 #endif
129
130 #ifdef VXWORKS
131 # error TaskVarAdd signalContext
132 #endif
133
134 initSigNames(signalNames);
135 Tcl_CreateObjCommand(iinfo->interp, "signal", elTclSignal, iinfo, NULL);
136 return TCL_OK;
137 }
138
139
140 /*
141 * elTclHandlersExit ----------------------------------------------------
142 *
143 * Free signal stuff
144 */
145
146 void
147 elTclHandlersExit(ElTclInterpInfo *iinfo)
148 {
149 ElTclSignalContext *ctx, *swp, *prev;
150 sigset_t set, oset;
151 int i;
152
153 /* block all signals */
154 sigfillset(&set);
155 sigprocmask(SIG_BLOCK, &set, &oset);
156
157 /* XXX must be protected... */
158 #ifdef SIGWINCH
159 prev = NULL;
160 for(ctx = sigWinchContext; ctx != NULL; ) {
161 if (ctx->iinfo == iinfo) {
162 swp = ctx;
163 ctx = ctx->next;
164
165 if (prev == NULL)
166 sigWinchContext = ctx;
167 else
168 prev->next = ctx;
169
170 free(swp);
171 } else {
172 prev = ctx;
173 ctx = ctx->next;
174 }
175 }
176 #endif
177
178 for(i=0; i<ELTCL_MAXSIG; i++) {
179 prev = NULL;
180 for(ctx = signalContext[i]; ctx != NULL; ) {
181 /* delete the handlers that are for us */
182 if (ctx->iinfo == iinfo) {
183 swp = ctx;
184 ctx = ctx->next;
185
186 if (prev == NULL)
187 signalContext[i] = ctx;
188 else
189 prev->next = ctx;
190
191 if (swp->script != ELTCL_SIGDFL &&
192 swp->script != ELTCL_SIGIGN) {
193 Tcl_DecrRefCount(swp->script);
194 Tcl_AsyncDelete(swp->asyncH);
195 }
196 free(swp);
197 } else {
198 prev = ctx;
199 ctx = ctx->next;
200 }
201 }
202 }
203
204 /* unblock */
205 sigprocmask(SIG_SETMASK, &oset, NULL);
206 }
207
208
209 /*
210 * elTclSignal ----------------------------------------------------------
211 *
212 * Implements the "signal" procedure.
213 */
214
215 static int
216 elTclSignal(ClientData data, Tcl_Interp *interp, int objc,
217 Tcl_Obj *const objv[])
218 {
219 ElTclInterpInfo *iinfo = data;
220 ElTclSignalContext *ctx;
221 sigset_t set, oset;
222 int i, signum;
223 char *action;
224
225 if (objc < 2 || objc > 3) {
226 Tcl_WrongNumArgs(interp, 1, objv,
227 "signal ?script|-ignore|-default|-block|-unblock?");
228 return TCL_ERROR;
229 }
230
231 if (objc == 2 &&
232 !strcmp(Tcl_GetStringFromObj(objv[1], NULL), "names")) {
233 /* [signal names] */
234 Tcl_DString dstring;
235
236 Tcl_DStringInit(&dstring);
237 for(i=0; i<ELTCL_MAXSIG; i++) if (signalNames[i] != NULL) {
238 Tcl_DStringAppendElement(&dstring, signalNames[i]);
239 }
240 Tcl_DStringResult(interp, &dstring);
241 return TCL_OK;
242 }
243
244 /* objv[1] must be a signal name */
245 signum = -1;
246 for(i=0; i<ELTCL_MAXSIG; i++) if (signalNames[i] != NULL)
247 if (!strcmp(Tcl_GetStringFromObj(objv[1], NULL), signalNames[i])) {
248 signum = i;
249 break;
250 }
251
252 if (signum < 0) {
253 /* or an integer */
254 if (Tcl_GetIntFromObj(interp, objv[1], &signum) == TCL_ERROR)
255 return TCL_ERROR;
256 }
257
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);
265 } else {
266 Tcl_SetObjResult(interp, ctx->script);
267 }
268
269 /* if no action given, return current script associated with
270 * signal */
271 if (objc == 2) { return TCL_OK; }
272
273 /* get the given action */
274 action = Tcl_GetStringFromObj(objv[2], NULL);
275
276 /* check if signal should be reset to default */
277 if (!strcmp(action, "-default")) {
278 /* special case of SIGWINCH, which we must keep processing */
279 #ifdef SIGWINCH
280 if (signum != SIGWINCH)
281 #endif
282 if (signal(signum, SIG_DFL) == (void *)-1) goto error;
283
284 if (ctx == NULL) return TCL_OK;
285
286 if (ctx->script != ELTCL_SIGDFL && ctx->script != ELTCL_SIGIGN) {
287 Tcl_DecrRefCount(ctx->script);
288 Tcl_AsyncDelete(ctx->asyncH);
289 }
290 ctx->script = ELTCL_SIGDFL;
291 return TCL_OK;
292 }
293
294 /* check if signal should be ignored */
295 if (!strcmp(action, "-ignore")) {
296 if (ctx == NULL) {
297 ctx = createSignalContext(signum, iinfo);
298 if (ctx == NULL) goto error;
299 }
300 /* special case of SIGWINCH, which we must keep processing */
301 #ifdef SIGWINCH
302 if (signum != SIGWINCH)
303 #endif
304 if (signal(signum, SIG_IGN) == (void *)-1) goto error;
305
306 if (ctx->script != ELTCL_SIGDFL && ctx->script != ELTCL_SIGIGN) {
307 Tcl_DecrRefCount(ctx->script);
308 Tcl_AsyncDelete(ctx->asyncH);
309 }
310 ctx->script = ELTCL_SIGIGN;
311 return TCL_OK;
312 }
313
314 /* check if signal should be (un)blocked */
315 if (!strcmp(action, "-block") || !strcmp(action, "-unblock")) {
316 Tcl_DString dstring;
317 int code;
318
319 sigemptyset(&set);
320 sigemptyset(&oset);
321 sigaddset(&set, signum);
322
323 if (!strcmp(action, "-block"))
324 code = sigprocmask(SIG_BLOCK, &set, &oset);
325 else
326 code = sigprocmask(SIG_UNBLOCK, &set, &oset);
327
328 if (code) goto error;
329
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]);
335 }
336 Tcl_DStringResult(interp, &dstring);
337 return TCL_OK;
338 }
339
340 /* a script was given: create async handler and register signal */
341
342 if (ctx == NULL) {
343 ctx = createSignalContext(signum, iinfo);
344 if (ctx == NULL) goto error;
345 }
346
347 /* block signal while installing handler */
348 sigemptyset(&set);
349 sigaddset(&set, signum);
350 if (sigprocmask(SIG_BLOCK, &set, &oset)) goto error;
351
352 #ifdef SIGWINCH
353 if (signum != SIGWINCH)
354 #endif
355 if (signal(signum, signalHandler) == (void *)-1) {
356 sigprocmask(SIG_SETMASK, &oset, NULL);
357 goto error;
358 }
359
360 if (ctx->script != ELTCL_SIGDFL && ctx->script != ELTCL_SIGIGN) {
361 Tcl_DecrRefCount(ctx->script);
362 Tcl_AsyncDelete(ctx->asyncH);
363 }
364
365 ctx->script = objv[2];
366 Tcl_IncrRefCount(ctx->script);
367 ctx->asyncH = Tcl_AsyncCreate(asyncSignalHandler, ctx);
368
369 sigprocmask(SIG_SETMASK, &oset, NULL);
370 return TCL_OK;
371
372 error:
373 Tcl_SetResult(interp, (char *)Tcl_ErrnoMsg(errno), TCL_VOLATILE);
374 Tcl_SetErrno(errno);
375 Tcl_PosixError(interp);
376 return TCL_ERROR;
377 }
378
379
380 /*
381 * getSignalContext -----------------------------------------------------
382 *
383 * Return the signal context for the given signal and interpreter, or one
384 * of the ELTCL_SIGIGN or ELTCL_SIGDFL macros.
385 */
386
387 static ElTclSignalContext *
388 getSignalContext(int signum, ElTclInterpInfo *iinfo)
389 {
390 ElTclSignalContext *ctx;
391
392 for(ctx = signalContext[signum]; ctx != NULL; ctx=ctx->next)
393 if (ctx->iinfo == iinfo) return ctx;
394
395 return NULL;
396 }
397
398
399 /*
400 * createSignalContext --------------------------------------------------
401 *
402 * Create the signal context for the given signal and interpreter.
403 */
404
405 static ElTclSignalContext *
406 createSignalContext(int signum, ElTclInterpInfo *iinfo)
407 {
408 ElTclSignalContext *ctx;
409
410 for(ctx = signalContext[signum]; ctx != NULL; ctx=ctx->next)
411 if (ctx->iinfo == iinfo) return ctx;
412
413 ctx = malloc(sizeof(*ctx));
414 if (ctx == NULL) return NULL;
415
416 ctx->script = ELTCL_SIGDFL;
417 ctx->iinfo = iinfo;
418 ctx->next = signalContext[signum];
419
420 /* XXX this might not be atomic */
421 signalContext[signum] = ctx;
422
423 return ctx;
424 }
425
426
427 /*
428 * asyncSignalHandler ---------------------------------------------------
429 *
430 * Tcl asynchronous signal handler. The ClientData is a pointer to the
431 * eltcl signal context struture.
432 */
433
434 static int
435 asyncSignalHandler(ClientData data, Tcl_Interp *interp, int code)
436 {
437 ElTclSignalContext *ctx = data;
438 Tcl_Obj *result, *errorInfo, *errorCode;
439
440 if (ctx->script == ELTCL_SIGDFL || ctx->script == ELTCL_SIGIGN) {
441 fputs("Warning: wrong signal delivered for Tcl\n", stdout);
442 return code;
443 }
444
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,
449 TCL_GLOBAL_ONLY);
450 if (errorInfo != NULL) Tcl_IncrRefCount(errorInfo);
451 errorCode = Tcl_GetVar2Ex(ctx->iinfo->interp, "errorCode", NULL,
452 TCL_GLOBAL_ONLY);
453 if (errorCode != NULL) Tcl_IncrRefCount(errorCode);
454
455 /* eval script */
456 if (Tcl_EvalObjEx(ctx->iinfo->interp,
457 ctx->script, TCL_EVAL_GLOBAL) != TCL_OK)
458 Tcl_BackgroundError(ctx->iinfo->interp);
459
460
461 /* restore interpreter state */
462 if (errorInfo != NULL) {
463 Tcl_SetVar2Ex(ctx->iinfo->interp, "errorInfo", NULL, errorInfo,
464 TCL_GLOBAL_ONLY);
465 Tcl_DecrRefCount(errorInfo);
466 }
467 if (errorCode != NULL) {
468 Tcl_SetVar2Ex(ctx->iinfo->interp, "errorCode", NULL, errorCode,
469 TCL_GLOBAL_ONLY);
470 Tcl_DecrRefCount(errorCode);
471 }
472 if (result != NULL) {
473 Tcl_SetObjResult(ctx->iinfo->interp, result);
474 Tcl_DecrRefCount(result);
475 }
476
477 return code;
478 }
479
480
481 /*
482 * signalHandler --------------------------------------------------------
483 *
484 * Simply mark the corresponding Tcl async handlers.
485 */
486
487 static void
488 signalHandler(int signum)
489 {
490 ElTclSignalContext *ctx;
491
492 #ifdef SIGWINCH
493 for(ctx = sigWinchContext; ctx != NULL; ctx=ctx->next) {
494 el_resize(ctx->iinfo->el);
495 elTclGetWindowSize(1, NULL, &ctx->iinfo->windowSize);
496 }
497 #endif
498
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);
502
503 #ifdef __svr4__ /* solaris 2 */
504 signal(signum, signalHandler);
505 #endif
506 }
507
508
509 /*
510 * initSigNames ---------------------------------------------------------
511 *
512 * Fill signalNames array with existing signals
513 */
514
515 static void
516 initSigNames(const char **signalNames)
517 {
518 #ifdef SIGHUP
519 signalNames[SIGHUP] = "HUP";
520 #endif
521 #ifdef SIGINT
522 signalNames[SIGINT] = "INT";
523 #endif
524 #ifdef SIGQUIT
525 signalNames[SIGQUIT] = "QUIT";
526 #endif
527 #ifdef SIGILL
528 signalNames[SIGILL] = "ILL";
529 #endif
530 #ifdef SIGTRAP
531 signalNames[SIGTRAP] = "TRAP";
532 #endif
533 #ifdef SIGABRT
534 signalNames[SIGABRT] = "ABRT";
535 #endif
536 #ifdef SIGEMT
537 signalNames[SIGEMT] = "EMT";
538 #endif
539 #ifdef SIGFPE
540 signalNames[SIGFPE] = "FPE";
541 #endif
542 #ifdef SIGKILL
543 signalNames[SIGKILL] = "KILL";
544 #endif
545 #ifdef SIGBUS
546 signalNames[SIGBUS] = "BUS";
547 #endif
548 #ifdef SIGSEGV
549 signalNames[SIGSEGV] = "SEGV";
550 #endif
551 #ifdef SIGSYS
552 signalNames[SIGSYS] = "SYS";
553 #endif
554 #ifdef SIGPIPE
555 signalNames[SIGPIPE] = "PIPE";
556 #endif
557 #ifdef SIGALRM
558 signalNames[SIGALRM] = "ALRM";
559 #endif
560 #ifdef SIGTERM
561 signalNames[SIGTERM] = "TERM";
562 #endif
563 #ifdef SIGURG
564 signalNames[SIGURG] = "URG";
565 #endif
566 #ifdef SIGSTOP
567 signalNames[SIGSTOP] = "STOP";
568 #endif
569 #ifdef SIGTSTP
570 signalNames[SIGTSTP] = "TSTP";
571 #endif
572 #ifdef SIGCONT
573 signalNames[SIGCONT] = "CONT";
574 #endif
575 #ifdef SIGCHLD
576 signalNames[SIGCHLD] = "CHLD";
577 #endif
578 #ifdef SIGTTIN
579 signalNames[SIGTTIN] = "TTIN";
580 #endif
581 #ifdef SIGTTOU
582 signalNames[SIGTTOU] = "TTOU";
583 #endif
584 #ifdef SIGIO
585 signalNames[SIGIO] = "IO";
586 #endif
587 #ifdef SIGXCPU
588 signalNames[SIGXCPU] = "XCPU";
589 #endif
590 #ifdef SIGXFSZ
591 signalNames[SIGXFSZ] = "XFSZ";
592 #endif
593 #ifdef SIGVTALRM
594 signalNames[SIGVTALRM] = "VTALRM";
595 #endif
596 #ifdef SIGPROF
597 signalNames[SIGPROF] = "PROF";
598 #endif
599 #ifdef SIGWINCH
600 signalNames[SIGWINCH] = "WINCH";
601 #endif
602 #ifdef SIGINFO
603 signalNames[SIGINFO] = "INFO";
604 #endif
605 #ifdef SIGUSR1
606 signalNames[SIGUSR1] = "USR1";
607 #endif
608 #ifdef SIGUSR2
609 signalNames[SIGUSR2] = "USR2";
610 #endif
611 #ifdef SIGPWR
612 signalNames[SIGPWR] = "PWR";
613 #endif
614 #ifdef SIGPOLL
615 signalNames[SIGPOLL] = "POLL";
616 #endif
617 #ifdef SIGSTP
618 signalNames[SIGSTP] = "STP";
619 #endif
620 #ifdef SIGWAITING
621 signalNames[SIGWAITING] = "WAITING";
622 #endif
623 #ifdef SIGLWP
624 signalNames[SIGLWP] = "LWP";
625 #endif
626 #ifdef SIGFREEZE
627 signalNames[SIGFREEZE] = "FREEZE";
628 #endif
629 #ifdef SIGTHAW
630 signalNames[SIGTHAW] = "THAW";
631 #endif
632 #ifdef SIGCANCEL
633 signalNames[SIGCANCEL] = "CANCEL";
634 #endif
635 }