summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/sh/fault.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/sh/fault.c
downloadksh-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.c672
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);
+}
+