#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 #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 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 to value */ 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 */ 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 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 */