/* * Copyright (c) 1982, 1986, 1989 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * @(#)kern_acct.c 7.5 (Berkeley) 05/09/89 */ #include "param.h" #include "systm.h" #include "user.h" #include "vnode.h" #include "mount.h" #include "kernel.h" #include "acct.h" #include "uio.h" #include "syslog.h" /* * Values associated with enabling and disabling accounting */ int acctsuspend = 2; /* stop accounting when < 2% free space left */ int acctresume = 4; /* resume when free space risen to > 4% */ struct timeval chk = { 15, 0 };/* frequency to check space for accounting */ /* * SHOULD REPLACE THIS WITH A DRIVER THAT CAN BE READ TO SIMPLIFY. */ struct vnode *acctp; struct vnode *savacctp; /* * Perform process accounting functions. */ sysacct() { register struct vnode *vp; register struct a { char *fname; } *uap = (struct a *)u.u_ap; register struct nameidata *ndp = &u.u_nd; extern int acctwatch(); struct vnode *oacctp; if (u.u_error = suser(u.u_cred, &u.u_acflag)) return; if (savacctp) { acctp = savacctp; savacctp = NULL; } if (uap->fname==NULL) { if (vp = acctp) { acctp = NULL; vrele(vp); untimeout(acctwatch, (caddr_t)&chk); } return; } ndp->ni_nameiop = LOOKUP | FOLLOW; ndp->ni_segflg = UIO_USERSPACE; ndp->ni_dirp = uap->fname; if (u.u_error = namei(ndp)) return; vp = ndp->ni_vp; if (vp->v_type != VREG) { u.u_error = EACCES; vrele(vp); return; } if (vp->v_mount->m_flag & M_RDONLY) { u.u_error = EROFS; vrele(vp); return; } oacctp = acctp; acctp = vp; if (oacctp) vrele(oacctp); acctwatch(&chk); } /* * Periodically check the file system to see if accounting * should be turned on or off. */ acctwatch(resettime) struct timeval *resettime; { struct statfs sb; if (savacctp) { (void)VFS_STATFS(savacctp->v_mount, &sb); if (sb.f_bavail > acctresume * sb.f_blocks / 100) { acctp = savacctp; savacctp = NULL; log(LOG_NOTICE, "Accounting resumed\n"); return; } } if (acctp == NULL) return; (void)VFS_STATFS(acctp->v_mount, &sb); if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) { savacctp = acctp; acctp = NULL; log(LOG_NOTICE, "Accounting suspended\n"); } timeout(acctwatch, (caddr_t)resettime, hzto(resettime)); } /* * On exit, write a record on the accounting file. */ acct() { register struct rusage *ru; struct vnode *vp; struct timeval t; int i; struct acct acctbuf; register struct acct *ap = &acctbuf; if ((vp = acctp) == NULL) return; bcopy(u.u_comm, ap->ac_comm, sizeof(ap->ac_comm)); 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; u.u_error = vn_rdwr(UIO_WRITE, vp, (caddr_t)ap, sizeof (acctbuf), (off_t)0, UIO_SYSSPACE, IO_UNIT|IO_APPEND, u.u_cred, (int *)0); } /* * 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); }