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 */
malloc_done(Vmalloc_t * vm,int type,Void_t * val,Vmdisc_t * dp)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 */
sh_fault(register int sig)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 */
sh_siginit(void * ptr)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 */
sh_sigtrap(register int sig)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 */
sh_sigdone(void)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 */
sh_sigreset(register int mode)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 */
sh_sigclear(register int sig)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
sh_chktrap(void)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 */
sh_trap(const char * trap,int mode)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 */
sh_exit(register int xno)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
array_notify(Namval_t * np,void * data)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
sh_done(void * ptr,register int sig)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