mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
This commit gets rid of dead weight related to an obscure early 1990s Bell Labs versioning file system research project called 3DFS, which has not existed for decades and for which I have not managed to find any evidence that it was ever used outside the lab. This removes: - the SHOPT_FS_3D compile option (which was forced on even when 0) - the obnoxious default alias 2d='set -f;_2d' that turned off your globbing and then tried to run a nonexistent _2d command - undocumented builtins 'vmap' and 'vpath' that only errored out - a non-functional -V unary operator for the test and [[ commands - some specific code for Apollo workstations (last made in 1997), which was inseparably intertwined with the 3DFS code (cherry picked from commit 20cdf3709f4fb4e468057b534dcee819b1961fb6)
1811 lines
39 KiB
C
1811 lines
39 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1982-2012 AT&T Intellectual Property *
|
|
* and is licensed under the *
|
|
* Eclipse Public License, Version 1.0 *
|
|
* by AT&T Intellectual Property *
|
|
* *
|
|
* A copy of the License is available at *
|
|
* http://www.eclipse.org/org/documents/epl-v10.html *
|
|
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
|
|
* *
|
|
* 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 "variables.h"
|
|
#include "path.h"
|
|
#include "io.h"
|
|
#include "jobs.h"
|
|
#include "history.h"
|
|
#include "test.h"
|
|
#include "FEATURE/dynamic"
|
|
#include "FEATURE/externs"
|
|
#if SHOPT_PFSH
|
|
# ifdef _hdr_exec_attr
|
|
# include <exec_attr.h>
|
|
# endif
|
|
# if _lib_vfork
|
|
# include <ast_vfork.h>
|
|
# else
|
|
# define vfork() fork()
|
|
# endif
|
|
#endif
|
|
|
|
#define RW_ALL (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)
|
|
#define LIBCMD "cmd"
|
|
|
|
|
|
static int canexecute(Shell_t*,char*,int);
|
|
static void funload(Shell_t*,int,const char*);
|
|
static void exscript(Shell_t*,char*, char*[], char**);
|
|
static int path_chkpaths(Shell_t*,Pathcomp_t*,Pathcomp_t*,Pathcomp_t*,int);
|
|
static void path_checkdup(Shell_t *shp,register Pathcomp_t*);
|
|
static Pathcomp_t *defpath_init(Shell_t *shp);
|
|
|
|
static const char *std_path = NULL;
|
|
|
|
static int onstdpath(Shell_t *shp, const char *name)
|
|
{
|
|
if(!std_path)
|
|
defpath_init(shp);
|
|
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);
|
|
}
|
|
|
|
#if SHOPT_PFSH
|
|
int path_xattr(Shell_t *shp, const char *path, char *rpath)
|
|
{
|
|
char resolvedpath[PATH_MAX + 1];
|
|
if (shp->gd->user && *shp->gd->user)
|
|
{
|
|
execattr_t *pf;
|
|
if(!rpath)
|
|
rpath = resolvedpath;
|
|
if (!realpath(path, resolvedpath))
|
|
return -1;
|
|
if(pf=getexecuser(shp->gd->user, KV_COMMAND, resolvedpath, GET_ONE))
|
|
{
|
|
if (!pf->attr || pf->attr->length == 0)
|
|
{
|
|
free_execattr(pf);
|
|
return(0);
|
|
}
|
|
free_execattr(pf);
|
|
return(1);
|
|
}
|
|
}
|
|
errno = ENOENT;
|
|
return(-1);
|
|
}
|
|
#endif /* SHOPT_PFSH */
|
|
|
|
static pid_t path_pfexecve(Shell_t *shp,const char *path, char *argv[],char *const envp[],int spawn)
|
|
{
|
|
#if SHOPT_PFSH
|
|
char resolvedpath[PATH_MAX + 1];
|
|
pid_t pid;
|
|
if(spawn)
|
|
{
|
|
while((pid = vfork()) < 0)
|
|
_sh_fork(shp,pid, 0, (int*)0);
|
|
if(pid)
|
|
return(pid);
|
|
}
|
|
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 */
|
|
|
|
/* we can exec the command directly instead of via pfexec(1) if */
|
|
/* there is a matching entry without attributes in exec_attr(4) */
|
|
if(!path_xattr(shp,path,resolvedpath))
|
|
return(execve(path, argv, envp));
|
|
--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(Shell_t *shp,const char *path, char* const argv[], char* const envp[], pid_t pgid)
|
|
{
|
|
pid_t pid;
|
|
while(1)
|
|
{
|
|
sh_stats(STAT_SPAWN);
|
|
pid = spawnveg(path,argv,envp,pgid);
|
|
if(pid>=0 || errno!=EAGAIN)
|
|
break;
|
|
}
|
|
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(Shell_t *shp,const char *path, char *argv[],char *const envp[], int spawn)
|
|
{
|
|
register char *cp, **av, **xv;
|
|
char **avlast= &argv[shp->xargmax], **saveargs=0;
|
|
char *const *ev;
|
|
long size, left;
|
|
int nlast=1,n,exitval=0;
|
|
pid_t pid;
|
|
if(shp->xargmin < 0)
|
|
return((pid_t)-1);
|
|
size = shp->gd->lim.arg_max-1024;
|
|
for(ev=envp; cp= *ev; ev++)
|
|
size -= strlen(cp)-1;
|
|
for(av=argv; (cp= *av) && av< &argv[shp->xargmin]; av++)
|
|
size -= strlen(cp)-1;
|
|
for(av=avlast; cp= *av; av++,nlast++)
|
|
size -= strlen(cp)-1;
|
|
av = &argv[shp->xargmin];
|
|
if(!spawn)
|
|
job_clear();
|
|
shp->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[shp->xargmin])
|
|
{
|
|
n = nlast*sizeof(char*);
|
|
saveargs = (char**)malloc(n);
|
|
memcpy((void*)saveargs, (void*)av, n);
|
|
memcpy((void*)av,(void*)avlast,n);
|
|
}
|
|
else
|
|
{
|
|
for(n=shp->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(shp,path,argv,envp,0)) < 0)
|
|
return(-1);
|
|
job_post(shp,pid,0);
|
|
job_wait(pid);
|
|
if(shp->exitval>exitval)
|
|
exitval = shp->exitval;
|
|
if(saveargs)
|
|
{
|
|
memcpy((void*)av,saveargs,n);
|
|
free((void*)saveargs);
|
|
saveargs = 0;
|
|
}
|
|
}
|
|
else if(spawn && !sh_isoption(SH_PFSH))
|
|
{
|
|
shp->xargexit = exitval;
|
|
if(saveargs)
|
|
free((void*)saveargs);
|
|
return(_spawnveg(shp,path,argv,envp,spawn>>1));
|
|
}
|
|
else
|
|
{
|
|
if(saveargs)
|
|
free((void*)saveargs);
|
|
return(path_pfexecve(shp,path,argv,envp,spawn));
|
|
}
|
|
}
|
|
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(Shell_t *shp,int flag)
|
|
{
|
|
register char *cp;
|
|
register char *dfault = (char*)e_dot;
|
|
register int count = 0;
|
|
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,0);
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* 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->bbuf)
|
|
free((void*)pp->bbuf);
|
|
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(Shell_t *shp,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)
|
|
{
|
|
path_checkdup(shp,pp);
|
|
if(pp->ino==statb.st_ino && pp->dev==statb.st_dev && pp->mtime==statb.st_mtime)
|
|
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(shp,(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
|
|
|
|
/*
|
|
* check for duplicate directories on PATH
|
|
*/
|
|
static void path_checkdup(Shell_t *shp,register Pathcomp_t *pp)
|
|
{
|
|
register char *name = pp->name;
|
|
register Pathcomp_t *oldpp,*first;
|
|
register int flag=0;
|
|
struct stat statb;
|
|
if(stat(name,&statb)<0 || !S_ISDIR(statb.st_mode))
|
|
{
|
|
pp->flags |= PATH_SKIP;
|
|
pp->dev = *name=='/';
|
|
return;
|
|
}
|
|
pp->mtime = statb.st_mtime;
|
|
pp->ino = statb.st_ino;
|
|
pp->dev = statb.st_dev;
|
|
if(*name=='/' && onstdpath(shp, name))
|
|
flag = PATH_STD_DIR;
|
|
first = (pp->flags&PATH_CDPATH)?(Pathcomp_t*)shp->cdpathlist:path_get(shp,"");
|
|
for(oldpp=first; oldpp && oldpp!=pp; oldpp=oldpp->next)
|
|
{
|
|
if(pp->ino==oldpp->ino && pp->dev==oldpp->dev && pp->mtime==oldpp->mtime)
|
|
{
|
|
flag |= PATH_SKIP;
|
|
break;
|
|
}
|
|
}
|
|
pp->flags |= flag;
|
|
if(((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH))
|
|
{
|
|
int offset = staktell();
|
|
stakputs(name);
|
|
path_chkpaths(shp,first,0,pp,offset);
|
|
stakseek(offset);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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(Shell_t *shp,register Pathcomp_t *pp, const char *name, Pathcomp_t *last)
|
|
{
|
|
Pathcomp_t *ppnext;
|
|
stakseek(PATH_OFFSET);
|
|
if(*name=='/')
|
|
pp = 0;
|
|
else
|
|
{
|
|
for(;pp && pp!=last;pp=ppnext)
|
|
{
|
|
ppnext = pp->next;
|
|
if(!pp->dev && !pp->ino)
|
|
path_checkdup(shp,pp);
|
|
if(pp->flags&PATH_SKIP)
|
|
return(ppnext);
|
|
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(shp,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)
|
|
{
|
|
if(!std_path && !(std_path=astconf("PATH",NIL(char*),NIL(char*))))
|
|
std_path = e_defpath;
|
|
Pathcomp_t *pp = (void*)path_addpath(shp,(Pathcomp_t*)0,(std_path),PATH_PATH);
|
|
return(pp);
|
|
}
|
|
|
|
static void path_init(Shell_t *shp)
|
|
{
|
|
const char *val;
|
|
Pathcomp_t *pp;
|
|
if(val=sh_scoped(shp,(PATHNOD))->nvalue.cp)
|
|
{
|
|
shp->pathlist = pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_PATH);
|
|
}
|
|
else
|
|
{
|
|
if(!(pp=(Pathcomp_t*)shp->defpathlist))
|
|
pp = defpath_init(shp);
|
|
shp->pathlist = (void*)path_dup(pp);
|
|
}
|
|
if(val=sh_scoped(shp,(FPATHNOD))->nvalue.cp)
|
|
{
|
|
pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_FPATH);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* returns that pathlist to search
|
|
*/
|
|
Pathcomp_t *path_get(register Shell_t *shp,register const char *name)
|
|
{
|
|
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 && (!(sh_scoped(shp,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(Shell_t *shp,const char *name, register Pathcomp_t *pp, int fun)
|
|
{
|
|
register int fd= -1;
|
|
struct stat statb;
|
|
Pathcomp_t *nextpp;
|
|
|
|
if(!pp && !shp->pathlist)
|
|
path_init(shp);
|
|
if(!fun && strchr(name,'/'))
|
|
{
|
|
if(sh_isoption(SH_RESTRICTED))
|
|
errormsg(SH_DICT,ERROR_exit(1),e_restricted,name);
|
|
}
|
|
|
|
nextpp = pp;
|
|
do
|
|
{
|
|
pp = nextpp;
|
|
nextpp = path_nextcomp(shp,pp,name,0);
|
|
if(pp && (pp->flags&PATH_SKIP))
|
|
continue;
|
|
if(fun && (!pp || !(pp->flags&PATH_FPATH)))
|
|
continue;
|
|
if((fd = sh_open(path_relative(shp,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 && nextpp);
|
|
|
|
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(Shell_t *shp,const char *name, register Pathcomp_t *pp)
|
|
{
|
|
return(path_opentype(shp,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(Shell_t *shp,const char *name)
|
|
{
|
|
int len=strlen(name)+1,dirlen=0;
|
|
char *path,*pwd;
|
|
if(*name!='/')
|
|
{
|
|
pwd = path_pwd(shp,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 *pname,*oldname=shp->st.filename, buff[IOBSIZE+1];
|
|
Namval_t *np;
|
|
struct Ufunction *rp,*rpfirst;
|
|
int savestates = sh_getstate(), oldload=shp->funload;
|
|
pname = path_fullname(shp,stakptr(PATH_OFFSET));
|
|
if(shp->fpathdict && (rp = dtmatch(shp->fpathdict,(void*)pname)))
|
|
{
|
|
Dt_t *funtree = sh_subfuntree(1);
|
|
while(1)
|
|
{
|
|
rpfirst = dtprev(shp->fpathdict,rp);
|
|
if(!rpfirst || strcmp(pname,rpfirst->fname))
|
|
break;
|
|
rp = rpfirst;
|
|
}
|
|
do
|
|
{
|
|
if((np = dtsearch(funtree,rp->np)) && is_afunction(np))
|
|
{
|
|
if(np->nvalue.rp)
|
|
np->nvalue.rp->fdict = 0;
|
|
nv_delete(np,funtree,NV_NOFREE);
|
|
}
|
|
dtinsert(funtree,rp->np);
|
|
rp->fdict = funtree;
|
|
}
|
|
while((rp=dtnext(shp->fpathdict,rp)) && strcmp(pname,rp->fname)==0);
|
|
sh_close(fno);
|
|
return;
|
|
}
|
|
sh_onstate(SH_NOLOG);
|
|
sh_onstate(SH_NOALIAS);
|
|
shp->readscript = (char*)name;
|
|
shp->st.filename = pname;
|
|
shp->funload = 1;
|
|
error_info.line = 0;
|
|
sh_eval(sfnew(NIL(Sfio_t*),buff,IOBSIZE,fno,SF_READ),SH_FUNEVAL);
|
|
sh_close(fno);
|
|
shp->readscript = 0;
|
|
#if SHOPT_NAMESPACE
|
|
if(shp->namespace)
|
|
np = sh_fsearch(shp,name,0);
|
|
else
|
|
#endif /* SHOPT_NAMESPACE */
|
|
np = nv_search(name,shp->fun_tree,0);
|
|
if(!np || !np->nvalue.ip)
|
|
pname = stakcopy(shp->st.filename);
|
|
else
|
|
pname = 0;
|
|
free((void*)shp->st.filename);
|
|
shp->funload = oldload;
|
|
shp->st.filename = oldname;
|
|
sh_setstate(savestates);
|
|
if(pname)
|
|
errormsg(SH_DICT,ERROR_exit(ERROR_NOEXEC),e_funload,name,pname);
|
|
}
|
|
|
|
/*
|
|
* 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 or 3, returns 1 if name found on FPATH
|
|
* if flag==3 no tracked alias will be set
|
|
* returns 1, if function was autoloaded.
|
|
* If oldpp is not NULL, it will contain a pointer to the path component
|
|
* where it was found.
|
|
*/
|
|
|
|
int path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int flag)
|
|
{
|
|
register Namval_t *np;
|
|
register int fno;
|
|
Pathcomp_t *pp=0;
|
|
if(name && strchr(name,'/'))
|
|
{
|
|
stakseek(PATH_OFFSET);
|
|
stakputs(name);
|
|
if(canexecute(shp,stakptr(PATH_OFFSET),0)<0)
|
|
{
|
|
*stakptr(PATH_OFFSET) = 0;
|
|
return(0);
|
|
}
|
|
if(*name=='/')
|
|
return(1);
|
|
stakseek(PATH_OFFSET);
|
|
stakputs(path_pwd(shp,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(!(flag&1) && (np=nv_search(name,shp->track_tree,0)) && !nv_isattr(np,NV_NOALIAS) && (pp=(Pathcomp_t*)np->nvalue.cp))
|
|
{
|
|
stakseek(PATH_OFFSET);
|
|
path_nextcomp(shp,pp,name,pp);
|
|
if(oldpp)
|
|
*oldpp = pp;
|
|
stakputc(0);
|
|
return(0);
|
|
}
|
|
pp = path_absolute(shp,name,oldpp?*oldpp:NIL(Pathcomp_t*));
|
|
if(oldpp)
|
|
*oldpp = pp;
|
|
if(!pp && (np=nv_search(name,shp->fun_tree,0))&&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(shp,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!='/' && flag<3)
|
|
{
|
|
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
|
|
*/
|
|
Pathcomp_t *path_absolute(Shell_t *shp,register const char *name, Pathcomp_t *pp)
|
|
{
|
|
register int f,isfun;
|
|
int noexec=0;
|
|
Pathcomp_t *oldpp;
|
|
Namval_t *np;
|
|
char *cp;
|
|
char *bp;
|
|
shp->path_err = ENOENT;
|
|
if(!pp && !(pp=path_get(shp,"")))
|
|
return(0);
|
|
shp->path_err = 0;
|
|
while(1)
|
|
{
|
|
sh_sigcheck(shp);
|
|
shp->bltin_dir = 0;
|
|
while(oldpp=pp)
|
|
{
|
|
pp = path_nextcomp(shp,pp,name,0);
|
|
if(!(oldpp->flags&PATH_SKIP))
|
|
break;
|
|
}
|
|
if(!oldpp)
|
|
{
|
|
shp->path_err = ENOENT;
|
|
return(0);
|
|
}
|
|
isfun = (oldpp->flags&PATH_FPATH);
|
|
if(!isfun && !sh_isoption(SH_RESTRICTED))
|
|
{
|
|
#if SHOPT_DYNAMIC
|
|
Shbltin_f addr;
|
|
int n;
|
|
#endif
|
|
if(*stakptr(PATH_OFFSET)=='/' && nv_search(stakptr(PATH_OFFSET),shp->bltin_tree,0))
|
|
return(oldpp);
|
|
#if SHOPT_DYNAMIC
|
|
n = staktell();
|
|
stakputs("b_");
|
|
stakputs(name);
|
|
stakputc(0);
|
|
if((addr = sh_getlib(shp, stakptr(n), oldpp)) &&
|
|
(np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)) &&
|
|
nv_isattr(np,NV_BLTINOPT))
|
|
{
|
|
shp->bltin_dir = 0;
|
|
return(oldpp);
|
|
}
|
|
stakseek(n);
|
|
while(bp = oldpp->blib)
|
|
{
|
|
char *fp;
|
|
void *dll;
|
|
int m;
|
|
if(fp = strchr(bp, ':'))
|
|
{
|
|
*fp++ = 0;
|
|
oldpp->blib = fp;
|
|
fp = 0;
|
|
}
|
|
else
|
|
{
|
|
fp = oldpp->bbuf;
|
|
oldpp->blib = oldpp->bbuf = 0;
|
|
}
|
|
n = staktell();
|
|
stakputs("b_");
|
|
stakputs(name);
|
|
stakputc(0);
|
|
m = staktell();
|
|
shp->bltin_dir = oldpp->name;
|
|
if(*bp!='/')
|
|
{
|
|
stakputs(oldpp->name);
|
|
stakputc('/');
|
|
}
|
|
stakputs(bp);
|
|
stakputc(0);
|
|
if(cp = strrchr(stakptr(m),'/'))
|
|
cp++;
|
|
else
|
|
cp = stakptr(m);
|
|
if(!strcmp(cp,LIBCMD) &&
|
|
(addr=(Shbltin_f)dlllook((void*)0,stakptr(n))) &&
|
|
(np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)) &&
|
|
nv_isattr(np,NV_BLTINOPT))
|
|
{
|
|
found:
|
|
if(fp)
|
|
free(fp);
|
|
shp->bltin_dir = 0;
|
|
return(oldpp);
|
|
}
|
|
#ifdef SH_PLUGIN_VERSION
|
|
if (dll = dllplugin(SH_ID, stakptr(m), NiL, SH_PLUGIN_VERSION, NiL, RTLD_LAZY, NiL, 0))
|
|
sh_addlib(shp,dll,stakptr(m),oldpp);
|
|
#else
|
|
#if (_AST_VERSION>=20040404)
|
|
if (dll = dllplug(SH_ID, stakptr(m), NiL, RTLD_LAZY, NiL, 0))
|
|
#else
|
|
if (dll = dllfind(stakptr(m), NiL, RTLD_LAZY, NiL, 0))
|
|
#endif
|
|
{
|
|
/*
|
|
* this detects the 2007-05-11 builtin context change and also
|
|
* the 2008-03-30 opt_info.num change that hit libcmd::b_head
|
|
*/
|
|
|
|
if (libcmd && !dlllook(dll, "b_pids"))
|
|
{
|
|
dlclose(dll);
|
|
dll = 0;
|
|
}
|
|
else
|
|
sh_addlib(shp,dll,stakptr(m),oldpp);
|
|
}
|
|
#endif
|
|
if(dll &&
|
|
(addr=(Shbltin_f)dlllook(dll,stakptr(n))) &&
|
|
(!(np = sh_addbuiltin(stakptr(PATH_OFFSET),NiL,NiL)) || np->nvalue.bfp!=(Nambfp_f)addr) &&
|
|
(np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)))
|
|
{
|
|
np->nvenv = dll;
|
|
goto found;
|
|
}
|
|
if(*stakptr(PATH_OFFSET)=='/' && nv_search(stakptr(PATH_OFFSET),shp->bltin_tree,0))
|
|
goto found;
|
|
if(fp)
|
|
free(fp);
|
|
stakseek(n);
|
|
}
|
|
#endif /* SHOPT_DYNAMIC */
|
|
}
|
|
shp->bltin_dir = 0;
|
|
sh_stats(STAT_PATHS);
|
|
f = canexecute(shp,stakptr(PATH_OFFSET),isfun);
|
|
if(isfun && f>=0 && (cp = strrchr(name,'.')))
|
|
{
|
|
*cp = 0;
|
|
if(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE))
|
|
f = -1;
|
|
*cp = '.';
|
|
}
|
|
if(isfun && f>=0)
|
|
{
|
|
nv_onattr(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE),NV_LTOU|NV_FUNCTION);
|
|
funload(shp,f,name);
|
|
close(f);
|
|
f = -1;
|
|
return(0);
|
|
}
|
|
else if(f>=0 && (oldpp->flags & PATH_STD_DIR))
|
|
{
|
|
int n = staktell();
|
|
stakputs("/bin/");
|
|
stakputs(name);
|
|
stakputc(0);
|
|
np = nv_search(stakptr(n),shp->bltin_tree,0);
|
|
stakseek(n);
|
|
if(np)
|
|
{
|
|
n = np->nvflag;
|
|
np = sh_addbuiltin(stakptr(PATH_OFFSET),(Shbltin_f)np->nvalue.bfp,nv_context(np));
|
|
np->nvflag = n;
|
|
}
|
|
}
|
|
if(!pp || f>=0)
|
|
break;
|
|
if(errno!=ENOENT)
|
|
noexec = errno;
|
|
}
|
|
if(f<0)
|
|
{
|
|
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(Shell_t *shp,register char *path, int isfun)
|
|
{
|
|
struct stat statb;
|
|
register int fd=0;
|
|
path = path_relative(shp,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(Shell_t *shp,register const char* file)
|
|
{
|
|
register const char *pwd;
|
|
register const char *fp = file;
|
|
/* can't relpath when shp->pwd not set */
|
|
if(!(pwd=shp->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(Shell_t *shp,register const char *arg0,register char *argv[],struct argnod *local)
|
|
{
|
|
char **envp;
|
|
const char *opath;
|
|
Pathcomp_t *libpath, *pp=0;
|
|
int slash=0;
|
|
nv_setlist(local,NV_EXPORT|NV_IDENT|NV_ASSIGN,0);
|
|
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(shp,arg0);
|
|
shp->path_err= ENOENT;
|
|
sfsync(NIL(Sfio_t*));
|
|
timerdel(NIL(void*));
|
|
/* find first path that has a library component */
|
|
while(pp && (pp->flags&PATH_SKIP))
|
|
pp = pp->next;
|
|
if(pp || slash) do
|
|
{
|
|
sh_sigcheck(shp);
|
|
if(libpath=pp)
|
|
{
|
|
pp = path_nextcomp(shp,pp,arg0,0);
|
|
opath = stakfreeze(1)+PATH_OFFSET;
|
|
}
|
|
else
|
|
opath = arg0;
|
|
path_spawn(shp,opath,argv,envp,libpath,0);
|
|
while(pp && (pp->flags&PATH_FPATH))
|
|
pp = path_nextcomp(shp,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(Shell_t *shp,const char *opath,register char **argv, char **envp, Pathcomp_t *libpath, int spawn)
|
|
{
|
|
register char *path;
|
|
char **xp=0, *xval, *libenv = (libpath?libpath->lib:0);
|
|
Namval_t* np;
|
|
char *s, *v;
|
|
int r, n, pidsize;
|
|
pid_t pid= -1;
|
|
/* leave room for inserting _= pathname in environment */
|
|
envp--;
|
|
#if _lib_readlink
|
|
/* save original pathname */
|
|
stakseek(PATH_OFFSET);
|
|
pidsize = sfprintf(stkstd,"*%d*",spawn?getpid():getppid());
|
|
stakputs(opath);
|
|
opath = stakfreeze(1)+PATH_OFFSET+pidsize;
|
|
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(shp,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 + 1;
|
|
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+pidsize);
|
|
envp[0][0] = '_';
|
|
envp[0][1] = '=';
|
|
sfsync(sfstderr);
|
|
sh_sigcheck(shp);
|
|
path = path_relative(shp,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(spawn && !sh_isoption(SH_PFSH))
|
|
pid = _spawnveg(shp,opath, &argv[0],envp, spawn>>1);
|
|
else
|
|
pid = path_pfexecve(shp,opath, &argv[0] ,envp,spawn);
|
|
if(xp)
|
|
*xp = xval;
|
|
#ifdef SHELLMAGIC
|
|
if(*path=='.' && path!=opath)
|
|
{
|
|
free(path);
|
|
path = path_relative(shp,opath);
|
|
}
|
|
#endif /* SHELLMAGIC */
|
|
if(pid>0)
|
|
return(pid);
|
|
retry:
|
|
switch(shp->path_err = errno)
|
|
{
|
|
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(shp->subshell)
|
|
return(-1);
|
|
do
|
|
{
|
|
if((pid=fork())>0)
|
|
return(pid);
|
|
}
|
|
while(_sh_fork(shp,pid,0,(int*)0) < 0);
|
|
((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT;
|
|
#else
|
|
return(-1);
|
|
#endif
|
|
}
|
|
exscript(shp,path,argv,envp);
|
|
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 */
|
|
#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(shp->xargmin)
|
|
{
|
|
pid = path_xargs(shp,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(shp,path);
|
|
shp->comdiv=0;
|
|
shp->bckpid = 0;
|
|
shp->coshell = 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->gd->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(shp,e_suidexec,argv,envp,0);
|
|
fail:
|
|
/*
|
|
* The following code is just for compatibility
|
|
*/
|
|
if((n=open(path,O_RDONLY,0)) < 0)
|
|
errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
|
|
if(savet)
|
|
*argv++ = savet;
|
|
openok:
|
|
shp->infd = n;
|
|
}
|
|
#else
|
|
if((shp->infd = sh_open(path,O_RDONLY,0)) < 0)
|
|
errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,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->gd->hist_ptr && (path=nv_getval(HISTFILE)) && strcmp(path,shp->gd->hist_ptr->histname))
|
|
{
|
|
hist_close(shp->gd->hist_ptr);
|
|
(HISTCUR)->nvalue.lp = 0;
|
|
}
|
|
sh_offstate(SH_FORKED);
|
|
if(shp->sigflag[SIGCHLD]==SH_SIGOFF)
|
|
shp->sigflag[SIGCHLD] = SH_SIGFAULT;
|
|
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 until 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(Shell_t *shp,Pathcomp_t *first, Pathcomp_t *old,const char *name, int flag)
|
|
{
|
|
register Pathcomp_t *pp, *oldpp;
|
|
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(len == pp->len && memcmp(name,pp->name,len)==0)
|
|
{
|
|
pp->flags |= flag;
|
|
return(first);
|
|
}
|
|
}
|
|
for(pp=first, oldpp=0; pp; oldpp=pp, pp=pp->next);
|
|
pp = newof((Pathcomp_t*)0,Pathcomp_t,1,len+1);
|
|
pp->shp = shp;
|
|
pp->refcount = 1;
|
|
memcpy((char*)(pp+1),name,len+1);
|
|
pp->name = (char*)(pp+1);
|
|
pp->len = len;
|
|
if(oldpp)
|
|
oldpp->next = pp;
|
|
else
|
|
first = pp;
|
|
pp->flags = flag;
|
|
if(strcmp(name,SH_CMDLIB_DIR)==0)
|
|
{
|
|
pp->dev = 1;
|
|
pp->flags |= PATH_BUILTIN_LIB;
|
|
pp->blib = pp->bbuf = malloc(sizeof(LIBCMD));
|
|
strcpy(pp->blib,LIBCMD);
|
|
return(first);
|
|
}
|
|
if((old||shp->pathinit) && ((flag&(PATH_PATH|PATH_SKIP))==PATH_PATH))
|
|
path_chkpaths(shp,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(Shell_t *shp,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==0 || m==6 && memcmp((void*)sp,(void*)"FPATH=",m)==0)
|
|
{
|
|
if(first)
|
|
{
|
|
char *ptr = stakptr(offset+pp->len+1);
|
|
if(ep)
|
|
strcpy(ptr,ep);
|
|
path_addcomp(shp,first,old,stakptr(offset),PATH_FPATH|PATH_BFPATH);
|
|
}
|
|
}
|
|
else if(m==11 && memcmp((void*)sp,(void*)"PLUGIN_LIB=",m)==0)
|
|
{
|
|
if(pp->bbuf)
|
|
free(pp->bbuf);
|
|
pp->blib = pp->bbuf = strdup(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(Shell_t *shp,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(shp,first,old,".",type);
|
|
while(*++path == ':');
|
|
}
|
|
else
|
|
{
|
|
int c;
|
|
while(*path && *path!=':')
|
|
path++;
|
|
c = *path++;
|
|
first = path_addcomp(shp,first,old,cp,type);
|
|
if(c==0)
|
|
break;
|
|
if(*path==0)
|
|
path--;
|
|
}
|
|
}
|
|
if(old)
|
|
{
|
|
if(!first && !path)
|
|
{
|
|
Pathcomp_t *pp = (Pathcomp_t*)shp->defpathlist;
|
|
if(!pp)
|
|
pp = defpath_init(shp);
|
|
first = path_dup(pp);
|
|
}
|
|
if(cp=(sh_scoped(shp,FPATHNOD))->nvalue.cp)
|
|
first = (void*)path_addpath(shp,(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(Shell_t *shp,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;
|
|
pp->mtime = statb.st_mtime;
|
|
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(shp,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(Shell_t *shp)
|
|
{
|
|
Pathcomp_t *first = (Pathcomp_t*)shp->pathlist;
|
|
register Pathcomp_t *pp=first, *old=0;
|
|
if(shp->fpathdict)
|
|
{
|
|
struct Ufunction *rp, *rpnext;
|
|
for(rp=(struct Ufunction*)dtfirst(shp->fpathdict);rp;rp=rpnext)
|
|
{
|
|
rpnext = (struct Ufunction*)dtnext(shp->fpathdict,rp);
|
|
if(rp->fdict)
|
|
nv_delete(rp->np,rp->fdict,NV_NOFREE);
|
|
rp->fdict = 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);
|
|
pp->shp->last_table = 0;
|
|
path_nextcomp(pp->shp,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->shp,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,0);
|
|
}
|
|
|