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