diff options
Diffstat (limited to 'usr/src/lib/libshell/common/sh/path.c')
-rw-r--r-- | usr/src/lib/libshell/common/sh/path.c | 1697 |
1 files changed, 1697 insertions, 0 deletions
diff --git a/usr/src/lib/libshell/common/sh/path.c b/usr/src/lib/libshell/common/sh/path.c new file mode 100644 index 0000000000..a07a9d6e00 --- /dev/null +++ b/usr/src/lib/libshell/common/sh/path.c @@ -0,0 +1,1697 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include <fcin.h> +#include <ls.h> +#include <nval.h> +#include <dlldefs.h> +#include "variables.h" +#include "path.h" +#include "io.h" +#include "jobs.h" +#include "history.h" +#include "test.h" +#include "FEATURE/externs" +#if SHOPT_PFSH +# ifdef _hdr_exec_attr +# include <exec_attr.h> +# else +# undef SHOPT_PFSH +# endif +#endif + +#define RW_ALL (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH) +#define LIBCMD "cmd" + + +static int canexecute(char*,int); +static void funload(Shell_t*,int,const char*); +static void exscript(Shell_t*,char*, char*[], char**); +static int path_chkpaths(Pathcomp_t*,Pathcomp_t*,Pathcomp_t*,int); + +static const char *std_path; + +static int onstdpath(const char *name) +{ + register const char *cp = std_path, *sp; + if(cp) + while(*cp) + { + for(sp=name; *sp && (*cp == *sp); sp++,cp++); + if(*sp==0 && (*cp==0 || *cp==':')) + return(1); + while(*cp && *cp++!=':'); + } + return(0); +} + +static int path_pfexecve(const char *path, char *argv[],char *const envp[]) +{ +#if SHOPT_PFSH + char resolvedpath[PATH_MAX + 1]; + if(!sh_isoption(SH_PFSH)) + return(execve(path, argv, envp)); + /* Solaris implements realpath(3C) using the resolvepath(2) */ + /* system call so we can save us to call access(2) first */ + if (!realpath(path, resolvedpath)) + return -1; + + /* we can exec the command directly instead of via pfexec(1) if */ + /* there is a matching entry without attributes in exec_attr(4) */ + if (sh.user && *sh.user) + { + execattr_t *pf; + if(pf=getexecuser(sh.user, KV_COMMAND, resolvedpath, GET_ONE)) + { + if (!pf->attr || pf->attr->length == 0) + { + int r = execve(path, argv, envp); + free_execattr(pf); + return r; + } + free_execattr(pf); + } + else + { + errno = ENOENT; + return -1; + } + } + --argv; + argv[0] = argv[1]; + argv[1] = resolvedpath; + return(execve("/usr/bin/pfexec", argv, envp)); +#else + return(execve(path, argv, envp)); +#endif +} + + +static pid_t _spawnveg(const char *path, char* const argv[], char* const envp[], pid_t pid) +{ + int waitsafe = job.waitsafe; + job_lock(); + pid = spawnveg(path,argv,envp,pid); + job.waitsafe = waitsafe; + job_unlock(); + return(pid); +} +/* + * used with command -x to run the command in multiple passes + * spawn is non-zero when invoked via spawn + * the exitval is set to the maximum for each execution + */ +static pid_t path_xargs(const char *path, char *argv[],char *const envp[], int spawn) +{ + register char *cp, **av, **xv; + char **avlast= &argv[sh.xargmax], **saveargs=0; + char *const *ev; + long size, left; + int nlast=1,n,exitval=0; + pid_t pid; + if(sh.xargmin < 0) + return((pid_t)-1); + size = sh.lim.arg_max-1024; + for(ev=envp; cp= *ev; ev++) + size -= strlen(cp)-1; + for(av=argv; (cp= *av) && av< &argv[sh.xargmin]; av++) + size -= strlen(cp)-1; + for(av=avlast; cp= *av; av++,nlast++) + size -= strlen(cp)-1; + av = &argv[sh.xargmin]; + if(!spawn) + job_clear(); + sh.exitval = 0; + while(av<avlast) + { + for(xv=av,left=size; left>0 && av<avlast;) + left -= strlen(*av++)+1; + /* leave at least two for last */ + if(left<0 && (avlast-av)<2) + av--; + if(xv==&argv[sh.xargmin]) + { + n = nlast*sizeof(char*); + saveargs = (char**)malloc(n); + memcpy((void*)saveargs, (void*)av, n); + memcpy((void*)av,(void*)avlast,n); + } + else + { + for(n=sh.xargmin; xv < av; xv++) + argv[n++] = *xv; + for(xv=avlast; cp= *xv; xv++) + argv[n++] = cp; + argv[n] = 0; + } + if(saveargs || av<avlast || (exitval && !spawn)) + { + if((pid=_spawnveg(path,argv,envp,0)) < 0) + return(-1); + job_post(pid,0); + job_wait(pid); + if(sh.exitval>exitval) + exitval = sh.exitval; + if(saveargs) + { + memcpy((void*)av,saveargs,n); + free((void*)saveargs); + saveargs = 0; + } + } + else if(spawn && !sh_isoption(SH_PFSH)) + { + sh.xargexit = exitval; + return(_spawnveg(path,argv,envp,spawn>>1)); + } + else + return((pid_t)path_pfexecve(path,argv,envp)); + } + if(!spawn) + exit(exitval); + return((pid_t)-1); +} + +/* + * make sure PWD is set up correctly + * Return the present working directory + * Invokes getcwd() if flag==0 and if necessary + * Sets the PWD variable to this value + */ +char *path_pwd(int flag) +{ + register char *cp; + register char *dfault = (char*)e_dot; + register int count = 0; + Shell_t *shp = &sh; + if(shp->pwd) + return((char*)shp->pwd); + while(1) + { + /* try from lowest to highest */ + switch(count++) + { + case 0: + cp = nv_getval(PWDNOD); + break; + case 1: + cp = nv_getval(HOME); + break; + case 2: + cp = "/"; + break; + case 3: + cp = (char*)e_crondir; + if(flag) /* skip next case when non-zero flag */ + ++count; + break; + case 4: + { + if(cp=getcwd(NIL(char*),0)) + { + nv_offattr(PWDNOD,NV_NOFREE); + nv_unset(PWDNOD); + PWDNOD->nvalue.cp = cp; + goto skip; + } + break; + } + case 5: + return(dfault); + } + if(cp && *cp=='/' && test_inode(cp,e_dot)) + break; + } + if(count>1) + { + nv_offattr(PWDNOD,NV_NOFREE); + nv_putval(PWDNOD,cp,NV_RDONLY); + } +skip: + nv_onattr(PWDNOD,NV_NOFREE|NV_EXPORT); + shp->pwd = (char*)(PWDNOD->nvalue.cp); + return(cp); +} + +static void free_bltin(Namval_t *np,void *data) +{ + register Pathcomp_t *pp= (Pathcomp_t*)data; + if(pp->flags&PATH_STD_DIR) + { + int offset=staktell();; + if(strcmp(pp->name,"/bin")==0 || memcmp(pp->name,np->nvname,pp->len) || np->nvname[pp->len]!='/') + return; + stakputs("/bin"); + stakputs(np->nvname+pp->len+1); + stakputc(0); + sh_addbuiltin(stakptr(offset),np->nvalue.bfp,NiL); + stakseek(offset); + return; + } + if((void*)np->nvenv==pp->bltin_lib) + dtdelete(sh_bltin_tree(),np); +} + +/* + * delete current Pathcomp_t structure + */ +void path_delete(Pathcomp_t *first) +{ + register Pathcomp_t *pp=first, *old=0, *ppnext; + while(pp) + { + ppnext = pp->next; + if(--pp->refcount<=0) + { + if(pp->lib) + free((void*)pp->lib); + if(pp->blib) + free((void*)pp->blib); + if(pp->bltin_lib || (pp->flags&PATH_STD_DIR)) + { + nv_scan(sh_bltin_tree(),free_bltin,pp,0,0); + if(pp->bltin_lib) + dlclose(pp->bltin_lib); + } + free((void*)pp); + if(old) + old->next = ppnext; + } + else + old = pp; + pp = ppnext; + } +} + +/* + * returns library variable from .paths + * The value might be returned on the stack overwriting path + */ +static char *path_lib(Pathcomp_t *pp, char *path) +{ + register char *last = strrchr(path,'/'); + register int r; + struct stat statb; + if(last) + *last = 0; + else + path = "."; + r = stat(path,&statb); + if(last) + *last = '/'; + if(r>=0) + { + Pathcomp_t pcomp; + char save[8]; + for( ;pp; pp=pp->next) + { + if(pp->ino==statb.st_ino && pp->dev==statb.st_dev) + return(pp->lib); + } + pcomp.len = 0; + if(last) + pcomp.len = last-path; + memcpy((void*)save, (void*)stakptr(PATH_OFFSET+pcomp.len),sizeof(save)); + if(path_chkpaths((Pathcomp_t*)0,(Pathcomp_t*)0,&pcomp,PATH_OFFSET)) + return(stakfreeze(1)); + memcpy((void*)stakptr(PATH_OFFSET+pcomp.len),(void*)save,sizeof(save)); + } + return(0); +} + +#if 0 +void path_dump(register Pathcomp_t *pp) +{ + sfprintf(sfstderr,"dump\n"); + while(pp) + { + sfprintf(sfstderr,"pp=%x dev=%d ino=%d len=%d flags=%o name=%.*s\n", + pp,pp->dev,pp->ino,pp->len,pp->flags,pp->len,pp->name); + pp = pp->next; + } +} +#endif + +/* + * write the next path to search on the current stack + * if last is given, all paths that come before <last> are skipped + * the next pathcomp is returned. + */ +Pathcomp_t *path_nextcomp(register Pathcomp_t *pp, const char *name, Pathcomp_t *last) +{ + stakseek(PATH_OFFSET); + if(*name=='/') + pp = 0; + else + { + for(;pp && pp!=last;pp=pp->next) + { + if(pp->flags&PATH_SKIP) + continue; + if(!last || *pp->name!='/') + break; + } + if(!pp) /* this should not happen */ + pp = last; + } + if(pp && (pp->name[0]!='.' || pp->name[1])) + { + if(*pp->name!='/') + { + stakputs(path_pwd(1)); + if(*stakptr(staktell()-1)!='/') + stakputc('/'); + } + stakwrite(pp->name,pp->len); + if(pp->name[pp->len-1]!='/') + stakputc('/'); + } + stakputs(name); + stakputc(0); + while(pp && pp!=last && (pp=pp->next)) + { + if(!(pp->flags&PATH_SKIP)) + return(pp); + } + return((Pathcomp_t*)0); +} + +static Pathcomp_t* defpath_init(Shell_t *shp) +{ + Pathcomp_t *pp = (void*)path_addpath((Pathcomp_t*)0,(std_path),PATH_PATH); + if(shp->defpathlist = (void*)pp) + pp->shp = shp; + return(pp); +} + +static void path_init(Shell_t *shp) +{ + const char *val; + Pathcomp_t *pp; + if(!std_path && !(std_path=astconf("PATH",NIL(char*),NIL(char*)))) + std_path = e_defpath; + if(val=nv_scoped((PATHNOD))->nvalue.cp) + { + pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_PATH); + if(shp->pathlist = (void*)pp) + pp->shp = shp; + } + else + { + if(!(pp=(Pathcomp_t*)shp->defpathlist)) + pp = defpath_init(shp); + shp->pathlist = (void*)path_dup(pp); + } + if(val=nv_scoped((FPATHNOD))->nvalue.cp) + { + pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_FPATH); + if(shp->pathlist = (void*)pp) + pp->shp = shp; + } +} + +/* + * returns that pathlist to search + */ +Pathcomp_t *path_get(register const char *name) +{ + register Shell_t *shp = &sh; + register Pathcomp_t *pp=0; + if(*name && strchr(name,'/')) + return(0); + if(!sh_isstate(SH_DEFPATH)) + { + if(!shp->pathlist) + path_init(shp); + pp = (Pathcomp_t*)shp->pathlist; + } + if(!pp && (!(PATHNOD)->nvalue.cp) || sh_isstate(SH_DEFPATH)) + { + if(!(pp=(Pathcomp_t*)shp->defpathlist)) + pp = defpath_init(shp); + } + return(pp); +} + +/* + * open file corresponding to name using path give by <pp> + */ +static int path_opentype(const char *name, register Pathcomp_t *pp, int fun) +{ + register int fd= -1; + struct stat statb; + Pathcomp_t *oldpp; + Shell_t *shp; + if(pp) + shp = pp->shp; + else + { + shp = sh_getinterp(); + if(!shp->pathlist) + path_init(shp); + } + if(!fun && strchr(name,'/')) + { + if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,name); + } + do + { + pp = path_nextcomp(oldpp=pp,name,0); + while(oldpp && (oldpp->flags&PATH_SKIP)) + oldpp = oldpp->next; + if(fun && (!oldpp || !(oldpp->flags&PATH_FPATH))) + continue; + if((fd = sh_open(path_relative(stakptr(PATH_OFFSET)),O_RDONLY,0)) >= 0) + { + if(fstat(fd,&statb)<0 || S_ISDIR(statb.st_mode)) + { + errno = EISDIR; + sh_close(fd); + fd = -1; + } + } + } + while( fd<0 && pp); + if(fd>=0 && (fd = sh_iomovefd(fd)) > 0) + { + fcntl(fd,F_SETFD,FD_CLOEXEC); + shp->fdstatus[fd] |= IOCLEX; + } + return(fd); +} + +/* + * open file corresponding to name using path give by <pp> + */ +int path_open(const char *name, register Pathcomp_t *pp) +{ + return(path_opentype(name,pp,0)); +} + +/* + * given a pathname return the base name + */ + +char *path_basename(register const char *name) +{ + register const char *start = name; + while (*name) + if ((*name++ == '/') && *name) /* don't trim trailing / */ + start = name; + return ((char*)start); +} + +char *path_fullname(const char *name) +{ + int len=strlen(name)+1,dirlen=0; + char *path,*pwd; + if(*name!='/') + { + pwd = path_pwd(1); + dirlen = strlen(pwd)+1; + } + path = (char*)malloc(len+dirlen); + if(dirlen) + { + memcpy((void*)path,(void*)pwd,dirlen); + path[dirlen-1] = '/'; + } + memcpy((void*)&path[dirlen],(void*)name,len); + pathcanon(path,0); + return(path); +} + +/* + * load functions from file <fno> + */ +static void funload(Shell_t *shp,int fno, const char *name) +{ + char *oldname=shp->st.filename, buff[IOBSIZE+1]; + int savestates = sh_getstate(); + sh_onstate(SH_NOLOG); + sh_onstate(SH_NOALIAS); + shp->readscript = (char*)name; + shp->st.filename = path_fullname(stakptr(PATH_OFFSET)); + error_info.line = 0; + sh_eval(sfnew(NIL(Sfio_t*),buff,IOBSIZE,fno,SF_READ),0); + shp->readscript = 0; + free((void*)shp->st.filename); + shp->st.filename = oldname; + sh_setstate(savestates); +} + +/* + * do a path search and track alias if requested + * if flag is 0, or if name not found, then try autoloading function + * if flag==2, returns 1 if name found on FPATH + * returns 1, if function was autoloaded. + * If endpath!=NULL, Path search ends when path matches endpath. + */ + +int path_search(register const char *name,Pathcomp_t *endpath, int flag) +{ + register Namval_t *np; + register int fno; + Pathcomp_t *pp=0; + Shell_t *shp = &sh; + if(name && strchr(name,'/')) + { + stakseek(PATH_OFFSET); + stakputs(name); + if(canexecute(stakptr(PATH_OFFSET),0)<0) + { + *stakptr(PATH_OFFSET) = 0; + return(0); + } + if(*name=='/') + return(1); + stakseek(PATH_OFFSET); + stakputs(path_pwd(1)); + stakputc('/'); + stakputs(name); + stakputc(0); + return(0); + } + if(sh_isstate(SH_DEFPATH)) + { + if(!shp->defpathlist) + defpath_init(shp); + } + else if(!shp->pathlist) + path_init(shp); + if(flag) + { + if(!(pp=path_absolute(name,endpath)) && endpath) + pp = path_absolute(name,NIL(Pathcomp_t*)); + if(!pp && (np=nv_search(name,sh.fun_tree,HASH_NOSCOPE))&&np->nvalue.ip) + return(1); + if(!pp) + *stakptr(PATH_OFFSET) = 0; + } + if(flag==0 || !pp || (pp->flags&PATH_FPATH)) + { + if(!pp) + pp=sh_isstate(SH_DEFPATH)?shp->defpathlist:shp->pathlist; + if(pp && strmatch(name,e_alphanum) && (fno=path_opentype(name,pp,1))>=0) + { + if(flag==2) + { + sh_close(fno); + return(1); + } + funload(shp,fno,name); + return(1); + } + *stakptr(PATH_OFFSET) = 0; + return(0); + } + else if(pp && !sh_isstate(SH_DEFPATH) && *name!='/') + { + if(np=nv_search(name,shp->track_tree,NV_ADD)) + path_alias(np,pp); + } + return(0); +} + + +/* + * do a path search and find the full pathname of file name + * end search of path matches endpath without checking execute permission + */ + +Pathcomp_t *path_absolute(register const char *name, Pathcomp_t *endpath) +{ + register int f,isfun; + int noexec=0; + Pathcomp_t *pp,*oldpp; + Shell_t *shp = &sh; + Namval_t *np; + shp->path_err = ENOENT; + if(!(pp=path_get(""))) + return(0); + shp->path_err = 0; + while(1) + { + sh_sigcheck(); + isfun = (pp->flags&PATH_FPATH); + if(oldpp=pp) + pp = path_nextcomp(pp,name,0); + if(endpath) + return(endpath); + if(!isfun && !sh_isoption(SH_RESTRICTED)) + { + if(nv_search(stakptr(PATH_OFFSET),sh.bltin_tree,0)) + return(oldpp); + if(oldpp->blib) + { + typedef int (*Fptr_t)(int, char*[], void*); + Fptr_t addr; + int n = staktell(); + char *cp; + stakputs("b_"); + stakputs(name); + stakputc(0); + if(!oldpp->bltin_lib) + { + if(cp = strrchr(oldpp->blib,'/')) + cp++; + else + cp = oldpp->blib; + if(strcmp(cp,LIBCMD)==0 && (addr=(Fptr_t)dlllook((void*)0,stakptr(n)))) + { + np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL); + np->nvfun = (Namfun_t*)np->nvname; + return(oldpp); + } +#if (_AST_VERSION>=20040404) + if (oldpp->bltin_lib = dllplug(SH_ID, oldpp->blib, NiL, RTLD_LAZY, NiL, 0)) +#else + if (oldpp->bltin_lib = dllfind(oldpp->blib, NiL, RTLD_LAZY, NiL, 0)) +#endif + sh_addlib(oldpp->bltin_lib); + } + if((addr=(Fptr_t)dlllook(oldpp->bltin_lib,stakptr(n))) && + (!(np = sh_addbuiltin(stakptr(PATH_OFFSET),NiL,NiL)) || np->nvalue.bfp!=addr) && + (np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL))) + { + np->nvenv = oldpp->bltin_lib; + return(oldpp); + } + } + } + f = canexecute(stakptr(PATH_OFFSET),isfun); + if(isfun && f>=0) + { + nv_onattr(nv_open(name,shp->fun_tree,NV_NOARRAY|NV_IDENT|NV_NOSCOPE),NV_LTOU|NV_FUNCTION); + close(f); + f = -1; + return(0); + } + else if(f>=0 && (oldpp->flags & PATH_STD_DIR)) + { + int offset = staktell(); + stakputs("/bin/"); + stakputs(name); + stakputc(0); + np = nv_search(stakptr(offset),sh.bltin_tree,0); + stakseek(offset); + if(np) + { + np = sh_addbuiltin(stakptr(PATH_OFFSET),np->nvalue.bfp,NiL); + np->nvfun = (Namfun_t*)np->nvname; + } + } + if(!pp || f>=0) + break; + if(errno!=ENOENT) + noexec = errno; + } + if(f<0) + { + if(!endpath) + shp->path_err = (noexec?noexec:ENOENT); + return(0); + } + stakputc(0); + return(oldpp); +} + +/* + * returns 0 if path can execute + * sets exec_err if file is found but can't be executable + */ +#undef S_IXALL +#ifdef S_IXUSR +# define S_IXALL (S_IXUSR|S_IXGRP|S_IXOTH) +#else +# ifdef S_IEXEC +# define S_IXALL (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) +# else +# define S_IXALL 0111 +# endif /*S_EXEC */ +#endif /* S_IXUSR */ + +static int canexecute(register char *path, int isfun) +{ + struct stat statb; + register int fd=0; + path = path_relative(path); + if(isfun) + { + if((fd=open(path,O_RDONLY,0))<0 || fstat(fd,&statb)<0) + goto err; + } + else if(stat(path,&statb) < 0) + { +#if _WINIX + /* check for .exe or .bat suffix */ + char *cp; + if(errno==ENOENT && (!(cp=strrchr(path,'.')) || strlen(cp)>4 || strchr(cp,'/'))) + { + int offset = staktell()-1; + stakseek(offset); + stakputs(".bat"); + path = stakptr(PATH_OFFSET); + if(stat(path,&statb) < 0) + { + if(errno!=ENOENT) + goto err; + memcpy(stakptr(offset),".sh",4); + if(stat(path,&statb) < 0) + goto err; + } + } + else +#endif /* _WINIX */ + goto err; + } + errno = EPERM; + if(S_ISDIR(statb.st_mode)) + errno = EISDIR; + else if((statb.st_mode&S_IXALL)==S_IXALL || sh_access(path,X_OK)>=0) + return(fd); + if(isfun && fd>=0) + sh_close(fd); +err: + return(-1); +} + +/* + * Return path relative to present working directory + */ + +char *path_relative(register const char* file) +{ + register const char *pwd; + register const char *fp = file; + /* can't relpath when sh.pwd not set */ + if(!(pwd=sh.pwd)) + return((char*)fp); + while(*pwd==*fp) + { + if(*pwd++==0) + return((char*)e_dot); + fp++; + } + if(*pwd==0 && *fp == '/') + { + while(*++fp=='/'); + if(*fp) + return((char*)fp); + return((char*)e_dot); + } + return((char*)file); +} + +void path_exec(register const char *arg0,register char *argv[],struct argnod *local) +{ + char **envp; + const char *opath; + Pathcomp_t *libpath, *pp=0; + Shell_t *shp = &sh; + int slash=0; + nv_setlist(local,NV_EXPORT|NV_IDENT|NV_ASSIGN); + envp = sh_envgen(); + if(strchr(arg0,'/')) + { + slash=1; + /* name containing / not allowed for restricted shell */ + if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,arg0); + } + else + pp=path_get(arg0); + shp->path_err= ENOENT; + sfsync(NIL(Sfio_t*)); + timerdel(NIL(void*)); + /* find first path that has a library component */ + if(pp || slash) do + { + sh_sigcheck(); + if(libpath=pp) + { + pp = path_nextcomp(pp,arg0,0); + opath = stakfreeze(1)+PATH_OFFSET; + } + else + opath = arg0; + path_spawn(opath,argv,envp,libpath,0); + while(pp && (pp->flags&PATH_FPATH)) + pp = path_nextcomp(pp,arg0,0); + } + while(pp); + /* force an exit */ + ((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT; + if((errno=shp->path_err)==ENOENT) + errormsg(SH_DICT,ERROR_exit(ERROR_NOENT),e_found,arg0); + else + errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,arg0); +} + +pid_t path_spawn(const char *opath,register char **argv, char **envp, Pathcomp_t *libpath, int spawn) +{ + Shell_t *shp = sh_getinterp(); + register char *path; + char **xp=0, *xval, *libenv = (libpath?libpath->lib:0); + Namval_t* np; + char *s, *v; + int r, n; + pid_t pid= -1; + /* leave room for inserting _= pathname in environment */ + envp--; +#if _lib_readlink + /* save original pathname */ + stakseek(PATH_OFFSET); + stakputs(opath); + opath = stakfreeze(1)+PATH_OFFSET; + np=nv_search(argv[0],shp->track_tree,0); + while(libpath && !libpath->lib) + libpath=libpath->next; + if(libpath && (!np || nv_size(np)>0)) + { + /* check for symlink and use symlink name */ + char buff[PATH_MAX+1]; + char save[PATH_MAX+1]; + stakseek(PATH_OFFSET); + stakputs(opath); + path = stakptr(PATH_OFFSET); + while((n=readlink(path,buff,PATH_MAX))>0) + { + buff[n] = 0; + n = PATH_OFFSET; + r = 0; + if((v=strrchr(path,'/')) && *buff!='/') + { + if(buff[0]=='.' && buff[1]=='.' && (r = strlen(path) + 1) <= PATH_MAX) + memcpy(save, path, r); + else + r = 0; + n += (v+1-path); + } + stakseek(n); + stakputs(buff); + stakputc(0); + path = stakptr(PATH_OFFSET); + if(v && buff[0]=='.' && buff[1]=='.') + { + pathcanon(path, 0); + if(r && access(path,X_OK)) + { + memcpy(path, save, r); + break; + } + } + if(libenv = path_lib(libpath,path)) + break; + } + stakseek(0); + } +#endif + if(libenv && (v = strchr(libenv,'='))) + { + n = v - libenv; + *v = 0; + np = nv_open(libenv,shp->var_tree,0); + *v = '='; + s = nv_getval(np); + stakputs(libenv); + if(s) + { + stakputc(':'); + stakputs(s); + } + v = stakfreeze(1); + r = 1; + xp = envp + 2; + while (s = *xp++) + { + if (strneq(s, v, n) && s[n] == '=') + { + xval = *--xp; + *xp = v; + r = 0; + break; + } + } + if (r) + { + *envp-- = v; + xp = 0; + } + } + if(!opath) + opath = stakptr(PATH_OFFSET); + envp[0] = (char*)opath-PATH_OFFSET; + envp[0][0] = '_'; + envp[0][1] = '='; + sfsync(sfstderr); + sh_sigcheck(); + path = path_relative(opath); +#ifdef SHELLMAGIC + if(*path!='/' && path!=opath) + { + /* + * The following code because execv(foo,) and execv(./foo,) + * may not yield the same results + */ + char *sp = (char*)malloc(strlen(path)+3); + sp[0] = '.'; + sp[1] = '/'; + strcpy(sp+2,path); + path = sp; + } +#endif /* SHELLMAGIC */ + if(sh_isoption(SH_RESTRICTED)) + { + int fd; + if((fd = sh_open(opath,O_RDONLY,0)) >= 0) + { + char buff[PATH_MAX]; + n = read(fd,buff,sizeof(buff)); + close(fd); + if(n>2 && buff[0]=='#' && buff[1]=='!') + { + for(s=buff; n>0 && *s!='\n'; n--,s++) + { + if(*s=='/') + errormsg(SH_DICT,ERROR_exit(1),e_restricted,opath); + } + } + } + } + if(spawn && !sh_isoption(SH_PFSH)) + pid = _spawnveg(opath, &argv[0],envp, spawn>>1); + else + path_pfexecve(opath, &argv[0] ,envp); + if(xp) + *xp = xval; +#ifdef SHELLMAGIC + if(*path=='.' && path!=opath) + { + free(path); + path = path_relative(opath); + } +#endif /* SHELLMAGIC */ + if(pid>0) + return(pid); +retry: + switch(sh.path_err = errno) + { +#ifdef apollo + /* + * On apollo's execve will fail with eacces when + * file has execute but not read permissions. So, + * for now we will pretend that EACCES and ENOEXEC + * mean the same thing. + */ + case EACCES: +#endif /* apollo */ + case ENOEXEC: +#if SHOPT_SUID_EXEC + case EPERM: + /* some systems return EPERM if setuid bit is on */ +#endif + errno = ENOEXEC; + if(spawn) + { +#ifdef _lib_fork + if(sh.subshell) + return(-1); + do + { + if((pid=fork())>0) + return(pid); + } + while(_sh_fork(pid,0,(int*)0) < 0); +#else + return(-1); +#endif + } + exscript(shp,path,argv,envp); +#ifndef apollo + case EACCES: + { + struct stat statb; + if(stat(path,&statb)>=0) + { + if(S_ISDIR(statb.st_mode)) + errno = EISDIR; +#ifdef S_ISSOCK + if(S_ISSOCK(statb.st_mode)) + exscript(shp,path,argv,envp); +#endif + } + } + /* FALL THROUGH */ +#endif /* !apollo */ +#ifdef ENAMETOOLONG + case ENAMETOOLONG: +#endif /* ENAMETOOLONG */ +#if !SHOPT_SUID_EXEC + case EPERM: +#endif + shp->path_err = errno; + return(-1); + case ENOTDIR: + case ENOENT: + case EINTR: +#ifdef EMLINK + case EMLINK: +#endif /* EMLINK */ + return(-1); + case E2BIG: + if(sh.xargmin) + { + pid = path_xargs(opath, &argv[0] ,envp,spawn); + if(pid<0) + goto retry; + return(pid); + } + default: + errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path); + } + return 0; +} + +/* + * File is executable but not machine code. + * Assume file is a Shell script and execute it. + */ + +static void exscript(Shell_t *shp,register char *path,register char *argv[],char **envp) +{ + register Sfio_t *sp; + path = path_relative(path); + shp->comdiv=0; + shp->bckpid = 0; + shp->st.ioset=0; + /* clean up any cooperating processes */ + if(shp->cpipe[0]>0) + sh_pclose(shp->cpipe); + if(shp->cpid && shp->outpipe) + sh_close(*shp->outpipe); + shp->cpid = 0; + if(sp=fcfile()) + while(sfstack(sp,SF_POPSTACK)); + job_clear(); + if(shp->infd>0 && (shp->fdstatus[shp->infd]&IOCLEX)) + sh_close(shp->infd); + sh_setstate(sh_state(SH_FORKED)); + sfsync(sfstderr); +#if SHOPT_SUID_EXEC && !SHOPT_PFSH + /* check if file cannot open for read or script is setuid/setgid */ + { + static char name[] = "/tmp/euidXXXXXXXXXX"; + register int n; + register uid_t euserid; + char *savet=0; + struct stat statb; + if((n=sh_open(path,O_RDONLY,0)) >= 0) + { + /* move <n> if n=0,1,2 */ + n = sh_iomovefd(n); + if(fstat(n,&statb)>=0 && !(statb.st_mode&(S_ISUID|S_ISGID))) + goto openok; + sh_close(n); + } + if((euserid=geteuid()) != shp->userid) + { + strncpy(name+9,fmtbase((long)getpid(),10,0),sizeof(name)-10); + /* create a suid open file with owner equal effective uid */ + if((n=open(name,O_CREAT|O_TRUNC|O_WRONLY,S_ISUID|S_IXUSR)) < 0) + goto fail; + unlink(name); + /* make sure that file has right owner */ + if(fstat(n,&statb)<0 || statb.st_uid != euserid) + goto fail; + if(n!=10) + { + sh_close(10); + fcntl(n, F_DUPFD, 10); + sh_close(n); + n=10; + } + } + savet = *--argv; + *argv = path; + path_pfexecve(e_suidexec,argv,envp); + fail: + /* + * The following code is just for compatibility + */ + if((n=open(path,O_RDONLY,0)) < 0) + errormsg(SH_DICT,ERROR_system(1),e_open,path); + if(savet) + *argv++ = savet; + openok: + shp->infd = n; + } +#else + shp->infd = sh_chkopen(path); +#endif + shp->infd = sh_iomovefd(shp->infd); +#if SHOPT_ACCT + sh_accbegin(path) ; /* reset accounting */ +#endif /* SHOPT_ACCT */ + shp->arglist = sh_argcreate(argv); + shp->lastarg = strdup(path); + /* save name of calling command */ + shp->readscript = error_info.id; + /* close history file if name has changed */ + if(shp->hist_ptr && (path=nv_getval(HISTFILE)) && strcmp(path,shp->hist_ptr->histname)) + { + hist_close(shp->hist_ptr); + (HISTCUR)->nvalue.lp = 0; + } + sh_offstate(SH_FORKED); + siglongjmp(*shp->jmplist,SH_JMPSCRIPT); +} + +#if SHOPT_ACCT +# include <sys/acct.h> +# include "FEATURE/time" + + static struct acct sabuf; + static struct tms buffer; + static clock_t before; + static char *SHACCT; /* set to value of SHACCT environment variable */ + static shaccton; /* non-zero causes accounting record to be written */ + static int compress(time_t); + /* + * initialize accounting, i.e., see if SHACCT variable set + */ + void sh_accinit(void) + { + SHACCT = getenv("SHACCT"); + } + /* + * suspend accounting unitl turned on by sh_accbegin() + */ + void sh_accsusp(void) + { + shaccton=0; +#ifdef AEXPAND + sabuf.ac_flag |= AEXPND; +#endif /* AEXPAND */ + } + + /* + * begin an accounting record by recording start time + */ + void sh_accbegin(const char *cmdname) + { + if(SHACCT) + { + sabuf.ac_btime = time(NIL(time_t *)); + before = times(&buffer); + sabuf.ac_uid = getuid(); + sabuf.ac_gid = getgid(); + strncpy(sabuf.ac_comm, (char*)path_basename(cmdname), + sizeof(sabuf.ac_comm)); + shaccton = 1; + } + } + /* + * terminate an accounting record and append to accounting file + */ + void sh_accend(void) + { + int fd; + clock_t after; + + if(shaccton) + { + after = times(&buffer); + sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime); + sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime); + sabuf.ac_etime = compress( (time_t)(after-before)); + fd = open( SHACCT , O_WRONLY | O_APPEND | O_CREAT,RW_ALL); + write(fd, (const char*)&sabuf, sizeof( sabuf )); + close( fd); + } + } + + /* + * Produce a pseudo-floating point representation + * with 3 bits base-8 exponent, 13 bits fraction. + */ + static int compress(register time_t t) + { + register int exp = 0, rund = 0; + + while (t >= 8192) + { + exp++; + rund = t&04; + t >>= 3; + } + if (rund) + { + t++; + if (t >= 8192) + { + t >>= 3; + exp++; + } + } + return((exp<<13) + t); + } +#endif /* SHOPT_ACCT */ + + + +/* + * add a pathcomponent to the path search list and eliminate duplicates + * and non-existing absolute paths. + */ +static Pathcomp_t *path_addcomp(Pathcomp_t *first, Pathcomp_t *old,const char *name, int flag) +{ + register Pathcomp_t *pp, *oldpp; + struct stat statb; + int len, offset=staktell(); + if(!(flag&PATH_BFPATH)) + { + register const char *cp = name; + while(*cp && *cp!=':') + stakputc(*cp++); + len = staktell()-offset; + stakputc(0); + stakseek(offset); + name = (const char*)stakptr(offset); + } + else + len = strlen(name); + for(pp=first; pp; pp=pp->next) + { + if(memcmp(name,pp->name,len)==0 && (pp->name[len]==':' || pp->name[len]==0)) + { + pp->flags |= flag; + return(first); + } + } + if(old && (old=path_dirfind(old,name,0))) + { + statb.st_ino = old->ino; + statb.st_dev = old->dev; + if(old->ino==0 && old->dev==0) + flag |= PATH_SKIP; + } + else if(stat(name,&statb)<0 || !S_ISDIR(statb.st_mode)) + { + if(*name=='/') + { + if(strcmp(name,SH_CMDLIB_DIR)) + return(first); + statb.st_dev = 1; + } + else + { + flag |= PATH_SKIP; + statb.st_dev = 0; + } + statb.st_ino = 0; + } + if(*name=='/' && onstdpath(name)) + flag |= PATH_STD_DIR; + for(pp=first, oldpp=0; pp; oldpp=pp, pp=pp->next) + { + if(pp->ino==statb.st_ino && pp->dev==statb.st_dev) + { + /* if both absolute paths, eliminate second */ + pp->flags |= flag; + if(*name=='/' && *pp->name=='/') + return(first); + /* keep the path but mark it as skip */ + flag |= PATH_SKIP; + } + } + pp = newof((Pathcomp_t*)0,Pathcomp_t,1,len+1); + pp->refcount = 1; + memcpy((char*)(pp+1),name,len+1); + pp->name = (char*)(pp+1); + pp->len = len; + pp->dev = statb.st_dev; + pp->ino = statb.st_ino; + if(oldpp) + oldpp->next = pp; + else + first = pp; + pp->flags = flag; + if(pp->ino==0 && pp->dev==1) + { + pp->flags |= PATH_BUILTIN_LIB; + pp->blib = malloc(4); + strcpy(pp->blib,LIBCMD); + return(first); + } + if((flag&(PATH_PATH|PATH_SKIP))==PATH_PATH) + path_chkpaths(first,old,pp,offset); + return(first); +} + +/* + * This function checks for the .paths file in directory in <pp> + * it assumes that the directory is on the stack at <offset> + */ +static int path_chkpaths(Pathcomp_t *first, Pathcomp_t* old,Pathcomp_t *pp, int offset) +{ + struct stat statb; + int k,m,n,fd; + char *sp,*cp,*ep; + stakseek(offset+pp->len); + if(pp->len==1 && *stakptr(offset)=='/') + stakseek(offset); + stakputs("/.paths"); + if((fd=open(stakptr(offset),O_RDONLY))>=0) + { + fstat(fd,&statb); + n = statb.st_size; + stakseek(offset+pp->len+n+2); + sp = stakptr(offset+pp->len); + *sp++ = '/'; + n=read(fd,cp=sp,n); + sp[n] = 0; + close(fd); + for(ep=0; n--; cp++) + { + if(*cp=='=') + { + ep = cp+1; + continue; + } + else if(*cp!='\r' && *cp!='\n') + continue; + if(*sp=='#' || sp==cp) + { + sp = cp+1; + continue; + } + *cp = 0; + m = ep ? (ep-sp) : 0; + if(!m || m==6 && memcmp((void*)sp,(void*)"FPATH=",6)==0) + { + if(first) + { + char *ptr = stakptr(offset+pp->len+1); + if(ep) + strcpy(ptr,ep); + path_addcomp(first,old,stakptr(offset),PATH_FPATH|PATH_BFPATH); + } + } + else if(m==12 && memcmp((void*)sp,(void*)"BUILTIN_LIB=",12)==0) + { + if(!(pp->flags & PATH_BUILTIN_LIB)) + { + pp->flags |= PATH_BUILTIN_LIB; + if (*ep == '.' && !*(ep + 1)) + pp->flags |= PATH_STD_DIR; + else + { + k = strlen(ep)+1; + if (*ep != '/') + k += pp->len+1; + pp->blib = sp = malloc(k); + if (*ep != '/') + { + strcpy(pp->blib,pp->name); + sp += pp->len; + *sp++ = '/'; + } + strcpy(sp,ep); + } + } + } + else if(m) + { + pp->lib = (char*)malloc(cp-sp+pp->len+2); + memcpy((void*)pp->lib,(void*)sp,m); + memcpy((void*)&pp->lib[m],stakptr(offset),pp->len); + pp->lib[k=m+pp->len] = '/'; + strcpy((void*)&pp->lib[k+1],ep); + pathcanon(&pp->lib[m],0); + if(!first) + { + stakseek(0); + stakputs(pp->lib); + free((void*)pp->lib); + return(1); + } + } + sp = cp+1; + ep = 0; + } + } + return(0); +} + + +Pathcomp_t *path_addpath(Pathcomp_t *first, register const char *path,int type) +{ + register const char *cp; + Pathcomp_t *old=0; + int offset = staktell(); + char *savptr; + + if(!path && type!=PATH_PATH) + return(first); + if(type!=PATH_FPATH) + { + old = first; + first = 0; + } + if(offset) + savptr = stakfreeze(0); + if(path) while(*(cp=path)) + { + if(*cp==':') + { + if(type!=PATH_FPATH) + first = path_addcomp(first,old,".",type); + while(*++path == ':'); + } + else + { + int c; + while(*path && *path!=':') + path++; + c = *path++; + first = path_addcomp(first,old,cp,type); + if(c==0) + break; + if(*path==0) + path--; + } + } + if(old) + { + if(!first && !path) + { + Pathcomp_t *pp = (Pathcomp_t*)old->shp->defpathlist; + if(!pp) + pp = defpath_init(old->shp); + first = path_dup(pp); + } + if(cp=(FPATHNOD)->nvalue.cp) + first = (void*)path_addpath((Pathcomp_t*)first,cp,PATH_FPATH); + path_delete(old); + } + if(offset) + stakset(savptr,offset); + else + stakseek(0); + return(first); +} + +/* + * duplicate the path give by <first> by incremented reference counts + */ +Pathcomp_t *path_dup(Pathcomp_t *first) +{ + register Pathcomp_t *pp=first; + while(pp) + { + pp->refcount++; + pp = pp->next; + } + return(first); +} + +/* + * called whenever the directory is changed + */ +void path_newdir(Pathcomp_t *first) +{ + register Pathcomp_t *pp=first, *next, *pq; + struct stat statb; + for(pp=first; pp; pp=pp->next) + { + pp->flags &= ~PATH_SKIP; + if(*pp->name=='/') + continue; + /* delete .paths component */ + if((next=pp->next) && (next->flags&PATH_BFPATH)) + { + pp->next = next->next; + if(--next->refcount<=0) + free((void*)next); + } + if(stat(pp->name,&statb)<0 || !S_ISDIR(statb.st_mode)) + { + pp->dev = 0; + pp->ino = 0; + continue; + } + pp->dev = statb.st_dev; + pp->ino = statb.st_ino; + for(pq=first;pq!=pp;pq=pq->next) + { + if(pp->ino==pq->ino && pp->dev==pq->dev) + pp->flags |= PATH_SKIP; + } + for(pq=pp;pq=pq->next;) + { + if(pp->ino==pq->ino && pp->dev==pq->dev) + pq->flags |= PATH_SKIP; + } + if((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH) + { + /* try to insert .paths component */ + int offset = staktell(); + stakputs(pp->name); + stakseek(offset); + next = pp->next; + pp->next = 0; + path_chkpaths(first,(Pathcomp_t*)0,pp,offset); + if(pp->next) + pp = pp->next; + pp->next = next; + } + } +#if 0 + path_dump(first); +#endif +} + +Pathcomp_t *path_unsetfpath(Pathcomp_t *first) +{ + register Pathcomp_t *pp=first, *old=0; + while(pp) + { + if((pp->flags&PATH_FPATH) && !(pp->flags&PATH_BFPATH)) + { + if(pp->flags&PATH_PATH) + pp->flags &= ~PATH_FPATH; + else + { + Pathcomp_t *ppsave=pp; + if(old) + old->next = pp->next; + else + first = pp->next; + pp = pp->next; + if(--ppsave->refcount<=0) + { + if(ppsave->lib) + free((void*)ppsave->lib); + free((void*)ppsave); + } + continue; + } + + } + old = pp; + pp = pp->next; + } + return(first); +} + +Pathcomp_t *path_dirfind(Pathcomp_t *first,const char *name,int c) +{ + register Pathcomp_t *pp=first; + while(pp) + { + if(memcmp(name,pp->name,pp->len)==0 && name[pp->len]==c) + return(pp); + pp = pp->next; + } + return(0); +} + +/* + * get discipline for tracked alias + */ +static char *talias_get(Namval_t *np, Namfun_t *nvp) +{ + Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp; + char *ptr; + if(!pp) + return(NULL); + path_nextcomp(pp,nv_name(np),pp); + ptr = stakfreeze(0); + return(ptr+PATH_OFFSET); +} + +static void talias_put(register Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + if(!val && np->nvalue.cp) + { + Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp; + if(--pp->refcount<=0) + free((void*)pp); + } + nv_putv(np,val,flags,fp); +} + +static const Namdisc_t talias_disc = { 0, talias_put, talias_get }; +static Namfun_t talias_init = { &talias_disc, 1 }; + +/* + * set tracked alias node <np> to value <pp> + */ +void path_alias(register Namval_t *np,register Pathcomp_t *pp) +{ + if(pp) + { + struct stat statb; + char *sp; + nv_offattr(np,NV_NOPRINT); + nv_stack(np,&talias_init); + np->nvalue.cp = (char*)pp; + pp->refcount++; + nv_setattr(np,NV_TAGGED|NV_NOFREE); + path_nextcomp(pp,nv_name(np),pp); + sp = stakptr(PATH_OFFSET); + if(sp && lstat(sp,&statb)>=0 && S_ISLNK(statb.st_mode)) + nv_setsize(np,statb.st_size+1); + else + nv_setsize(np,0); + } + else + nv_unset(np); +} + |