1/***********************************************************************
2*                                                                      *
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                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21/*
22 * Fault handling routines
23 *
24 *   David Korn
25 *   AT&T Labs
26 *
27 */
28
29#include	"defs.h"
30#include	<fcin.h>
31#include	"io.h"
32#include	"history.h"
33#include	"shlex.h"
34#include	"variables.h"
35#include	"jobs.h"
36#include	"path.h"
37#include	"builtins.h"
38
39#define abortsig(sig)	(sig==SIGABRT || sig==SIGBUS || sig==SIGILL || sig==SIGSEGV)
40
41static char	indone;
42
43#if !_std_malloc
44#   include	<vmalloc.h>
45#endif
46#if  defined(VMFL) && (VMALLOC_VERSION>=20031205L)
47    /*
48     * This exception handler is called after vmalloc() unlocks the region
49     */
50    static int malloc_done(Vmalloc_t* vm, int type, Void_t* val, Vmdisc_t* dp)
51    {
52	dp->exceptf = 0;
53	sh_exit(SH_EXITSIG);
54	return(0);
55    }
56#endif
57
58/*
59 * Most signals caught or ignored by the shell come here
60*/
61void	sh_fault(register int sig)
62{
63	register Shell_t	*shp = sh_getinterp();
64	register int 		flag=0;
65	register char		*trap;
66	register struct checkpt	*pp = (struct checkpt*)shp->jmplist;
67	int	action=0;
68	/* reset handler */
69	if(!(sig&SH_TRAP))
70		signal(sig, sh_fault);
71	sig &= ~SH_TRAP;
72#ifdef SIGWINCH
73	if(sig==SIGWINCH)
74	{
75		int rows=0, cols=0;
76		int32_t v;
77		astwinsize(2,&rows,&cols);
78		if(v = cols)
79			nv_putval(COLUMNS, (char*)&v, NV_INT32|NV_RDONLY);
80		if(v = rows)
81			nv_putval(LINES, (char*)&v, NV_INT32|NV_RDONLY);
82		shp->winch++;
83	}
84#endif  /* SIGWINCH */
85	if(shp->savesig)
86	{
87		/* critical region, save and process later */
88		shp->savesig = sig;
89		return;
90	}
91	trap = shp->st.trapcom[sig];
92	if(sig==SIGALRM && shp->bltinfun==b_sleep)
93	{
94		if(trap && *trap)
95		{
96			shp->trapnote |= SH_SIGTRAP;
97			shp->sigflag[sig] |= SH_SIGTRAP;
98		}
99		return;
100	}
101	if(shp->subshell && sig!=SIGINT && sig!=SIGQUIT && sig!=SIGWINCH && sig!=SIGCONT)
102	{
103		shp->exitval = SH_EXITSIG|sig;
104		sh_subfork();
105		shp->exitval = 0;
106		return;
107	}
108	/* handle ignored signals */
109	if(trap && *trap==0)
110		return;
111	flag = shp->sigflag[sig]&~SH_SIGOFF;
112	if(!trap)
113	{
114		if(sig==SIGINT && (shp->trapnote&SH_SIGIGNORE))
115			return;
116		if(flag&SH_SIGIGNORE)
117			return;
118		if(flag&SH_SIGDONE)
119		{
120			void *ptr=0;
121			if((flag&SH_SIGINTERACTIVE) && sh_isstate(SH_INTERACTIVE) && !sh_isstate(SH_FORKED) && ! shp->subshell)
122			{
123				/* check for TERM signal between fork/exec */
124				if(sig==SIGTERM && job.in_critical)
125					shp->trapnote |= SH_SIGTERM;
126				return;
127			}
128			shp->lastsig = sig;
129			sigrelease(sig);
130			if(pp->mode < SH_JMPFUN)
131				pp->mode = SH_JMPFUN;
132			else
133				pp->mode = SH_JMPEXIT;
134			if(sig==SIGABRT || (abortsig(sig) && (ptr = malloc(1))))
135			{
136				if(ptr)
137					free(ptr);
138				if(!shp->subshell)
139					sh_done(shp,sig);
140				sh_exit(SH_EXITSIG);
141			}
142			/* mark signal and continue */
143			shp->trapnote |= SH_SIGSET;
144			if(sig <= shp->sigmax)
145				shp->sigflag[sig] |= SH_SIGSET;
146#if  defined(VMFL) && (VMALLOC_VERSION>=20031205L)
147			if(abortsig(sig))
148			{
149				/* abort inside malloc, process when malloc returns */
150				/* VMFL defined when using vmalloc() */
151				Vmdisc_t* dp = vmdisc(Vmregion,0);
152				if(dp)
153					dp->exceptf = malloc_done;
154			}
155#endif
156			return;
157		}
158	}
159	errno = 0;
160	if(pp->mode==SH_JMPCMD)
161		shp->lastsig = sig;
162	if(trap)
163	{
164		/*
165		 * propogate signal to foreground group
166		 */
167		if(sig==SIGHUP && job.curpgid)
168			killpg(job.curpgid,SIGHUP);
169		flag = SH_SIGTRAP;
170	}
171	else
172	{
173		shp->lastsig = sig;
174		flag = SH_SIGSET;
175#ifdef SIGTSTP
176		if(sig==SIGTSTP)
177		{
178			shp->trapnote |= SH_SIGTSTP;
179			if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK))
180			{
181				sigrelease(sig);
182				sh_exit(SH_EXITSIG);
183				flag = 0;
184			}
185		}
186#endif /* SIGTSTP */
187	}
188#ifdef ERROR_NOTIFY
189	if((error_info.flags&ERROR_NOTIFY) && shp->bltinfun)
190		action = (*shp->bltinfun)(-sig,(char**)0,(void*)0);
191	if(action>0)
192		return;
193#endif
194	if(shp->bltinfun && shp->bltindata.notify)
195	{
196		shp->bltindata.sigset = 1;
197		return;
198	}
199	shp->trapnote |= flag;
200	if(sig <= shp->sigmax)
201		shp->sigflag[sig] |= flag;
202	if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK))
203	{
204		if(action<0)
205			return;
206		sigrelease(sig);
207		sh_exit(SH_EXITSIG);
208	}
209}
210
211/*
212 * initialize signal handling
213 */
214void sh_siginit(void *ptr)
215{
216	Shell_t	*shp = (Shell_t*)ptr;
217	register int sig, n;
218	register const struct shtable2	*tp = shtab_signals;
219	sig_begin();
220	/* find the largest signal number in the table */
221#if defined(SIGRTMIN) && defined(SIGRTMAX)
222	if ((n = SIGRTMIN) > 0 && (sig = SIGRTMAX) > n && sig < SH_TRAP)
223	{
224		shp->sigruntime[SH_SIGRTMIN] = n;
225		shp->sigruntime[SH_SIGRTMAX] = sig;
226	}
227#endif /* SIGRTMIN && SIGRTMAX */
228	n = SIGTERM;
229	while(*tp->sh_name)
230	{
231		sig = (tp->sh_number&((1<<SH_SIGBITS)-1));
232		if (!(sig-- & SH_TRAP))
233		{
234			if ((tp->sh_number>>SH_SIGBITS) & SH_SIGRUNTIME)
235				sig = shp->sigruntime[sig];
236			if(sig>n && sig<SH_TRAP)
237				n = sig;
238		}
239		tp++;
240	}
241	shp->sigmax = n++;
242	shp->st.trapcom = (char**)calloc(n,sizeof(char*));
243	shp->sigflag = (unsigned char*)calloc(n,1);
244	shp->sigmsg = (char**)calloc(n,sizeof(char*));
245	for(tp=shtab_signals; sig=tp->sh_number; tp++)
246	{
247		n = (sig>>SH_SIGBITS);
248		if((sig &= ((1<<SH_SIGBITS)-1)) > (shp->sigmax+1))
249			continue;
250		sig--;
251		if(n&SH_SIGRUNTIME)
252			sig = shp->sigruntime[sig];
253		if(sig>=0)
254		{
255			shp->sigflag[sig] = n;
256			if(*tp->sh_name)
257				shp->sigmsg[sig] = (char*)tp->sh_value;
258		}
259	}
260}
261
262/*
263 * Turn on trap handler for signal <sig>
264 */
265void	sh_sigtrap(register int sig)
266{
267	register int flag;
268	void (*fun)(int);
269	sh.st.otrapcom = 0;
270	if(sig==0)
271		sh_sigdone();
272	else if(!((flag=sh.sigflag[sig])&(SH_SIGFAULT|SH_SIGOFF)))
273	{
274		/* don't set signal if already set or off by parent */
275		if((fun=signal(sig,sh_fault))==SIG_IGN)
276		{
277			signal(sig,SIG_IGN);
278			flag |= SH_SIGOFF;
279		}
280		else
281		{
282			flag |= SH_SIGFAULT;
283			if(sig==SIGALRM && fun!=SIG_DFL && fun!=sh_fault)
284				signal(sig,fun);
285		}
286		flag &= ~(SH_SIGSET|SH_SIGTRAP);
287		sh.sigflag[sig] = flag;
288	}
289}
290
291/*
292 * set signal handler so sh_done is called for all caught signals
293 */
294void	sh_sigdone(void)
295{
296	register int 	flag, sig = sh.sigmax;
297	sh.sigflag[0] |= SH_SIGFAULT;
298	for(sig=sh.sigmax; sig>0; sig--)
299	{
300		flag = sh.sigflag[sig];
301		if((flag&(SH_SIGDONE|SH_SIGIGNORE|SH_SIGINTERACTIVE)) && !(flag&(SH_SIGFAULT|SH_SIGOFF)))
302			sh_sigtrap(sig);
303	}
304}
305
306/*
307 * Restore to default signals
308 * Free the trap strings if mode is non-zero
309 * If mode>1 then ignored traps cause signal to be ignored
310 */
311void	sh_sigreset(register int mode)
312{
313	register char	*trap;
314	register int 	flag, sig=sh.st.trapmax;
315	while(sig-- > 0)
316	{
317		if(trap=sh.st.trapcom[sig])
318		{
319			flag  = sh.sigflag[sig]&~(SH_SIGTRAP|SH_SIGSET);
320			if(*trap)
321			{
322				if(mode)
323					free(trap);
324				sh.st.trapcom[sig] = 0;
325			}
326			else if(sig && mode>1)
327			{
328				if(sig!=SIGCHLD)
329					signal(sig,SIG_IGN);
330				flag &= ~SH_SIGFAULT;
331				flag |= SH_SIGOFF;
332			}
333			sh.sigflag[sig] = flag;
334		}
335	}
336	for(sig=SH_DEBUGTRAP-1;sig>=0;sig--)
337	{
338		if(trap=sh.st.trap[sig])
339		{
340			if(mode)
341				free(trap);
342			sh.st.trap[sig] = 0;
343		}
344
345	}
346	sh.st.trapcom[0] = 0;
347	if(mode)
348		sh.st.trapmax = 0;
349	sh.trapnote=0;
350}
351
352/*
353 * free up trap if set and restore signal handler if modified
354 */
355void	sh_sigclear(register int sig)
356{
357	register int flag = sh.sigflag[sig];
358	register char *trap;
359	sh.st.otrapcom=0;
360	if(!(flag&SH_SIGFAULT))
361		return;
362	flag &= ~(SH_SIGTRAP|SH_SIGSET);
363	if(trap=sh.st.trapcom[sig])
364	{
365		if(!sh.subshell)
366			free(trap);
367		sh.st.trapcom[sig]=0;
368	}
369	sh.sigflag[sig] = flag;
370}
371
372/*
373 * check for traps
374 */
375
376void	sh_chktrap(void)
377{
378	register int 	sig=sh.st.trapmax;
379	register char *trap;
380	if(!(sh.trapnote&~SH_SIGIGNORE))
381		sig=0;
382	sh.trapnote &= ~SH_SIGTRAP;
383	/* execute errexit trap first */
384	if(sh_isstate(SH_ERREXIT) && sh.exitval)
385	{
386		int	sav_trapnote = sh.trapnote;
387		sh.trapnote &= ~SH_SIGSET;
388		if(sh.st.trap[SH_ERRTRAP])
389		{
390			trap = sh.st.trap[SH_ERRTRAP];
391			sh.st.trap[SH_ERRTRAP] = 0;
392			sh_trap(trap,0);
393			sh.st.trap[SH_ERRTRAP] = trap;
394		}
395		sh.trapnote = sav_trapnote;
396		if(sh_isoption(SH_ERREXIT))
397		{
398			struct checkpt	*pp = (struct checkpt*)sh.jmplist;
399			pp->mode = SH_JMPEXIT;
400			sh_exit(sh.exitval);
401		}
402	}
403	if(sh.sigflag[SIGALRM]&SH_SIGALRM)
404		sh_timetraps();
405#ifdef SHOPT_BGX
406	if((sh.sigflag[SIGCHLD]&SH_SIGTRAP) && sh.st.trapcom[SIGCHLD])
407		job_chldtrap(&sh,sh.st.trapcom[SIGCHLD],1);
408#endif /* SHOPT_BGX */
409	while(--sig>=0)
410	{
411#ifdef SHOPT_BGX
412		if(sig==SIGCHLD)
413			continue;
414#endif /* SHOPT_BGX */
415		if(sh.sigflag[sig]&SH_SIGTRAP)
416		{
417			sh.sigflag[sig] &= ~SH_SIGTRAP;
418			if(trap=sh.st.trapcom[sig])
419			{
420				Sfio_t *fp;
421				if(sig==SIGPIPE && (fp=sfpool((Sfio_t*)0,sh.outpool,SF_WRITE)) && sferror(fp))
422					sfclose(fp);
423 				sh.oldexit = SH_EXITSIG|sig;
424 				sh_trap(trap,0);
425 			}
426		}
427	}
428}
429
430
431/*
432 * parse and execute the given trap string, stream or tree depending on mode
433 * mode==0 for string, mode==1 for stream, mode==2 for parse tree
434 */
435int sh_trap(const char *trap, int mode)
436{
437	Shell_t	*shp = sh_getinterp();
438	int	jmpval, savxit = shp->exitval;
439	int	was_history = sh_isstate(SH_HISTORY);
440	int	was_verbose = sh_isstate(SH_VERBOSE);
441	int	staktop = staktell();
442	char	*savptr = stakfreeze(0);
443	char	ifstable[256];
444	struct	checkpt buff;
445	Fcin_t	savefc;
446	fcsave(&savefc);
447	memcpy(ifstable,shp->ifstable,sizeof(ifstable));
448	sh_offstate(SH_HISTORY);
449	sh_offstate(SH_VERBOSE);
450	shp->intrap++;
451	sh_pushcontext(&buff,SH_JMPTRAP);
452	jmpval = sigsetjmp(buff.buff,0);
453	if(jmpval == 0)
454	{
455		if(mode==2)
456			sh_exec((Shnode_t*)trap,sh_isstate(SH_ERREXIT));
457		else
458		{
459			Sfio_t *sp;
460			if(mode)
461				sp = (Sfio_t*)trap;
462			else
463				sp = sfopen(NIL(Sfio_t*),trap,"s");
464			sh_eval(sp,0);
465		}
466	}
467	else if(indone)
468	{
469		if(jmpval==SH_JMPSCRIPT)
470			indone=0;
471		else
472		{
473			if(jmpval==SH_JMPEXIT)
474				savxit = shp->exitval;
475			jmpval=SH_JMPTRAP;
476		}
477	}
478	sh_popcontext(&buff);
479	shp->intrap--;
480	sfsync(shp->outpool);
481	if(!shp->indebug && jmpval!=SH_JMPEXIT && jmpval!=SH_JMPFUN)
482		shp->exitval=savxit;
483	stakset(savptr,staktop);
484	fcrestore(&savefc);
485	memcpy(shp->ifstable,ifstable,sizeof(ifstable));
486	if(was_history)
487		sh_onstate(SH_HISTORY);
488	if(was_verbose)
489		sh_onstate(SH_VERBOSE);
490	exitset();
491	if(jmpval>SH_JMPTRAP && (((struct checkpt*)shp->jmpbuffer)->prev || ((struct checkpt*)shp->jmpbuffer)->mode==SH_JMPSCRIPT))
492		siglongjmp(*shp->jmplist,jmpval);
493	return(shp->exitval);
494}
495
496/*
497 * exit the current scope and jump to an earlier one based on pp->mode
498 */
499void sh_exit(register int xno)
500{
501	Shell_t	*shp = &sh;
502	register struct checkpt	*pp = (struct checkpt*)shp->jmplist;
503	register int		sig=0;
504	register Sfio_t*	pool;
505	shp->exitval=xno;
506	if(xno==SH_EXITSIG)
507		shp->exitval |= (sig=shp->lastsig);
508#ifdef SIGTSTP
509	if(shp->trapnote&SH_SIGTSTP)
510	{
511		/* ^Z detected by the shell */
512		shp->trapnote = 0;
513		shp->sigflag[SIGTSTP] = 0;
514		if(!shp->subshell && sh_isstate(SH_MONITOR) && !sh_isstate(SH_STOPOK))
515			return;
516		if(sh_isstate(SH_TIMING))
517			return;
518		/* Handles ^Z for shell builtins, subshells, and functs */
519		shp->lastsig = 0;
520		sh_onstate(SH_MONITOR);
521		sh_offstate(SH_STOPOK);
522		shp->trapnote = 0;
523		if(!shp->subshell && (sig=sh_fork(0,NIL(int*))))
524		{
525			job.curpgid = 0;
526			job.parent = (pid_t)-1;
527			job_wait(sig);
528			job.parent = 0;
529			shp->sigflag[SIGTSTP] = 0;
530			/* wait for child to stop */
531			shp->exitval = (SH_EXITSIG|SIGTSTP);
532			/* return to prompt mode */
533			pp->mode = SH_JMPERREXIT;
534		}
535		else
536		{
537			if(shp->subshell)
538				sh_subfork();
539			/* child process, put to sleep */
540			sh_offstate(SH_STOPOK);
541			sh_offstate(SH_MONITOR);
542			shp->sigflag[SIGTSTP] = 0;
543			/* stop child job */
544			killpg(job.curpgid,SIGTSTP);
545			/* child resumes */
546			job_clear();
547			shp->forked = 1;
548			shp->exitval = (xno&SH_EXITMASK);
549			return;
550		}
551	}
552#endif /* SIGTSTP */
553	/* unlock output pool */
554	sh_offstate(SH_NOTRACK);
555	if(!(pool=sfpool(NIL(Sfio_t*),shp->outpool,SF_WRITE)))
556		pool = shp->outpool; /* can't happen? */
557	sfclrlock(pool);
558#ifdef SIGPIPE
559	if(shp->lastsig==SIGPIPE)
560		sfpurge(pool);
561#endif /* SIGPIPE */
562	sfclrlock(sfstdin);
563	if(!pp)
564		sh_done(shp,sig);
565	shp->prefix = 0;
566#if SHOPT_TYPEDEF
567	shp->mktype = 0;
568#endif /* SHOPT_TYPEDEF*/
569	if(pp->mode == SH_JMPSCRIPT && !pp->prev)
570		sh_done(shp,sig);
571	if(pp->mode)
572		siglongjmp(pp->buff,pp->mode);
573}
574
575static void array_notify(Namval_t *np, void *data)
576{
577	Namarr_t	*ap = nv_arrayptr(np);
578	NOT_USED(data);
579	if(ap && ap->fun)
580		(*ap->fun)(np, 0, NV_AFREE);
581}
582
583/*
584 * This is the exit routine for the shell
585 */
586
587void sh_done(void *ptr, register int sig)
588{
589	Shell_t	*shp = (Shell_t*)ptr;
590	register char *t;
591	register int savxit = shp->exitval;
592	shp->trapnote = 0;
593	indone=1;
594	if(sig)
595		savxit = SH_EXITSIG|sig;
596	if(shp->userinit)
597		(*shp->userinit)(shp, -1);
598	if(t=shp->st.trapcom[0])
599	{
600		shp->st.trapcom[0]=0; /*should free but not long */
601		shp->oldexit = savxit;
602		sh_trap(t,0);
603		savxit = shp->exitval;
604	}
605	else
606	{
607		/* avoid recursive call for set -e */
608		sh_offstate(SH_ERREXIT);
609		sh_chktrap();
610	}
611	nv_scan(shp->var_tree,array_notify,(void*)0,NV_ARRAY,NV_ARRAY);
612	sh_freeup(shp);
613#if SHOPT_ACCT
614	sh_accend();
615#endif	/* SHOPT_ACCT */
616#if SHOPT_VSH || SHOPT_ESH
617	if(sh_isoption(SH_EMACS)||sh_isoption(SH_VI)||sh_isoption(SH_GMACS))
618		tty_cooked(-1);
619#endif
620#ifdef JOBS
621	if((sh_isoption(SH_INTERACTIVE) && shp->login_sh) || (!sh_isoption(SH_INTERACTIVE) && (sig==SIGHUP)))
622		job_walk(sfstderr,job_terminate,SIGHUP,NIL(char**));
623#endif	/* JOBS */
624	job_close(shp);
625	if(nv_search("VMTRACE", shp->var_tree,0))
626		strmatch((char*)0,(char*)0);
627	sfsync((Sfio_t*)sfstdin);
628	sfsync((Sfio_t*)shp->outpool);
629	sfsync((Sfio_t*)sfstdout);
630	if(savxit&SH_EXITSIG)
631		sig = savxit&SH_EXITMASK;
632	if(sig)
633	{
634		/* generate fault termination code */
635		signal(sig,SIG_DFL);
636		sigrelease(sig);
637		kill(getpid(),sig);
638		pause();
639	}
640#if SHOPT_KIA
641	if(sh_isoption(SH_NOEXEC))
642		kiaclose((Lex_t*)shp->lex_context);
643#endif /* SHOPT_KIA */
644	exit(savxit&SH_EXITMASK);
645}
646
647