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