/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: kern_acct.c 1.20 90/01/17 $"

/*
 * kern_acct.c
 *	Accounting Support
 */

/* $Log:	kern_acct.c,v $
 */

#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/signal.h"
#include "sys/mutex.h"
#include "sys/systm.h"
#include "sys/user.h"
#include "sys/errno.h"
#include "sys/vnode.h"
#include "sys/vfs.h"
#include "sys/timer.h"
#include "sys/session.h"
#include "sys/proc.h"
#include "sys/statfs.h"
#include "sys/acct.h"
#include "sys/uio.h"
#include "sys/vmmeter.h"

#include "sys/vmparam.h"
#include "sys/pte.h"
#include "sys/intctl.h"
#include "sys/plocal.h"
#include "sys/clock.h"

struct	acct acctbuf;
struct	vnode	*acctp;

#ifdef DEBUG
extern	int	jc_debug;
#endif

/*
 * Semaphore to coordinate access to accounting mechanism.
 */

sema_t	acct_sema;

/*
 * sysacct()
 *	Perform process accounting functions (system-call).
 */

sysacct()
{
 	struct vnode *vp;
	register struct a {
		char	*fname;
	} *uap = (struct a *)u.u_ap;

	if (suser()) { 
		(void)p_sema(&acct_sema, PZERO);
		/*
		 * If argument is NULL, turn accounting OFF.
		 */
		if (uap->fname==NULL) {
			if (acctp) {
				VN_RELE(acctp);
				acctp = NULL;
			}
		} else {
			/*
			 * Want to turn on accounting...  get a vnode for
			 * the accounting file. Error if already on.
			 */
			if (acctp) {
				u.u_error = EBUSY;
				goto out;
			}
			u.u_error = lookupname(uap->fname, UIOSEG_USER,
					FOLLOW_LINK, (struct vnode **)0, &vp);
			if (u.u_error)
				goto out;
			if (vp->v_type != VREG)
				u.u_error = EACCES;
			else
				u.u_error = VOP_ACCESS(vp, VWRITE, u.u_cred);
			if (u.u_error) {
				VN_PUT(vp);
				goto out;
			}
			acctp = vp;
			VN_UNLOCKNODE(vp);
		}
out:
		v_sema(&acct_sema);
	}
}

/*
 * acct()
 *	On exit, write a record on the accounting file.
 */

acct(rv)
	int	rv;
{
	register struct acct *ap = &acctbuf;
	register struct process_stats *ps;
	proc_t		*p;
	struct	session	*sessp;
	struct	ctty	*ctty;
	struct vnode *vp;
	struct vnode *cvp;
	spl_t	spl;
	timeval_t t;

	/*
	 * First make quick check if accounting is enabled.
	 */

	if (acctp == NULL)
		return;

	(void)p_sema(&acct_sema, PZERO);

	/*
	 * If not accounting now, nothing to do.
	 */

	if ((vp = acctp) == NULL) {
		v_sema(&acct_sema);
		return;
	}

	/*
	 * generate and write accounting record.
	 */

	bcopy(u.u_comm, ap->ac_comm, sizeof(ap->ac_comm));
	ps = &u.u_pst;
	/*
	 * Store times in HZ units (ticks)
	 */
#define	toticks(s,us)	((s) * HZ + (us) / (1000000/HZ))

	ap->ac_utime = compress(toticks(ps->ps_utime.tv_sec,
						ps->ps_utime.tv_usec));
	ap->ac_stime = compress(toticks(ps->ps_stime.tv_sec,
						ps->ps_stime.tv_usec));
	t = time;
	timevalsub(&t, &u.u_start);
	ap->ac_etime = compress(toticks(t.tv_sec, t.tv_usec));
#undef	toticks
	ap->ac_btime = u.u_start.tv_sec;
	ap->ac_uid = u.u_ruid;
	ap->ac_gid = u.u_rgid;
	ap->ac_mem = 0;			/* not maintained */
	ap->ac_rw = compress((long)(ps->ps_bread + ps->ps_bwrite));
	ap->ac_io = compress(u.u_ioch);
	p = u.u_procp;
	sessp = p->p_session;
	spl = p_lock(&sessp->s_lock, SPLSESS);
	cvp = sessp->s_vp;
	if ( (ctty = sessp->s_ctty) && (ctty->c_sid == p->p_sid) && cvp) {
		ap->ac_tty = cvp->v_rdev;
		v_lock(&sessp->s_lock, spl);
#ifdef DEBUG
		if (jc_debug)
			printf("kern_acct: ac_tty = 0x%x\n", ap->ac_tty);
#endif
	} else {
		v_lock(&sessp->s_lock, spl);
		ap->ac_tty = NODEV;
	}
	ap->ac_flag = u.u_acflag;
	ap->ac_stat = (char) rv;

	VN_LOCKNODE(vp);
	u.u_error = 0;				/* in case died from signal */
	u.u_error = vn_rdwr(UIO_WRITE, vp, (caddr_t)ap, sizeof(acctbuf), 0,
					UIOSEG_KERNEL, IO_APPEND, (int *)0);

	/*
	 * Ignore write error.  In the *rare* case that this happens, worst
	 * case is mangled acct file. System V restores the old file size.
	 */

	VN_UNLOCKNODE(vp);
	v_sema(&acct_sema);
}

/*
 * compress()
 *	Produce a pseudo-floating point representation
 *	with 3 bits base-8 exponent, 13 bits fraction.
 */

compress(t)
	register long t;
{
	register exp = 0, round = 0;

	while (t >= 8192) {
		exp++;
		round = t&04;
		t >>= 3;
	}
	if (round) {
		t++;
		if (t >= 8192) {
			t >>= 3;
			exp++;
		}
	}
	return ((exp<<13) + t);
}
