xref: /titanic_44/usr/src/lib/libshell/common/sh/fault.c (revision a60349c89adffc0902b2353230891d8e7f2b24d9)
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 
41 static 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 */
61 void	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  */
214 void 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  */
265 void	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  */
294 void	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  */
311 void	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  */
355 void	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 
376 void	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  */
435 int 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  */
499 void 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 
575 static 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 
587 void 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