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 * Create and manage subshells avoiding forks when possible 23 * 24 * David Korn 25 * AT&T Labs 26 * 27 */ 28 29 #include "defs.h" 30 #include <ls.h> 31 #include "io.h" 32 #include "fault.h" 33 #include "shnodes.h" 34 #include "shlex.h" 35 #include "jobs.h" 36 #include "variables.h" 37 #include "path.h" 38 39 #ifndef PIPE_BUF 40 # define PIPE_BUF 512 41 #endif 42 43 /* 44 * Note that the following structure must be the same 45 * size as the Dtlink_t structure 46 */ 47 struct Link 48 { 49 struct Link *next; 50 Namval_t *node; 51 }; 52 53 /* 54 * The following structure is used for command substitution and (...) 55 */ 56 static struct subshell 57 { 58 struct subshell *prev; /* previous subshell data */ 59 struct subshell *pipe; /* subshell where output goes to pipe on fork */ 60 Dt_t *var; /* variable table at time of subshell */ 61 struct Link *svar; /* save shell variable table */ 62 Dt_t *sfun; /* function scope for subshell */ 63 Dt_t *salias;/* alias scope for subshell */ 64 #ifdef PATH_BFPATH 65 Pathcomp_t *pathlist; /* for PATH variable */ 66 #endif 67 #if (ERROR_VERSION >= 20030214L) 68 struct Error_context_s *errcontext; 69 #else 70 struct errorcontext *errcontext; 71 #endif 72 Shopt_t options;/* save shell options */ 73 pid_t subpid; /* child process id */ 74 Sfio_t* saveout;/*saved standard output */ 75 char *pwd; /* present working directory */ 76 const char *shpwd; /* saved pointer to sh.pwd */ 77 void *jobs; /* save job info */ 78 mode_t mask; /* saved umask */ 79 short tmpfd; /* saved tmp file descriptor */ 80 short pipefd; /* read fd if pipe is created */ 81 char jobcontrol; 82 char monitor; 83 unsigned char fdstatus; 84 int fdsaved; /* bit make for saved files */ 85 int bckpid; 86 } *subshell_data; 87 88 static int subenv; 89 90 /* 91 * This routine will turn the sftmp() file into a real /tmp file or pipe 92 * if the /tmp file create fails 93 */ 94 void sh_subtmpfile(void) 95 { 96 if(sfset(sfstdout,0,0)&SF_STRING) 97 { 98 register int fd; 99 register struct checkpt *pp = (struct checkpt*)sh.jmplist; 100 register struct subshell *sp = subshell_data->pipe; 101 /* save file descriptor 1 if open */ 102 if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0) 103 { 104 fcntl(fd,F_SETFD,FD_CLOEXEC); 105 sh.fdstatus[fd] = sh.fdstatus[1]|IOCLEX; 106 close(1); 107 } 108 else if(errno!=EBADF) 109 errormsg(SH_DICT,ERROR_system(1),e_toomany); 110 /* popping a discipline forces a /tmp file create */ 111 sfdisc(sfstdout,SF_POPDISC); 112 if((fd=sffileno(sfstdout))<0) 113 { 114 /* unable to create the /tmp file so use a pipe */ 115 int fds[2]; 116 Sfoff_t off; 117 sh_pipe(fds); 118 sp->pipefd = fds[0]; 119 sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC); 120 /* write the data to the pipe */ 121 if(off = sftell(sfstdout)) 122 write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off); 123 sfclose(sfstdout); 124 if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1) 125 errormsg(SH_DICT,ERROR_system(1),e_file+4); 126 sh_close(fds[1]); 127 } 128 else 129 { 130 sh.fdstatus[fd] = IOREAD|IOWRITE; 131 sfsync(sfstdout); 132 if(fd==1) 133 fcntl(1,F_SETFD,0); 134 else 135 { 136 sfsetfd(sfstdout,1); 137 sh.fdstatus[1] = sh.fdstatus[fd]; 138 sh.fdstatus[fd] = IOCLOSE; 139 } 140 } 141 sh_iostream(1); 142 sfset(sfstdout,SF_SHARE|SF_PUBLIC,1); 143 sfpool(sfstdout,sh.outpool,SF_WRITE); 144 if(pp && pp->olist && pp->olist->strm == sfstdout) 145 pp->olist->strm = 0; 146 } 147 } 148 149 /* 150 * This routine creates a temp file if necessary and creates a subshell. 151 * The parent routine longjmps back to sh_subshell() 152 * The child continues possibly with its standard output replaced by temp file 153 */ 154 void sh_subfork(void) 155 { 156 register struct subshell *sp = subshell_data; 157 pid_t pid; 158 /* see whether inside $(...) */ 159 if(sp->pipe) 160 sh_subtmpfile(); 161 if(pid = sh_fork(0,NIL(int*))) 162 { 163 /* this is the parent part of the fork */ 164 if(sp->subpid==0) 165 sp->subpid = pid; 166 siglongjmp(*sh.jmplist,SH_JMPSUB); 167 } 168 else 169 { 170 int16_t subshell; 171 /* this is the child part of the fork */ 172 /* setting subpid to 1 causes subshell to exit when reached */ 173 sh_onstate(SH_FORKED); 174 sh_onstate(SH_NOLOG); 175 sh_offstate(SH_MONITOR); 176 subshell_data = 0; 177 subshell = sh.subshell = 0; 178 nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16); 179 sp->subpid=0; 180 } 181 } 182 183 /* 184 * This routine will make a copy of the given node in the 185 * layer created by the most recent subshell_fork if the 186 * node hasn't already been copied 187 */ 188 Namval_t *sh_assignok(register Namval_t *np,int add) 189 { 190 register Namval_t *mp; 191 register struct Link *lp; 192 register struct subshell *sp = (struct subshell*)subshell_data; 193 int save; 194 /* don't bother with this */ 195 if(!sp->shpwd || (nv_isnull(np) && !add)) 196 return(np); 197 /* don't bother to save if in newer scope */ 198 if(nv_search((char*)np,sp->var,HASH_BUCKET)!=np) 199 return(np); 200 for(lp=subshell_data->svar; lp; lp = lp->next) 201 { 202 if(lp->node==np) 203 return(np); 204 } 205 mp = newof(0,Namval_t,1,0); 206 lp = (struct Link*)mp; 207 lp->node = np; 208 lp->next = subshell_data->svar; 209 subshell_data->svar = lp; 210 save = sh.subshell; 211 sh.subshell = 0;; 212 nv_clone(np,mp,NV_NOFREE); 213 sh.subshell = save; 214 return(np); 215 } 216 217 /* 218 * restore the variables 219 */ 220 static void nv_restore(struct subshell *sp) 221 { 222 register struct Link *lp, *lq; 223 register Namval_t *mp, *np; 224 const char *save = sp->shpwd; 225 sp->shpwd = 0; /* make sure sh_assignok doesn't save with nv_unset() */ 226 for(lp=sp->svar; lp; lp=lq) 227 { 228 np = (Namval_t*)lp; 229 mp = lp->node; 230 lq = lp->next; 231 if(nv_isarray(mp)) 232 nv_putsub(mp,NIL(char*),ARRAY_SCAN); 233 _nv_unset(mp,NV_RDONLY); 234 nv_setsize(mp,nv_size(np)); 235 if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT)) 236 mp->nvenv = np->nvenv; 237 mp->nvfun = np->nvfun; 238 mp->nvflag = np->nvflag; 239 if((mp==nv_scoped(PATHNOD)) || (mp==nv_scoped(IFSNOD))) 240 nv_putval(mp, np->nvalue.cp,0); 241 else 242 mp->nvalue.cp = np->nvalue.cp; 243 np->nvfun = 0; 244 if(nv_isattr(mp,NV_EXPORT)) 245 { 246 char *name = nv_name(mp); 247 sh_envput(sh.env,mp); 248 if(*name=='_' && strcmp(name,"_AST_FEATURES")==0) 249 astconf(NiL, NiL, NiL); 250 } 251 else if(nv_isattr(np,NV_EXPORT)) 252 env_delete(sh.env,nv_name(mp)); 253 free((void*)np); 254 } 255 sp->shpwd=save; 256 } 257 258 /* 259 * return pointer to alias tree 260 * create new one if in a subshell and one doesn't exist and create is non-zero 261 */ 262 Dt_t *sh_subaliastree(int create) 263 { 264 register struct subshell *sp = subshell_data; 265 if(!sp || sh.curenv==0) 266 return(sh.alias_tree); 267 if(!sp->salias && create) 268 { 269 sp->salias = dtopen(&_Nvdisc,Dtoset); 270 dtview(sp->salias,sh.alias_tree); 271 sh.alias_tree = sp->salias; 272 } 273 return(sp->salias); 274 } 275 276 /* 277 * return pointer to function tree 278 * create new one if in a subshell and one doesn't exist and create is non-zero 279 */ 280 Dt_t *sh_subfuntree(int create) 281 { 282 register struct subshell *sp = subshell_data; 283 if(!sp || sh.curenv==0) 284 return(sh.fun_tree); 285 if(!sp->sfun && create) 286 { 287 sp->sfun = dtopen(&_Nvdisc,Dtoset); 288 dtview(sp->sfun,sh.fun_tree); 289 sh.fun_tree = sp->sfun; 290 } 291 return(sp->sfun); 292 } 293 294 static void table_unset(register Dt_t *root) 295 { 296 register Namval_t *np,*nq; 297 for(np=(Namval_t*)dtfirst(root);np;np=nq) 298 { 299 _nv_unset(np,NV_RDONLY); 300 nq = (Namval_t*)dtnext(root,np); 301 dtdelete(root,np); 302 free((void*)np); 303 } 304 } 305 306 int sh_subsavefd(register int fd) 307 { 308 register struct subshell *sp = subshell_data; 309 register int old=0; 310 if(sp) 311 { 312 old = !(sp->fdsaved&(1<<(fd-1))); 313 sp->fdsaved |= (1<<(fd-1)); 314 } 315 return(old); 316 } 317 318 /* 319 * Run command tree <t> in a virtual sub-shell 320 * If comsub is not null, then output will be placed in temp file (or buffer) 321 * If comsub is not null, the return value will be a stream consisting of 322 * output of command <t>. Otherwise, NULL will be returned. 323 */ 324 325 Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) 326 { 327 Shell_t *shp = &sh; 328 struct subshell sub_data; 329 register struct subshell *sp = &sub_data; 330 int jmpval,nsig; 331 int savecurenv = shp->curenv; 332 int16_t subshell; 333 char *savsig; 334 Sfio_t *iop=0; 335 struct checkpt buff; 336 struct sh_scoped savst; 337 struct dolnod *argsav=0; 338 memset((char*)sp, 0, sizeof(*sp)); 339 sfsync(shp->outpool); 340 argsav = sh_arguse(); 341 if(shp->curenv==0) 342 { 343 subshell_data=0; 344 subenv = 0; 345 } 346 shp->curenv = ++subenv; 347 savst = shp->st; 348 sh_pushcontext(&buff,SH_JMPSUB); 349 subshell = shp->subshell+1; 350 nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16); 351 shp->subshell = subshell; 352 sp->prev = subshell_data; 353 subshell_data = sp; 354 sp->errcontext = &buff.err; 355 sp->var = shp->var_tree; 356 sp->options = shp->options; 357 sp->jobs = job_subsave(); 358 #ifdef PATH_BFPATH 359 /* make sure initialization has occurred */ 360 if(!shp->pathlist) 361 path_get("."); 362 sp->pathlist = path_dup((Pathcomp_t*)shp->pathlist); 363 #endif 364 if(!shp->pwd) 365 path_pwd(0); 366 sp->bckpid = shp->bckpid; 367 if(!comsub || !sh_isoption(SH_SUBSHARE)) 368 { 369 sp->shpwd = shp->pwd; 370 sp->pwd = (shp->pwd?strdup(shp->pwd):0); 371 sp->mask = shp->mask; 372 /* save trap table */ 373 shp->st.otrapcom = 0; 374 if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0]) 375 { 376 nsig += sizeof(char*); 377 memcpy(savsig=malloc(nsig),(char*)&shp->st.trapcom[0],nsig); 378 /* this nonsense needed for $(trap) */ 379 shp->st.otrapcom = (char**)savsig; 380 } 381 sh_sigreset(0); 382 } 383 jmpval = sigsetjmp(buff.buff,0); 384 if(jmpval==0) 385 { 386 if(comsub) 387 { 388 /* disable job control */ 389 sp->jobcontrol = job.jobcontrol; 390 sp->monitor = (sh_isstate(SH_MONITOR)!=0); 391 job.jobcontrol=0; 392 sh_offstate(SH_MONITOR); 393 sp->pipe = sp; 394 /* save sfstdout and status */ 395 sp->saveout = sfswap(sfstdout,NIL(Sfio_t*)); 396 sp->fdstatus = shp->fdstatus[1]; 397 sp->tmpfd = -1; 398 sp->pipefd = -1; 399 /* use sftmp() file for standard output */ 400 if(!(iop = sftmp(PIPE_BUF))) 401 { 402 sfswap(sp->saveout,sfstdout); 403 errormsg(SH_DICT,ERROR_system(1),e_tmpcreate); 404 } 405 sfswap(iop,sfstdout); 406 sfset(sfstdout,SF_READ,0); 407 shp->fdstatus[1] = IOWRITE; 408 } 409 else if(sp->prev) 410 { 411 sp->pipe = sp->prev->pipe; 412 flags &= ~sh_state(SH_NOFORK); 413 } 414 sh_exec(t,flags); 415 } 416 if(jmpval!=SH_JMPSUB && shp->st.trapcom[0] && shp->subshell) 417 { 418 /* trap on EXIT not handled by child */ 419 char *trap=shp->st.trapcom[0]; 420 shp->st.trapcom[0] = 0; /* prevent recursion */ 421 shp->oldexit = shp->exitval; 422 sh_trap(trap,0); 423 free(trap); 424 } 425 sh_popcontext(&buff); 426 if(shp->subshell==0) /* must be child process */ 427 { 428 subshell_data = sp->prev; 429 if(jmpval==SH_JMPSCRIPT) 430 siglongjmp(*shp->jmplist,jmpval); 431 sh_done(0); 432 } 433 if(comsub) 434 { 435 /* re-enable job control */ 436 job.jobcontrol = sp->jobcontrol; 437 if(sp->monitor) 438 sh_onstate(SH_MONITOR); 439 if(sp->pipefd>=0) 440 { 441 /* sftmp() file has been returned into pipe */ 442 iop = sh_iostream(sp->pipefd); 443 sfdisc(iop,SF_POPDISC); 444 sfclose(sfstdout); 445 } 446 else 447 { 448 /* move tmp file to iop and restore sfstdout */ 449 iop = sfswap(sfstdout,NIL(Sfio_t*)); 450 if(!iop) 451 { 452 /* maybe locked try again */ 453 sfclrlock(sfstdout); 454 iop = sfswap(sfstdout,NIL(Sfio_t*)); 455 } 456 if(iop && sffileno(iop)==1) 457 { 458 int fd=sfsetfd(iop,3); 459 if(fd<0) 460 errormsg(SH_DICT,ERROR_system(1),e_toomany); 461 shp->sftable[fd] = iop; 462 fcntl(fd,F_SETFD,FD_CLOEXEC); 463 shp->fdstatus[fd] = (shp->fdstatus[1]|IOCLEX); 464 shp->fdstatus[1] = IOCLOSE; 465 } 466 sfset(iop,SF_READ,1); 467 } 468 sfswap(sp->saveout,sfstdout); 469 /* check if standard output was preserved */ 470 if(sp->tmpfd>=0) 471 { 472 close(1); 473 fcntl(sp->tmpfd,F_DUPFD,1); 474 sh_close(sp->tmpfd); 475 } 476 shp->fdstatus[1] = sp->fdstatus; 477 } 478 if(sp->subpid) 479 job_wait(sp->subpid); 480 if(comsub && iop) 481 sfseek(iop,(off_t)0,SEEK_SET); 482 if(shp->subshell) 483 shp->subshell--; 484 subshell = shp->subshell; 485 nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16); 486 #ifdef PATH_BFPATH 487 path_delete((Pathcomp_t*)shp->pathlist); 488 shp->pathlist = (void*)sp->pathlist; 489 #endif 490 job_subrestore(sp->jobs); 491 shp->jobenv = savecurenv; 492 shp->bckpid = sp->bckpid; 493 if(sp->shpwd) /* restore environment if saved */ 494 { 495 shp->options = sp->options; 496 nv_restore(sp); 497 if(sp->salias) 498 { 499 shp->alias_tree = dtview(sp->salias,0); 500 table_unset(sp->salias); 501 dtclose(sp->salias); 502 } 503 if(sp->sfun) 504 { 505 shp->fun_tree = dtview(sp->sfun,0); 506 table_unset(sp->sfun); 507 dtclose(sp->sfun); 508 } 509 sh_sigreset(1); 510 shp->st = savst; 511 shp->curenv = savecurenv; 512 if(nsig) 513 { 514 memcpy((char*)&shp->st.trapcom[0],savsig,nsig); 515 free((void*)savsig); 516 } 517 shp->options = sp->options; 518 if(!shp->pwd || strcmp(sp->pwd,shp->pwd)) 519 { 520 /* restore PWDNOD */ 521 Namval_t *pwdnod = nv_scoped(PWDNOD); 522 if(shp->pwd) 523 { 524 chdir(shp->pwd=sp->pwd); 525 #ifdef PATH_BFPATH 526 path_newdir(shp->pathlist); 527 #endif 528 } 529 if(nv_isattr(pwdnod,NV_NOFREE)) 530 pwdnod->nvalue.cp = (const char*)sp->pwd; 531 } 532 else if(sp->shpwd != shp->pwd) 533 { 534 shp->pwd = sp->pwd; 535 if(PWDNOD->nvalue.cp==sp->shpwd) 536 PWDNOD->nvalue.cp = sp->pwd; 537 } 538 else 539 free((void*)sp->pwd); 540 if(sp->mask!=shp->mask) 541 umask(shp->mask); 542 } 543 subshell_data = sp->prev; 544 sh_argfree(argsav,0); 545 shp->trapnote = 0; 546 if(shp->topfd != buff.topfd) 547 sh_iorestore(buff.topfd|IOSUBSHELL,jmpval); 548 if(shp->exitval > SH_EXITSIG) 549 { 550 int sig = shp->exitval&SH_EXITMASK; 551 if(sig==SIGINT || sig== SIGQUIT) 552 sh_fault(sig); 553 } 554 return(iop); 555 } 556