xref: /original-bsd/local/toolchest/ksh/sh/service.c (revision 85e979c5)
1 /*
2 
3  *      Copyright (c) 1984, 1985, 1986 AT&T
4  *      All Rights Reserved
5 
6  *      THIS IS UNPUBLISHED PROPRIETARY SOURCE
7  *      CODE OF AT&T.
8  *      The copyright notice above does not
9  *      evidence any actual or intended
10  *      publication of such source code.
11 
12  */
13 /* @(#)service.c	1.1 */
14 /*
15  * UNIX shell
16  *
17  * S. R. Bourne
18  * Rewritten by David Korn
19  * AT&T Bell Laboratories
20  *
21  */
22 
23 #include	<errno.h>
24 #include	<sys/types.h>
25 #include	<sys/stat.h>
26 #include	"flags.h"
27 #include	"defs.h"
28 #include	"sym.h"
29 #include	"io.h"
30 #include	"name.h"
31 #include	"brkincr.h"
32 #include	"mode.h"
33 #include	"stak.h"
34 #include	"shtype.h"
35 #include	"builtins.h"
36 #include	"jobs.h"
37 
38 #ifndef BSD_4_2
39 # ifdef BSD
40 # define fcntl(a,b,c)	dup2(a,b)
41 # endif /* BSD */
42 #endif /* BSD_4_2 */
43 
44 FILE	*pathopen();
45 char	*getpath();
46 char	*fullname();
47 int	initio();
48 void	exfunct();
49 #ifndef JOBS
50 void	postclr();
51 int	post();
52 void	await();
53 #endif	/* JOBS */
54 
55 extern void	arg_clear();
56 extern DOLPTR	arg_new();
57 extern void	arg_set();
58 extern void	arg_reset();
59 extern char	*catpath();
60 extern FILE	*chkopen();
61 extern STKPTR	cpystak();
62 extern FILE	*create();
63 extern void	exitsh();
64 extern void	failed();
65 extern NAMPTR	findnod();
66 extern void	free();
67 extern void	gscan_some();
68 extern char	*itos();
69 extern STKPTR	locstak();
70 extern void	mac_subst();
71 extern char	*mactrim();
72 extern void	mem_scope();
73 extern void	mem_unscope();
74 extern char	*movstr();
75 extern void	p_setout();
76 extern void	p_flush();
77 extern void	p_prp();
78 #ifdef JOBS
79 extern void	postclr();
80 #endif	/* JOBS */
81 extern char	*qvalup();
82 extern char	*realias();
83 extern void	rmlocal();
84 extern char	**setenv();
85 extern void	setlist();
86 extern char	*simple();
87 extern char	*strchr();
88 extern FILE	*tmp_open();
89 extern void	trim();
90 extern char	*valup();
91 
92 static char	*prune();
93 #ifndef VFORK
94 static void	 exscript();
95 #endif /* VFORK */
96 static char	 *execs();
97 
98 #define LOBYTE 0377
99 #ifdef u370
100 #define MAXP 75
101 #else
102 #define MAXP 32
103 #endif	/* u370 */
104 #define MAXDEPTH (32*sizeof(int))	/* maximum levels of recursion */
105 
106 
107 #ifndef JOBS
108 static int	maxpost;	/* highest number running process */
109 static int	numpost;	/* number of running processes */
110 
111 /* for processes to be waited for */
112 static struct process pwlist[MAXP];
113 #endif	/* JOBS */
114 static char *xecmsg;
115 static char **xecenv;
116 #ifdef VFORK
117 NAMPTR lookup();
118 #endif	/* VFORK */
119 
120 
121 /*
122  * service routines for `execute'
123  * flag > 0 if files are to be restored
124  * flag < 0 if files are to be closed on exec
125  */
126 
initio(iop,flag)127 int	initio(iop,flag)
128 IOPTR		iop;
129 {
130 	register char *ion;
131 	register int 	iof;
132 	register FILE	*fd;
133 	char fname[TMPSIZ];
134 	int fn;
135 	int mark = MARK;
136 	int indx = topfd;
137 	if(flag<0)
138 		mark = 0;
139 	for(;iop;iop=iop->ionxt)
140 	{
141 		iof=iop->iofile;
142 		if(flag>0)
143 		{
144 			/* save current file descriptor */
145 			savefd(iof&IOUFD,indx);
146 		}
147 		ion=mactrim(iop->ioname,1);
148 		if(*ion && is_option(NOEXEC)==0)
149 		{
150 			if(iof&IODOC)
151 			{
152 				fd = tmp_open(fname);
153 				unlink(fname);
154 				mac_subst(chkopen(ion),fd);
155 			}
156 			else if(iof&IOMOV)
157 			{
158 				if(eq(minus,ion))
159 				{
160 					fclose(file_fd(iof&IOUFD));
161 					fd = NULL;
162 				}
163 				else if(((fn=atoi(ion))>=USERIO) ||
164 					(fd=fdopen(dup(fn),(iof&IOPUT)?"w+":"r")) == NULL)
165 					failed(ion,badfile);
166 			}
167 			else if((iof&IOPUT)==0)
168 			{
169 				fd=chkopen(ion);
170 			}
171 			else if(is_option(RSHFLG))
172 				failed(ion,restricted);
173 			else if((iof&IOAPP) == 0 || (fd=fdopen(open(ion,1),"a"))==NULL)
174 			{
175 				if((fd=create(ion)) == NULL)
176 					failed(ion,badcreate);
177 			}
178 			if(fd!=NULL)
179 				frenumber(fd,(iof&IOUFD)|mark);
180 		}
181 	}
182 	return(indx);
183 }
184 
185 /*
186  * given <s> return a colon separated list of directories to search on the stack
187  * This routine adds names to the tracked alias list, if possible, and returns
188  * a reduced path string for tracked aliases
189  */
190 
getpath(s)191 char *getpath(s)
192 char *s;
193 {
194 	register char *path;
195 	register NAMPTR np;
196 	if(strchr(s,'/'))
197 		return(nullstr);
198 	path = qvalup(PATHNOD);
199 	if(path==NULL)
200 		path = defpath;
201 	path = cpystak(path);
202 	/* track alias if possible */
203 	np=findnod(s,alias,CHK_FOR);
204 	if(np==NULL && is_option(HASHALL) && (np=findnod(s,alias,CHK_FOR|1)))
205 		attrib(np, N_EXPORT|NO_ALIAS|T_FLAG);
206 	if(np && attest(np,T_FLAG))
207 	{
208 		if(attest(np,NO_ALIAS))
209 		{
210 			/* don't bother to look up alias if forked  */
211 			if(states&FORKED)
212 				return(path);
213 			/* if realias fails then a search won't find anything */
214 			if(realias(np)==NIL)
215 				return(nullstr);
216 		}
217 		/* tracked alias use reduced path */
218 		path = prune(path,valup(np));
219 	}
220 	return(path);
221 }
222 
pathopen(name)223 FILE	*pathopen(name)
224 register char *name;
225 {
226 	register char *path;
227 	register int n;
228 	struct stat statb;
229 	if(strchr(name,'/'))
230 	{
231 		if(is_option(RSHFLG))
232 			failed(name, restricted);
233 		else
234 			path = nullstr;
235 	}
236 	else
237 	{
238 		path = qvalup(PATHNOD);
239 		if(path==NULL)
240 			path = defpath;
241 		path = cpystak(path);
242 	}
243 	do
244 	{
245 		path=catpath(path,name);
246 		if((n = open(curstak(),0)) >= 0)
247 		{
248 			if(fstat(n,&statb)<0 || (statb.st_mode&S_IFMT)!=S_IFREG)
249 			{
250 				close(n);
251 				n = -1;
252 			}
253 		}
254 	}
255 	while( n<0 && path);
256 	return(fdopen(n,"r"));
257 }
258 
259 
260 /*
261  * do a path search and find the full pathname of file name
262  * if name is not a simple name, then name is a tracked alias
263  */
264 
fullname(fname)265 char	*fullname(fname)
266 char *fname;
267 {
268 	register char *name;
269 	register int	f;
270 	register char *path;
271 	if(*fname!='/')
272 		name=fname;
273 	else
274 		name = simple(fname);
275 	path = getpath(name);
276 	do
277 	{
278 		path=catpath(path,name);
279 		if((f=access(curstak(),1))>=0)
280 		{
281 			if(!ftype(curstak(),S_IFMT,S_IFREG))
282 				f = -1;
283 		}
284 	}
285 	while(f<0 && path);
286 	return(f<0?0:curstak());
287 }
288 
catpath(path,name)289 char *catpath(path,name)
290 register char *path;
291 char *	name;
292 {
293 	/* leaves result on top of stack */
294 	register char *scanp = path;
295 	register char *argp = locstak();
296 	while(*scanp && *scanp!=':')
297 		*argp++ = *scanp++;
298 	if(scanp!=path)
299 	{
300 		*argp++= '/';
301 		/* position past ":" unless a trailing colon after pathname */
302 		if(*scanp && *++scanp==0)
303 			scanp--;
304 	}
305 	else
306 		while(*scanp == ':')
307 			scanp++;
308 	path=(*scanp ? scanp : 0);
309 	scanp=name;
310 	while((*argp++ = *scanp++));
311 	staktop = argp;
312 	return(path);
313 }
314 
315 
execa(at,local)316 void	execa(at,local)
317 char *	at[];
318 ARGPTR local;		/* local environment modification */
319 {
320 	register char *path = nullstr;
321 	register char **t = at;
322 	if(is_option(NOEXEC)==0)
323 	{
324 		xecmsg=notfound;
325 #ifdef VFORK
326 		if(local)
327 			mem_scope(local);
328 		xecenv=setenv();
329 #else
330 		setlist(local,N_EXPORT);
331 		xecenv=setenv();
332 #endif	/* VFORK */
333 		if(strchr(t[0],'/'))
334 		{
335 			/* name containing / not allowed for restricted shell */
336 			if(is_option(RSHFLG))
337 				failed(t[0],restricted);
338 		}
339 		else
340 			path=getpath(*t);
341 #ifdef VFORK
342 		if(local)
343 			mem_unscope();
344 #endif	/* VFORK */
345 		/* insert _= onto stack in front of pathname */
346 		*--xecenv =  stakbot;
347 		*stakbot++ = '_';
348 		*stakbot++ = '=';
349 		while(path=execs(path,t));
350 		failed(*t,xecmsg);
351 	}
352 }
353 
354 /*
355  * This routine constructs a short path consisting of all
356  * Relative directories up to the directory of fullname <name>
357  */
prune(path,fullname)358 static char *prune(path,fullname)
359 register char *path;
360 char *fullname;
361 {
362 	register char *p = path;
363 	register char *s;
364 	int n = 1;
365 	char *base;
366 	char *inpath = path;
367 	if(fullname==NULL  || *fullname != '/' || *path==0)
368 		return(path);
369 	base = simple(fullname);
370 	do
371 	{
372 		/* a null path means current directory */
373 		if(*path == ':')
374 		{
375 			*p++ = ':';
376 			path++;
377 			continue;
378 		}
379 		s = path;
380 		path=catpath(path,base);
381 		if(*s != '/' || (n=strcmp(curstak(),fullname))==0)
382 		{
383 			/* position p past end of path */
384 			while(*s && *s!=':')
385 				*p++ = *s++;
386 			if(n==0)
387 			{
388 				*p = 0;
389 				return(inpath);
390 			}
391 			*p++ = ':';
392 		}
393 	}
394 	while(path);
395 	/* if there is no match just return path */
396 	path = qvalup(PATHNOD);
397 	if(path==NULL)
398 		path = defpath;
399 	strcpy(inpath,path);
400 	return(inpath);
401 }
402 
403 #ifdef XENIX
404 /*
405  *  This code takes care of a bug in the XENIX exec routine
406  *  Contributed by Pat Wood
407  */
ex_xenix(file)408 static ex_xenix(file)
409 char *file;
410 {
411 	struct stat stats;
412 	register int fd;
413 	unsigned short magic;
414 	if((fd = open(file,0)) == -1) /* can't read, so can't be shell prog */
415 		return(1);
416 	read(fd, &magic, sizeof(magic));
417 	if(magic == 01006) /* magic for xenix executable */
418 	{
419 		close(fd);
420 		return(1);
421 	}
422 	fstat(fd, &stats);
423 	close(fd);
424 	errno = ENOEXEC;
425 	if(!geteuid())
426 	{
427 		if(!(stats.st_mode & 0111))
428 			errno = EACCES;
429 		return(0);
430 	}
431 	if((geteuid() == stats.st_uid))
432 	{
433 		if(!(stats.st_mode & 0100))
434 			errno = EACCES;
435 		return(0);
436 	}
437 	if((getegid() == stats.st_gid))
438 	{
439 		if(!(stats.st_mode & 0010))
440 			errno = EACCES;
441 		return(0);
442 	}
443 	if(!(stats.st_mode & 0001))
444 		errno = EACCES;
445 	return(0);
446 }
447 #endif	/* XENIX */
448 
execs(ap,t)449 static char *execs(ap,t)
450 char *	ap;
451 register char **t;
452 {
453 	register char *p, *prefix;
454 	prefix=catpath(ap,t[0]);
455 	trim(p=curstak());
456 	p_flush();
457 	if(trapnote&SIGSET)
458 		exitsh(SIGFAIL);
459 #ifdef XENIX
460 	if(ex_xenix(p))
461 #endif	/* XENIX */
462 	execve(p, &t[0] ,xecenv);
463 	switch(errno)
464 	{
465 		case ENOEXEC:
466 #ifdef VFORK
467 		{
468 			/* this code handles the !# interpreter name convention */
469 			char iname[256];
470 #ifdef SUID_EXEC
471 			/* check if file cannot open for read or script is setuid/setgid  */
472 			static char name[] = "/tmp/euidXXXXXX";
473 			register int n;
474 			register int euserid;
475 			struct stat statb;
476 			if((n=open(p,0)) >= 0)
477 			{
478 				if(fstat(n,&statb)==0)
479 				{
480 					if((statb.st_mode&(S_ISUID|S_ISGID))==0)
481 						goto openok;
482 				}
483 				close(n);
484 			}
485 			if((euserid=geteuid()) != userid)
486 			{
487 				strcpy(name+9,itos(getpid()));
488 				/* create a suid open file with owner equal effective uid */
489 				if((n=creat(name,04100)) < 0)
490 					goto fail;
491 				unlink(name);
492 				/* make sure that file has right owner */
493 				if(fstat(n,&statb)<0 || statb.st_uid != euserid)
494 					goto fail;
495 				if(n!=10)
496 				{
497 					close(10);
498 					fcntl(n,0,10);
499 					close(n);
500 				}
501 			}
502 			*--t = p;
503 			execve(suid_exec,t,xecenv);
504 	fail:
505 			failed(p, badopen);
506 	openok:
507 			close(n);
508 #endif SUID_EXEC
509 			/* get name returns the interpreter name */
510 			if(get_shell(p, iname)<0)
511 				failed(p, badexec);
512 			t--;
513 			t[0] = iname;
514 			execve(iname, t, xecenv);
515 			if(access(iname,0)==0)
516 				xecmsg=badexec;
517 			failed(iname, xecmsg);
518 		}
519 #else
520 			exscript(p,t);
521 #endif	/* VFORK */
522 
523 		case ENOMEM:
524 			failed(p,toobig);
525 
526 		case E2BIG:
527 			failed(p,arglist);
528 
529 		case ETXTBSY:
530 			failed(p,txtbsy);
531 
532 		default:
533 			if(access(p,0)==0)
534 				xecmsg=badexec;
535 		case ENOENT:
536 			return(prefix);
537 	}
538 }
539 
540 /*
541  * File is executable but not machine code.
542  * Assume file is a Shell script and execute it.
543  */
544 
545 
exscript(p,t)546 static void exscript(p,t)
547 register char *p;
548 register char *t[];
549 {
550 	char *savet;
551 	flags=0;
552 	states = 0;
553 	comdiv=0;
554 	ioset=0;
555 	arg_clear(); /* remove open files and for loop junk */
556 	postclr();
557 	if(fileno(input))
558 		fclose(input);
559 	p_flush();
560 	standout= stdout;
561 	setbuf(stdin,(char*)_sibuf);
562 #ifdef SUID_EXEC
563 	/* check if file cannot open for read or script is setuid/setgid  */
564 	{
565 		static char name[] = "/tmp/euidXXXXXX";
566 		register int n;
567 		register int euserid;
568 		struct stat statb;
569 		if((n=open(p,0)) >= 0)
570 		{
571 			if(fstat(n,&statb)==0)
572 			{
573 				if((statb.st_mode&(S_ISUID|S_ISGID))==0)
574 					goto openok;
575 			}
576 			close(n);
577 		}
578 		if((euserid=geteuid()) != userid)
579 		{
580 			strcpy(name+9,itos(getpid()));
581 			/* create a suid open file with owner equal effective uid */
582 			if((n=creat(name,04100)) < 0)
583 				goto fail;
584 			unlink(name);
585 			/* make sure that file has right owner */
586 			if(fstat(n,&statb)<0 || statb.st_uid != euserid)
587 				goto fail;
588 			if(n!=10)
589 			{
590 				close(10);
591 				fcntl(n,0,10);
592 				close(n);
593 			}
594 		}
595 		savet = *--t;
596 		*t = p;
597 		execve(suid_exec,t,xecenv);
598 	fail:
599 		/*
600 		 *  The following code is just for compatibility
601 		 *  It should be replaced with the line failed(p,badexec);
602 		 */
603 		n = open(p,0);
604 		if(n < 0)
605 			failed(p, badopen);
606 		*t++ = savet;
607 		close(10);
608 
609 	openok:
610 		input = fdopen(n,"r");
611 	}
612 #else
613 	input = chkopen(p);
614 #endif /* SUID_EXEC */
615 #ifdef ACCT
616 	preacct(p) ;  /* reset accounting */
617 #endif	/* ACCT */
618 	gscan_some(rmlocal,namep,N_EXPORT,0);	/* remove local variables*/
619 	gscan_some(rmlocal,alias,N_EXPORT,0);	/* remove local aliases*/
620 	gscan_some(rmlocal,prnames,N_EXPORT,0);	/* remove local functions*/
621 	if(attest(IFSNOD,N_EXPORT)==0)
622 		assign(IFSNOD,sptbnl);
623 /* set up new args */
624 	arg_set(t);
625 	longjmp(subshell,1);
626 }
627 
628 /*
629  * The following routine is used to execute shell functions and command subs
630  * when com!=NULL $* is saved and restored
631  */
632 
exfunct(t,com,execflg,envlist)633 void exfunct(t,com,execflg,envlist)
634 TREPTR t;
635 register char *com[];
636 register unsigned execflg;
637 ARGPTR envlist;
638 {
639 	/* execute user defined function */
640 	register char *trap;
641 	jmp_buf retbuf;
642 	jmp_buf *savreturn = freturn;
643 	DOLPTR	argsav=0;
644 	int mode;
645 	DOLPTR savargfor;
646 	char *savtrap0 = trapcom[0];
647 	char *savtrap1 = trapcom[MAXTRAP];
648 	SHFILE savstandin;
649 	struct State savst;
650 	savst = st;
651 	loopcnt = 0;
652 	if(com)
653 	{
654 		mem_scope(envlist);
655 		if(execflg&EXECPR)
656 			on_option(EXECPR);
657 		else
658 			off_option(EXECPR);
659 		execflg &= ~EXECPR;
660 		cmdadr = com[0];
661 		trapcom[MAXTRAP] = 0;
662 		argsav = arg_new(com,&savargfor);
663 	}
664 	freturn = (jmp_buf*)retbuf;
665 	if((mode=setjmp(retbuf)) == 0)
666 	{
667 		states |= FUNCTION;
668 		if(fn_depth++ > MAXDEPTH)
669 			longjmp(*freturn,2);
670 		else
671 			execute(t,execflg);
672 	}
673 	fn_depth--;
674 	freturn = savreturn;
675 	if(com)
676 	{
677 		mem_unscope();
678 		arg_reset(argsav,savargfor);
679 		trapcom[MAXTRAP] = savtrap1;
680 		trapnote = 0;
681 	}
682 	savstandin = standin;
683 	st = savst;
684 	while((savstandin != standin) &&  pop(0));
685 	if(mode == 2)
686 	{
687 		if(fn_depth==0)
688 			failed(com[0],recursive);
689 		else
690 			longjmp(*freturn,2);
691 	}
692 	if(com && (trap=trapcom[0]) && (savtrap0!=trap))
693 	{
694 		trapcom[0] = savtrap0;
695 		execexp(trap,(FILE*)0);
696 		free(trap);
697 	}
698 }
699 
700 #ifndef JOBS
701 /*
702  * These routines have been moved to jobs.c when compiling with JOBS option
703  */
704 
705 /*
706  * Initialize the process posting array
707  */
postclr()708 void postclr()
709 {
710 	register struct process  *pw = pwlist;
711 	while(pw < &pwlist[maxpost])
712 		(pw++)->p_pid = 0;
713 	numpost=0;
714 	maxpost=0;
715 }
716 
post(pcsid)717 int	post(pcsid)
718 int 	pcsid;
719 {
720 	register struct process  *pw = pwlist;
721 	if(pcsid)
722 	{
723 		while(pw->p_pid)
724 			pw++;
725 		if(numpost >= MAXP-1)
726 			pw--;
727 		else
728 			numpost++;
729 		if(numpost > maxpost)
730 			maxpost = numpost;
731 		pw->p_pid = pcsid;
732 		if(numpost >= MAXP-1)
733 			await(0,0);
734 		return(pw-pwlist);
735 	}
736 	return(-1);
737 }
738 
await(i,bckg)739 void	await(i, bckg)
740 int 	i, bckg;
741 {
742 	int 	rc=0, wx=0;
743 	int 	w;
744 	post(i);
745 	while(numpost)
746 	{
747 		register int 	p;
748 		register int 	sig;
749 		int 	w_hi;
750 		int found = 0;
751 		{
752 			register struct process *pw=pwlist;
753 			errno = 0;
754 			p=wait(&w);
755 			if((p== -1) && bckg && errno==EINTR)
756 				break;
757 			while(pw <= &pwlist[maxpost])
758 			{
759 				if(pw->p_pid==p)
760 				{
761 					if(p==cpid)
762 					{
763 						cpid = 0;
764 						cpipe[1] = NULL;
765 					}
766 					pw->p_pid=0;
767 					numpost--;
768 					found++;
769 				}
770 				else
771 					 pw++;
772 			}
773 		}
774 		if(p == -1)
775 		{
776 			if(bckg)
777 			{
778 				register struct process *pw =pwlist;
779 				while(pw <= &pwlist[maxpost] && i != pw->p_pid)
780 					pw++;
781 				if(i == pw->p_pid)
782 				{
783 					pw->p_pid = 0;
784 					numpost--;
785 				}
786 			}
787 			continue;
788 		}
789 		w_hi = (w>>8)&LOBYTE;
790 		if(sig = w&0177)
791 		{
792 			p_setout(stderr);
793 			if(sig == 0177	/* ptrace! return */)
794 			{
795 				fputs(ptrace,output);
796 				sig = w_hi;
797 			}
798 #ifdef apollo
799 			if(*sysmsg[sig])
800 #else
801 			if(sysmsg[sig])
802 #endif	/* apollo */
803 			{
804 				if(i!=p || (states&PROMPT)==0)
805 				{
806 					p_prp(itos(p),SP);
807 				}
808 				fputs(sysmsg[sig],output);
809 				if(w&HIGHBIT)
810 					fputs(coredump,output);
811 			}
812 			newline();
813 		}
814 		wx |= w;
815 		if(p == i)
816 		{
817 			rc = (sig ? sig|SIGFLG : w_hi);
818 			break;
819 		}
820 	}
821 	exitval=rc;
822 	exitset();
823 }
824 #endif	/* JOBS */
825 
826 #ifdef ACCT
827 #include <acctdef.h>
828 #include <sys/acct.h>
829 #include <sys/times.h>
830 
831 static int compress();
832 
833 struct acct sabuf;
834 struct tms buffer;
835 extern long times();
836 static long	before;
837 static char *SHACCT; /* 0 environment variable SHACCT not set so never acct
838 		ptr to SHACCT value if set, so acct if shell procedure*/
839 static shaccton; /* 0 implies do not write record on exit
840 			  1 implies write acct record on exit
841 		*/
842 /*
843  *	initialize accounting, i.e., see if SHACCT variable set
844  */
initacct()845 void initacct()
846 {
847 
848 	SHACCT = valup(ACCTNOD);
849 }
850 /*
851 * suspend accounting unitl turned on by preacct()
852 */
suspacct()853 void suspacct()
854 {
855 	shaccton=0;
856 }
857 
preacct(cmdname)858 int preacct(cmdname)
859 char	*cmdname;
860 {
861 	char * strncpy();
862 	if(SHACCT)
863 	{
864 		sabuf.ac_btime = time((long *)0);
865 		before = times(&buffer);
866 		sabuf.ac_uid = getuid();
867 		sabuf.ac_gid = getgid();
868 		strncpy(sabuf.ac_comm, (char*)simple(cmdname),
869 			sizeof(sabuf.ac_comm));
870 		shaccton = 1;
871 	}
872 }
873 #include <fcntl.h>
doacct()874 void	doacct()
875 {
876 	int	fd;
877 	long	after;
878 
879 	if(shaccton)
880 	{
881 		after = times(&buffer);
882 		sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime);
883 		sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime);
884 		sabuf.ac_etime = compress( after - before);
885 		fd = open( SHACCT , O_WRONLY | O_APPEND | O_CREAT,0666);
886 		write(fd, &sabuf, sizeof( sabuf ));
887 		close( fd);
888 	}
889 }
890 
891 /*
892  * Produce a pseudo-floating point representation
893  * with 3 bits base-8 exponent, 13 bits fraction.
894  */
compress(t)895 static int compress(t)
896 register time_t t;
897 {
898 	register int exp = 0, rund = 0;
899 
900 	while (t >= 8192)
901 	{
902 		exp++;
903 		rund = t&04;
904 		t >>= 3;
905 	}
906 	if (rund)
907 	{
908 		t++;
909 		if (t >= 8192)
910 		{
911 			t >>= 3;
912 			exp++;
913 		}
914 	}
915 	return((exp<<13) + t);
916 }
917 #endif	/* ACCT */
918 
919