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