1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * David Korn <dgk@research.att.com> *
19 ***********************************************************************/
22 * Fault handling routines
34 #include "variables.h"
39 #define abortsig(sig) (sig==SIGABRT || sig==SIGBUS || sig==SIGILL || sig==SIGSEGV)
46 #if defined(VMFL) && (VMALLOC_VERSION>=20031205L)
48 * This exception handler is called after vmalloc() unlocks the region
50 static int malloc_done(Vmalloc_t
* vm
, int type
, Void_t
* val
, Vmdisc_t
* dp
)
59 * Most signals caught or ignored by the shell come here
61 void sh_fault(register int sig
)
63 register Shell_t
*shp
= sh_getinterp();
66 register struct checkpt
*pp
= (struct checkpt
*)shp
->jmplist
;
70 signal(sig
, sh_fault
);
77 astwinsize(2,&rows
,&cols
);
79 nv_putval(COLUMNS
, (char*)&v
, NV_INT32
|NV_RDONLY
);
81 nv_putval(LINES
, (char*)&v
, NV_INT32
|NV_RDONLY
);
87 /* critical region, save and process later */
91 trap
= shp
->st
.trapcom
[sig
];
92 if(sig
==SIGALRM
&& shp
->bltinfun
==b_sleep
)
96 shp
->trapnote
|= SH_SIGTRAP
;
97 shp
->sigflag
[sig
] |= SH_SIGTRAP
;
101 if(shp
->subshell
&& sig
!=SIGINT
&& sig
!=SIGQUIT
&& sig
!=SIGWINCH
&& sig
!=SIGCONT
)
103 shp
->exitval
= SH_EXITSIG
|sig
;
108 /* handle ignored signals */
111 flag
= shp
->sigflag
[sig
]&~SH_SIGOFF
;
114 if(sig
==SIGINT
&& (shp
->trapnote
&SH_SIGIGNORE
))
116 if(flag
&SH_SIGIGNORE
)
121 if((flag
&SH_SIGINTERACTIVE
) && sh_isstate(SH_INTERACTIVE
) && !sh_isstate(SH_FORKED
) && ! shp
->subshell
)
123 /* check for TERM signal between fork/exec */
124 if(sig
==SIGTERM
&& job
.in_critical
)
125 shp
->trapnote
|= SH_SIGTERM
;
130 if(pp
->mode
< SH_JMPFUN
)
131 pp
->mode
= SH_JMPFUN
;
133 pp
->mode
= SH_JMPEXIT
;
134 if(sig
==SIGABRT
|| (abortsig(sig
) && (ptr
= malloc(1))))
141 /* mark signal and continue */
142 shp
->trapnote
|= SH_SIGSET
;
143 if(sig
<= shp
->sigmax
)
144 shp
->sigflag
[sig
] |= SH_SIGSET
;
145 #if defined(VMFL) && (VMALLOC_VERSION>=20031205L)
148 /* abort inside malloc, process when malloc returns */
149 /* VMFL defined when using vmalloc() */
150 Vmdisc_t
* dp
= vmdisc(Vmregion
,0);
152 dp
->exceptf
= malloc_done
;
159 if(pp
->mode
==SH_JMPCMD
)
164 * propogate signal to foreground group
166 if(sig
==SIGHUP
&& job
.curpgid
)
167 killpg(job
.curpgid
,SIGHUP
);
177 shp
->trapnote
|= SH_SIGTSTP
;
178 if(pp
->mode
==SH_JMPCMD
&& sh_isstate(SH_STOPOK
))
188 if((error_info
.flags
&ERROR_NOTIFY
) && shp
->bltinfun
)
189 action
= (*shp
->bltinfun
)(-sig
,(char**)0,(void*)0);
193 if(shp
->bltinfun
&& shp
->bltindata
.notify
)
195 shp
->bltindata
.sigset
= 1;
198 shp
->trapnote
|= flag
;
199 if(sig
<= shp
->sigmax
)
200 shp
->sigflag
[sig
] |= flag
;
201 if(pp
->mode
==SH_JMPCMD
&& sh_isstate(SH_STOPOK
))
211 * initialize signal handling
213 void sh_siginit(void *ptr
)
215 Shell_t
*shp
= (Shell_t
*)ptr
;
217 register const struct shtable2
*tp
= shtab_signals
;
219 /* find the largest signal number in the table */
220 #if defined(SIGRTMIN) && defined(SIGRTMAX)
221 if ((n
= SIGRTMIN
) > 0 && (sig
= SIGRTMAX
) > n
&& sig
< SH_TRAP
)
223 shp
->sigruntime
[SH_SIGRTMIN
] = n
;
224 shp
->sigruntime
[SH_SIGRTMAX
] = sig
;
226 #endif /* SIGRTMIN && SIGRTMAX */
230 sig
= (tp
->sh_number
&((1<<SH_SIGBITS
)-1));
231 if (!(sig
-- & SH_TRAP
))
233 if ((tp
->sh_number
>>SH_SIGBITS
) & SH_SIGRUNTIME
)
234 sig
= shp
->sigruntime
[sig
];
235 if(sig
>n
&& sig
<SH_TRAP
)
241 shp
->st
.trapcom
= (char**)calloc(n
,sizeof(char*));
242 shp
->sigflag
= (unsigned char*)calloc(n
,1);
243 shp
->sigmsg
= (char**)calloc(n
,sizeof(char*));
244 for(tp
=shtab_signals
; sig
=tp
->sh_number
; tp
++)
246 n
= (sig
>>SH_SIGBITS
);
247 if((sig
&= ((1<<SH_SIGBITS
)-1)) > (shp
->sigmax
+1))
251 sig
= shp
->sigruntime
[sig
];
254 shp
->sigflag
[sig
] = n
;
256 shp
->sigmsg
[sig
] = (char*)tp
->sh_value
;
262 * Turn on trap handler for signal <sig>
264 void sh_sigtrap(register int sig
)
271 else if(!((flag
=sh
.sigflag
[sig
])&(SH_SIGFAULT
|SH_SIGOFF
)))
273 /* don't set signal if already set or off by parent */
274 if((fun
=signal(sig
,sh_fault
))==SIG_IGN
)
282 if(sig
==SIGALRM
&& fun
!=SIG_DFL
&& fun
!=sh_fault
)
285 flag
&= ~(SH_SIGSET
|SH_SIGTRAP
);
286 sh
.sigflag
[sig
] = flag
;
291 * set signal handler so sh_done is called for all caught signals
293 void sh_sigdone(void)
295 register int flag
, sig
= sh
.sigmax
;
296 sh
.sigflag
[0] |= SH_SIGFAULT
;
297 for(sig
=sh
.sigmax
; sig
>0; sig
--)
299 flag
= sh
.sigflag
[sig
];
300 if((flag
&(SH_SIGDONE
|SH_SIGIGNORE
|SH_SIGINTERACTIVE
)) && !(flag
&(SH_SIGFAULT
|SH_SIGOFF
)))
306 * Restore to default signals
307 * Free the trap strings if mode is non-zero
308 * If mode>1 then ignored traps cause signal to be ignored
310 void sh_sigreset(register int mode
)
313 register int flag
, sig
=sh
.st
.trapmax
;
316 if(trap
=sh
.st
.trapcom
[sig
])
318 flag
= sh
.sigflag
[sig
]&~(SH_SIGTRAP
|SH_SIGSET
);
323 sh
.st
.trapcom
[sig
] = 0;
325 else if(sig
&& mode
>1)
329 flag
&= ~SH_SIGFAULT
;
332 sh
.sigflag
[sig
] = flag
;
335 for(sig
=SH_DEBUGTRAP
-1;sig
>=0;sig
--)
337 if(trap
=sh
.st
.trap
[sig
])
345 sh
.st
.trapcom
[0] = 0;
352 * free up trap if set and restore signal handler if modified
354 void sh_sigclear(register int sig
)
356 register int flag
= sh
.sigflag
[sig
];
359 if(!(flag
&SH_SIGFAULT
))
361 flag
&= ~(SH_SIGTRAP
|SH_SIGSET
);
362 if(trap
=sh
.st
.trapcom
[sig
])
366 sh
.st
.trapcom
[sig
]=0;
368 sh
.sigflag
[sig
] = flag
;
375 void sh_chktrap(void)
377 register int sig
=sh
.st
.trapmax
;
379 if(!(sh
.trapnote
&~SH_SIGIGNORE
))
381 sh
.trapnote
&= ~SH_SIGTRAP
;
382 /* execute errexit trap first */
383 if(sh_isstate(SH_ERREXIT
) && sh
.exitval
)
385 int sav_trapnote
= sh
.trapnote
;
386 sh
.trapnote
&= ~SH_SIGSET
;
387 if(sh
.st
.trap
[SH_ERRTRAP
])
389 trap
= sh
.st
.trap
[SH_ERRTRAP
];
390 sh
.st
.trap
[SH_ERRTRAP
] = 0;
392 sh
.st
.trap
[SH_ERRTRAP
] = trap
;
394 sh
.trapnote
= sav_trapnote
;
395 if(sh_isoption(SH_ERREXIT
))
397 struct checkpt
*pp
= (struct checkpt
*)sh
.jmplist
;
398 pp
->mode
= SH_JMPEXIT
;
402 if(sh
.sigflag
[SIGALRM
]&SH_SIGALRM
)
405 if((sh
.sigflag
[SIGCHLD
]&SH_SIGTRAP
) && sh
.st
.trapcom
[SIGCHLD
])
406 job_chldtrap(&sh
,sh
.st
.trapcom
[SIGCHLD
],1);
407 #endif /* SHOPT_BGX */
413 #endif /* SHOPT_BGX */
414 if(sh
.sigflag
[sig
]&SH_SIGTRAP
)
416 sh
.sigflag
[sig
] &= ~SH_SIGTRAP
;
417 if(trap
=sh
.st
.trapcom
[sig
])
420 if(sig
==SIGPIPE
&& (fp
=sfpool((Sfio_t
*)0,sh
.outpool
,SF_WRITE
)) && sferror(fp
))
422 sh
.oldexit
= SH_EXITSIG
|sig
;
431 * parse and execute the given trap string, stream or tree depending on mode
432 * mode==0 for string, mode==1 for stream, mode==2 for parse tree
434 int sh_trap(const char *trap
, int mode
)
436 Shell_t
*shp
= sh_getinterp();
437 int jmpval
, savxit
= shp
->exitval
;
438 int was_history
= sh_isstate(SH_HISTORY
);
439 int was_verbose
= sh_isstate(SH_VERBOSE
);
440 int staktop
= staktell();
441 char *savptr
= stakfreeze(0);
446 memcpy(ifstable
,shp
->ifstable
,sizeof(ifstable
));
447 sh_offstate(SH_HISTORY
);
448 sh_offstate(SH_VERBOSE
);
450 sh_pushcontext(&buff
,SH_JMPTRAP
);
451 jmpval
= sigsetjmp(buff
.buff
,0);
455 sh_exec((Shnode_t
*)trap
,sh_isstate(SH_ERREXIT
));
462 sp
= sfopen(NIL(Sfio_t
*),trap
,"s");
468 if(jmpval
==SH_JMPSCRIPT
)
472 if(jmpval
==SH_JMPEXIT
)
473 savxit
= shp
->exitval
;
477 sh_popcontext(&buff
);
479 sfsync(shp
->outpool
);
480 if(!shp
->indebug
&& jmpval
!=SH_JMPEXIT
&& jmpval
!=SH_JMPFUN
)
482 stakset(savptr
,staktop
);
484 memcpy(shp
->ifstable
,ifstable
,sizeof(ifstable
));
486 sh_onstate(SH_HISTORY
);
488 sh_onstate(SH_VERBOSE
);
490 if(jmpval
>SH_JMPTRAP
&& (((struct checkpt
*)shp
->jmpbuffer
)->prev
|| ((struct checkpt
*)shp
->jmpbuffer
)->mode
==SH_JMPSCRIPT
))
491 siglongjmp(*shp
->jmplist
,jmpval
);
492 return(shp
->exitval
);
496 * exit the current scope and jump to an earlier one based on pp->mode
498 void sh_exit(register int xno
)
501 register struct checkpt
*pp
= (struct checkpt
*)shp
->jmplist
;
503 register Sfio_t
* pool
;
506 shp
->exitval
|= (sig
=shp
->lastsig
);
508 if(shp
->trapnote
&SH_SIGTSTP
)
510 /* ^Z detected by the shell */
512 shp
->sigflag
[SIGTSTP
] = 0;
513 if(!shp
->subshell
&& sh_isstate(SH_MONITOR
) && !sh_isstate(SH_STOPOK
))
515 if(sh_isstate(SH_TIMING
))
517 /* Handles ^Z for shell builtins, subshells, and functs */
519 sh_onstate(SH_MONITOR
);
520 sh_offstate(SH_STOPOK
);
522 if(!shp
->subshell
&& (sig
=sh_fork(0,NIL(int*))))
525 job
.parent
= (pid_t
)-1;
528 shp
->sigflag
[SIGTSTP
] = 0;
529 /* wait for child to stop */
530 shp
->exitval
= (SH_EXITSIG
|SIGTSTP
);
531 /* return to prompt mode */
532 pp
->mode
= SH_JMPERREXIT
;
538 /* child process, put to sleep */
539 sh_offstate(SH_STOPOK
);
540 sh_offstate(SH_MONITOR
);
541 shp
->sigflag
[SIGTSTP
] = 0;
543 killpg(job
.curpgid
,SIGTSTP
);
547 shp
->exitval
= (xno
&SH_EXITMASK
);
552 /* unlock output pool */
553 sh_offstate(SH_NOTRACK
);
554 if(!(pool
=sfpool(NIL(Sfio_t
*),shp
->outpool
,SF_WRITE
)))
555 pool
= shp
->outpool
; /* can't happen? */
558 if(shp
->lastsig
==SIGPIPE
)
567 #endif /* SHOPT_TYPEDEF*/
568 if(pp
->mode
== SH_JMPSCRIPT
&& !pp
->prev
)
571 siglongjmp(pp
->buff
,pp
->mode
);
574 static void array_notify(Namval_t
*np
, void *data
)
576 Namarr_t
*ap
= nv_arrayptr(np
);
579 (*ap
->fun
)(np
, 0, NV_AFREE
);
583 * This is the exit routine for the shell
586 void sh_done(void *ptr
, register int sig
)
588 Shell_t
*shp
= (Shell_t
*)ptr
;
590 register int savxit
= shp
->exitval
;
594 savxit
= SH_EXITSIG
|sig
;
596 (*shp
->userinit
)(shp
, -1);
597 if(t
=shp
->st
.trapcom
[0])
599 shp
->st
.trapcom
[0]=0; /*should free but not long */
600 shp
->oldexit
= savxit
;
602 savxit
= shp
->exitval
;
606 /* avoid recursive call for set -e */
607 sh_offstate(SH_ERREXIT
);
610 nv_scan(shp
->var_tree
,array_notify
,(void*)0,NV_ARRAY
,NV_ARRAY
);
614 #endif /* SHOPT_ACCT */
615 #if SHOPT_VSH || SHOPT_ESH
616 if(sh_isoption(SH_EMACS
)||sh_isoption(SH_VI
)||sh_isoption(SH_GMACS
))
620 if((sh_isoption(SH_INTERACTIVE
) && shp
->login_sh
) || (!sh_isoption(SH_INTERACTIVE
) && (sig
==SIGHUP
)))
621 job_walk(sfstderr
,job_terminate
,SIGHUP
,NIL(char**));
624 if(nv_search("VMTRACE", shp
->var_tree
,0))
625 strmatch((char*)0,(char*)0);
626 sfsync((Sfio_t
*)sfstdin
);
627 sfsync((Sfio_t
*)shp
->outpool
);
628 sfsync((Sfio_t
*)sfstdout
);
629 if(savxit
&SH_EXITSIG
)
630 sig
= savxit
&SH_EXITMASK
;
633 /* generate fault termination code */
640 if(sh_isoption(SH_NOEXEC
))
641 kiaclose((Lex_t
*)shp
->lex_context
);
642 #endif /* SHOPT_KIA */
643 exit(savxit
&SH_EXITMASK
);