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