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