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