summaryrefslogtreecommitdiff
path: root/usr/src/lib/libshell/common/sh/fault.c
diff options
context:
space:
mode:
authorchin <none@none>2007-08-17 12:01:52 -0700
committerchin <none@none>2007-08-17 12:01:52 -0700
commitda2e3ebdc1edfbc5028edf1354e7dd2fa69a7968 (patch)
tree5280d3b78e289fe9551371ab6e7f15ef9944ea14 /usr/src/lib/libshell/common/sh/fault.c
parent073dbf9103ef2a2b05d8a16e2d26db04e0374b0e (diff)
downloadillumos-gate-da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968.tar.gz
6437624 RFE: Add ksh93 (as /usr/bin/ksh93) and libshell.so to OS/Net
6505835 AST tools and library (libpp) required for creating l10n messages for ksh93 PSARC/2006/550 Korn Shell 93 Integration PSARC/2006/587 /etc/ksh.kshrc for ksh93 PSARC/2007/035 ksh93 Amendments Contributed by Roland Mainz <roland.mainz@nrubsig.org> --HG-- rename : usr/src/lib/libcmd/common/mapfile-vers => deleted_files/usr/src/lib/libcmd/common/mapfile-vers rename : usr/src/lib/libcmd/common/placeholder.c => deleted_files/usr/src/lib/libcmd/common/placeholder.c
Diffstat (limited to 'usr/src/lib/libshell/common/sh/fault.c')
-rw-r--r--usr/src/lib/libshell/common/sh/fault.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/usr/src/lib/libshell/common/sh/fault.c b/usr/src/lib/libshell/common/sh/fault.c
new file mode 100644
index 0000000000..2d71736187
--- /dev/null
+++ b/usr/src/lib/libshell/common/sh/fault.c
@@ -0,0 +1,573 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2007 AT&T Knowledge Ventures *
+* and is licensed under the *
+* Common Public License, Version 1.0 *
+* by AT&T Knowledge Ventures *
+* *
+* A copy of the License is available at *
+* http://www.opensource.org/licenses/cpl1.0.txt *
+* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
+* *
+* 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 "shnodes.h"
+#include "variables.h"
+#include "jobs.h"
+#include "path.h"
+
+#define abortsig(sig) (sig==SIGABRT || sig==SIGBUS || sig==SIGILL || sig==SIGSEGV)
+
+static char indone;
+
+#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 int flag=0;
+ register char *trap;
+ register struct checkpt *pp = (struct checkpt*)sh.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);
+ if(v = rows)
+ nv_putval(LINES, (char*)&v, NV_INT32);
+ }
+#endif /* SIGWINCH */
+ if(sh.savesig)
+ {
+ /* critical region, save and process later */
+ sh.savesig = sig;
+ return;
+ }
+
+ /* handle ignored signals */
+ if((trap=sh.st.trapcom[sig]) && *trap==0)
+ return;
+ flag = sh.sigflag[sig]&~SH_SIGOFF;
+ if(!trap)
+ {
+ if(flag&SH_SIGIGNORE)
+ return;
+ if(flag&SH_SIGDONE)
+ {
+ void *ptr=0;
+ if((flag&SH_SIGINTERACTIVE) && sh_isstate(SH_INTERACTIVE) && !sh_isstate(SH_FORKED) && ! sh.subshell)
+ {
+ /* check for TERM signal between fork/exec */
+ if(sig==SIGTERM && job.in_critical)
+ sh.trapnote |= SH_SIGTERM;
+ return;
+ }
+ sh.lastsig = sig;
+ sigrelease(sig);
+ if(pp->mode < SH_JMPFUN)
+ pp->mode = SH_JMPFUN;
+ else
+ pp->mode = SH_JMPEXIT;
+ if(sig==SIGABRT || (abortsig(sig) && (ptr = malloc(1))))
+ {
+ if(ptr)
+ free(ptr);
+ if(!sh.subshell)
+ sh_done(sig);
+ sh_exit(SH_EXITSIG);
+ }
+ /* mark signal and continue */
+ sh.trapnote |= SH_SIGSET;
+ if(sig < sh.sigmax)
+ sh.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)
+ sh.lastsig = sig;
+ if(trap)
+ {
+ /*
+ * propogate signal to foreground group
+ */
+ if(sig==SIGHUP && job.curpgid)
+ killpg(job.curpgid,SIGHUP);
+ flag = SH_SIGTRAP;
+ }
+ else
+ {
+ sh.lastsig = sig;
+ flag = SH_SIGSET;
+#ifdef SIGTSTP
+ if(sig==SIGTSTP)
+ {
+ sh.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) && sh.bltinfun)
+ action = (*sh.bltinfun)(-sig,(char**)0,(void*)0);
+#endif
+ if(action>0)
+ return;
+ sh.trapnote |= flag;
+ if(sig < sh.sigmax)
+ sh.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)
+{
+ register int sig, n=SIGTERM+1;
+ register const struct shtable2 *tp = shtab_signals;
+ sig_begin();
+ /* find the largest signal number in the table */
+ while(*tp->sh_name)
+ {
+ if((sig=tp->sh_number&((1<<SH_SIGBITS)-1))>n && sig<SH_TRAP)
+ n = sig;
+ tp++;
+ }
+#if defined(_SC_SIGRT_MAX) && defined(_SIGRTMAX)
+ if((sig=SIGRTMAX+1)>n && sig<SH_TRAP)
+ n = sig;
+#endif
+ sh.sigmax = n;
+ sh.st.trapcom = (char**)calloc(n,sizeof(char*));
+ sh.sigflag = (unsigned char*)calloc(n,1);
+ sh.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)) > sh.sigmax)
+ continue;
+ sig--;
+#if defined(_SC_SIGRT_MIN) && defined(_SIGRTMIN)
+ if(sig==_SIGRTMIN)
+ sig = SIGRTMIN;
+#endif
+#if defined(_SC_SIGRT_MAX) && defined(_SIGRTMAX)
+ if(sig==_SIGRTMAX)
+ sig = SIGRTMAX;
+#endif
+ if(sig>=0)
+ {
+ sh.sigflag[sig] = n;
+ if(*tp->sh_name)
+ sh.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 = sh.sigmax;
+ sh.sigflag[0] |= SH_SIGFAULT;
+ while(--sig>0)
+ {
+ 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)
+ {
+ signal(sig,SIG_IGN);
+ flag &= ~SH_SIGFAULT;
+ flag |= SH_SIGOFF;
+ }
+ sh.sigflag[sig] = flag;
+ }
+ }
+ for(sig=SH_DEBUGTRAP;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])
+ {
+ free(trap);
+ sh.st.trapcom[sig]=0;
+ }
+ sh.sigflag[sig] = flag;
+}
+
+/*
+ * check for traps
+ */
+
+void sh_chktrap(void)
+{
+ register int sig=sh.st.trapmax;
+ register char *trap;
+ if(!sh.trapnote)
+ sig=0;
+ sh.trapnote &= ~SH_SIGTRAP;
+ /* execute errexit trap first */
+ if(sh_isstate(SH_ERREXIT) && sh.exitval)
+ {
+ int sav_trapnote = sh.trapnote;
+ sh.trapnote &= ~SH_SIGSET;
+ if(sh.st.trap[SH_ERRTRAP])
+ sh_trap(sh.st.trap[SH_ERRTRAP],0);
+ sh.trapnote = sav_trapnote;
+ if(sh_isoption(SH_ERREXIT))
+ {
+ struct checkpt *pp = (struct checkpt*)sh.jmplist;
+ pp->mode = SH_JMPEXIT;
+ sh_exit(sh.exitval);
+ }
+ }
+ if(sh.sigflag[SIGALRM]&SH_SIGALRM)
+ sh_timetraps();
+ while(--sig>=0)
+ {
+ if(sh.sigflag[sig]&SH_SIGTRAP)
+ {
+ sh.sigflag[sig] &= ~SH_SIGTRAP;
+ if(trap=sh.st.trapcom[sig])
+ sh_trap(trap,0);
+ }
+ }
+}
+
+
+/*
+ * 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)
+{
+ int jmpval, savxit = sh.exitval;
+ int was_history = sh_isstate(SH_HISTORY);
+ int was_verbose = sh_isstate(SH_VERBOSE);
+ int staktop = staktell();
+ char *savptr = stakfreeze(0);
+ struct checkpt buff;
+ Fcin_t savefc;
+ fcsave(&savefc);
+ sh_offstate(SH_HISTORY);
+ sh_offstate(SH_VERBOSE);
+ sh.intrap++;
+ sh_pushcontext(&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 = sh.exitval;
+ jmpval=SH_JMPTRAP;
+ }
+ }
+ sh_popcontext(&buff);
+ sh.intrap--;
+ sfsync(sh.outpool);
+ if(jmpval!=SH_JMPEXIT && jmpval!=SH_JMPFUN)
+ sh.exitval=savxit;
+ stakset(savptr,staktop);
+ fcrestore(&savefc);
+ if(was_history)
+ sh_onstate(SH_HISTORY);
+ if(was_verbose)
+ sh_onstate(SH_VERBOSE);
+ exitset();
+ if(jmpval>SH_JMPTRAP)
+ siglongjmp(*sh.jmplist,jmpval);
+ return(sh.exitval);
+}
+
+/*
+ * exit the current scope and jump to an earlier one based on pp->mode
+ */
+void sh_exit(register int xno)
+{
+ register struct checkpt *pp = (struct checkpt*)sh.jmplist;
+ register int sig=0;
+ register Sfio_t* pool;
+ sh.exitval=xno;
+ if(xno==SH_EXITSIG)
+ sh.exitval |= (sig=sh.lastsig);
+#ifdef SIGTSTP
+ if(sh.trapnote&SH_SIGTSTP)
+ {
+ /* ^Z detected by the shell */
+ sh.trapnote = 0;
+ sh.sigflag[SIGTSTP] = 0;
+ if(!sh.subshell && sh_isstate(SH_MONITOR) && !sh_isstate(SH_STOPOK))
+ return;
+ if(sh_isstate(SH_TIMING))
+ return;
+ /* Handles ^Z for shell builtins, subshells, and functs */
+ sh.lastsig = 0;
+ sh_onstate(SH_MONITOR);
+ sh_offstate(SH_STOPOK);
+ sh.trapnote = 0;
+ if(!sh.subshell && (sig=sh_fork(0,NIL(int*))))
+ {
+ job.curpgid = 0;
+ job.parent = (pid_t)-1;
+ job_wait(sig);
+ job.parent = 0;
+ sh.sigflag[SIGTSTP] = 0;
+ /* wait for child to stop */
+ sh.exitval = (SH_EXITSIG|SIGTSTP);
+ /* return to prompt mode */
+ pp->mode = SH_JMPERREXIT;
+ }
+ else
+ {
+ if(sh.subshell)
+ sh_subfork();
+ /* child process, put to sleep */
+ sh_offstate(SH_STOPOK);
+ sh_offstate(SH_MONITOR);
+ sh.sigflag[SIGTSTP] = 0;
+ /* stop child job */
+ killpg(job.curpgid,SIGTSTP);
+ /* child resumes */
+ job_clear();
+ sh.forked = 1;
+ sh.exitval = (xno&SH_EXITMASK);
+ return;
+ }
+ }
+#endif /* SIGTSTP */
+ /* unlock output pool */
+ sh_offstate(SH_NOTRACK);
+ if(!(pool=sfpool(NIL(Sfio_t*),sh.outpool,SF_WRITE)))
+ pool = sh.outpool; /* can't happen? */
+ sfclrlock(pool);
+#ifdef SIGPIPE
+ if(sh.lastsig==SIGPIPE)
+ sfpurge(pool);
+#endif /* SIGPIPE */
+ sfclrlock(sfstdin);
+ if(!pp)
+ sh_done(sig);
+ sh.prefix = 0;
+ if(pp->mode == SH_JMPSCRIPT && !pp->prev)
+ sh_done(sig);
+ siglongjmp(pp->buff,pp->mode);
+}
+
+/*
+ * This is the exit routine for the shell
+ */
+
+void sh_done(register int sig)
+{
+ register char *t;
+ register int savxit = sh.exitval;
+ sh.trapnote = 0;
+ indone=1;
+ if(sig==0)
+ sig = sh.lastsig;
+ if(sh.userinit)
+ (*sh.userinit)(-1);
+ if(t=sh.st.trapcom[0])
+ {
+ sh.st.trapcom[0]=0; /*should free but not long */
+ sh.oldexit = savxit;
+ sh_trap(t,0);
+ savxit = sh.exitval;
+ }
+ else
+ {
+ /* avoid recursive call for set -e */
+ sh_offstate(SH_ERREXIT);
+ sh_chktrap();
+ }
+ sh_freeup();
+#if SHOPT_ACCT
+ sh_accend();
+#endif /* SHOPT_ACCT */
+#if SHOPT_VSH || SHOPT_ESH
+ if(sh_isoption(SH_EMACS)||sh_isoption(SH_VI)||sh_isoption(SH_GMACS))
+ tty_cooked(-1);
+#endif
+#ifdef JOBS
+ if((sh_isoption(SH_INTERACTIVE) && sh.login_sh) || (!sh_isoption(SH_INTERACTIVE) && (sig==SIGHUP)))
+ job_walk(sfstderr,job_terminate,SIGHUP,NIL(char**));
+#endif /* JOBS */
+ job_close();
+ if(nv_search("VMTRACE", sh.var_tree,0))
+ strmatch((char*)0,(char*)0);
+ sfsync((Sfio_t*)sfstdin);
+ sfsync((Sfio_t*)sh.outpool);
+ sfsync((Sfio_t*)sfstdout);
+ if(sig)
+ {
+ /* generate fault termination code */
+ signal(sig,SIG_DFL);
+ sigrelease(sig);
+ kill(getpid(),sig);
+ pause();
+ }
+#if SHOPT_KIA
+ if(sh_isoption(SH_NOEXEC))
+ kiaclose();
+#endif /* SHOPT_KIA */
+ exit(savxit&SH_EXITMASK);
+}
+