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