149587Sbostic /*-
263432Sbostic * Copyright (c) 1982, 1986, 1989, 1993
363432Sbostic * The Regents of the University of California. All rights reserved.
465771Sbostic * (c) UNIX System Laboratories, Inc.
565771Sbostic * All or some portions of this file are derived from material licensed
665771Sbostic * to the University of California by American Telephone and Telegraph
765771Sbostic * Co. or Unix System Laboratories, Inc. and are reproduced herein with
865771Sbostic * the permission of UNIX System Laboratories, Inc.
923365Smckusick *
1049587Sbostic * %sccs.include.proprietary.c%
1149587Sbostic *
12*69409Smckusick * @(#)kern_acct.c 8.8 (Berkeley) 05/14/95
1323365Smckusick */
1412788Ssam
1556517Sbostic #include <sys/param.h>
1656517Sbostic #include <sys/systm.h>
1756517Sbostic #include <sys/namei.h>
1856517Sbostic #include <sys/resourcevar.h>
1956517Sbostic #include <sys/proc.h>
2056517Sbostic #include <sys/ioctl.h>
2156517Sbostic #include <sys/termios.h>
2256517Sbostic #include <sys/tty.h>
2356517Sbostic #include <sys/vnode.h>
2456517Sbostic #include <sys/mount.h>
2556517Sbostic #include <sys/kernel.h>
2656517Sbostic #include <sys/file.h>
2756517Sbostic #include <sys/acct.h>
2856517Sbostic #include <sys/syslog.h>
2968296Scgd #include <sys/syscallargs.h>
3012788Ssam
3112788Ssam /*
3237728Smckusick * Values associated with enabling and disabling accounting
3337728Smckusick */
3437728Smckusick int acctsuspend = 2; /* stop accounting when < 2% free space left */
3537728Smckusick int acctresume = 4; /* resume when free space risen to > 4% */
3658149Smckusick int acctchkfreq = 15; /* frequency (in seconds) to check space */
3737728Smckusick
3837728Smckusick /*
3912788Ssam * SHOULD REPLACE THIS WITH A DRIVER THAT CAN BE READ TO SIMPLIFY.
4012788Ssam */
4137728Smckusick struct vnode *acctp;
4237728Smckusick struct vnode *savacctp;
4312788Ssam
4412788Ssam /*
4549671Smckusick * Enable or disable process accounting.
4649671Smckusick *
4749671Smckusick * If a non-null filename is given, that file is used to store accounting
4849671Smckusick * records on process exit. If a null filename is given process accounting
4949671Smckusick * is suspended. If accounting is enabled, the system checks the amount
5049671Smckusick * of freespace on the filesystem at timeval intervals. If the amount of
5149671Smckusick * freespace is below acctsuspend percent, accounting is suspended. If
5249671Smckusick * accounting has been suspended, and freespace rises above acctresume,
5349671Smckusick * accounting is resumed.
5412788Ssam */
5563431Sbostic acct(p, uap, retval)
5643379Smckusick struct proc *p;
5768296Scgd struct acct_args /* {
5868296Scgd syscallarg(char *) path;
5968296Scgd } */ *uap;
6068296Scgd register_t *retval;
6112788Ssam {
6237728Smckusick register struct vnode *vp;
6354789Storek extern void acctwatch __P((void *));
6437728Smckusick struct vnode *oacctp;
6543379Smckusick int error;
6647545Skarels struct nameidata nd;
6712788Ssam
6847545Skarels if (error = suser(p->p_ucred, &p->p_acflag))
6944404Skarels return (error);
7037553Smckusick if (savacctp) {
7137553Smckusick acctp = savacctp;
7237553Smckusick savacctp = NULL;
7337553Smckusick }
7468296Scgd if (SCARG(uap, path) == NULL) {
7537728Smckusick if (vp = acctp) {
7637553Smckusick acctp = NULL;
7750107Smckusick error = vn_close(vp, FWRITE, p->p_ucred, p);
7854789Storek untimeout(acctwatch, NULL);
7912788Ssam }
8050107Smckusick return (error);
8112788Ssam }
8268296Scgd NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
8352307Smckusick if (error = vn_open(&nd, FWRITE, 0644))
8444404Skarels return (error);
8547545Skarels vp = nd.ni_vp;
86*69409Smckusick VOP_UNLOCK(vp, 0, p);
8737728Smckusick if (vp->v_type != VREG) {
8850107Smckusick (void) vn_close(vp, FWRITE, p->p_ucred, p);
8944404Skarels return (EACCES);
9037553Smckusick }
9137728Smckusick oacctp = acctp;
9237728Smckusick acctp = vp;
9337728Smckusick if (oacctp)
9450107Smckusick error = vn_close(oacctp, FWRITE, p->p_ucred, p);
9554789Storek acctwatch(NULL);
9650107Smckusick return (error);
9712788Ssam }
9812788Ssam
9912788Ssam /*
10037728Smckusick * Periodically check the file system to see if accounting
10167359Smckusick * should be turned on or off. Beware the case where the vnode
10267359Smckusick * has been vgone()'d out from underneath us, e.g. when the file
10367359Smckusick * system containing the accounting file has been forcibly unmounted.
10412788Ssam */
10554789Storek /* ARGSUSED */
10654789Storek void
acctwatch(a)10754789Storek acctwatch(a)
10854789Storek void *a;
10912788Ssam {
11037728Smckusick struct statfs sb;
11112788Ssam
11212788Ssam if (savacctp) {
11367359Smckusick if (savacctp->v_type == VBAD) {
11467359Smckusick (void) vn_close(savacctp, FWRITE, NOCRED, NULL);
11567359Smckusick savacctp = NULL;
11667359Smckusick return;
11767359Smckusick }
11848021Smckusick (void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0);
11937728Smckusick if (sb.f_bavail > acctresume * sb.f_blocks / 100) {
12012788Ssam acctp = savacctp;
12112788Ssam savacctp = NULL;
12237728Smckusick log(LOG_NOTICE, "Accounting resumed\n");
12358149Smckusick }
12458149Smckusick } else {
12558149Smckusick if (acctp == NULL)
12637728Smckusick return;
12767359Smckusick if (acctp->v_type == VBAD) {
12867359Smckusick (void) vn_close(acctp, FWRITE, NOCRED, NULL);
12967359Smckusick acctp = NULL;
13067359Smckusick return;
13167359Smckusick }
13258149Smckusick (void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0);
13358149Smckusick if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) {
13458149Smckusick savacctp = acctp;
13558149Smckusick acctp = NULL;
13658149Smckusick log(LOG_NOTICE, "Accounting suspended\n");
13712788Ssam }
13812788Ssam }
13958149Smckusick timeout(acctwatch, NULL, acctchkfreq * hz);
14037728Smckusick }
14137728Smckusick
14237728Smckusick /*
14349671Smckusick * This routine calculates an accounting record for a process and,
14449671Smckusick * if accounting is enabled, writes it to the accounting file.
14537728Smckusick */
acct_process(p)14663431Sbostic acct_process(p)
14743379Smckusick register struct proc *p;
14837728Smckusick {
14937728Smckusick register struct rusage *ru;
15037728Smckusick struct vnode *vp;
15140803Smarc struct timeval t, ut, st;
15268074Spendry int error, i, s;
15337728Smckusick struct acct acctbuf;
15437728Smckusick register struct acct *ap = &acctbuf;
15537728Smckusick
15668074Spendry s = splclock();
15768074Spendry if ((vp = acctp) == NULL) {
15868074Spendry splx(s);
15943379Smckusick return (0);
16068074Spendry }
16167359Smckusick if (vp->v_type == VBAD) {
16267359Smckusick (void) vn_close(vp, FWRITE, NOCRED, NULL);
16367359Smckusick acctp = NULL;
16468074Spendry splx(s);
16567359Smckusick return (0);
16667359Smckusick }
16740803Smarc bcopy(p->p_comm, ap->ac_comm, sizeof(ap->ac_comm));
16847545Skarels ru = &p->p_stats->p_ru;
16954789Storek calcru(p, &ut, &st, NULL);
17016715Ssam t = time;
17140803Smarc ap->ac_utime = compress(ut.tv_sec, ut.tv_usec);
17240803Smarc ap->ac_stime = compress(st.tv_sec, st.tv_usec);
17347545Skarels timevalsub(&t, &p->p_stats->p_start);
17416715Ssam ap->ac_etime = compress(t.tv_sec, t.tv_usec);
17547545Skarels ap->ac_btime = p->p_stats->p_start.tv_sec;
17647545Skarels ap->ac_uid = p->p_cred->p_ruid;
17747545Skarels ap->ac_gid = p->p_cred->p_rgid;
17840803Smarc t = st;
17940803Smarc timevaladd(&t, &ut);
18016719Skarels if (i = t.tv_sec * hz + t.tv_usec / tick)
18147545Skarels ap->ac_mem = (ru->ru_ixrss + ru->ru_idrss + ru->ru_isrss) / i;
18216715Ssam else
18316715Ssam ap->ac_mem = 0;
18426277Skarels ap->ac_io = compress(ru->ru_inblock + ru->ru_oublock, (long)0);
18564572Sbostic if (p->p_flag & P_CONTROLT && p->p_session->s_ttyp)
18640803Smarc ap->ac_tty = p->p_session->s_ttyp->t_dev;
18712788Ssam else
18812788Ssam ap->ac_tty = NODEV;
18947545Skarels ap->ac_flag = p->p_acflag;
19067655Smckusick VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE);
19168074Spendry error = vn_rdwr(UIO_WRITE, vp, (caddr_t)ap, sizeof (acctbuf), (off_t)0,
19248021Smckusick UIO_SYSSPACE, IO_UNIT|IO_APPEND, p->p_ucred, (int *)0,
19368074Spendry (struct proc *)0);
19468074Spendry splx(s);
19568074Spendry return (error);
19612788Ssam }
19712788Ssam
19812788Ssam /*
19912788Ssam * Produce a pseudo-floating point representation
20012788Ssam * with 3 bits base-8 exponent, 13 bits fraction.
20112788Ssam */
compress(t,ut)20216715Ssam compress(t, ut)
20312788Ssam register long t;
20416715Ssam long ut;
20512788Ssam {
20612788Ssam register exp = 0, round = 0;
20712788Ssam
20817501Skarels t = t * AHZ; /* compiler will convert only this format to a shift */
20916715Ssam if (ut)
21017501Skarels t += ut / (1000000 / AHZ);
21112788Ssam while (t >= 8192) {
21212788Ssam exp++;
21312788Ssam round = t&04;
21412788Ssam t >>= 3;
21512788Ssam }
21612788Ssam if (round) {
21712788Ssam t++;
21812788Ssam if (t >= 8192) {
21912788Ssam t >>= 3;
22012788Ssam exp++;
22112788Ssam }
22212788Ssam }
22312788Ssam return ((exp<<13) + t);
22412788Ssam }
225