diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
commit | 3950ffe2a485479f6561c27364d3d7df5a21d124 (patch) | |
tree | 468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/sh/fault.c | |
download | ksh-upstream.tar.gz |
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/sh/fault.c')
-rw-r--r-- | src/cmd/ksh93/sh/fault.c | 672 |
1 files changed, 672 insertions, 0 deletions
diff --git a/src/cmd/ksh93/sh/fault.c b/src/cmd/ksh93/sh/fault.c new file mode 100644 index 0000000..2d22e42 --- /dev/null +++ b/src/cmd/ksh93/sh/fault.c @@ -0,0 +1,672 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2012 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Fault handling routines + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include <fcin.h> +#include "io.h" +#include "history.h" +#include "shlex.h" +#include "variables.h" +#include "jobs.h" +#include "path.h" +#include "builtins.h" +#include "ulimit.h" + +#define abortsig(sig) (sig==SIGABRT || sig==SIGBUS || sig==SIGILL || sig==SIGSEGV) + +static char indone; +static int cursig = -1; + +#if !_std_malloc +# include <vmalloc.h> +#endif +#if defined(VMFL) && (VMALLOC_VERSION>=20031205L) + /* + * This exception handler is called after vmalloc() unlocks the region + */ + static int malloc_done(Vmalloc_t* vm, int type, Void_t* val, Vmdisc_t* dp) + { + dp->exceptf = 0; + sh_exit(SH_EXITSIG); + return(0); + } +#endif + +/* + * Most signals caught or ignored by the shell come here +*/ +void sh_fault(register int sig) +{ + register Shell_t *shp = sh_getinterp(); + register int flag=0; + register char *trap; + register struct checkpt *pp = (struct checkpt*)shp->jmplist; + int action=0; + /* reset handler */ + if(!(sig&SH_TRAP)) + signal(sig, sh_fault); + sig &= ~SH_TRAP; +#ifdef SIGWINCH + if(sig==SIGWINCH) + { + int rows=0, cols=0; + int32_t v; + astwinsize(2,&rows,&cols); + if(v = cols) + nv_putval(COLUMNS, (char*)&v, NV_INT32|NV_RDONLY); + if(v = rows) + nv_putval(LINES, (char*)&v, NV_INT32|NV_RDONLY); + shp->winch++; + } +#endif /* SIGWINCH */ + trap = shp->st.trapcom[sig]; + if(shp->savesig) + { + /* critical region, save and process later */ + if(!(shp->sigflag[sig]&SH_SIGIGNORE)) + shp->savesig = sig; + return; + } + if(sig==SIGALRM && shp->bltinfun==b_sleep) + { + if(trap && *trap) + { + shp->trapnote |= SH_SIGTRAP; + shp->sigflag[sig] |= SH_SIGTRAP; + } + return; + } + if(shp->subshell && trap && sig!=SIGINT && sig!=SIGQUIT && sig!=SIGWINCH && sig!=SIGCONT) + { + shp->exitval = SH_EXITSIG|sig; + sh_subfork(); + shp->exitval = 0; + return; + } + /* handle ignored signals */ + if(trap && *trap==0) + return; + flag = shp->sigflag[sig]&~SH_SIGOFF; + if(!trap) + { + if(sig==SIGINT && (shp->trapnote&SH_SIGIGNORE)) + return; + if(flag&SH_SIGIGNORE) + { + if(shp->subshell) + shp->ignsig = sig; + sigrelease(sig); + return; + } + if(flag&SH_SIGDONE) + { + void *ptr=0; + if((flag&SH_SIGINTERACTIVE) && sh_isstate(SH_INTERACTIVE) && !sh_isstate(SH_FORKED) && ! shp->subshell) + { + /* check for TERM signal between fork/exec */ + if(sig==SIGTERM && job.in_critical) + shp->trapnote |= SH_SIGTERM; + return; + } + shp->lastsig = sig; + sigrelease(sig); + if(pp->mode != SH_JMPSUB) + { + if(pp->mode < SH_JMPSUB) + pp->mode = shp->subshell?SH_JMPSUB:SH_JMPFUN; + else + pp->mode = SH_JMPEXIT; + } + if(shp->subshell) + sh_exit(SH_EXITSIG); + if(sig==SIGABRT || (abortsig(sig) && (ptr = malloc(1)))) + { + if(ptr) + free(ptr); + sh_done(shp,sig); + } + /* mark signal and continue */ + shp->trapnote |= SH_SIGSET; + if(sig <= shp->gd->sigmax) + shp->sigflag[sig] |= SH_SIGSET; +#if defined(VMFL) && (VMALLOC_VERSION>=20031205L) + if(abortsig(sig)) + { + /* abort inside malloc, process when malloc returns */ + /* VMFL defined when using vmalloc() */ + Vmdisc_t* dp = vmdisc(Vmregion,0); + if(dp) + dp->exceptf = malloc_done; + } +#endif + return; + } + } + errno = 0; + if(pp->mode==SH_JMPCMD || (pp->mode==1 && shp->bltinfun) && !(flag&SH_SIGIGNORE)) + shp->lastsig = sig; + if(trap) + { + /* + * propogate signal to foreground group + */ + if(sig==SIGHUP && job.curpgid) + killpg(job.curpgid,SIGHUP); + flag = SH_SIGTRAP; + } + else + { + shp->lastsig = sig; + flag = SH_SIGSET; +#ifdef SIGTSTP + if(sig==SIGTSTP) + { + shp->trapnote |= SH_SIGTSTP; + if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK)) + { + sigrelease(sig); + sh_exit(SH_EXITSIG); + flag = 0; + } + } +#endif /* SIGTSTP */ + } +#ifdef ERROR_NOTIFY + if((error_info.flags&ERROR_NOTIFY) && shp->bltinfun) + action = (*shp->bltinfun)(-sig,(char**)0,(void*)0); + if(action>0) + return; +#endif + if(shp->bltinfun && shp->bltindata.notify) + { + shp->bltindata.sigset = 1; + return; + } + shp->trapnote |= flag; + if(sig <= shp->gd->sigmax) + shp->sigflag[sig] |= flag; + if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK)) + { + if(action<0) + return; + sigrelease(sig); + sh_exit(SH_EXITSIG); + } +} + +/* + * initialize signal handling + */ +void sh_siginit(void *ptr) +{ + Shell_t *shp = (Shell_t*)ptr; + register int sig, n; + register const struct shtable2 *tp = shtab_signals; + sig_begin(); + /* find the largest signal number in the table */ +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if ((n = SIGRTMIN) > 0 && (sig = SIGRTMAX) > n && sig < SH_TRAP) + { + shp->gd->sigruntime[SH_SIGRTMIN] = n; + shp->gd->sigruntime[SH_SIGRTMAX] = sig; + } +#endif /* SIGRTMIN && SIGRTMAX */ + n = SIGTERM; + while(*tp->sh_name) + { + sig = (tp->sh_number&((1<<SH_SIGBITS)-1)); + if (!(sig-- & SH_TRAP)) + { + if ((tp->sh_number>>SH_SIGBITS) & SH_SIGRUNTIME) + sig = shp->gd->sigruntime[sig]; + if(sig>n && sig<SH_TRAP) + n = sig; + } + tp++; + } + shp->gd->sigmax = n++; + shp->st.trapcom = (char**)calloc(n,sizeof(char*)); + shp->sigflag = (unsigned char*)calloc(n,1); + shp->gd->sigmsg = (char**)calloc(n,sizeof(char*)); + for(tp=shtab_signals; sig=tp->sh_number; tp++) + { + n = (sig>>SH_SIGBITS); + if((sig &= ((1<<SH_SIGBITS)-1)) > (shp->gd->sigmax+1)) + continue; + sig--; + if(n&SH_SIGRUNTIME) + sig = shp->gd->sigruntime[sig]; + if(sig>=0) + { + shp->sigflag[sig] = n; + if(*tp->sh_name) + shp->gd->sigmsg[sig] = (char*)tp->sh_value; + } + } +} + +/* + * Turn on trap handler for signal <sig> + */ +void sh_sigtrap(register int sig) +{ + register int flag; + void (*fun)(int); + sh.st.otrapcom = 0; + if(sig==0) + sh_sigdone(); + else if(!((flag=sh.sigflag[sig])&(SH_SIGFAULT|SH_SIGOFF))) + { + /* don't set signal if already set or off by parent */ + if((fun=signal(sig,sh_fault))==SIG_IGN) + { + signal(sig,SIG_IGN); + flag |= SH_SIGOFF; + } + else + { + flag |= SH_SIGFAULT; + if(sig==SIGALRM && fun!=SIG_DFL && fun!=sh_fault) + signal(sig,fun); + } + flag &= ~(SH_SIGSET|SH_SIGTRAP); + sh.sigflag[sig] = flag; + } +} + +/* + * set signal handler so sh_done is called for all caught signals + */ +void sh_sigdone(void) +{ + register int flag, sig = shgd->sigmax; + sh.sigflag[0] |= SH_SIGFAULT; + for(sig=shgd->sigmax; sig>0; sig--) + { + flag = sh.sigflag[sig]; + if((flag&(SH_SIGDONE|SH_SIGIGNORE|SH_SIGINTERACTIVE)) && !(flag&(SH_SIGFAULT|SH_SIGOFF))) + sh_sigtrap(sig); + } +} + +/* + * Restore to default signals + * Free the trap strings if mode is non-zero + * If mode>1 then ignored traps cause signal to be ignored + */ +void sh_sigreset(register int mode) +{ + register char *trap; + register int flag, sig=sh.st.trapmax; + while(sig-- > 0) + { + if(trap=sh.st.trapcom[sig]) + { + flag = sh.sigflag[sig]&~(SH_SIGTRAP|SH_SIGSET); + if(*trap) + { + if(mode) + free(trap); + sh.st.trapcom[sig] = 0; + } + else if(sig && mode>1) + { + if(sig!=SIGCHLD) + signal(sig,SIG_IGN); + flag &= ~SH_SIGFAULT; + flag |= SH_SIGOFF; + } + sh.sigflag[sig] = flag; + } + } + for(sig=SH_DEBUGTRAP-1;sig>=0;sig--) + { + if(trap=sh.st.trap[sig]) + { + if(mode) + free(trap); + sh.st.trap[sig] = 0; + } + + } + sh.st.trapcom[0] = 0; + if(mode) + sh.st.trapmax = 0; + sh.trapnote=0; +} + +/* + * free up trap if set and restore signal handler if modified + */ +void sh_sigclear(register int sig) +{ + register int flag = sh.sigflag[sig]; + register char *trap; + sh.st.otrapcom=0; + if(!(flag&SH_SIGFAULT)) + return; + flag &= ~(SH_SIGTRAP|SH_SIGSET); + if(trap=sh.st.trapcom[sig]) + { + if(!sh.subshell) + free(trap); + sh.st.trapcom[sig]=0; + } + sh.sigflag[sig] = flag; +} + +/* + * check for traps + */ + +void sh_chktrap(Shell_t* shp) +{ + register int sig=shp->st.trapmax; + register char *trap; + if(!(shp->trapnote&~SH_SIGIGNORE)) + sig=0; + shp->trapnote &= ~SH_SIGTRAP; + /* execute errexit trap first */ + if(sh_isstate(SH_ERREXIT) && shp->exitval) + { + int sav_trapnote = shp->trapnote; + shp->trapnote &= ~SH_SIGSET; + if(shp->st.trap[SH_ERRTRAP]) + { + trap = shp->st.trap[SH_ERRTRAP]; + shp->st.trap[SH_ERRTRAP] = 0; + sh_trap(trap,0); + shp->st.trap[SH_ERRTRAP] = trap; + } + shp->trapnote = sav_trapnote; + if(sh_isoption(SH_ERREXIT)) + { + struct checkpt *pp = (struct checkpt*)shp->jmplist; + pp->mode = SH_JMPEXIT; + sh_exit(shp->exitval); + } + } + if(shp->sigflag[SIGALRM]&SH_SIGALRM) + sh_timetraps(shp); +#ifdef SHOPT_BGX + if((shp->sigflag[SIGCHLD]&SH_SIGTRAP) && shp->st.trapcom[SIGCHLD]) + job_chldtrap(shp,shp->st.trapcom[SIGCHLD],1); +#endif /* SHOPT_BGX */ + while(--sig>=0) + { + if(sig==cursig) + continue; +#ifdef SHOPT_BGX + if(sig==SIGCHLD) + continue; +#endif /* SHOPT_BGX */ + if(shp->sigflag[sig]&SH_SIGTRAP) + { + shp->sigflag[sig] &= ~SH_SIGTRAP; + if(trap=shp->st.trapcom[sig]) + { + cursig = sig; + sh_trap(trap,0); + cursig = -1; + } + } + } +} + + +/* + * parse and execute the given trap string, stream or tree depending on mode + * mode==0 for string, mode==1 for stream, mode==2 for parse tree + */ +int sh_trap(const char *trap, int mode) +{ + Shell_t *shp = sh_getinterp(); + int jmpval, savxit = shp->exitval; + int was_history = sh_isstate(SH_HISTORY); + int was_verbose = sh_isstate(SH_VERBOSE); + int staktop = staktell(); + char *savptr = stakfreeze(0); + char ifstable[256]; + struct checkpt buff; + Fcin_t savefc; + fcsave(&savefc); + memcpy(ifstable,shp->ifstable,sizeof(ifstable)); + sh_offstate(SH_HISTORY); + sh_offstate(SH_VERBOSE); + shp->intrap++; + sh_pushcontext(shp,&buff,SH_JMPTRAP); + jmpval = sigsetjmp(buff.buff,0); + if(jmpval == 0) + { + if(mode==2) + sh_exec((Shnode_t*)trap,sh_isstate(SH_ERREXIT)); + else + { + Sfio_t *sp; + if(mode) + sp = (Sfio_t*)trap; + else + sp = sfopen(NIL(Sfio_t*),trap,"s"); + sh_eval(sp,0); + } + } + else if(indone) + { + if(jmpval==SH_JMPSCRIPT) + indone=0; + else + { + if(jmpval==SH_JMPEXIT) + savxit = shp->exitval; + jmpval=SH_JMPTRAP; + } + } + sh_popcontext(shp,&buff); + shp->intrap--; + sfsync(shp->outpool); + if(!shp->indebug && jmpval!=SH_JMPEXIT && jmpval!=SH_JMPFUN) + shp->exitval=savxit; + stakset(savptr,staktop); + fcrestore(&savefc); + memcpy(shp->ifstable,ifstable,sizeof(ifstable)); + if(was_history) + sh_onstate(SH_HISTORY); + if(was_verbose) + sh_onstate(SH_VERBOSE); + exitset(); + if(jmpval>SH_JMPTRAP && (((struct checkpt*)shp->jmpbuffer)->prev || ((struct checkpt*)shp->jmpbuffer)->mode==SH_JMPSCRIPT)) + siglongjmp(*shp->jmplist,jmpval); + return(shp->exitval); +} + +/* + * exit the current scope and jump to an earlier one based on pp->mode + */ +void sh_exit(register int xno) +{ + Shell_t *shp = sh_getinterp(); + register struct checkpt *pp = (struct checkpt*)shp->jmplist; + register int sig=0; + register Sfio_t* pool; + shp->exitval=xno; + if(xno==SH_EXITSIG) + shp->exitval |= (sig=shp->lastsig); + if(pp && pp->mode>1) + cursig = -1; +#ifdef SIGTSTP + if(shp->trapnote&SH_SIGTSTP) + { + /* ^Z detected by the shell */ + shp->trapnote = 0; + shp->sigflag[SIGTSTP] = 0; + if(!shp->subshell && sh_isstate(SH_MONITOR) && !sh_isstate(SH_STOPOK)) + return; + if(sh_isstate(SH_TIMING)) + return; + /* Handles ^Z for shell builtins, subshells, and functs */ + shp->lastsig = 0; + sh_onstate(SH_MONITOR); + sh_offstate(SH_STOPOK); + shp->trapnote = 0; + if(!shp->subshell && (sig=sh_fork(shp,0,NIL(int*)))) + { + job.curpgid = 0; + job.parent = (pid_t)-1; + job_wait(sig); + job.parent = 0; + shp->sigflag[SIGTSTP] = 0; + /* wait for child to stop */ + shp->exitval = (SH_EXITSIG|SIGTSTP); + /* return to prompt mode */ + pp->mode = SH_JMPERREXIT; + } + else + { + if(shp->subshell) + sh_subfork(); + /* child process, put to sleep */ + sh_offstate(SH_STOPOK); + sh_offstate(SH_MONITOR); + shp->sigflag[SIGTSTP] = 0; + /* stop child job */ + killpg(job.curpgid,SIGTSTP); + /* child resumes */ + job_clear(); + shp->forked = 1; + shp->exitval = (xno&SH_EXITMASK); + return; + } + } +#endif /* SIGTSTP */ + /* unlock output pool */ + sh_offstate(SH_NOTRACK); + if(!(pool=sfpool(NIL(Sfio_t*),shp->outpool,SF_WRITE))) + pool = shp->outpool; /* can't happen? */ + sfclrlock(pool); +#ifdef SIGPIPE + if(shp->lastsig==SIGPIPE) + sfpurge(pool); +#endif /* SIGPIPE */ + sfclrlock(sfstdin); + if(!pp) + sh_done(shp,sig); + shp->prefix = 0; +#if SHOPT_TYPEDEF + shp->mktype = 0; +#endif /* SHOPT_TYPEDEF*/ + if(job.in_critical) + job_unlock(); + if(pp->mode == SH_JMPSCRIPT && !pp->prev) + sh_done(shp,sig); + if(pp->mode) + siglongjmp(pp->buff,pp->mode); +} + +static void array_notify(Namval_t *np, void *data) +{ + Namarr_t *ap = nv_arrayptr(np); + NOT_USED(data); + if(ap && ap->fun) + (*ap->fun)(np, 0, NV_AFREE); +} + +/* + * This is the exit routine for the shell + */ + +void sh_done(void *ptr, register int sig) +{ + Shell_t *shp = (Shell_t*)ptr; + register char *t; + register int savxit = shp->exitval; + shp->trapnote = 0; + indone=1; + if(sig) + savxit = SH_EXITSIG|sig; + if(shp->userinit) + (*shp->userinit)(shp, -1); + if(t=shp->st.trapcom[0]) + { + shp->st.trapcom[0]=0; /*should free but not long */ + shp->oldexit = savxit; + sh_trap(t,0); + savxit = shp->exitval; + } + else + { + /* avoid recursive call for set -e */ + sh_offstate(SH_ERREXIT); + sh_chktrap(shp); + } + nv_scan(shp->var_tree,array_notify,(void*)0,NV_ARRAY,NV_ARRAY); + sh_freeup(shp); +#if SHOPT_ACCT + sh_accend(); +#endif /* SHOPT_ACCT */ +#if SHOPT_VSH || SHOPT_ESH + if(mbwide()||sh_isoption(SH_EMACS)||sh_isoption(SH_VI)||sh_isoption(SH_GMACS)) + tty_cooked(-1); +#endif +#ifdef JOBS + if((sh_isoption(SH_INTERACTIVE) && shp->login_sh) || (!sh_isoption(SH_INTERACTIVE) && (sig==SIGHUP))) + job_walk(sfstderr,job_terminate,SIGHUP,NIL(char**)); +#endif /* JOBS */ + job_close(shp); + if(nv_search("VMTRACE", shp->var_tree,0)) + strmatch((char*)0,(char*)0); + sfsync((Sfio_t*)sfstdin); + sfsync((Sfio_t*)shp->outpool); + sfsync((Sfio_t*)sfstdout); + if(savxit&SH_EXITSIG) + sig = savxit&SH_EXITMASK; + if(sig) + { + /* generate fault termination code */ + if(RLIMIT_CORE!=RLIMIT_UNKNOWN) + { +#ifdef _lib_getrlimit + struct rlimit rlp; + getrlimit(RLIMIT_CORE,&rlp); + rlp.rlim_cur = 0; + setrlimit(RLIMIT_CORE,&rlp); +#else + vlimit(RLIMIT_CORE,0); +#endif + } + signal(sig,SIG_DFL); + sigrelease(sig); + kill(getpid(),sig); + pause(); + } +#if SHOPT_KIA + if(sh_isoption(SH_NOEXEC)) + kiaclose((Lex_t*)shp->lex_context); +#endif /* SHOPT_KIA */ + exit(savxit&SH_EXITMASK); +} + |