resetting manifest requested domain to floor
[platform/upstream/expect.git] / exp_trap.c
1 /* exp_trap.c - Expect's trap command
2
3 Written by: Don Libes, NIST, 9/1/93
4
5 Design and implementation of this program was paid for by U.S. tax
6 dollars.  Therefore it is public domain.  However, the author and NIST
7 would appreciate credit if this program or parts of it are used.
8
9 */
10
11 #include "expect_cf.h"
12
13 #include <stdio.h>
14 #include <signal.h>
15 #include <sys/types.h>
16 #include <string.h>
17
18 #ifdef HAVE_SYS_WAIT_H
19 #include <sys/wait.h>
20 #endif
21 #ifdef HAVE_STRING_H
22 #include <string.h>
23 #endif
24
25 #if defined(SIGCLD) && !defined(SIGCHLD)
26 #define SIGCHLD SIGCLD
27 #endif
28
29 #include "tcl.h"
30
31 #include "exp_rename.h"
32 #include "exp_prog.h"
33 #include "exp_command.h"
34 #include "exp_log.h"
35
36 #ifdef TCL_DEBUGGER
37 #include "tcldbg.h"
38 #endif
39
40 #define NO_SIG 0
41
42 static struct trap {
43         char *action;           /* Tcl command to execute upon sig */
44                                 /* Each is handled by the eval_trap_action */
45         int mark;               /* TRUE if signal has occurred */
46         Tcl_Interp *interp;     /* interp to use or 0 if we should use the */
47                                 /* interpreter active at the time the sig */
48                                 /* is processed */
49         int code;               /* return our new code instead of code */
50                                 /* available when signal is processed */
51         CONST char *name;       /* name of signal */
52         int reserved;           /* if unavailable for trapping */
53 } traps[NSIG];
54
55 int sigchld_count = 0;  /* # of sigchlds caught but not yet processed */
56
57 static int eval_trap_action();
58
59 static int got_sig;             /* this records the last signal received */
60                                 /* it is only a hint and can be wiped out */
61                                 /* by multiple signals, but it will always */
62                                 /* be left with a valid signal that is */
63                                 /* pending */
64
65 static Tcl_AsyncHandler async_handler;
66
67 static CONST char *
68 signal_to_string(sig)
69 int sig;
70 {
71         if (sig <= 0 || sig > NSIG) return("SIGNAL OUT OF RANGE");
72         return(traps[sig].name);
73 }
74
75 /* current sig being processed by user sig handler */
76 static int current_sig = NO_SIG;
77
78 int exp_nostack_dump = FALSE;   /* TRUE if user has requested unrolling of */
79                                 /* stack with no trace */
80
81
82
83 /*ARGSUSED*/
84 static int
85 tophalf(clientData,interp,code)
86 ClientData clientData;
87 Tcl_Interp *interp;
88 int code;
89 {
90         struct trap *trap;      /* last trap processed */
91         int rc;
92         int i;
93         Tcl_Interp *sig_interp;
94
95         expDiagLog("sighandler: handling signal(%d)\r\n",got_sig);
96
97         if (got_sig <= 0 || got_sig >= NSIG) {
98                 expErrorLog("caught impossible signal %d\r\n",got_sig);
99                 abort();
100         }
101
102         /* start to work on this sig.  got_sig can now be overwritten */
103         /* and it won't cause a problem */
104         current_sig = got_sig;
105         trap = &traps[current_sig];
106
107         trap->mark = FALSE;
108
109         /* decrement below looks dangerous */
110         /* Don't we need to temporarily block bottomhalf? */
111         if (current_sig == SIGCHLD) {
112                 sigchld_count--;
113                 expDiagLog("sigchld_count-- == %d\n",sigchld_count);
114         }
115
116         if (!trap->action) {
117                 /* In this one case, we let ourselves be called when no */
118                 /* signaler predefined, since we are calling explicitly */
119                 /* from another part of the program, and it is just simpler */
120                 if (current_sig == 0) return code;
121                 expErrorLog("caught unexpected signal: %s (%d)\r\n",
122                         signal_to_string(current_sig),current_sig);
123                 abort();
124         }
125
126         if (trap->interp) {
127                 /* if trap requested original interp, use it */
128                 sig_interp = trap->interp;
129         } else if (interp) {
130                 /* else if another interp is available, use it */
131                 sig_interp = interp;
132         } else {
133                 /* fall back to exp_interp */
134                 sig_interp = exp_interp;
135         }
136
137         rc = eval_trap_action(sig_interp,current_sig,trap,code);
138         current_sig = NO_SIG;
139
140         /*
141          * scan for more signals to process
142          */
143
144         /* first check for additional SIGCHLDs */
145         if (sigchld_count) {
146                 got_sig = SIGCHLD;
147                 traps[SIGCHLD].mark = TRUE;
148                 Tcl_AsyncMark(async_handler);
149         } else {
150                 got_sig = -1;
151                 for (i=1;i<NSIG;i++) {
152                         if (traps[i].mark) {
153                                 got_sig = i;
154                                 Tcl_AsyncMark(async_handler);
155                                 break;
156                         }
157                 }
158         }
159         return rc;
160 }
161
162 #ifdef REARM_SIG
163 int sigchld_sleep;
164 static int rearm_sigchld = FALSE;       /* TRUE if sigchld needs to be */
165                                         /* rearmed (i.e., because it has */
166                                         /* just gone off) */
167 static int rearming_sigchld = FALSE;
168 #endif
169
170 /* called upon receipt of a user-declared signal */
171 static void
172 bottomhalf(sig)
173 int sig;
174 {
175 #ifdef REARM_SIG
176         /*
177          * tiny window of death if same signal should arrive here
178          * before we've reinstalled it
179          */
180
181         /* In SV, sigchld must be rearmed after wait to avoid recursion */
182         if (sig != SIGCHLD) {
183                 signal(sig,bottomhalf);
184         } else {
185                 /* request rearm */
186                 rearm_sigchld = TRUE;
187                 if (rearming_sigchld) sigchld_sleep = TRUE;
188         }
189 #endif
190
191         traps[sig].mark = TRUE;
192         got_sig = sig;          /* just a hint - can be wiped out by another */
193         Tcl_AsyncMark(async_handler);
194
195         /* if we are called while this particular async is being processed */
196         /* original async_proc will turn off "mark" so that when async_proc */
197         /* is recalled, it will see that nothing was left to do */
198
199         /* In case of SIGCHLD though, we must recall it as many times as
200          * we have received it.
201          */
202         if (sig == SIGCHLD) {
203                 sigchld_count++;
204         }
205 #if 0
206         /* if we are doing an i_read, restart it */
207 #ifdef HAVE_SIGLONGJMP
208       if (env_valid && (sig != 0)) siglongjmp(env,2);
209 #else
210       if (env_valid && (sig != 0)) longjmp(env,2);
211 #endif  /* HAVE_SIGLONGJMP */
212 #endif /* 0 */
213 }
214
215 /*ARGSUSED*/
216 void
217 exp_rearm_sigchld(interp)
218 Tcl_Interp *interp;
219 {
220 #ifdef REARM_SIG
221         if (rearm_sigchld) {
222                 rearm_sigchld = FALSE;
223                 rearming_sigchld = TRUE;
224                 signal(SIGCHLD,bottomhalf);
225         }
226
227         rearming_sigchld = FALSE;
228
229         /* if the rearming immediately caused another SIGCHLD, slow down */
230         /* It's probably one of Tcl's intermediary pipeline processes that */
231         /* Tcl hasn't caught up with yet. */
232         if (sigchld_sleep) {
233                 exp_dsleep(interp,0.2);
234                 sigchld_sleep = FALSE;
235         }
236 #endif
237 }
238
239
240 void
241 exp_init_trap()
242 {
243         int i;
244
245         for (i=1;i<NSIG;i++) {
246                 traps[i].name = Tcl_SignalId(i);
247                 traps[i].action = 0;
248                 traps[i].reserved = FALSE;
249         }
250
251         /*
252          * fix up any special cases
253          */
254
255 #if defined(SIGCLD)
256         /* Tcl names it SIGCLD, not good for portable scripts */
257         traps[SIGCLD].name = "SIGCHLD";
258 #endif
259 #if defined(SIGALRM)
260         traps[SIGALRM].reserved = TRUE;
261 #endif
262 #if defined(SIGKILL)
263         traps[SIGKILL].reserved = TRUE;
264 #endif
265 #if defined(SIGSTOP)
266         traps[SIGSTOP].reserved = TRUE;
267 #endif
268
269         async_handler = Tcl_AsyncCreate(tophalf,(ClientData)0);
270
271 }
272
273 /* given signal index or name as string, */
274 /* returns signal index or -1 if bad arg */
275 int
276 exp_string_to_signal(interp,s)
277 Tcl_Interp *interp;
278 char *s;
279 {
280         int sig;
281         CONST char *name;
282
283         /* try interpreting as an integer */
284         if (1 == sscanf(s,"%d",&sig)) {
285                 if (sig > 0 && sig < NSIG) return sig;
286         } else {
287                 /* try interpreting as a string */
288                 for (sig=1;sig<NSIG;sig++) {
289                         name = traps[sig].name;
290                         if (streq(s,name) || streq(s,name+3)) return(sig);
291                 }
292         }
293
294         exp_error(interp,"invalid signal %s",s);
295         
296         return -1;
297 }
298
299 /*ARGSUSED*/
300 int
301 Exp_TrapObjCmd(clientData, interp, objc, objv)
302 ClientData clientData;
303 Tcl_Interp *interp;
304 int objc;
305 Tcl_Obj *CONST objv[];
306 {
307         char *action = 0;
308         int n;          /* number of signals in list */
309         Tcl_Obj **list; /* list of signals */
310         char *arg;
311         int len;        /* length of action */
312         int i;
313         int show_name = FALSE;  /* if user asked for current sig by name */
314         int show_number = FALSE;/* if user asked for current sig by number */
315         int show_max = FALSE;   /* if user asked for NSIG-1 */
316         int rc = TCL_OK;
317         int new_code = FALSE;   /* if action result should overwrite orig */
318         Tcl_Interp *new_interp = interp;/* interp in which to evaluate */
319                                         /* action when signal occurs */
320
321         objc--; objv++;
322
323         while (objc) {
324           arg = Tcl_GetString(*objv);
325
326                 if (streq(arg,"-code")) {
327                         objc--; objv++; 
328                         new_code = TRUE;
329                 } else if (streq(arg,"-interp")) {
330                         objc--; objv++; 
331                         new_interp = 0;
332                 } else if (streq(arg,"-name")) {
333                         objc--; objv++;
334                         show_name = TRUE;
335                 } else if (streq(arg,"-number")) {
336                         objc--; objv++;
337                         show_number = TRUE;
338                 } else if (streq(arg,"-max")) {
339                         objc--; objv++;
340                         show_max = TRUE;
341                 } else break;
342         }
343
344         if (show_name || show_number || show_max) {
345                 if (objc > 0) goto usage_error;
346                 if (show_max) {
347                   Tcl_SetObjResult(interp,Tcl_NewIntObj(NSIG-1));
348                 }
349
350                 if (current_sig == NO_SIG) {
351                   Tcl_SetResult(interp,"no signal in progress",TCL_STATIC);
352                   return TCL_ERROR;
353                 }
354                 if (show_name) {
355                   /* skip over "SIG" */
356                   /* TIP 27: Casting away the CONST should be ok because of TCL_STATIC
357                    */
358                   Tcl_SetResult(interp,(char*)signal_to_string(current_sig) + 3,TCL_STATIC);
359                 } else {
360                   Tcl_SetObjResult(interp,Tcl_NewIntObj(current_sig));
361                 }
362                 return TCL_OK;
363         }
364
365         if (objc == 0 || objc > 2) goto usage_error;
366
367         if (objc == 1) {
368                 int sig = exp_string_to_signal(interp,arg);
369                 if (sig == -1) return TCL_ERROR;
370
371                 if (traps[sig].action) {
372                         Tcl_SetResult(interp,traps[sig].action,TCL_STATIC);
373                 } else {
374                         Tcl_SetResult(interp,"SIG_DFL",TCL_STATIC);
375                 }
376                 return TCL_OK;
377         }
378
379         action = arg;
380
381         /* objv[1] is the list of signals - crack it open */
382         if (TCL_OK != Tcl_ListObjGetElements(interp,objv[1],&n,&list)) {
383           return TCL_ERROR;
384         }
385
386         for (i=0;i<n;i++) {
387           char *s;
388           int sig;
389
390           s = Tcl_GetString(list[i]);
391
392                 sig = exp_string_to_signal(interp,s);
393                 if (sig == -1) {
394                         rc = TCL_ERROR;
395                         break;
396                 }
397
398                 if (traps[sig].reserved) {
399                         exp_error(interp,"cannot trap %s",signal_to_string(sig));
400                         rc = TCL_ERROR;
401                         break;
402                 }
403
404                 expDiagLog("trap: setting up signal %d (\"%s\")\r\n",sig,s);
405                 if (traps[sig].action) ckfree(traps[sig].action);
406                 if (streq(action,"SIG_DFL")) {
407                         /* should've been free'd by now if nec. */
408                         traps[sig].action = 0;
409                         signal(sig,SIG_DFL);
410 #ifdef REARM_SIG
411                         if (sig == SIGCHLD)
412                                 rearm_sigchld = FALSE;
413 #endif /*REARM_SIG*/
414                 } else {
415                         len = 1 + strlen(action);
416                         traps[sig].action = ckalloc(len);
417                         memcpy(traps[sig].action,action,len);
418                         traps[sig].interp = new_interp;
419                         traps[sig].code = new_code;
420                         if (streq(action,"SIG_IGN")) {
421                                 signal(sig,SIG_IGN);
422                         } else signal(sig,bottomhalf);
423                 }
424         }
425         /* It is no longer necessary to free the split list since it */
426         /* is still owned by Tcl, yes? */
427         /*      ckfree((char *)list); */
428         return(rc);
429  usage_error:
430         exp_error(interp,"usage: trap [command or SIG_DFL or SIG_IGN] {list of signals}");
431         return TCL_ERROR;
432 }
433
434 /* called by tophalf() to process the given signal */
435 static int
436 eval_trap_action(interp,sig,trap,oldcode)
437 Tcl_Interp *interp;
438 int sig;
439 struct trap *trap;
440 int oldcode;
441 {
442         int code_flag;
443         int newcode;
444         Tcl_Obj *eip;   /* errorInfo */
445         Tcl_Obj *ecp;   /* errorCode */
446         Tcl_Obj *irp;   /* interp's result */
447
448         expDiagLogU("async event handler: Tcl_Eval(");
449         expDiagLogU(trap->action);
450         expDiagLogU(")\r\n");
451
452         /* save to prevent user from redefining trap->code while trap */
453         /* is executing */
454         code_flag = trap->code;
455
456         if (!code_flag) {
457                 /* 
458                  * save return values
459                  */
460
461                 eip = Tcl_GetVar2Ex(interp,"errorInfo","",TCL_GLOBAL_ONLY);
462                 if (eip) eip = Tcl_DuplicateObj(eip);
463                 ecp = Tcl_GetVar2Ex(interp,"errorCode","",TCL_GLOBAL_ONLY);
464                 if (ecp) ecp = Tcl_DuplicateObj(ecp);
465                 irp = Tcl_GetObjResult(interp);
466                 if (irp) irp = Tcl_DuplicateObj(irp);
467         }
468
469         newcode = Tcl_GlobalEval(interp,trap->action);
470
471         /*
472          * if new code is to be ignored (usual case - see "else" below)
473          *      allow only OK/RETURN from trap, otherwise complain
474          */
475
476         if (code_flag) {
477                 expDiagLog("return value = %d for trap %s, action ",newcode,signal_to_string(sig));
478                 expDiagLogU(trap->action);
479                 expDiagLogU("\r\n");
480                 if (0 != strcmp(Tcl_GetStringResult(interp),"")) {
481
482                         /*
483                          * Check errorinfo and see if it contains -nostack.
484                          * This shouldn't be necessary, but John changed the
485                          * top level interp so that it distorts arbitrary
486                          * return values into TCL_ERROR, so by the time we
487                          * get back, we'll have lost the value of errorInfo
488                          */
489
490                         eip = Tcl_GetVar2Ex(interp,"errorInfo","",TCL_GLOBAL_ONLY);
491                         if (eip) {
492                           exp_nostack_dump = (0 == strncmp("-nostack",Tcl_GetString(eip),8));
493                         }
494                 }
495         } else if (newcode != TCL_OK && newcode != TCL_RETURN) {
496           if (newcode != TCL_ERROR) {
497             exp_error(interp,"return value = %d for trap %s, action %s\r\n",newcode,signal_to_string(sig),trap->action);
498           }
499           Tcl_BackgroundError(interp);
500         }
501
502         if (!code_flag) {
503                 /*
504                  * restore values
505                  */
506                 Tcl_ResetResult(interp);        /* turns off Tcl's internal */
507                    /* flags: ERR_IN_PROGRESS, ERROR_CODE_SET */
508                    /* This also wipes clean errorInfo/Code/result which is why */
509                    /* all the calls to Tcl_Dup earlier */
510
511                 if (eip) {
512                   /* odd that Tcl doesn't have a call that does all this at once */
513                   int len;
514                   char *s = Tcl_GetStringFromObj(eip,&len);
515                   Tcl_AddObjErrorInfo(interp,s,len);
516                   Tcl_DecrRefCount(eip);
517                   /* we never incr'd it, but the code allows this */
518                 } else {
519                   Tcl_UnsetVar(interp,"errorInfo",0);
520                 }
521
522                 /* restore errorCode.  Note that Tcl_AddErrorInfo (above) */
523                 /* resets it to NONE.  If the previous value is NONE, it's */
524                 /* important to avoid calling Tcl_SetErrorCode since this */
525                 /* with cause Tcl to set its internal ERROR_CODE_SET flag. */
526                 if (ecp) {
527                   if (!streq("NONE",Tcl_GetString(ecp)))
528                     Tcl_SetErrorCode(interp,ecp);
529                   /* we're just passing on the errorcode obj */
530                   /* presumably, Tcl will incr ref count */
531                 } else {
532                   Tcl_UnsetVar(interp,"errorCode",0);
533                 }
534
535                 newcode = oldcode;
536
537                 /* note that since newcode gets overwritten here by old code */
538                 /* it is possible to return in the middle of a trap by using */
539                 /* "return" (or "continue" for that matter)! */
540         }
541         return newcode;
542 }
543
544 static struct exp_cmd_data
545 cmd_data[]  = {
546 {"trap",        Exp_TrapObjCmd, 0,      (ClientData)EXP_SPAWN_ID_BAD,   0},
547 {0}};
548
549 void
550 exp_init_trap_cmds(interp)
551 Tcl_Interp *interp;
552 {
553         exp_create_commands(interp,cmd_data);
554 }
555