149587Sbostic /*- 263432Sbostic * Copyright (c) 1982, 1986, 1989, 1993 363432Sbostic * The Regents of the University of California. All rights reserved. 4*65771Sbostic * (c) UNIX System Laboratories, Inc. 5*65771Sbostic * All or some portions of this file are derived from material licensed 6*65771Sbostic * to the University of California by American Telephone and Telegraph 7*65771Sbostic * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8*65771Sbostic * the permission of UNIX System Laboratories, Inc. 923365Smckusick * 1049587Sbostic * %sccs.include.proprietary.c% 1149587Sbostic * 12*65771Sbostic * @(#)kern_acct.c 8.3 (Berkeley) 01/21/94 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> 2912788Ssam 3012788Ssam /* 3137728Smckusick * Values associated with enabling and disabling accounting 3237728Smckusick */ 3337728Smckusick int acctsuspend = 2; /* stop accounting when < 2% free space left */ 3437728Smckusick int acctresume = 4; /* resume when free space risen to > 4% */ 3558149Smckusick int acctchkfreq = 15; /* frequency (in seconds) to check space */ 3637728Smckusick 3737728Smckusick /* 3812788Ssam * SHOULD REPLACE THIS WITH A DRIVER THAT CAN BE READ TO SIMPLIFY. 3912788Ssam */ 4037728Smckusick struct vnode *acctp; 4137728Smckusick struct vnode *savacctp; 4212788Ssam 4312788Ssam /* 4449671Smckusick * Enable or disable process accounting. 4549671Smckusick * 4649671Smckusick * If a non-null filename is given, that file is used to store accounting 4749671Smckusick * records on process exit. If a null filename is given process accounting 4849671Smckusick * is suspended. If accounting is enabled, the system checks the amount 4949671Smckusick * of freespace on the filesystem at timeval intervals. If the amount of 5049671Smckusick * freespace is below acctsuspend percent, accounting is suspended. If 5149671Smckusick * accounting has been suspended, and freespace rises above acctresume, 5249671Smckusick * accounting is resumed. 5312788Ssam */ 5463431Sbostic struct acct_args { 5554919Storek char *fname; 5654919Storek }; 5763431Sbostic acct(p, uap, retval) 5843379Smckusick struct proc *p; 5963431Sbostic struct acct_args *uap; 6043379Smckusick int *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 } 7445055Smckusick if (uap->fname == 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 } 8252307Smckusick NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->fname, 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 10137728Smckusick * should be turned on or off. 10212788Ssam */ 10354789Storek /* ARGSUSED */ 10454789Storek void 10554789Storek acctwatch(a) 10654789Storek void *a; 10712788Ssam { 10837728Smckusick struct statfs sb; 10912788Ssam 11012788Ssam if (savacctp) { 11148021Smckusick (void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0); 11237728Smckusick if (sb.f_bavail > acctresume * sb.f_blocks / 100) { 11312788Ssam acctp = savacctp; 11412788Ssam savacctp = NULL; 11537728Smckusick log(LOG_NOTICE, "Accounting resumed\n"); 11658149Smckusick } 11758149Smckusick } else { 11858149Smckusick if (acctp == NULL) 11937728Smckusick return; 12058149Smckusick (void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0); 12158149Smckusick if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) { 12258149Smckusick savacctp = acctp; 12358149Smckusick acctp = NULL; 12458149Smckusick log(LOG_NOTICE, "Accounting suspended\n"); 12512788Ssam } 12612788Ssam } 12758149Smckusick timeout(acctwatch, NULL, acctchkfreq * hz); 12837728Smckusick } 12937728Smckusick 13037728Smckusick /* 13149671Smckusick * This routine calculates an accounting record for a process and, 13249671Smckusick * if accounting is enabled, writes it to the accounting file. 13337728Smckusick */ 13463431Sbostic acct_process(p) 13543379Smckusick register struct proc *p; 13637728Smckusick { 13737728Smckusick register struct rusage *ru; 13837728Smckusick struct vnode *vp; 13940803Smarc struct timeval t, ut, st; 14040803Smarc int i, s; 14137728Smckusick struct acct acctbuf; 14237728Smckusick register struct acct *ap = &acctbuf; 14337728Smckusick 14437728Smckusick if ((vp = acctp) == NULL) 14543379Smckusick return (0); 14640803Smarc bcopy(p->p_comm, ap->ac_comm, sizeof(ap->ac_comm)); 14747545Skarels ru = &p->p_stats->p_ru; 14854789Storek calcru(p, &ut, &st, NULL); 14940803Smarc s = splclock(); 15016715Ssam t = time; 15140803Smarc splx(s); 15240803Smarc ap->ac_utime = compress(ut.tv_sec, ut.tv_usec); 15340803Smarc ap->ac_stime = compress(st.tv_sec, st.tv_usec); 15447545Skarels timevalsub(&t, &p->p_stats->p_start); 15516715Ssam ap->ac_etime = compress(t.tv_sec, t.tv_usec); 15647545Skarels ap->ac_btime = p->p_stats->p_start.tv_sec; 15747545Skarels ap->ac_uid = p->p_cred->p_ruid; 15847545Skarels ap->ac_gid = p->p_cred->p_rgid; 15940803Smarc t = st; 16040803Smarc timevaladd(&t, &ut); 16116719Skarels if (i = t.tv_sec * hz + t.tv_usec / tick) 16247545Skarels ap->ac_mem = (ru->ru_ixrss + ru->ru_idrss + ru->ru_isrss) / i; 16316715Ssam else 16416715Ssam ap->ac_mem = 0; 16526277Skarels ap->ac_io = compress(ru->ru_inblock + ru->ru_oublock, (long)0); 16664572Sbostic if (p->p_flag & P_CONTROLT && p->p_session->s_ttyp) 16740803Smarc ap->ac_tty = p->p_session->s_ttyp->t_dev; 16812788Ssam else 16912788Ssam ap->ac_tty = NODEV; 17047545Skarels ap->ac_flag = p->p_acflag; 17159377Smckusick LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 17248021Smckusick return (vn_rdwr(UIO_WRITE, vp, (caddr_t)ap, sizeof (acctbuf), (off_t)0, 17348021Smckusick UIO_SYSSPACE, IO_UNIT|IO_APPEND, p->p_ucred, (int *)0, 17448021Smckusick (struct proc *)0)); 17512788Ssam } 17612788Ssam 17712788Ssam /* 17812788Ssam * Produce a pseudo-floating point representation 17912788Ssam * with 3 bits base-8 exponent, 13 bits fraction. 18012788Ssam */ 18116715Ssam compress(t, ut) 18212788Ssam register long t; 18316715Ssam long ut; 18412788Ssam { 18512788Ssam register exp = 0, round = 0; 18612788Ssam 18717501Skarels t = t * AHZ; /* compiler will convert only this format to a shift */ 18816715Ssam if (ut) 18917501Skarels t += ut / (1000000 / AHZ); 19012788Ssam while (t >= 8192) { 19112788Ssam exp++; 19212788Ssam round = t&04; 19312788Ssam t >>= 3; 19412788Ssam } 19512788Ssam if (round) { 19612788Ssam t++; 19712788Ssam if (t >= 8192) { 19812788Ssam t >>= 3; 19912788Ssam exp++; 20012788Ssam } 20112788Ssam } 20212788Ssam return ((exp<<13) + t); 20312788Ssam } 204