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*68296Scgd * @(#)kern_acct.c 8.7 (Berkeley) 02/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> 29*68296Scgd #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; 57*68296Scgd struct acct_args /* { 58*68296Scgd syscallarg(char *) path; 59*68296Scgd } */ *uap; 60*68296Scgd 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 } 74*68296Scgd 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 } 82*68296Scgd 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; 8649916Smckusick VOP_UNLOCK(vp); 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 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 */ 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 */ 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