/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:kern_acct.c 12.0$ */
/* $ACIS:kern_acct.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/sys/RCS/kern_acct.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:kern_acct.c 12.0$";
#endif

/*
 * Copyright (c) 1982, 1986 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)kern_acct.c	7.1 (Berkeley) 6/5/86
 */

#include "param.h"
#include "systm.h"
#include "dir.h"
#include "user.h"
#ifndef VFS
#include "inode.h"
#include "fs.h"
#else !VFS
#include "vnode.h"
#include "../h/vfs.h"
#endif !VFS
#include "kernel.h"
#include "acct.h"
#include "uio.h"

/*
 * SHOULD REPLACE THIS WITH A DRIVER THAT CAN BE READ TO SIMPLIFY.
 */
#ifndef VFS
struct	inode *acctp;
struct	inode *savacctp;
#else !VFS
struct	vnode *acctp;
struct	vnode *savacctp;
#endif !VFS

/*
 * Perform process accounting functions.
 */
sysacct()
{
#ifndef VFS
	register struct inode *ip;
#else !VFS
	struct vnode *vp;
#endif !VFS
	register struct a {
		char	*fname;
	} *uap = (struct a *)u.u_ap;
#ifndef VFS
	register struct nameidata *ndp = &u.u_nd;
#endif !VFS

	if (suser()) {
		if (savacctp) {
			acctp = savacctp;
			savacctp = NULL;
		}
		if (uap->fname==NULL) {
#ifndef VFS
			if (ip = acctp) {
				irele(ip);
				acctp = NULL;
			}
#else !VFS
			if (vp = acctp) {
			/* 
			 * Assign acctp to NULL then release the vnode pointer
			 * This way, you won't be trying to use an 
			 * invalid vnode pointer to write out the accounting
			 * record.
			 */
				acctp = NULL;
				VN_RELE(vp);
			}
#endif !VFS
			return;
		}
#ifndef VFS
		ndp->ni_nameiop = LOOKUP | FOLLOW;
		ndp->ni_segflg = UIO_USERSPACE;
		ndp->ni_dirp = uap->fname;
		ip = namei(ndp);
		if (ip == NULL)
#else !VFS
		u.u_error = 
			lookupname(uap->fname, UIO_USERSPACE, FOLLOW_LINK,
				(struct vnode **)0, &vp);
		if (u.u_error)
#endif !VFS
			return;
#ifndef VFS
		if ((ip->i_mode&IFMT) != IFREG) {
#else !VFS
		if (vp->v_type != VREG) {
#endif !VFS
			u.u_error = EACCES;
#ifndef VFS
			iput(ip);
#else !VFS
			VN_RELE(vp);
#endif !VFS
			return;
		}
#ifndef VFS
		if (ip->i_fs->fs_ronly) {
			u.u_error = EROFS;
			iput(ip);
			return;
		}
		if (acctp && (acctp->i_number != ip->i_number ||
		    acctp->i_dev != ip->i_dev))
			irele(acctp);
		acctp = ip;
		iunlock(ip);
#else !VFS
		if (acctp) {			
			register struct vnode *tvp;
			/* 
			 * Assign acctp to a temp vnode pointer and release
			 * The old vnode pointer and assign a new pointer to
			 * acctp.  Similar to the 'if (vp = acctp)' above.
			 */
			tvp = acctp;
			acctp = vp;
			VN_RELE(tvp);
		} else {
			acctp = vp;
		}
#endif !VFS
	}
}

int	acctsuspend = 2;	/* stop accounting when < 2% free space left */
int	acctresume = 4;		/* resume when free space risen to > 4% */

struct	acct acctbuf;
/*
 * On exit, write a record on the accounting file.
 */
acct()
{
	register int i;
#ifndef VFS
	register struct inode *ip;
	register struct fs *fs;
#else !VFS
	register struct vnode *vp;
#endif !VFS
	register struct rusage *ru;
#ifndef VFS
	off_t siz;
#endif !VFS
	struct timeval t;
	register struct acct *ap = &acctbuf;
#ifdef VFS
	struct statfs sb;
#endif VFS

	if (savacctp) {
#ifndef VFS
		fs = savacctp->i_fs;
		if (freespace(fs, fs->fs_minfree + acctresume) > 0) {
#else !VFS
		(void)VFS_STATFS(savacctp->v_vfsp, &sb, savacctp);
		if (sb.f_bavail > (acctresume * sb.f_blocks / 100)) {
#endif !VFS
			acctp = savacctp;
			savacctp = NULL;
			printf("Accounting resumed\n");
		}
	}
#ifndef VFS
	if ((ip = acctp) == NULL)
#else !VFS
	if ((vp = acctp) == NULL)
#endif !VFS
		return;
#ifndef VFS
	fs = acctp->i_fs;
	if (freespace(fs, fs->fs_minfree + acctsuspend) <= 0) {
#else !VFS
	(void)VFS_STATFS(acctp->v_vfsp, &sb, acctp);
	if (sb.f_bavail <= (acctsuspend * sb.f_blocks / 100)) {
#endif !VFS
		savacctp = acctp;
		acctp = NULL;
		printf("Accounting suspended\n");
		return;
	}
#ifndef VFS
	ilock(ip);
#endif !VFS
	for (i = 0; i < sizeof (ap->ac_comm); i++)
		ap->ac_comm[i] = u.u_comm[i];
	ru = &u.u_ru;
	ap->ac_utime = compress(ru->ru_utime.tv_sec, ru->ru_utime.tv_usec);
	ap->ac_stime = compress(ru->ru_stime.tv_sec, ru->ru_stime.tv_usec);
	t = time;
	timevalsub(&t, &u.u_start);
	ap->ac_etime = compress(t.tv_sec, t.tv_usec);
	ap->ac_btime = u.u_start.tv_sec;
	ap->ac_uid = u.u_ruid;
	ap->ac_gid = u.u_rgid;
	t = ru->ru_stime;
	timevaladd(&t, &ru->ru_utime);
	if (i = t.tv_sec * hz + t.tv_usec / tick)
		ap->ac_mem = (ru->ru_ixrss+ru->ru_idrss+ru->ru_isrss) / i;
	else
		ap->ac_mem = 0;
	ap->ac_mem >>= CLSIZELOG2;
	ap->ac_io = compress(ru->ru_inblock + ru->ru_oublock, (long)0);
	if (u.u_ttyp)
		ap->ac_tty = u.u_ttyd;
	else
		ap->ac_tty = NODEV;
	ap->ac_flag = u.u_acflag;
#ifndef VFS
	siz = ip->i_size;
	u.u_error = 0;				/* XXX */
#endif !VFS
	u.u_error =
#ifndef VFS
	    rdwri(UIO_WRITE, ip, (caddr_t)ap, sizeof (acctbuf), siz,
		1, (int *)0);
	if (u.u_error)
		itrunc(ip, (u_long)siz);
	iunlock(ip);
#else !VFS
		vn_rdwr(UIO_WRITE, vp, (caddr_t)ap, sizeof(acctbuf), 0,
			UIO_SYSSPACE, IO_UNIT|IO_APPEND, (int *)0);
#endif !VFS
}

/*
 * Produce a pseudo-floating point representation
 * with 3 bits base-8 exponent, 13 bits fraction.
 */
compress(t, ut)
	register long t;
	long ut;
{
	register exp = 0, round = 0;

	t = t * AHZ;  /* compiler will convert only this format to a shift */
	if (ut)
		t += ut / (1000000 / AHZ);
	while (t >= 8192) {
		exp++;
		round = t&04;
		t >>= 3;
	}
	if (round) {
		t++;
		if (t >= 8192) {
			t >>= 3;
			exp++;
		}
	}
	return ((exp<<13) + t);
}
