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