1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2008 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) 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 /* This is obsolete */ 190 if((error_info.flags&ERROR_NOTIFY) && shp->bltinfun) 191 action = (*shp->bltinfun)(-sig,(char**)0,(void*)0); 192 if(action>0) 193 return; 194 #endif 195 if(shp->bltinfun && shp->bltindata.notify) 196 { 197 shp->bltindata.sigset = 1; 198 return; 199 } 200 shp->trapnote |= flag; 201 if(sig < shp->sigmax) 202 shp->sigflag[sig] |= flag; 203 if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK)) 204 { 205 if(action<0) 206 return; 207 sigrelease(sig); 208 sh_exit(SH_EXITSIG); 209 } 210 } 211 212 /* 213 * initialize signal handling 214 */ 215 void sh_siginit(void *ptr) 216 { 217 Shell_t *shp = (Shell_t*)ptr; 218 register int sig, n=SIGTERM+1; 219 register const struct shtable2 *tp = shtab_signals; 220 sig_begin(); 221 /* find the largest signal number in the table */ 222 #ifdef SIGRTMIN 223 shp->sigruntime[SH_SIGRTMIN] = SIGRTMIN; 224 #endif /* SIGRTMIN */ 225 #ifdef SIGRTMAX 226 shp->sigruntime[SH_SIGRTMAX] = SIGRTMAX; 227 #endif /* SIGRTMAX */ 228 while(*tp->sh_name) 229 { 230 sig = tp->sh_number&((1<<SH_SIGBITS)-1); 231 if ((tp->sh_number>>SH_SIGBITS) & SH_SIGRUNTIME) 232 sig = shp->sigruntime[sig-1]; 233 if(sig>n && sig<SH_TRAP) 234 n = sig; 235 tp++; 236 } 237 shp->sigmax = n++; 238 shp->st.trapcom = (char**)calloc(n,sizeof(char*)); 239 shp->sigflag = (unsigned char*)calloc(n,1); 240 shp->sigmsg = (char**)calloc(n,sizeof(char*)); 241 for(tp=shtab_signals; sig=tp->sh_number; tp++) 242 { 243 n = (sig>>SH_SIGBITS); 244 if((sig &= ((1<<SH_SIGBITS)-1)) > shp->sigmax) 245 continue; 246 sig--; 247 if(n&SH_SIGRUNTIME) 248 sig = shp->sigruntime[sig]; 249 if(sig>=0) 250 { 251 shp->sigflag[sig] = n; 252 if(*tp->sh_name) 253 shp->sigmsg[sig] = (char*)tp->sh_value; 254 } 255 } 256 } 257 258 /* 259 * Turn on trap handler for signal <sig> 260 */ 261 void sh_sigtrap(register int sig) 262 { 263 register int flag; 264 void (*fun)(int); 265 sh.st.otrapcom = 0; 266 if(sig==0) 267 sh_sigdone(); 268 else if(!((flag=sh.sigflag[sig])&(SH_SIGFAULT|SH_SIGOFF))) 269 { 270 /* don't set signal if already set or off by parent */ 271 if((fun=signal(sig,sh_fault))==SIG_IGN) 272 { 273 signal(sig,SIG_IGN); 274 flag |= SH_SIGOFF; 275 } 276 else 277 { 278 flag |= SH_SIGFAULT; 279 if(sig==SIGALRM && fun!=SIG_DFL && fun!=sh_fault) 280 signal(sig,fun); 281 } 282 flag &= ~(SH_SIGSET|SH_SIGTRAP); 283 sh.sigflag[sig] = flag; 284 } 285 } 286 287 /* 288 * set signal handler so sh_done is called for all caught signals 289 */ 290 void sh_sigdone(void) 291 { 292 register int flag, sig = sh.sigmax; 293 sh.sigflag[0] |= SH_SIGFAULT; 294 while(--sig>0) 295 { 296 flag = sh.sigflag[sig]; 297 if((flag&(SH_SIGDONE|SH_SIGIGNORE|SH_SIGINTERACTIVE)) && !(flag&(SH_SIGFAULT|SH_SIGOFF))) 298 sh_sigtrap(sig); 299 } 300 } 301 302 /* 303 * Restore to default signals 304 * Free the trap strings if mode is non-zero 305 * If mode>1 then ignored traps cause signal to be ignored 306 */ 307 void sh_sigreset(register int mode) 308 { 309 register char *trap; 310 register int flag, sig=sh.st.trapmax; 311 while(sig-- > 0) 312 { 313 if(trap=sh.st.trapcom[sig]) 314 { 315 flag = sh.sigflag[sig]&~(SH_SIGTRAP|SH_SIGSET); 316 if(*trap) 317 { 318 if(mode) 319 free(trap); 320 sh.st.trapcom[sig] = 0; 321 } 322 else if(sig && mode>1) 323 { 324 signal(sig,SIG_IGN); 325 flag &= ~SH_SIGFAULT; 326 flag |= SH_SIGOFF; 327 } 328 sh.sigflag[sig] = flag; 329 } 330 } 331 for(sig=SH_DEBUGTRAP-1;sig>=0;sig--) 332 { 333 if(trap=sh.st.trap[sig]) 334 { 335 if(mode) 336 free(trap); 337 sh.st.trap[sig] = 0; 338 } 339 340 } 341 sh.st.trapcom[0] = 0; 342 if(mode) 343 sh.st.trapmax = 0; 344 sh.trapnote=0; 345 } 346 347 /* 348 * free up trap if set and restore signal handler if modified 349 */ 350 void sh_sigclear(register int sig) 351 { 352 register int flag = sh.sigflag[sig]; 353 register char *trap; 354 sh.st.otrapcom=0; 355 if(!(flag&SH_SIGFAULT)) 356 return; 357 flag &= ~(SH_SIGTRAP|SH_SIGSET); 358 if(trap=sh.st.trapcom[sig]) 359 { 360 free(trap); 361 sh.st.trapcom[sig]=0; 362 } 363 sh.sigflag[sig] = flag; 364 } 365 366 /* 367 * check for traps 368 */ 369 370 void sh_chktrap(void) 371 { 372 register int sig=sh.st.trapmax; 373 register char *trap; 374 if(!(sh.trapnote&~SH_SIGIGNORE)) 375 sig=0; 376 sh.trapnote &= ~SH_SIGTRAP; 377 /* execute errexit trap first */ 378 if(sh_isstate(SH_ERREXIT) && sh.exitval) 379 { 380 int sav_trapnote = sh.trapnote; 381 sh.trapnote &= ~SH_SIGSET; 382 if(sh.st.trap[SH_ERRTRAP]) 383 { 384 trap = sh.st.trap[SH_ERRTRAP]; 385 sh.st.trap[SH_ERRTRAP] = 0; 386 sh_trap(trap,0); 387 sh.st.trap[SH_ERRTRAP] = trap; 388 } 389 sh.trapnote = sav_trapnote; 390 if(sh_isoption(SH_ERREXIT)) 391 { 392 struct checkpt *pp = (struct checkpt*)sh.jmplist; 393 pp->mode = SH_JMPEXIT; 394 sh_exit(sh.exitval); 395 } 396 } 397 if(sh.sigflag[SIGALRM]&SH_SIGALRM) 398 sh_timetraps(); 399 while(--sig>=0) 400 { 401 if(sh.sigflag[sig]&SH_SIGTRAP) 402 { 403 sh.sigflag[sig] &= ~SH_SIGTRAP; 404 if(trap=sh.st.trapcom[sig]) 405 sh_trap(trap,0); 406 } 407 } 408 } 409 410 411 /* 412 * parse and execute the given trap string, stream or tree depending on mode 413 * mode==0 for string, mode==1 for stream, mode==2 for parse tree 414 */ 415 int sh_trap(const char *trap, int mode) 416 { 417 Shell_t *shp = sh_getinterp(); 418 int jmpval, savxit = shp->exitval; 419 int was_history = sh_isstate(SH_HISTORY); 420 int was_verbose = sh_isstate(SH_VERBOSE); 421 int staktop = staktell(); 422 char *savptr = stakfreeze(0); 423 struct checkpt buff; 424 Fcin_t savefc; 425 fcsave(&savefc); 426 sh_offstate(SH_HISTORY); 427 sh_offstate(SH_VERBOSE); 428 shp->intrap++; 429 sh_pushcontext(&buff,SH_JMPTRAP); 430 jmpval = sigsetjmp(buff.buff,0); 431 if(jmpval == 0) 432 { 433 if(mode==2) 434 sh_exec((Shnode_t*)trap,sh_isstate(SH_ERREXIT)); 435 else 436 { 437 Sfio_t *sp; 438 if(mode) 439 sp = (Sfio_t*)trap; 440 else 441 sp = sfopen(NIL(Sfio_t*),trap,"s"); 442 sh_eval(sp,0); 443 } 444 } 445 else if(indone) 446 { 447 if(jmpval==SH_JMPSCRIPT) 448 indone=0; 449 else 450 { 451 if(jmpval==SH_JMPEXIT) 452 savxit = shp->exitval; 453 jmpval=SH_JMPTRAP; 454 } 455 } 456 sh_popcontext(&buff); 457 shp->intrap--; 458 sfsync(shp->outpool); 459 if(!shp->indebug && jmpval!=SH_JMPEXIT && jmpval!=SH_JMPFUN) 460 shp->exitval=savxit; 461 stakset(savptr,staktop); 462 fcrestore(&savefc); 463 if(was_history) 464 sh_onstate(SH_HISTORY); 465 if(was_verbose) 466 sh_onstate(SH_VERBOSE); 467 exitset(); 468 if(jmpval>SH_JMPTRAP) 469 siglongjmp(*shp->jmplist,jmpval); 470 return(shp->exitval); 471 } 472 473 /* 474 * exit the current scope and jump to an earlier one based on pp->mode 475 */ 476 void sh_exit(register int xno) 477 { 478 Shell_t *shp = &sh; 479 register struct checkpt *pp = (struct checkpt*)shp->jmplist; 480 register int sig=0; 481 register Sfio_t* pool; 482 shp->exitval=xno; 483 if(xno==SH_EXITSIG) 484 shp->exitval |= (sig=shp->lastsig); 485 #ifdef SIGTSTP 486 if(shp->trapnote&SH_SIGTSTP) 487 { 488 /* ^Z detected by the shell */ 489 shp->trapnote = 0; 490 shp->sigflag[SIGTSTP] = 0; 491 if(!shp->subshell && sh_isstate(SH_MONITOR) && !sh_isstate(SH_STOPOK)) 492 return; 493 if(sh_isstate(SH_TIMING)) 494 return; 495 /* Handles ^Z for shell builtins, subshells, and functs */ 496 shp->lastsig = 0; 497 sh_onstate(SH_MONITOR); 498 sh_offstate(SH_STOPOK); 499 shp->trapnote = 0; 500 if(!shp->subshell && (sig=sh_fork(0,NIL(int*)))) 501 { 502 job.curpgid = 0; 503 job.parent = (pid_t)-1; 504 job_wait(sig); 505 job.parent = 0; 506 shp->sigflag[SIGTSTP] = 0; 507 /* wait for child to stop */ 508 shp->exitval = (SH_EXITSIG|SIGTSTP); 509 /* return to prompt mode */ 510 pp->mode = SH_JMPERREXIT; 511 } 512 else 513 { 514 if(shp->subshell) 515 sh_subfork(); 516 /* child process, put to sleep */ 517 sh_offstate(SH_STOPOK); 518 sh_offstate(SH_MONITOR); 519 shp->sigflag[SIGTSTP] = 0; 520 /* stop child job */ 521 killpg(job.curpgid,SIGTSTP); 522 /* child resumes */ 523 job_clear(); 524 shp->forked = 1; 525 shp->exitval = (xno&SH_EXITMASK); 526 return; 527 } 528 } 529 #endif /* SIGTSTP */ 530 /* unlock output pool */ 531 sh_offstate(SH_NOTRACK); 532 if(!(pool=sfpool(NIL(Sfio_t*),shp->outpool,SF_WRITE))) 533 pool = shp->outpool; /* can't happen? */ 534 sfclrlock(pool); 535 #ifdef SIGPIPE 536 if(shp->lastsig==SIGPIPE) 537 sfpurge(pool); 538 #endif /* SIGPIPE */ 539 sfclrlock(sfstdin); 540 if(!pp) 541 sh_done(shp,sig); 542 shp->prefix = 0; 543 #if SHOPT_TYPEDEF 544 shp->mktype = 0; 545 #endif /* SHOPT_TYPEDEF*/ 546 if(pp->mode == SH_JMPSCRIPT && !pp->prev) 547 sh_done(shp,sig); 548 if(pp->mode) 549 siglongjmp(pp->buff,pp->mode); 550 } 551 552 static void array_notify(Namval_t *np, void *data) 553 { 554 Namarr_t *ap = nv_arrayptr(np); 555 NOT_USED(data); 556 if(ap && ap->fun) 557 (*ap->fun)(np, 0, NV_AFREE); 558 } 559 560 /* 561 * This is the exit routine for the shell 562 */ 563 564 void sh_done(void *ptr, register int sig) 565 { 566 Shell_t *shp = (Shell_t*)ptr; 567 register char *t; 568 register int savxit = shp->exitval; 569 shp->trapnote = 0; 570 indone=1; 571 if(sig==0) 572 sig = shp->lastsig; 573 if(shp->userinit) 574 (*shp->userinit)(shp, -1); 575 if(t=shp->st.trapcom[0]) 576 { 577 shp->st.trapcom[0]=0; /*should free but not long */ 578 shp->oldexit = savxit; 579 sh_trap(t,0); 580 savxit = shp->exitval; 581 } 582 else 583 { 584 /* avoid recursive call for set -e */ 585 sh_offstate(SH_ERREXIT); 586 sh_chktrap(); 587 } 588 nv_scan(shp->var_tree,array_notify,(void*)0,NV_ARRAY,NV_ARRAY); 589 sh_freeup(shp); 590 #if SHOPT_ACCT 591 sh_accend(); 592 #endif /* SHOPT_ACCT */ 593 #if SHOPT_VSH || SHOPT_ESH 594 if(sh_isoption(SH_EMACS)||sh_isoption(SH_VI)||sh_isoption(SH_GMACS)) 595 tty_cooked(-1); 596 #endif 597 #ifdef JOBS 598 if((sh_isoption(SH_INTERACTIVE) && shp->login_sh) || (!sh_isoption(SH_INTERACTIVE) && (sig==SIGHUP))) 599 job_walk(sfstderr,job_terminate,SIGHUP,NIL(char**)); 600 #endif /* JOBS */ 601 job_close(shp); 602 if(nv_search("VMTRACE", shp->var_tree,0)) 603 strmatch((char*)0,(char*)0); 604 sfsync((Sfio_t*)sfstdin); 605 sfsync((Sfio_t*)shp->outpool); 606 sfsync((Sfio_t*)sfstdout); 607 if(sig) 608 { 609 /* generate fault termination code */ 610 signal(sig,SIG_DFL); 611 sigrelease(sig); 612 kill(getpid(),sig); 613 pause(); 614 } 615 #if SHOPT_KIA 616 if(sh_isoption(SH_NOEXEC)) 617 kiaclose((Lex_t*)shp->lex_context); 618 #endif /* SHOPT_KIA */ 619 exit(savxit&SH_EXITMASK); 620 } 621 622