Files
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

1254 lines
24 KiB
C
Executable File

#ident "@(#)service.c 1.10 95/08/18 SMI" /* From AT&T Toolchest */
/*
* UNIX shell
*
* S. R. Bourne
* Rewritten by David Korn
* AT&T Bell Laboratories
*
*/
#include <errno.h>
#include "defs.h"
#include "jobs.h"
#include "sym.h"
#include "builtins.h"
#include "history.h"
#define MAXDEPTH (32*sizeof(int)) /* maximum levels of recursion */
extern int gscan_some();
#ifdef SUID_EXEC
extern char *utos();
#endif /* SUID_EXEC */
static char *prune();
static char *execs();
static int canexecute();
#ifndef VFORK
static void exscript();
#endif /* VFORK */
#ifdef VPIX
static int suffix_offset;
extern char *suffix_list[];
static int dospath();
#endif /* VPIX */
static struct namnod *tracknod;
const char *xecmsg = (char *)NULL;
static char **xecenv;
static int pruned;
/* make sure PWD is set up correctly */
/*
* Return the present working directory
* Invokes /bin/pwd if flag==0 and if necessary
* Sets the PWD variable to this value
*/
char *path_pwd(flag)
int flag;
{
register char *cp;
register char *dfault = (char*)e_dot;
register int count = 0;
extern MSG e_crondir;
if(sh.pwd)
return(sh.pwd);
while(1)
{
/* try from lowest to highest */
switch(count++)
{
case 0:
cp = nam_strval(PWDNOD);
break;
case 1:
cp = nam_strval(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:
{
#ifdef apollo
char buff[PATH_MAX+1];
extern char *getwd();
cp=getwd(buff+1);
#else
/* even restricted shells can pwd */
optflag savflags = opt_flags;
unsigned savestates = st.states;
off_option(RSHFLG);
st.states &= ~(EXECPR|READPR);
sh_eval((char*)e_setpwd);
opt_flags = savflags;
st.states = savestates;
#endif /* apollo */
cp = nam_strval(PWDNOD);
if(*cp=='/')
dfault = cp;
break;
}
case 5:
return(dfault);
}
if(cp && *cp=='/' && test_inode(cp,e_dot))
break;
}
if(count>1 && cp!=nam_strval(PWDNOD))
nam_fputval(PWDNOD,cp);
nam_ontype(PWDNOD,N_FREE|N_EXPORT);
sh.pwd = PWDNOD->value.namval.cp;
return(cp);
}
/*
* given <s> return a colon separated list of directories to search on the stack
* This routine adds names to the tracked alias list, if possible, and returns
* a reduced path string for tracked aliases
*/
char *path_get(s)
const char *s;
/*@
assume s!=NULL;
return path satisfying path!=NULL;
@*/
{
register char *path;
register char *sp = sh.lastpath;
if(strchr(s,'/'))
return(NULLSTR);
path = nam_fstrval(PATHNOD);
if(path==NULL)
path = (char*)e_defpath;
path = stakcopy(path);
if(sp || ((tracknod=nam_search(s,sh.track_tree,0)) &&
nam_istype(tracknod,NO_ALIAS)==0 &&
(sp=nam_strval(tracknod))))
{
path = prune(path,sp);
pruned++;
}
return(path);
}
int path_open(name,path)
register const char *name;
register char *path;
/*@
assume name!=NULL;
@*/
{
register int n;
struct stat statb;
if(strchr(name,'/'))
{
if(is_option(RSHFLG))
sh_fail(name, e_restricted);
}
else
{
if(path==NULL)
path = (char*)e_defpath;
path = stakcopy(path);
}
do
{
path=path_join(path,name);
if((n = open(path_relative(stakptr(OPATH)),O_RDONLY)) >= 0)
{
if(fstat(n,&statb)<0 || S_ISDIR(statb.st_mode))
{
close(n);
n = -1;
}
}
}
while( n<0 && path);
/* opened file must have file descriptor >=2 */
n = io_movefd(n);
return(n);
}
#ifdef VPIX
/*
* This routine returns 1 if first directory in path is also in
* the DOSPATH variable, 0 otherwise
*/
static int dospath(path)
char *path;
{
register char *dp = nam_fstrval(DOSPATHNOD);
register char *sp = path;
register int c;
int match = 1;
int pwd=0; /* set for in preset working directory */
if(dp==0 || *sp==0)
return(0);
if(sp==0)
return(0);
if(*sp==':')
{
sp = path = path_pwd(1);
pwd++;
}
if(pwd && *dp==':')
return(1);
while(1)
{
if((c= *dp++)==0 || c == ':')
{
if(match==1 && (*sp==0 || *sp==':'))
return(1);
if(c==0)
return(0);
if(pwd && (*dp==':' || *dp==0))
return(1);
match = 1;
sp = path;
}
else if(match)
{
if(*sp++ != c)
match = 0;
}
}
/* NOTREACHED */
}
#endif /* VPIX */
/*
* set tracked alias node <np> to value <sp>
*/
void path_alias(np,sp)
register struct namnod *np;
register char *sp;
/*@
assume np!=NULL;
@*/
{
if(sp==NIL)
nam_free(np);
else
{
register char *vp = np->value.namval.cp;
register int n = 1;
register int nofree = nam_istype(np,N_FREE);
nam_offtype(np,~NO_PRINT);
if(vp==0 || (n=strcmp(sp,vp))!=0)
nam_putval(np,sp);
nam_typeset(np,T_FLAG|N_EXPORT);
if(nofree && n==0)
nam_ontype(np,N_FREE);
}
}
/*
* given a pathname return the base name
*/
char *path_basename(name)
register const char *name;
/*@
assume name!=NULL;
return x satisfying x>=name && *x!='/';
@*/
{
register const char *start = name;
while (*name)
if ((*name++ == '/') && *name) /* don't trim trailing / */
start = name;
return ((char*)start);
}
/*
* 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.
*/
int path_search(name,flag)
register const char *name;
/*@
assume name!=NULL;
assume flag==0 || flag==1 || flag==2;
@*/
{
register struct namnod *np;
register int fno;
if(flag)
{
/* if not found on pruned path, rehash and try again */
while((sh.lastpath=path_absolute(name))==0 && pruned)
nam_ontype(tracknod,NO_ALIAS);
}
if(flag==0 || sh.lastpath==0)
{
register char *path;
int savestates;
path = nam_fstrval(FPATHNOD);
if(path && (fno=path_open(name,path))>=0)
{
if(flag==2)
{
close(fno);
return(1);
}
sh.un.fd = fno;
st.exec_flag--;
savestates = st.states;
st.states |= NOLOG;
sh_eval(NIL);
st.states = savestates;
st.exec_flag++;
if((np=nam_search(name,sh.fun_tree,0))&&np->value.namval.ip)
return(1);
}
return(0);
}
else
{
if((np=tracknod) || (is_option(HASHALL) &&
(np=nam_search(name,sh.track_tree,N_ADD)))
)
path_alias(np,sh.lastpath);
}
return(0);
}
/*
* do a path search and find the full pathname of file name
*/
char *path_absolute(name)
register const char *name;
/*@
assume name!=NULL;
return x satisfying x && *x=='/';
@*/
{
register int f;
register char *path;
#ifdef VPIX
char **suffix = 0;
char *top;
#endif /* VPIX */
pruned = 0;
path = path_get(name);
do
{
if(sh.trapnote&SIGSET)
sh_exit(SIGFAIL);
#ifdef VPIX
if(suffix==0)
{
if(dospath(path))
suffix = suffix_list;
path=path_join(path,name);
if(suffix)
top = stakptr(suffix_offset);
}
if(suffix)
{
sh_copy(*suffix,top);
if(**suffix==0)
suffix = 0;
else
suffix++;
}
#else
path=path_join(path,name);
#endif /* VPIX */
f = canexecute(stakptr(OPATH));
}
#ifdef VPIX
while(f<0 && (path||suffix));
#else
while(f<0 && path);
#endif /* VPIX */
if(f<0)
return(0);
/* check for relative pathname */
if(*stakptr(OPATH)!='/')
path_join(path_pwd(1),(char*)stakfreeze(1)+OPATH);
return((char*)stakfreeze(1)+OPATH);
}
/*
* returns 0 if path can execute
* sets xecmsg to e_exec 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(path)
register char *path;
/*@
assume path!=NULL;
@*/
{
struct stat statb;
path = path_relative(path);
xecmsg = e_found;
if(stat(path,&statb) < 0)
{
if(errno!=ENOENT)
xecmsg = e_exec;
return(-1);
}
xecmsg = e_exec;
if(!S_ISDIR(statb.st_mode))
{
if((statb.st_mode&S_IXALL)==S_IXALL)
return(0);
return(sh_access(path,X_OK));
}
return(-1);
}
#ifndef INT16
/*
* Return path relative to present working directory
*/
char *path_relative(file)
register const char *file;
/*@
assume file!=NULL;
return x satisfying x!=NULL;
@*/
{
register char *pwd;
register char *fp = (char*)file;
/* can't relpath when sh.pwd not set */
if(!(pwd=sh.pwd))
return(fp);
while(*pwd==*fp)
{
if(*pwd++==0)
return((char*)e_dot);
fp++;
}
if(*pwd==0 && *fp == '/')
{
while(*++fp=='/');
if(*fp)
return(fp);
return((char*)e_dot);
}
return((char*)file);
}
#endif /* INT16 */
char *path_join(path,name)
register char *path;
const char *name;
/*@
assume path!=NULL;
assume name!=NULL;
@*/
{
/* leaves result on top of stack */
register char *scanp = path;
register int c;
stakseek(OPATH);
/* eliminate . and ./ */
if(*scanp=='.')
{
if((c= *++scanp)==0 || c==':')
path = scanp;
else if(c=='/')
path = ++scanp;
else
scanp--;
}
while((c = *scanp) && c!=':')
{
stakputc(c);
scanp++;
}
if(scanp!=path)
{
if(scanp[-1]!='/')
stakputc('/');
/* position past ":" unless a trailing colon after pathname */
if(*scanp && *++scanp==0)
scanp--;
}
else
while(*scanp == ':')
scanp++;
path=(*scanp ? scanp : 0);
stakputs(name);
#ifdef VPIX
/* make sure that there is room for suffixes */
suffix_offset = staktell();
stakputs(*suffix_list);
*stakptr(suffix_offset) = 0;
#endif /*VPIX */
return(path);
}
void path_exec(at,local)
char * at[];
struct argnod *local; /* local environment modification */
/*@
assume at!=NULL && *at!=NULL;
@*/
{
register const char *path = e_nullstr;
register char **t = at;
register int erret;
xecmsg=e_found;
#ifdef VFORK
if(local)
nam_scope(local);
xecenv=env_gen();
#else
env_setlist(local,N_EXPORT);
xecenv=env_gen();
#endif /* VFORK */
if(strchr(t[0],'/'))
{
/* name containing / not allowed for restricted shell */
if(is_option(RSHFLG))
sh_fail(t[0],e_restricted);
}
else
path=path_get(*t);
#ifdef VFORK
if(local)
nam_unscope();
#endif /* VFORK */
/* leave room for inserting _= pathname in environment */
xecenv--;
while(path=execs(path,t));
/* XPG4: exit status must conform XPG4 standard */
if (xecmsg == e_found)
erret = ENOTFOUND;
else if (xecmsg == e_exec)
erret = ECANTEXEC;
else
erret = ERROR;
cmd_shfail(*t, xecmsg, erret);
}
/*
* This routine constructs a short path consisting of all
* Relative directories up to the directory of fullname <name>
*/
static char *prune(path,fullname)
register char *path;
const char *fullname;
/*@
assume path!=NULL;
return x satisfying x!=NULL && strlen(x)<=strlen(in path);
@*/
{
register char *p = path;
register char *s;
int n = 1;
const char *base;
char *inpath = path;
if(fullname==NULL || *fullname != '/' || *path==0)
return(path);
base = path_basename(fullname);
do
{
/* a null path means current directory */
if(*path == ':')
{
*p++ = ':';
path++;
continue;
}
s = path;
path=path_join(path,base);
if(*s != '/' || (n=strcmp(stakptr(OPATH),fullname))==0)
{
/* position p past end of path */
while(*s && *s!=':')
*p++ = *s++;
if(n==0)
{
*p = 0;
return(inpath);
}
*p++ = ':';
}
}
while(path);
/* if there is no match just return path */
path = nam_fstrval(PATHNOD);
if(path==NULL)
path = (char*)e_defpath;
strcpy(inpath,path);
return(inpath);
}
#ifdef XENIX
/*
* This code takes care of a bug in the XENIX exec routine
* Contributed by Pat Wood
*/
static ex_xenix(file)
char *file;
{
struct stat stats;
register int fd;
unsigned short magic;
if((fd = open(file,O_RDONLY)) == -1) /* can't read, so can't be shell prog */
return(1);
read(fd, &magic, sizeof(magic));
if(magic == 01006) /* magic for xenix executable */
{
close(fd);
return(1);
}
fstat(fd, &stats);
close(fd);
errno = ENOEXEC;
if(!geteuid())
{
if(!(stats.st_mode & 0111))
errno = EACCES;
return(0);
}
if((geteuid() == stats.st_uid))
{
if(!(stats.st_mode & 0100))
errno = EACCES;
return(0);
}
if((getegid() == stats.st_gid))
{
if(!(stats.st_mode & 0010))
errno = EACCES;
return(0);
}
if(!(stats.st_mode & 0001))
errno = EACCES;
return(0);
}
#endif /* XENIX */
static char *execs(ap,t)
char * ap;
register char **t;
/*@
assume ap!=NULL;
assume t!=NULL && *t!=NULL;
@*/
{
register int space = 0;
register char *p, *prefix;
prefix=path_join(ap,t[0]);
xecenv[0] = stakptr(0);
*stakptr(0) = '_';
*stakptr(1) = '=';
p=stakptr(OPATH);
p_flush();
#ifdef VPIX
if(dospath(ap))
{
char **suffix;
char *savet = t[0];
t[0] = p;
t[-2] = (char*)e_vpix+1;
t[-1] = "-c";
suffix = suffix_list;
while(**suffix)
{
char *vp;
sh_copy(*suffix++,stakptr(suffix_offset));
if(canexecute(p)>=0)
{
stakfreeze(1);
if(p=nam_fstrval(VPIXNOD))
stakputs(p);
else
stakputs(e_vpixdir);
stakputs(e_vpix);
execve(stakptr(0), &t[-2] ,xecenv);
switch(errno)
{
case ENOENT:
sh_fail(vp,e_found);
default:
sh_fail(vp,e_exec);
}
}
}
t[0] = savet;
*stakptr(suffix_offset) = 0;
}
#endif /* VPIX */
if(sh.trapnote&SIGSET)
sh_exit(SIGFAIL);
p = path_relative(p);
#ifdef XENIX
if(ex_xenix(p))
#endif /* XENIX */
#ifdef SHELLMAGIC
if(*p!='/' && p!=stakptr(OPATH) && strchr(p,'/') == (char *)NULL)
{
/*
* The following code because execv(foo,) and execv(./foo,)
* may not yield the same resulst
*/
char *sp = malloc(strlen(p)+3);
sp[0] = '.';
sp[1] = '/';
strcpy(sp+2,p);
p = sp;
space++;
}
#endif /* SHELLMAGIC */
execve(p, &t[0] ,xecenv);
#ifdef SHELLMAGIC
if(*p=='.' && p!=stakptr(OPATH) && space)
{
free(p);
p = path_relative(stakptr(OPATH));
space = 0;
}
#endif /* SHELLMAGIC */
switch(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:
#ifdef VFORK
{
/* this code handles the !# interpreter name convention */
char iname[PATH_MAX];
#ifdef SUID_EXEC
/* check if file cannot open for read or script is setuid/setgid */
static char name[] = "/tmp/euidXXXXXXXXXXX";
register int n;
register uid_t euserid;
struct stat statb;
if((n=open(p,O_RDONLY)) >= 0)
{
if(fstat(n,&statb)==0)
{
if((statb.st_mode&(S_ISUID|S_ISGID))==0)
goto openok;
}
close(n);
}
if((euserid=geteuid()) != sh.userid)
{
strncpy(name+9,utos((long)getpid(),10),sizeof(name)-10);
/* create a suid open file with owner equal effective uid */
if((n=creat(name,04100)) < 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)
{
close(10);
fcntl(n, F_DUPFD,10);
close(n);
}
}
*--t = p;
execve(e_suidexec,t,xecenv);
fail:
sh_fail(p, e_open);
openok:
close(n);
#endif /*SUID_EXEC */
/* get name returns the interpreter name */
if(get_shell(p, iname)<0)
sh_fail(p, e_exec);
t--;
t[0] = iname;
execve(iname, t, xecenv);
if(sh_access(iname,F_OK)==0)
xecmsg=e_exec;
sh_fail(iname, xecmsg);
}
#else
exscript(p,t);
#endif /* VFORK */
#ifdef ENAMETOOLONG
case ENAMETOOLONG:
xecmsg = e_longname;
return(prefix);
#endif /* ENAMETOOLONG */
case ENOMEM:
sh_fail(p,e_toobig);
case E2BIG:
sh_fail(p,e_arglist);
#ifdef ETXTBSY
case ETXTBSY:
sh_fail(p,e_txtbsy);
#endif /* ETXTBSY */
#ifdef ELIBACC
case ELIBACC:
sh_fail(p, e_libacc);
case ELIBBAD:
sh_fail(p, e_libbad);
case ELIBSCN:
sh_fail(p, e_libscn);
case ELIBMAX:
sh_fail(p, e_libmax);
#endif /* ELIBACC */
default:
if(sh_access(p,F_OK)==0)
xecmsg=e_exec;
case ENOENT:
return(prefix);
}
}
/*
* File is executable but not machine code.
* Assume file is a Shell script and execute it.
*/
static void exscript(p,t)
register char *p;
register char *t[];
/*@
assume p!=NULL;
assume t!=NULL && *t!=NULL;
@*/
{
#ifdef _OPTIM_
st.flags.i[_LOW_] = 0;
st.flags.i[_HIGH_] &= (HASHALL|EMACS|GMACS|VIRAW|EDITVI)>>16;
#else
off_option(~(HASHALL|EMACS|GMACS|VIRAW|EDITVI));
#endif /* _OPTIM_ */
sh.comdiv=0;
sh.bckpid = 0;
st.ioset=0;
/* clean up any cooperating processes */
if(sh.cpipe[INPIPE]>0)
io_pclose(sh.cpipe);
if(sh.cpid)
io_fclose(COTPIPE);
arg_clear(); /* remove for loop junk */
io_clear((struct fileblk*)0); /* remove open files */
job_clear();
if(input>0 && input!=F_STRING)
io_fclose(input);
st.states = 0;
p_flush();
st.standout= 1;
#ifdef SUID_EXEC
/* 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;
struct stat statb;
if((n=open(p,O_RDONLY)) >= 0)
{
if(fstat(n,&statb)==0)
{
if((statb.st_mode&(S_ISUID|S_ISGID))==0)
goto openok;
}
close(n);
}
if((euserid=geteuid()) != sh.userid)
{
strncpy(name+9,utos((long)getpid(),10),sizeof(name)-10);
/* create a suid open file with owner equal effective uid */
if((n=creat(name,04100)) < 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)
{
close(10);
fcntl(n, F_DUPFD, 10);
close(n);
}
}
savet = *--t;
*t = p;
execve(e_suidexec,t,xecenv);
fail:
/*
* The following code is just for compatibility
* It should be replaced with the line sh_fail(p,e_exec);
*/
if((n=open(p,O_RDONLY)) < 0)
sh_fail(p, e_open);
*t++ = savet;
close(10);
openok:
input = n;
}
#else
input = io_fopen(p);
#endif /* SUID_EXEC */
hist_close();
#ifdef ACCT
preacct(p) ; /* reset accounting */
#endif /* ACCT */
/* remove locals */
gscan_some(env_nolocal,sh.var_tree,N_EXPORT,0); /* local variables*/
gscan_some(env_nolocal,sh.alias_tree,N_EXPORT,0);/* local aliases*/
gscan_some(env_nolocal,sh.fun_tree,N_EXPORT,0); /* local functions*/
/* set up new args */
arg_set(t);
nam_ontype(L_ARGNOD, N_INDIRECT);
nam_offtype(SHELLNOD,~N_RESTRICT);
nam_offtype(PATHNOD,~N_RESTRICT);
sh.lastarg = sh_heap(p);
/* save name of calling command */
sh.readscript = st.cmdadr;
st.cmdadr = sh_heap(t[0]);
st.fn_depth = st.dot_depth = 0;
LONGJMP(sh.subshell,1);
}
/*
* The following routine is used to execute shell functions and command subs
* when com!=NULL $* is saved and restored
*/
void sh_funct(t,com,execflg,envlist)
union anynode *t;
register char *com[];
register int execflg;
struct argnod *envlist;
/*@
assume t!=NULL;
@*/
{
/* execute user defined function */
register char *trap;
jmp_buf retbuf;
jmp_buf *savreturn = sh.freturn;
int savop_char, savop_index;
struct dolnod *argsav=0;
int mode;
struct dolnod *savargfor;
struct fileblk *savstandin = st.standin;
struct sh_scoped savst;
savst = st;
savop_index = opt_index;
savop_char = opt_char;
opt_char = opt_index = 0;
st.loopcnt = 0;
if(com)
{
nam_scope(envlist);
if(execflg&EXECPR)
on_option(EXECPR);
else
off_option(EXECPR);
execflg &= ~EXECPR;
/* P.55, section 2.9.5 XPG4 manual: Preserve $0 */
sig_funset(0);
argsav = arg_new(com,&savargfor);
}
sh.freturn = (jmp_buf*)retbuf;
mode = SETJMP(retbuf);
if(mode==0)
{
st.states |= FUNCTION;
if(st.fn_depth++ > MAXDEPTH)
LONGJMP(*sh.freturn,3);
else
sh_exec(t,execflg);
}
if(--st.fn_depth==1 && mode==3)
sh_fail(com[0],e_recursive);
sh.freturn = savreturn;
if(com)
{
nam_unscope();
arg_reset(argsav,savargfor);
trap = st.trapcom[0];
st.trapcom[0] = 0;
sig_funset(1);
}
else
{
/* remember signals that occur for processing later */
savst.trapflg[SIGINT] = st.trapflg[SIGINT];
savst.trapflg[SIGTERM] = st.trapflg[SIGTERM];
savst.trapflg[SIGHUP] = st.trapflg[SIGHUP];
}
io_clear(savstandin);
st = savst;
opt_index = savop_index;
opt_char = savop_char;
if(com)
{
if(sh.exitval > SIGFAIL)
sh_fault(sh.exitval-SIGFAIL);
if(trap)
{
/* Latest EXIT trap is used */
if (st.trapcom[0])
free(st.trapcom[0]);
st.trapcom[0] = trap;
}
/* do a debug trap on return unless in a debug trap handler */
if(st.trapcom[DEBUGTRAP] && sh.intrap != DEBUGTRAP + 1)
{
st.trapflg[DEBUGTRAP] |= TRAPSET;
sh.trapnote |= TRAPSET;
}
}
if(mode > 2)
{
/* mode == 4 for exit command - Exit immediately */
if(mode==4)
{
st.states &= ~(PROMPT|PROFILE|BUILTIN|FUNCTION|LASTPIPE);
sh_exit(sh.exitval);
}
LONGJMP(*sh.freturn,mode);
}
}
#ifdef LSTAT
/*
* Given an absolute pathname, find physical pathname by resolving links
* path_phys returns 1, if successful, 0 for recursive link or overflow
* path must be an array of at least PATH_MAX characters
* The resulting path is canonicalized
* Coded by David Korn
* ulysses!dgk
*/
path_physical(path)
register char *path;
/*@
assume path!=NULL;
@*/
{
#ifdef apollo
/*
* This code has been added to figure out where we
* really are instead performing operations on the
* path string. This is much faster the walking through
* the path doing readlink(s). Note: code has been added in
* SYSCD(builtin.c) to look at the errno value in case getwd() fails.
*/
extern char *getwd();
char realpath[PATH_MAX+1];
if (!getwd(realpath))
return(0);
strcpy(path, realpath);
return(1);
#else /* apollo */
register char *cp = path;
char buffer[PATH_MAX];
register char *savecp;
int depth = 0;
int c;
int n;
while(*cp)
{
/* skip over '/' */
savecp = cp+1;
while(*cp=='/')
cp++;
/* eliminate multiple slashes */
if(cp > savecp)
cp = strcpy(savecp,cp);
/* check for .. */
if(*cp=='.')
{
switch(cp[1])
{
case 0: case '/':
/* eliminate /. */
cp--;
strcpy(cp,cp+2);
continue;
case '.':
if(cp[2]=='/' || cp[2]==0)
{
/* backup, but not past root */
savecp = cp+2;
cp--;
while(cp>path && *--cp!='/');
if(cp==path && *cp=='/')
cp++;
strcpy(cp,savecp);
continue;
}
break;
}
}
savecp = cp;
/* go to end of component */
while(*cp && *cp!='/')
cp++;
c = *cp;
*cp = 0;
n = readlink(path,buffer,PATH_MAX);
*cp = c;
if(n>0)
{
if(++depth > MAXDEPTH)
return(0);
strcpy(buffer+n,cp);
if(*buffer=='/')
cp = strcpy(path,buffer);
else
{
/* check for path overflow */
cp = savecp;
if((strlen(buffer)+(cp-path)) >= PATH_MAX)
return(0);
strcpy(cp,buffer);
}
}
}
if(cp==path)
*++cp = 0;
else while(--cp > path && *cp=='/')
/* eliminate trailing slashes */
*cp = 0;
return(1);
#endif /* apollo */
}
#endif /* LSTAT */
#ifdef ACCT
# include <sys/acct.h>
static int compress();
static struct acct sabuf;
static struct tms buffer;
static clock_t before;
static char *SHACCT; /* 0 environment variable SHACCT not set so never acct
ptr to SHACCT value if set, so acct if shell procedure*/
static shaccton; /* 0 implies do not write record on exit
1 implies write acct record on exit
*/
/*
* initialize accounting, i.e., see if SHACCT variable set
*/
void initacct()
{
SHACCT = nam_strval(ACCTNOD);
}
/*
* suspend accounting unitl turned on by preacct()
*/
void suspacct()
{
shaccton=0;
}
int preacct(cmdname)
char *cmdname;
{
if(SHACCT)
{
sabuf.ac_btime = time((time_t *)0);
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;
}
}
void doacct()
{
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, &sabuf, sizeof( sabuf ));
close( fd);
}
}
/*
* Produce a pseudo-floating point representation
* with 3 bits base-8 exponent, 13 bits fraction.
*/
static int compress(t)
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 /* ACCT */