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