1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2009 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 * David Korn 23 * AT&T Labs 24 * 25 */ 26 27 #include "defs.h" 28 #include <fcin.h> 29 #include <ls.h> 30 #include <nval.h> 31 #include "variables.h" 32 #include "path.h" 33 #include "io.h" 34 #include "jobs.h" 35 #include "history.h" 36 #include "test.h" 37 #include "FEATURE/dynamic" 38 #include "FEATURE/externs" 39 #if SHOPT_PFSH 40 # ifdef _hdr_exec_attr 41 # include <exec_attr.h> 42 # else 43 # undef SHOPT_PFSH 44 # endif 45 # if _lib_vfork 46 # include <ast_vfork.h> 47 # else 48 # define vfork() fork() 49 # endif 50 #endif 51 52 #define RW_ALL (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH) 53 #define LIBCMD "cmd" 54 55 56 static int canexecute(char*,int); 57 static void funload(Shell_t*,int,const char*); 58 static void exscript(Shell_t*,char*, char*[], char**); 59 static int path_chkpaths(Pathcomp_t*,Pathcomp_t*,Pathcomp_t*,int); 60 static void path_checkdup(register Pathcomp_t*); 61 62 static const char *std_path; 63 64 static int onstdpath(const char *name) 65 { 66 register const char *cp = std_path, *sp; 67 if(cp) 68 while(*cp) 69 { 70 for(sp=name; *sp && (*cp == *sp); sp++,cp++); 71 if(*sp==0 && (*cp==0 || *cp==':')) 72 return(1); 73 while(*cp && *cp++!=':'); 74 } 75 return(0); 76 } 77 78 static pid_t path_pfexecve(const char *path, char *argv[],char *const envp[],int spawn) 79 { 80 #if SHOPT_PFSH 81 pid_t pid; 82 char resolvedpath[PATH_MAX + 1]; 83 if(spawn) 84 { 85 while((pid = vfork()) < 0) 86 _sh_fork(pid, 0, (int*)0); 87 if(pid) 88 return(pid); 89 } 90 if(!sh_isoption(SH_PFSH)) 91 return(execve(path, argv, envp)); 92 /* Solaris implements realpath(3C) using the resolvepath(2) */ 93 /* system call so we can save us to call access(2) first */ 94 if (!realpath(path, resolvedpath)) 95 return -1; 96 97 /* we can exec the command directly instead of via pfexec(1) if */ 98 /* there is a matching entry without attributes in exec_attr(4) */ 99 if (sh.user && *sh.user) 100 { 101 execattr_t *pf; 102 if(pf=getexecuser(sh.user, KV_COMMAND, resolvedpath, GET_ONE)) 103 { 104 if (!pf->attr || pf->attr->length == 0) 105 { 106 int r = execve(path, argv, envp); 107 free_execattr(pf); 108 return r; 109 } 110 free_execattr(pf); 111 } 112 else 113 { 114 errno = ENOENT; 115 return -1; 116 } 117 } 118 --argv; 119 argv[0] = argv[1]; 120 argv[1] = resolvedpath; 121 return(execve("/usr/bin/pfexec", argv, envp)); 122 #else 123 return(execve(path, argv, envp)); 124 #endif 125 } 126 127 128 static pid_t _spawnveg(const char *path, char* const argv[], char* const envp[], pid_t pgid) 129 { 130 int waitsafe = job.waitsafe; 131 pid_t pid; 132 job_lock(); 133 while(1) 134 { 135 sh_stats(STAT_SPAWN); 136 pid = spawnveg(path,argv,envp,pgid); 137 if(pid>=0 || errno!=EAGAIN) 138 break; 139 _sh_fork(pid, 0, (int*)0); 140 } 141 job.waitsafe = waitsafe; 142 job_unlock(); 143 return(pid); 144 } 145 /* 146 * used with command -x to run the command in multiple passes 147 * spawn is non-zero when invoked via spawn 148 * the exitval is set to the maximum for each execution 149 */ 150 static pid_t path_xargs(const char *path, char *argv[],char *const envp[], int spawn) 151 { 152 register char *cp, **av, **xv; 153 char **avlast= &argv[sh.xargmax], **saveargs=0; 154 char *const *ev; 155 long size, left; 156 int nlast=1,n,exitval=0; 157 pid_t pid; 158 if(sh.xargmin < 0) 159 return((pid_t)-1); 160 size = sh.lim.arg_max-1024; 161 for(ev=envp; cp= *ev; ev++) 162 size -= strlen(cp)-1; 163 for(av=argv; (cp= *av) && av< &argv[sh.xargmin]; av++) 164 size -= strlen(cp)-1; 165 for(av=avlast; cp= *av; av++,nlast++) 166 size -= strlen(cp)-1; 167 av = &argv[sh.xargmin]; 168 if(!spawn) 169 job_clear(); 170 sh.exitval = 0; 171 while(av<avlast) 172 { 173 for(xv=av,left=size; left>0 && av<avlast;) 174 left -= strlen(*av++)+1; 175 /* leave at least two for last */ 176 if(left<0 && (avlast-av)<2) 177 av--; 178 if(xv==&argv[sh.xargmin]) 179 { 180 n = nlast*sizeof(char*); 181 saveargs = (char**)malloc(n); 182 memcpy((void*)saveargs, (void*)av, n); 183 memcpy((void*)av,(void*)avlast,n); 184 } 185 else 186 { 187 for(n=sh.xargmin; xv < av; xv++) 188 argv[n++] = *xv; 189 for(xv=avlast; cp= *xv; xv++) 190 argv[n++] = cp; 191 argv[n] = 0; 192 } 193 if(saveargs || av<avlast || (exitval && !spawn)) 194 { 195 if((pid=_spawnveg(path,argv,envp,0)) < 0) 196 return(-1); 197 job_post(pid,0); 198 job_wait(pid); 199 if(sh.exitval>exitval) 200 exitval = sh.exitval; 201 if(saveargs) 202 { 203 memcpy((void*)av,saveargs,n); 204 free((void*)saveargs); 205 saveargs = 0; 206 } 207 } 208 else if(spawn && !sh_isoption(SH_PFSH)) 209 { 210 sh.xargexit = exitval; 211 return(_spawnveg(path,argv,envp,spawn>>1)); 212 } 213 else 214 return(path_pfexecve(path,argv,envp,spawn)); 215 } 216 if(!spawn) 217 exit(exitval); 218 return((pid_t)-1); 219 } 220 221 /* 222 * make sure PWD is set up correctly 223 * Return the present working directory 224 * Invokes getcwd() if flag==0 and if necessary 225 * Sets the PWD variable to this value 226 */ 227 char *path_pwd(int flag) 228 { 229 register char *cp; 230 register char *dfault = (char*)e_dot; 231 register int count = 0; 232 Shell_t *shp = &sh; 233 if(shp->pwd) 234 return((char*)shp->pwd); 235 while(1) 236 { 237 /* try from lowest to highest */ 238 switch(count++) 239 { 240 case 0: 241 cp = nv_getval(PWDNOD); 242 break; 243 case 1: 244 cp = nv_getval(HOME); 245 break; 246 case 2: 247 cp = "/"; 248 break; 249 case 3: 250 cp = (char*)e_crondir; 251 if(flag) /* skip next case when non-zero flag */ 252 ++count; 253 break; 254 case 4: 255 { 256 if(cp=getcwd(NIL(char*),0)) 257 { 258 nv_offattr(PWDNOD,NV_NOFREE); 259 nv_unset(PWDNOD); 260 PWDNOD->nvalue.cp = cp; 261 goto skip; 262 } 263 break; 264 } 265 case 5: 266 return(dfault); 267 } 268 if(cp && *cp=='/' && test_inode(cp,e_dot)) 269 break; 270 } 271 if(count>1) 272 { 273 nv_offattr(PWDNOD,NV_NOFREE); 274 nv_putval(PWDNOD,cp,NV_RDONLY); 275 } 276 skip: 277 nv_onattr(PWDNOD,NV_NOFREE|NV_EXPORT); 278 shp->pwd = (char*)(PWDNOD->nvalue.cp); 279 return(cp); 280 } 281 282 static void free_bltin(Namval_t *np,void *data) 283 { 284 register Pathcomp_t *pp= (Pathcomp_t*)data; 285 if(pp->flags&PATH_STD_DIR) 286 { 287 int offset=staktell();; 288 if(strcmp(pp->name,"/bin")==0 || memcmp(pp->name,np->nvname,pp->len) || np->nvname[pp->len]!='/') 289 return; 290 stakputs("/bin"); 291 stakputs(np->nvname+pp->len+1); 292 stakputc(0); 293 sh_addbuiltin(stakptr(offset),np->nvalue.bfp,NiL); 294 stakseek(offset); 295 return; 296 } 297 if((void*)np->nvenv==pp->bltin_lib) 298 nv_delete(np,sh_bltin_tree(),NV_NOFREE); 299 } 300 301 /* 302 * delete current Pathcomp_t structure 303 */ 304 void path_delete(Pathcomp_t *first) 305 { 306 register Pathcomp_t *pp=first, *old=0, *ppnext; 307 while(pp) 308 { 309 ppnext = pp->next; 310 if(--pp->refcount<=0) 311 { 312 if(pp->lib) 313 free((void*)pp->lib); 314 if(pp->blib) 315 free((void*)pp->blib); 316 if(pp->bltin_lib || (pp->flags&PATH_STD_DIR)) 317 { 318 nv_scan(sh_bltin_tree(),free_bltin,pp,0,0); 319 #if SHOPT_DYNAMIC 320 if(pp->bltin_lib) 321 dlclose(pp->bltin_lib); 322 #endif /* SHOPT_DYNAMIC */ 323 } 324 free((void*)pp); 325 if(old) 326 old->next = ppnext; 327 } 328 else 329 old = pp; 330 pp = ppnext; 331 } 332 } 333 334 /* 335 * returns library variable from .paths 336 * The value might be returned on the stack overwriting path 337 */ 338 static char *path_lib(Pathcomp_t *pp, char *path) 339 { 340 register char *last = strrchr(path,'/'); 341 register int r; 342 struct stat statb; 343 if(last) 344 *last = 0; 345 else 346 path = "."; 347 r = stat(path,&statb); 348 if(last) 349 *last = '/'; 350 if(r>=0) 351 { 352 Pathcomp_t pcomp; 353 char save[8]; 354 for( ;pp; pp=pp->next) 355 { 356 path_checkdup(pp); 357 if(pp->ino==statb.st_ino && pp->dev==statb.st_dev && pp->mtime==statb.st_mtime) 358 return(pp->lib); 359 } 360 pcomp.len = 0; 361 if(last) 362 pcomp.len = last-path; 363 memcpy((void*)save, (void*)stakptr(PATH_OFFSET+pcomp.len),sizeof(save)); 364 if(path_chkpaths((Pathcomp_t*)0,(Pathcomp_t*)0,&pcomp,PATH_OFFSET)) 365 return(stakfreeze(1)); 366 memcpy((void*)stakptr(PATH_OFFSET+pcomp.len),(void*)save,sizeof(save)); 367 } 368 return(0); 369 } 370 371 #if 0 372 void path_dump(register Pathcomp_t *pp) 373 { 374 sfprintf(sfstderr,"dump\n"); 375 while(pp) 376 { 377 sfprintf(sfstderr,"pp=%x dev=%d ino=%d len=%d flags=%o name=%.*s\n", 378 pp,pp->dev,pp->ino,pp->len,pp->flags,pp->len,pp->name); 379 pp = pp->next; 380 } 381 } 382 #endif 383 384 /* 385 * check for duplicate directories on PATH 386 */ 387 static void path_checkdup(register Pathcomp_t *pp) 388 { 389 register char *name = pp->name; 390 register Pathcomp_t *oldpp,*first; 391 register int flag=0; 392 struct stat statb; 393 if(stat(name,&statb)<0 || !S_ISDIR(statb.st_mode)) 394 { 395 pp->flags |= PATH_SKIP; 396 pp->dev = *name=='/'; 397 return; 398 } 399 pp->mtime = statb.st_mtime; 400 pp->ino = statb.st_ino; 401 pp->dev = statb.st_dev; 402 if(*name=='/' && onstdpath(name)) 403 flag = PATH_STD_DIR; 404 first = (pp->flags&PATH_CDPATH)?pp->shp->cdpathlist:path_get(""); 405 for(oldpp=first; oldpp && oldpp!=pp; oldpp=oldpp->next) 406 { 407 if(pp->ino==oldpp->ino && pp->dev==oldpp->dev && pp->mtime==oldpp->mtime) 408 { 409 flag |= PATH_SKIP; 410 break; 411 } 412 } 413 pp->flags |= flag; 414 if(((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH)) 415 { 416 int offset = staktell(); 417 stakputs(name); 418 path_chkpaths(first,0,pp,offset); 419 stakseek(offset); 420 } 421 } 422 423 /* 424 * write the next path to search on the current stack 425 * if last is given, all paths that come before <last> are skipped 426 * the next pathcomp is returned. 427 */ 428 Pathcomp_t *path_nextcomp(register Pathcomp_t *pp, const char *name, Pathcomp_t *last) 429 { 430 Pathcomp_t *ppnext; 431 stakseek(PATH_OFFSET); 432 if(*name=='/') 433 pp = 0; 434 else 435 { 436 for(;pp && pp!=last;pp=ppnext) 437 { 438 if(ppnext=pp->next) 439 ppnext->shp = pp->shp; 440 if(!pp->dev && !pp->ino) 441 path_checkdup(pp); 442 if(pp->flags&PATH_SKIP) 443 continue; 444 if(!last || *pp->name!='/') 445 break; 446 } 447 if(!pp) /* this should not happen */ 448 pp = last; 449 } 450 if(pp && (pp->name[0]!='.' || pp->name[1])) 451 { 452 if(*pp->name!='/') 453 { 454 stakputs(path_pwd(1)); 455 if(*stakptr(staktell()-1)!='/') 456 stakputc('/'); 457 } 458 stakwrite(pp->name,pp->len); 459 if(pp->name[pp->len-1]!='/') 460 stakputc('/'); 461 } 462 stakputs(name); 463 stakputc(0); 464 while(pp && pp!=last && (pp=pp->next)) 465 { 466 if(!(pp->flags&PATH_SKIP)) 467 return(pp); 468 } 469 return((Pathcomp_t*)0); 470 } 471 472 static Pathcomp_t* defpath_init(Shell_t *shp) 473 { 474 Pathcomp_t *pp = (void*)path_addpath((Pathcomp_t*)0,(std_path),PATH_PATH); 475 if(shp->defpathlist = (void*)pp) 476 pp->shp = shp; 477 return(pp); 478 } 479 480 static void path_init(Shell_t *shp) 481 { 482 const char *val; 483 Pathcomp_t *pp; 484 if(!std_path && !(std_path=astconf("PATH",NIL(char*),NIL(char*)))) 485 std_path = e_defpath; 486 if(val=sh_scoped(shp,(PATHNOD))->nvalue.cp) 487 { 488 pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_PATH); 489 if(shp->pathlist = (void*)pp) 490 pp->shp = shp; 491 } 492 else 493 { 494 if(!(pp=(Pathcomp_t*)shp->defpathlist)) 495 pp = defpath_init(shp); 496 shp->pathlist = (void*)path_dup(pp); 497 } 498 if(val=sh_scoped(shp,(FPATHNOD))->nvalue.cp) 499 { 500 pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_FPATH); 501 if(shp->pathlist = (void*)pp) 502 pp->shp = shp; 503 } 504 } 505 506 /* 507 * returns that pathlist to search 508 */ 509 Pathcomp_t *path_get(register const char *name) 510 { 511 register Shell_t *shp = &sh; 512 register Pathcomp_t *pp=0; 513 if(*name && strchr(name,'/')) 514 return(0); 515 if(!sh_isstate(SH_DEFPATH)) 516 { 517 if(!shp->pathlist) 518 path_init(shp); 519 pp = (Pathcomp_t*)shp->pathlist; 520 } 521 if(!pp && (!(PATHNOD)->nvalue.cp) || sh_isstate(SH_DEFPATH)) 522 { 523 if(!(pp=(Pathcomp_t*)shp->defpathlist)) 524 pp = defpath_init(shp); 525 } 526 return(pp); 527 } 528 529 /* 530 * open file corresponding to name using path give by <pp> 531 */ 532 static int path_opentype(const char *name, register Pathcomp_t *pp, int fun) 533 { 534 register int fd= -1; 535 struct stat statb; 536 Pathcomp_t *oldpp; 537 Shell_t *shp; 538 if(pp) 539 shp = pp->shp; 540 else 541 { 542 shp = sh_getinterp(); 543 if(!shp->pathlist) 544 path_init(shp); 545 } 546 if(!fun && strchr(name,'/')) 547 { 548 if(sh_isoption(SH_RESTRICTED)) 549 errormsg(SH_DICT,ERROR_exit(1),e_restricted,name); 550 } 551 do 552 { 553 pp = path_nextcomp(oldpp=pp,name,0); 554 while(oldpp && (oldpp->flags&PATH_SKIP)) 555 oldpp = oldpp->next; 556 if(fun && (!oldpp || !(oldpp->flags&PATH_FPATH))) 557 continue; 558 if((fd = sh_open(path_relative(stakptr(PATH_OFFSET)),O_RDONLY,0)) >= 0) 559 { 560 if(fstat(fd,&statb)<0 || S_ISDIR(statb.st_mode)) 561 { 562 errno = EISDIR; 563 sh_close(fd); 564 fd = -1; 565 } 566 } 567 } 568 while( fd<0 && pp); 569 if(fd>=0 && (fd = sh_iomovefd(fd)) > 0) 570 { 571 fcntl(fd,F_SETFD,FD_CLOEXEC); 572 if(!shp) 573 { 574 shp = sh_getinterp(); 575 #if _UWIN 576 close(0x10001); /* this results in a /var/log/uwin message with "0x10001" for debugging */ 577 #endif 578 } 579 shp->fdstatus[fd] |= IOCLEX; 580 } 581 return(fd); 582 } 583 584 /* 585 * open file corresponding to name using path give by <pp> 586 */ 587 int path_open(const char *name, register Pathcomp_t *pp) 588 { 589 return(path_opentype(name,pp,0)); 590 } 591 592 /* 593 * given a pathname return the base name 594 */ 595 596 char *path_basename(register const char *name) 597 { 598 register const char *start = name; 599 while (*name) 600 if ((*name++ == '/') && *name) /* don't trim trailing / */ 601 start = name; 602 return ((char*)start); 603 } 604 605 char *path_fullname(const char *name) 606 { 607 int len=strlen(name)+1,dirlen=0; 608 char *path,*pwd; 609 if(*name!='/') 610 { 611 pwd = path_pwd(1); 612 dirlen = strlen(pwd)+1; 613 } 614 path = (char*)malloc(len+dirlen); 615 if(dirlen) 616 { 617 memcpy((void*)path,(void*)pwd,dirlen); 618 path[dirlen-1] = '/'; 619 } 620 memcpy((void*)&path[dirlen],(void*)name,len); 621 pathcanon(path,0); 622 return(path); 623 } 624 625 /* 626 * load functions from file <fno> 627 */ 628 static void funload(Shell_t *shp,int fno, const char *name) 629 { 630 char *pname,*oldname=shp->st.filename, buff[IOBSIZE+1]; 631 Namval_t *np; 632 struct Ufunction *rp; 633 int savestates = sh_getstate(), oldload=shp->funload; 634 pname = path_fullname(stakptr(PATH_OFFSET)); 635 if(shp->fpathdict && (rp = dtmatch(shp->fpathdict,(void*)pname))) 636 { 637 Dt_t *funtree = sh_subfuntree(1); 638 do 639 { 640 if((np = dtsearch(funtree,rp->np)) && is_afunction(np)) 641 { 642 if(np->nvalue.rp) 643 np->nvalue.rp->fdict = 0; 644 nv_delete(np,funtree,NV_NOFREE); 645 } 646 dtinsert(funtree,rp->np); 647 rp->fdict = funtree; 648 } 649 while((rp=dtnext(shp->fpathdict,rp)) && strcmp(pname,rp->fname)==0); 650 return; 651 } 652 sh_onstate(SH_NOLOG); 653 sh_onstate(SH_NOALIAS); 654 shp->readscript = (char*)name; 655 shp->st.filename = pname; 656 shp->funload = 1; 657 error_info.line = 0; 658 sh_eval(sfnew(NIL(Sfio_t*),buff,IOBSIZE,fno,SF_READ),SH_FUNEVAL); 659 shp->readscript = 0; 660 free((void*)shp->st.filename); 661 shp->funload = oldload; 662 shp->st.filename = oldname; 663 sh_setstate(savestates); 664 } 665 666 /* 667 * do a path search and track alias if requested 668 * if flag is 0, or if name not found, then try autoloading function 669 * if flag==2 or 3, returns 1 if name found on FPATH 670 * if flag==3 no tracked alias will be set 671 * returns 1, if function was autoloaded. 672 * If oldpp is not NULL, it will contain a pointer to the path component 673 * where it was found. 674 */ 675 676 int path_search(register const char *name,Pathcomp_t **oldpp, int flag) 677 { 678 register Namval_t *np; 679 register int fno; 680 Pathcomp_t *pp=0; 681 Shell_t *shp = &sh; 682 if(name && strchr(name,'/')) 683 { 684 stakseek(PATH_OFFSET); 685 stakputs(name); 686 if(canexecute(stakptr(PATH_OFFSET),0)<0) 687 { 688 *stakptr(PATH_OFFSET) = 0; 689 return(0); 690 } 691 if(*name=='/') 692 return(1); 693 stakseek(PATH_OFFSET); 694 stakputs(path_pwd(1)); 695 stakputc('/'); 696 stakputs(name); 697 stakputc(0); 698 return(0); 699 } 700 if(sh_isstate(SH_DEFPATH)) 701 { 702 if(!shp->defpathlist) 703 defpath_init(shp); 704 } 705 else if(!shp->pathlist) 706 path_init(shp); 707 if(flag) 708 { 709 if((np=nv_search(name,shp->track_tree,0)) && !nv_isattr(np,NV_NOALIAS) && (pp=(Pathcomp_t*)np->nvalue.cp)) 710 { 711 stakseek(PATH_OFFSET); 712 path_nextcomp(pp,name,pp); 713 stakputc(0); 714 return(0); 715 } 716 pp = path_absolute(name,oldpp?*oldpp:NIL(Pathcomp_t*)); 717 if(oldpp) 718 *oldpp = pp; 719 if(!pp && (np=nv_search(name,shp->fun_tree,HASH_NOSCOPE))&&np->nvalue.ip) 720 return(1); 721 if(!pp) 722 *stakptr(PATH_OFFSET) = 0; 723 } 724 if(flag==0 || !pp || (pp->flags&PATH_FPATH)) 725 { 726 if(!pp) 727 pp=sh_isstate(SH_DEFPATH)?shp->defpathlist:shp->pathlist; 728 if(pp && strmatch(name,e_alphanum) && (fno=path_opentype(name,pp,1))>=0) 729 { 730 if(flag==2) 731 { 732 sh_close(fno); 733 return(1); 734 } 735 funload(shp,fno,name); 736 return(1); 737 } 738 *stakptr(PATH_OFFSET) = 0; 739 return(0); 740 } 741 else if(pp && !sh_isstate(SH_DEFPATH) && *name!='/' && flag<3) 742 { 743 if(np=nv_search(name,shp->track_tree,NV_ADD)) 744 path_alias(np,pp); 745 } 746 return(0); 747 } 748 749 /* 750 * do a path search and find the full pathname of file name 751 */ 752 Pathcomp_t *path_absolute(register const char *name, Pathcomp_t *pp) 753 { 754 register int f,isfun; 755 int noexec=0; 756 Pathcomp_t *oldpp; 757 Shell_t *shp = &sh; 758 Namval_t *np; 759 shp->path_err = ENOENT; 760 if(!pp && !(pp=path_get(""))) 761 return(0); 762 shp->path_err = 0; 763 while(1) 764 { 765 sh_sigcheck(); 766 isfun = (pp->flags&PATH_FPATH); 767 if(oldpp=pp) 768 { 769 pp = path_nextcomp(pp,name,0); 770 while(oldpp->flags&PATH_SKIP) 771 { 772 if(!(oldpp=oldpp->next)) 773 { 774 shp->path_err = ENOENT; 775 return(0); 776 } 777 } 778 } 779 780 if(!isfun && !sh_isoption(SH_RESTRICTED)) 781 { 782 if(*stakptr(PATH_OFFSET)=='/' && nv_search(stakptr(PATH_OFFSET),sh.bltin_tree,0)) 783 return(oldpp); 784 #if SHOPT_DYNAMIC 785 if(oldpp->blib) 786 { 787 typedef int (*Fptr_t)(int, char*[], void*); 788 Fptr_t addr; 789 int n = staktell(); 790 int libcmd; 791 char *cp; 792 stakputs("b_"); 793 stakputs(name); 794 stakputc(0); 795 if(!oldpp->bltin_lib) 796 { 797 if(cp = strrchr(oldpp->blib,'/')) 798 cp++; 799 else 800 cp = oldpp->blib; 801 if((libcmd = !strcmp(cp,LIBCMD)) && (addr=(Fptr_t)dlllook((void*)0,stakptr(n)))) 802 { 803 if((np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)) && nv_isattr(np,NV_BLTINOPT)) 804 return(oldpp); 805 } 806 #if (_AST_VERSION>=20040404) 807 if (oldpp->bltin_lib = dllplug(SH_ID, oldpp->blib, NiL, RTLD_LAZY, NiL, 0)) 808 #else 809 if (oldpp->bltin_lib = dllfind(oldpp->blib, NiL, RTLD_LAZY, NiL, 0)) 810 #endif 811 { 812 /* 813 * this detects the 2007-05-11 builtin context change and also 814 * the 2008-03-30 opt_info.num change that hit libcmd::b_head 815 */ 816 817 if (libcmd && !dlllook(oldpp->bltin_lib, "b_pids")) 818 { 819 dlclose(oldpp->bltin_lib); 820 oldpp->bltin_lib = 0; 821 oldpp->blib = 0; 822 } 823 else 824 sh_addlib(oldpp->bltin_lib); 825 } 826 } 827 if((addr=(Fptr_t)dlllook(oldpp->bltin_lib,stakptr(n))) && 828 (!(np = sh_addbuiltin(stakptr(PATH_OFFSET),NiL,NiL)) || np->nvalue.bfp!=addr) && 829 (np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL))) 830 { 831 np->nvenv = oldpp->bltin_lib; 832 return(oldpp); 833 } 834 } 835 #endif /* SHOPT_DYNAMIC */ 836 } 837 sh_stats(STAT_PATHS); 838 f = canexecute(stakptr(PATH_OFFSET),isfun); 839 if(isfun && f>=0) 840 { 841 nv_onattr(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE),NV_LTOU|NV_FUNCTION); 842 funload(shp,f,name); 843 close(f); 844 f = -1; 845 return(0); 846 } 847 else if(f>=0 && (oldpp->flags & PATH_STD_DIR)) 848 { 849 int n = staktell(); 850 stakputs("/bin/"); 851 stakputs(name); 852 stakputc(0); 853 np = nv_search(stakptr(n),sh.bltin_tree,0); 854 stakseek(n); 855 if(np) 856 { 857 n = np->nvflag; 858 np = sh_addbuiltin(stakptr(PATH_OFFSET),np->nvalue.bfp,nv_context(np)); 859 np->nvflag = n; 860 } 861 } 862 if(!pp || f>=0) 863 break; 864 if(errno!=ENOENT) 865 noexec = errno; 866 } 867 if(f<0) 868 { 869 shp->path_err = (noexec?noexec:ENOENT); 870 return(0); 871 } 872 stakputc(0); 873 return(oldpp); 874 } 875 876 /* 877 * returns 0 if path can execute 878 * sets exec_err if file is found but can't be executable 879 */ 880 #undef S_IXALL 881 #ifdef S_IXUSR 882 # define S_IXALL (S_IXUSR|S_IXGRP|S_IXOTH) 883 #else 884 # ifdef S_IEXEC 885 # define S_IXALL (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) 886 # else 887 # define S_IXALL 0111 888 # endif /*S_EXEC */ 889 #endif /* S_IXUSR */ 890 891 static int canexecute(register char *path, int isfun) 892 { 893 struct stat statb; 894 register int fd=0; 895 path = path_relative(path); 896 if(isfun) 897 { 898 if((fd=open(path,O_RDONLY,0))<0 || fstat(fd,&statb)<0) 899 goto err; 900 } 901 else if(stat(path,&statb) < 0) 902 { 903 #if _WINIX 904 /* check for .exe or .bat suffix */ 905 char *cp; 906 if(errno==ENOENT && (!(cp=strrchr(path,'.')) || strlen(cp)>4 || strchr(cp,'/'))) 907 { 908 int offset = staktell()-1; 909 stakseek(offset); 910 stakputs(".bat"); 911 path = stakptr(PATH_OFFSET); 912 if(stat(path,&statb) < 0) 913 { 914 if(errno!=ENOENT) 915 goto err; 916 memcpy(stakptr(offset),".sh",4); 917 if(stat(path,&statb) < 0) 918 goto err; 919 } 920 } 921 else 922 #endif /* _WINIX */ 923 goto err; 924 } 925 errno = EPERM; 926 if(S_ISDIR(statb.st_mode)) 927 errno = EISDIR; 928 else if((statb.st_mode&S_IXALL)==S_IXALL || sh_access(path,X_OK)>=0) 929 return(fd); 930 if(isfun && fd>=0) 931 sh_close(fd); 932 err: 933 return(-1); 934 } 935 936 /* 937 * Return path relative to present working directory 938 */ 939 940 char *path_relative(register const char* file) 941 { 942 register const char *pwd; 943 register const char *fp = file; 944 /* can't relpath when sh.pwd not set */ 945 if(!(pwd=sh.pwd)) 946 return((char*)fp); 947 while(*pwd==*fp) 948 { 949 if(*pwd++==0) 950 return((char*)e_dot); 951 fp++; 952 } 953 if(*pwd==0 && *fp == '/') 954 { 955 while(*++fp=='/'); 956 if(*fp) 957 return((char*)fp); 958 return((char*)e_dot); 959 } 960 return((char*)file); 961 } 962 963 void path_exec(register const char *arg0,register char *argv[],struct argnod *local) 964 { 965 char **envp; 966 const char *opath; 967 Pathcomp_t *libpath, *pp=0; 968 Shell_t *shp = &sh; 969 int slash=0; 970 nv_setlist(local,NV_EXPORT|NV_IDENT|NV_ASSIGN,0); 971 envp = sh_envgen(); 972 if(strchr(arg0,'/')) 973 { 974 slash=1; 975 /* name containing / not allowed for restricted shell */ 976 if(sh_isoption(SH_RESTRICTED)) 977 errormsg(SH_DICT,ERROR_exit(1),e_restricted,arg0); 978 } 979 else 980 pp=path_get(arg0); 981 shp->path_err= ENOENT; 982 sfsync(NIL(Sfio_t*)); 983 timerdel(NIL(void*)); 984 /* find first path that has a library component */ 985 while(pp && (pp->flags&PATH_SKIP)) 986 pp = pp->next; 987 if(pp || slash) do 988 { 989 sh_sigcheck(); 990 if(libpath=pp) 991 { 992 pp = path_nextcomp(pp,arg0,0); 993 opath = stakfreeze(1)+PATH_OFFSET; 994 } 995 else 996 opath = arg0; 997 path_spawn(opath,argv,envp,libpath,0); 998 while(pp && (pp->flags&PATH_FPATH)) 999 pp = path_nextcomp(pp,arg0,0); 1000 } 1001 while(pp); 1002 /* force an exit */ 1003 ((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT; 1004 if((errno=shp->path_err)==ENOENT) 1005 errormsg(SH_DICT,ERROR_exit(ERROR_NOENT),e_found,arg0); 1006 else 1007 errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,arg0); 1008 } 1009 1010 pid_t path_spawn(const char *opath,register char **argv, char **envp, Pathcomp_t *libpath, int spawn) 1011 { 1012 Shell_t *shp = sh_getinterp(); 1013 register char *path; 1014 char **xp=0, *xval, *libenv = (libpath?libpath->lib:0); 1015 Namval_t* np; 1016 char *s, *v; 1017 int r, n, pidsize; 1018 pid_t pid= -1; 1019 /* leave room for inserting _= pathname in environment */ 1020 envp--; 1021 #if _lib_readlink 1022 /* save original pathname */ 1023 stakseek(PATH_OFFSET); 1024 pidsize = sfprintf(stkstd,"*%d*",spawn?getpid():getppid()); 1025 stakputs(opath); 1026 opath = stakfreeze(1)+PATH_OFFSET+pidsize; 1027 np=nv_search(argv[0],shp->track_tree,0); 1028 while(libpath && !libpath->lib) 1029 libpath=libpath->next; 1030 if(libpath && (!np || nv_size(np)>0)) 1031 { 1032 /* check for symlink and use symlink name */ 1033 char buff[PATH_MAX+1]; 1034 char save[PATH_MAX+1]; 1035 stakseek(PATH_OFFSET); 1036 stakputs(opath); 1037 path = stakptr(PATH_OFFSET); 1038 while((n=readlink(path,buff,PATH_MAX))>0) 1039 { 1040 buff[n] = 0; 1041 n = PATH_OFFSET; 1042 r = 0; 1043 if((v=strrchr(path,'/')) && *buff!='/') 1044 { 1045 if(buff[0]=='.' && buff[1]=='.' && (r = strlen(path) + 1) <= PATH_MAX) 1046 memcpy(save, path, r); 1047 else 1048 r = 0; 1049 n += (v+1-path); 1050 } 1051 stakseek(n); 1052 stakputs(buff); 1053 stakputc(0); 1054 path = stakptr(PATH_OFFSET); 1055 if(v && buff[0]=='.' && buff[1]=='.') 1056 { 1057 pathcanon(path, 0); 1058 if(r && access(path,X_OK)) 1059 { 1060 memcpy(path, save, r); 1061 break; 1062 } 1063 } 1064 if(libenv = path_lib(libpath,path)) 1065 break; 1066 } 1067 stakseek(0); 1068 } 1069 #endif 1070 if(libenv && (v = strchr(libenv,'='))) 1071 { 1072 n = v - libenv; 1073 *v = 0; 1074 np = nv_open(libenv,shp->var_tree,0); 1075 *v = '='; 1076 s = nv_getval(np); 1077 stakputs(libenv); 1078 if(s) 1079 { 1080 stakputc(':'); 1081 stakputs(s); 1082 } 1083 v = stakfreeze(1); 1084 r = 1; 1085 xp = envp + 1; 1086 while (s = *xp++) 1087 { 1088 if (strneq(s, v, n) && s[n] == '=') 1089 { 1090 xval = *--xp; 1091 *xp = v; 1092 r = 0; 1093 break; 1094 } 1095 } 1096 if (r) 1097 { 1098 *envp-- = v; 1099 xp = 0; 1100 } 1101 } 1102 if(!opath) 1103 opath = stakptr(PATH_OFFSET); 1104 envp[0] = (char*)opath-(PATH_OFFSET+pidsize); 1105 envp[0][0] = '_'; 1106 envp[0][1] = '='; 1107 sfsync(sfstderr); 1108 sh_sigcheck(); 1109 path = path_relative(opath); 1110 #ifdef SHELLMAGIC 1111 if(*path!='/' && path!=opath) 1112 { 1113 /* 1114 * The following code because execv(foo,) and execv(./foo,) 1115 * may not yield the same results 1116 */ 1117 char *sp = (char*)malloc(strlen(path)+3); 1118 sp[0] = '.'; 1119 sp[1] = '/'; 1120 strcpy(sp+2,path); 1121 path = sp; 1122 } 1123 #endif /* SHELLMAGIC */ 1124 if(spawn && !sh_isoption(SH_PFSH)) 1125 pid = _spawnveg(opath, &argv[0],envp, spawn>>1); 1126 else 1127 pid = path_pfexecve(opath, &argv[0] ,envp,spawn); 1128 if(xp) 1129 *xp = xval; 1130 #ifdef SHELLMAGIC 1131 if(*path=='.' && path!=opath) 1132 { 1133 free(path); 1134 path = path_relative(opath); 1135 } 1136 #endif /* SHELLMAGIC */ 1137 if(pid>0) 1138 return(pid); 1139 retry: 1140 switch(sh.path_err = errno) 1141 { 1142 #ifdef apollo 1143 /* 1144 * On apollo's execve will fail with eacces when 1145 * file has execute but not read permissions. So, 1146 * for now we will pretend that EACCES and ENOEXEC 1147 * mean the same thing. 1148 */ 1149 case EACCES: 1150 #endif /* apollo */ 1151 case ENOEXEC: 1152 #if SHOPT_SUID_EXEC 1153 case EPERM: 1154 /* some systems return EPERM if setuid bit is on */ 1155 #endif 1156 errno = ENOEXEC; 1157 if(spawn) 1158 { 1159 #ifdef _lib_fork 1160 if(sh.subshell) 1161 return(-1); 1162 do 1163 { 1164 if((pid=fork())>0) 1165 return(pid); 1166 } 1167 while(_sh_fork(pid,0,(int*)0) < 0); 1168 ((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT; 1169 #else 1170 return(-1); 1171 #endif 1172 } 1173 exscript(shp,path,argv,envp); 1174 #ifndef apollo 1175 case EACCES: 1176 { 1177 struct stat statb; 1178 if(stat(path,&statb)>=0) 1179 { 1180 if(S_ISDIR(statb.st_mode)) 1181 errno = EISDIR; 1182 #ifdef S_ISSOCK 1183 if(S_ISSOCK(statb.st_mode)) 1184 exscript(shp,path,argv,envp); 1185 #endif 1186 } 1187 } 1188 /* FALL THROUGH */ 1189 #endif /* !apollo */ 1190 #ifdef ENAMETOOLONG 1191 case ENAMETOOLONG: 1192 #endif /* ENAMETOOLONG */ 1193 #if !SHOPT_SUID_EXEC 1194 case EPERM: 1195 #endif 1196 shp->path_err = errno; 1197 return(-1); 1198 case ENOTDIR: 1199 case ENOENT: 1200 case EINTR: 1201 #ifdef EMLINK 1202 case EMLINK: 1203 #endif /* EMLINK */ 1204 return(-1); 1205 case E2BIG: 1206 if(sh.xargmin) 1207 { 1208 pid = path_xargs(opath, &argv[0] ,envp,spawn); 1209 if(pid<0) 1210 goto retry; 1211 return(pid); 1212 } 1213 default: 1214 errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path); 1215 } 1216 return 0; 1217 } 1218 1219 /* 1220 * File is executable but not machine code. 1221 * Assume file is a Shell script and execute it. 1222 */ 1223 1224 static void exscript(Shell_t *shp,register char *path,register char *argv[],char **envp) 1225 { 1226 register Sfio_t *sp; 1227 path = path_relative(path); 1228 shp->comdiv=0; 1229 shp->bckpid = 0; 1230 shp->st.ioset=0; 1231 /* clean up any cooperating processes */ 1232 if(shp->cpipe[0]>0) 1233 sh_pclose(shp->cpipe); 1234 if(shp->cpid && shp->outpipe) 1235 sh_close(*shp->outpipe); 1236 shp->cpid = 0; 1237 if(sp=fcfile()) 1238 while(sfstack(sp,SF_POPSTACK)); 1239 job_clear(); 1240 if(shp->infd>0 && (shp->fdstatus[shp->infd]&IOCLEX)) 1241 sh_close(shp->infd); 1242 sh_setstate(sh_state(SH_FORKED)); 1243 sfsync(sfstderr); 1244 #if SHOPT_SUID_EXEC && !SHOPT_PFSH 1245 /* check if file cannot open for read or script is setuid/setgid */ 1246 { 1247 static char name[] = "/tmp/euidXXXXXXXXXX"; 1248 register int n; 1249 register uid_t euserid; 1250 char *savet=0; 1251 struct stat statb; 1252 if((n=sh_open(path,O_RDONLY,0)) >= 0) 1253 { 1254 /* move <n> if n=0,1,2 */ 1255 n = sh_iomovefd(n); 1256 if(fstat(n,&statb)>=0 && !(statb.st_mode&(S_ISUID|S_ISGID))) 1257 goto openok; 1258 sh_close(n); 1259 } 1260 if((euserid=geteuid()) != shp->userid) 1261 { 1262 strncpy(name+9,fmtbase((long)getpid(),10,0),sizeof(name)-10); 1263 /* create a suid open file with owner equal effective uid */ 1264 if((n=open(name,O_CREAT|O_TRUNC|O_WRONLY,S_ISUID|S_IXUSR)) < 0) 1265 goto fail; 1266 unlink(name); 1267 /* make sure that file has right owner */ 1268 if(fstat(n,&statb)<0 || statb.st_uid != euserid) 1269 goto fail; 1270 if(n!=10) 1271 { 1272 sh_close(10); 1273 fcntl(n, F_DUPFD, 10); 1274 sh_close(n); 1275 n=10; 1276 } 1277 } 1278 savet = *--argv; 1279 *argv = path; 1280 path_pfexecve(e_suidexec,argv,envp,0); 1281 fail: 1282 /* 1283 * The following code is just for compatibility 1284 */ 1285 if((n=open(path,O_RDONLY,0)) < 0) 1286 errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path); 1287 if(savet) 1288 *argv++ = savet; 1289 openok: 1290 shp->infd = n; 1291 } 1292 #else 1293 if((shp->infd = sh_open(path,O_RDONLY,0)) < 0) 1294 errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path); 1295 #endif 1296 shp->infd = sh_iomovefd(shp->infd); 1297 #if SHOPT_ACCT 1298 sh_accbegin(path) ; /* reset accounting */ 1299 #endif /* SHOPT_ACCT */ 1300 shp->arglist = sh_argcreate(argv); 1301 shp->lastarg = strdup(path); 1302 /* save name of calling command */ 1303 shp->readscript = error_info.id; 1304 /* close history file if name has changed */ 1305 if(shp->hist_ptr && (path=nv_getval(HISTFILE)) && strcmp(path,shp->hist_ptr->histname)) 1306 { 1307 hist_close(shp->hist_ptr); 1308 (HISTCUR)->nvalue.lp = 0; 1309 } 1310 sh_offstate(SH_FORKED); 1311 if(shp->sigflag[SIGCHLD]==SH_SIGOFF) 1312 shp->sigflag[SIGCHLD] = SH_SIGFAULT; 1313 siglongjmp(*shp->jmplist,SH_JMPSCRIPT); 1314 } 1315 1316 #if SHOPT_ACCT 1317 # include <sys/acct.h> 1318 # include "FEATURE/time" 1319 1320 static struct acct sabuf; 1321 static struct tms buffer; 1322 static clock_t before; 1323 static char *SHACCT; /* set to value of SHACCT environment variable */ 1324 static shaccton; /* non-zero causes accounting record to be written */ 1325 static int compress(time_t); 1326 /* 1327 * initialize accounting, i.e., see if SHACCT variable set 1328 */ 1329 void sh_accinit(void) 1330 { 1331 SHACCT = getenv("SHACCT"); 1332 } 1333 /* 1334 * suspend accounting until turned on by sh_accbegin() 1335 */ 1336 void sh_accsusp(void) 1337 { 1338 shaccton=0; 1339 #ifdef AEXPAND 1340 sabuf.ac_flag |= AEXPND; 1341 #endif /* AEXPAND */ 1342 } 1343 1344 /* 1345 * begin an accounting record by recording start time 1346 */ 1347 void sh_accbegin(const char *cmdname) 1348 { 1349 if(SHACCT) 1350 { 1351 sabuf.ac_btime = time(NIL(time_t *)); 1352 before = times(&buffer); 1353 sabuf.ac_uid = getuid(); 1354 sabuf.ac_gid = getgid(); 1355 strncpy(sabuf.ac_comm, (char*)path_basename(cmdname), 1356 sizeof(sabuf.ac_comm)); 1357 shaccton = 1; 1358 } 1359 } 1360 /* 1361 * terminate an accounting record and append to accounting file 1362 */ 1363 void sh_accend(void) 1364 { 1365 int fd; 1366 clock_t after; 1367 1368 if(shaccton) 1369 { 1370 after = times(&buffer); 1371 sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime); 1372 sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime); 1373 sabuf.ac_etime = compress( (time_t)(after-before)); 1374 fd = open( SHACCT , O_WRONLY | O_APPEND | O_CREAT,RW_ALL); 1375 write(fd, (const char*)&sabuf, sizeof( sabuf )); 1376 close( fd); 1377 } 1378 } 1379 1380 /* 1381 * Produce a pseudo-floating point representation 1382 * with 3 bits base-8 exponent, 13 bits fraction. 1383 */ 1384 static int compress(register time_t t) 1385 { 1386 register int exp = 0, rund = 0; 1387 1388 while (t >= 8192) 1389 { 1390 exp++; 1391 rund = t&04; 1392 t >>= 3; 1393 } 1394 if (rund) 1395 { 1396 t++; 1397 if (t >= 8192) 1398 { 1399 t >>= 3; 1400 exp++; 1401 } 1402 } 1403 return((exp<<13) + t); 1404 } 1405 #endif /* SHOPT_ACCT */ 1406 1407 1408 1409 /* 1410 * add a pathcomponent to the path search list and eliminate duplicates 1411 * and non-existing absolute paths. 1412 */ 1413 static Pathcomp_t *path_addcomp(Pathcomp_t *first, Pathcomp_t *old,const char *name, int flag) 1414 { 1415 register Pathcomp_t *pp, *oldpp; 1416 int len, offset=staktell(); 1417 if(!(flag&PATH_BFPATH)) 1418 { 1419 register const char *cp = name; 1420 while(*cp && *cp!=':') 1421 stakputc(*cp++); 1422 len = staktell()-offset; 1423 stakputc(0); 1424 stakseek(offset); 1425 name = (const char*)stakptr(offset); 1426 } 1427 else 1428 len = strlen(name); 1429 for(pp=first; pp; pp=pp->next) 1430 { 1431 if(memcmp(name,pp->name,len)==0 && (pp->name[len]==':' || pp->name[len]==0)) 1432 { 1433 pp->flags |= flag; 1434 return(first); 1435 } 1436 } 1437 for(pp=first, oldpp=0; pp; oldpp=pp, pp=pp->next); 1438 pp = newof((Pathcomp_t*)0,Pathcomp_t,1,len+1); 1439 pp->refcount = 1; 1440 memcpy((char*)(pp+1),name,len+1); 1441 pp->name = (char*)(pp+1); 1442 pp->len = len; 1443 if(oldpp) 1444 oldpp->next = pp; 1445 else 1446 first = pp; 1447 pp->flags = flag; 1448 if(strcmp(name,SH_CMDLIB_DIR)==0) 1449 { 1450 pp->dev = 1; 1451 pp->flags |= PATH_BUILTIN_LIB; 1452 pp->blib = malloc(4); 1453 strcpy(pp->blib,LIBCMD); 1454 return(first); 1455 } 1456 if(old && ((flag&(PATH_PATH|PATH_SKIP))==PATH_PATH)) 1457 path_chkpaths(first,old,pp,offset); 1458 return(first); 1459 } 1460 1461 /* 1462 * This function checks for the .paths file in directory in <pp> 1463 * it assumes that the directory is on the stack at <offset> 1464 */ 1465 static int path_chkpaths(Pathcomp_t *first, Pathcomp_t* old,Pathcomp_t *pp, int offset) 1466 { 1467 struct stat statb; 1468 int k,m,n,fd; 1469 char *sp,*cp,*ep; 1470 stakseek(offset+pp->len); 1471 if(pp->len==1 && *stakptr(offset)=='/') 1472 stakseek(offset); 1473 stakputs("/.paths"); 1474 if((fd=open(stakptr(offset),O_RDONLY))>=0) 1475 { 1476 fstat(fd,&statb); 1477 n = statb.st_size; 1478 stakseek(offset+pp->len+n+2); 1479 sp = stakptr(offset+pp->len); 1480 *sp++ = '/'; 1481 n=read(fd,cp=sp,n); 1482 sp[n] = 0; 1483 close(fd); 1484 for(ep=0; n--; cp++) 1485 { 1486 if(*cp=='=') 1487 { 1488 ep = cp+1; 1489 continue; 1490 } 1491 else if(*cp!='\r' && *cp!='\n') 1492 continue; 1493 if(*sp=='#' || sp==cp) 1494 { 1495 sp = cp+1; 1496 continue; 1497 } 1498 *cp = 0; 1499 m = ep ? (ep-sp) : 0; 1500 if(!m || m==6 && memcmp((void*)sp,(void*)"FPATH=",6)==0) 1501 { 1502 if(first) 1503 { 1504 char *ptr = stakptr(offset+pp->len+1); 1505 if(ep) 1506 strcpy(ptr,ep); 1507 path_addcomp(first,old,stakptr(offset),PATH_FPATH|PATH_BFPATH); 1508 } 1509 } 1510 else if(m==12 && memcmp((void*)sp,(void*)"BUILTIN_LIB=",12)==0) 1511 { 1512 if(!(pp->flags & PATH_BUILTIN_LIB)) 1513 { 1514 pp->flags |= PATH_BUILTIN_LIB; 1515 if (*ep == '.' && !*(ep + 1)) 1516 pp->flags |= PATH_STD_DIR; 1517 else 1518 { 1519 k = strlen(ep)+1; 1520 if (*ep != '/') 1521 k += pp->len+1; 1522 pp->blib = sp = malloc(k); 1523 if (*ep != '/') 1524 { 1525 strcpy(pp->blib,pp->name); 1526 sp += pp->len; 1527 *sp++ = '/'; 1528 } 1529 strcpy(sp,ep); 1530 } 1531 } 1532 } 1533 else if(m) 1534 { 1535 pp->lib = (char*)malloc(cp-sp+pp->len+2); 1536 memcpy((void*)pp->lib,(void*)sp,m); 1537 memcpy((void*)&pp->lib[m],stakptr(offset),pp->len); 1538 pp->lib[k=m+pp->len] = '/'; 1539 strcpy((void*)&pp->lib[k+1],ep); 1540 pathcanon(&pp->lib[m],0); 1541 if(!first) 1542 { 1543 stakseek(0); 1544 stakputs(pp->lib); 1545 free((void*)pp->lib); 1546 return(1); 1547 } 1548 } 1549 sp = cp+1; 1550 ep = 0; 1551 } 1552 } 1553 return(0); 1554 } 1555 1556 1557 Pathcomp_t *path_addpath(Pathcomp_t *first, register const char *path,int type) 1558 { 1559 register const char *cp; 1560 Pathcomp_t *old=0; 1561 int offset = staktell(); 1562 char *savptr; 1563 1564 if(!path && type!=PATH_PATH) 1565 return(first); 1566 if(type!=PATH_FPATH) 1567 { 1568 old = first; 1569 first = 0; 1570 } 1571 if(offset) 1572 savptr = stakfreeze(0); 1573 if(path) while(*(cp=path)) 1574 { 1575 if(*cp==':') 1576 { 1577 if(type!=PATH_FPATH) 1578 first = path_addcomp(first,old,".",type); 1579 while(*++path == ':'); 1580 } 1581 else 1582 { 1583 int c; 1584 while(*path && *path!=':') 1585 path++; 1586 c = *path++; 1587 first = path_addcomp(first,old,cp,type); 1588 if(c==0) 1589 break; 1590 if(*path==0) 1591 path--; 1592 } 1593 } 1594 if(old) 1595 { 1596 if(!first && !path) 1597 { 1598 Pathcomp_t *pp = (Pathcomp_t*)old->shp->defpathlist; 1599 if(!pp) 1600 pp = defpath_init(old->shp); 1601 first = path_dup(pp); 1602 } 1603 if(cp=(FPATHNOD)->nvalue.cp) 1604 first = (void*)path_addpath((Pathcomp_t*)first,cp,PATH_FPATH); 1605 path_delete(old); 1606 } 1607 if(offset) 1608 stakset(savptr,offset); 1609 else 1610 stakseek(0); 1611 return(first); 1612 } 1613 1614 /* 1615 * duplicate the path give by <first> by incremented reference counts 1616 */ 1617 Pathcomp_t *path_dup(Pathcomp_t *first) 1618 { 1619 register Pathcomp_t *pp=first; 1620 while(pp) 1621 { 1622 pp->refcount++; 1623 pp = pp->next; 1624 } 1625 return(first); 1626 } 1627 1628 /* 1629 * called whenever the directory is changed 1630 */ 1631 void path_newdir(Pathcomp_t *first) 1632 { 1633 register Pathcomp_t *pp=first, *next, *pq; 1634 struct stat statb; 1635 for(pp=first; pp; pp=pp->next) 1636 { 1637 pp->flags &= ~PATH_SKIP; 1638 if(*pp->name=='/') 1639 continue; 1640 /* delete .paths component */ 1641 if((next=pp->next) && (next->flags&PATH_BFPATH)) 1642 { 1643 pp->next = next->next; 1644 if(--next->refcount<=0) 1645 free((void*)next); 1646 } 1647 if(stat(pp->name,&statb)<0 || !S_ISDIR(statb.st_mode)) 1648 { 1649 pp->dev = 0; 1650 pp->ino = 0; 1651 continue; 1652 } 1653 pp->dev = statb.st_dev; 1654 pp->ino = statb.st_ino; 1655 pp->mtime = statb.st_mtime; 1656 for(pq=first;pq!=pp;pq=pq->next) 1657 { 1658 if(pp->ino==pq->ino && pp->dev==pq->dev) 1659 pp->flags |= PATH_SKIP; 1660 } 1661 for(pq=pp;pq=pq->next;) 1662 { 1663 if(pp->ino==pq->ino && pp->dev==pq->dev) 1664 pq->flags |= PATH_SKIP; 1665 } 1666 if((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH) 1667 { 1668 /* try to insert .paths component */ 1669 int offset = staktell(); 1670 stakputs(pp->name); 1671 stakseek(offset); 1672 next = pp->next; 1673 pp->next = 0; 1674 path_chkpaths(first,(Pathcomp_t*)0,pp,offset); 1675 if(pp->next) 1676 pp = pp->next; 1677 pp->next = next; 1678 } 1679 } 1680 #if 0 1681 path_dump(first); 1682 #endif 1683 } 1684 1685 Pathcomp_t *path_unsetfpath(Pathcomp_t *first) 1686 { 1687 register Pathcomp_t *pp=first, *old=0; 1688 Shell_t *shp = &sh; 1689 if(shp->fpathdict) 1690 { 1691 struct Ufunction *rp, *rpnext; 1692 for(rp=(struct Ufunction*)dtfirst(shp->fpathdict);rp;rp=rpnext) 1693 { 1694 rpnext = (struct Ufunction*)dtnext(shp->fpathdict,rp); 1695 if(rp->fdict) 1696 nv_delete(rp->np,rp->fdict,NV_NOFREE); 1697 rp->fdict = 0; 1698 } 1699 } 1700 while(pp) 1701 { 1702 if((pp->flags&PATH_FPATH) && !(pp->flags&PATH_BFPATH)) 1703 { 1704 if(pp->flags&PATH_PATH) 1705 pp->flags &= ~PATH_FPATH; 1706 else 1707 { 1708 Pathcomp_t *ppsave=pp; 1709 if(old) 1710 old->next = pp->next; 1711 else 1712 first = pp->next; 1713 pp = pp->next; 1714 if(--ppsave->refcount<=0) 1715 { 1716 if(ppsave->lib) 1717 free((void*)ppsave->lib); 1718 free((void*)ppsave); 1719 } 1720 continue; 1721 } 1722 1723 } 1724 old = pp; 1725 pp = pp->next; 1726 } 1727 return(first); 1728 } 1729 1730 Pathcomp_t *path_dirfind(Pathcomp_t *first,const char *name,int c) 1731 { 1732 register Pathcomp_t *pp=first; 1733 while(pp) 1734 { 1735 if(memcmp(name,pp->name,pp->len)==0 && name[pp->len]==c) 1736 return(pp); 1737 pp = pp->next; 1738 } 1739 return(0); 1740 } 1741 1742 /* 1743 * get discipline for tracked alias 1744 */ 1745 static char *talias_get(Namval_t *np, Namfun_t *nvp) 1746 { 1747 Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp; 1748 char *ptr; 1749 if(!pp) 1750 return(NULL); 1751 path_nextcomp(pp,nv_name(np),pp); 1752 ptr = stakfreeze(0); 1753 return(ptr+PATH_OFFSET); 1754 } 1755 1756 static void talias_put(register Namval_t* np,const char *val,int flags,Namfun_t *fp) 1757 { 1758 if(!val && np->nvalue.cp) 1759 { 1760 Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp; 1761 if(--pp->refcount<=0) 1762 free((void*)pp); 1763 } 1764 nv_putv(np,val,flags,fp); 1765 } 1766 1767 static const Namdisc_t talias_disc = { 0, talias_put, talias_get }; 1768 static Namfun_t talias_init = { &talias_disc, 1 }; 1769 1770 /* 1771 * set tracked alias node <np> to value <pp> 1772 */ 1773 void path_alias(register Namval_t *np,register Pathcomp_t *pp) 1774 { 1775 if(pp) 1776 { 1777 struct stat statb; 1778 char *sp; 1779 nv_offattr(np,NV_NOPRINT); 1780 nv_stack(np,&talias_init); 1781 np->nvalue.cp = (char*)pp; 1782 pp->refcount++; 1783 nv_setattr(np,NV_TAGGED|NV_NOFREE); 1784 path_nextcomp(pp,nv_name(np),pp); 1785 sp = stakptr(PATH_OFFSET); 1786 if(sp && lstat(sp,&statb)>=0 && S_ISLNK(statb.st_mode)) 1787 nv_setsize(np,statb.st_size+1); 1788 else 1789 nv_setsize(np,0); 1790 } 1791 else 1792 nv_unset(np); 1793 } 1794 1795