149587Sbostic /*- 249587Sbostic * Copyright (c) 1982, 1986, 1989 The Regents of the University of California. 349587Sbostic * All rights reserved. 423365Smckusick * 549587Sbostic * %sccs.include.proprietary.c% 649587Sbostic * 7*59377Smckusick * @(#)kern_acct.c 7.28 (Berkeley) 04/27/93 823365Smckusick */ 912788Ssam 1056517Sbostic #include <sys/param.h> 1156517Sbostic #include <sys/systm.h> 1256517Sbostic #include <sys/namei.h> 1356517Sbostic #include <sys/resourcevar.h> 1456517Sbostic #include <sys/proc.h> 1556517Sbostic #include <sys/ioctl.h> 1656517Sbostic #include <sys/termios.h> 1756517Sbostic #include <sys/tty.h> 1856517Sbostic #include <sys/vnode.h> 1956517Sbostic #include <sys/mount.h> 2056517Sbostic #include <sys/kernel.h> 2156517Sbostic #include <sys/file.h> 2256517Sbostic #include <sys/acct.h> 2356517Sbostic #include <sys/syslog.h> 2412788Ssam 2512788Ssam /* 2637728Smckusick * Values associated with enabling and disabling accounting 2737728Smckusick */ 2837728Smckusick int acctsuspend = 2; /* stop accounting when < 2% free space left */ 2937728Smckusick int acctresume = 4; /* resume when free space risen to > 4% */ 3058149Smckusick int acctchkfreq = 15; /* frequency (in seconds) to check space */ 3137728Smckusick 3237728Smckusick /* 3312788Ssam * SHOULD REPLACE THIS WITH A DRIVER THAT CAN BE READ TO SIMPLIFY. 3412788Ssam */ 3537728Smckusick struct vnode *acctp; 3637728Smckusick struct vnode *savacctp; 3712788Ssam 3812788Ssam /* 3949671Smckusick * Enable or disable process accounting. 4049671Smckusick * 4149671Smckusick * If a non-null filename is given, that file is used to store accounting 4249671Smckusick * records on process exit. If a null filename is given process accounting 4349671Smckusick * is suspended. If accounting is enabled, the system checks the amount 4449671Smckusick * of freespace on the filesystem at timeval intervals. If the amount of 4549671Smckusick * freespace is below acctsuspend percent, accounting is suspended. If 4649671Smckusick * accounting has been suspended, and freespace rises above acctresume, 4749671Smckusick * accounting is resumed. 4812788Ssam */ 4943379Smckusick /* ARGSUSED */ 5054919Storek struct sysacct_args { 5154919Storek char *fname; 5254919Storek }; 5343379Smckusick sysacct(p, uap, retval) 5443379Smckusick struct proc *p; 5554919Storek struct sysacct_args *uap; 5643379Smckusick int *retval; 5712788Ssam { 5837728Smckusick register struct vnode *vp; 5954789Storek extern void acctwatch __P((void *)); 6037728Smckusick struct vnode *oacctp; 6143379Smckusick int error; 6247545Skarels struct nameidata nd; 6312788Ssam 6447545Skarels if (error = suser(p->p_ucred, &p->p_acflag)) 6544404Skarels return (error); 6637553Smckusick if (savacctp) { 6737553Smckusick acctp = savacctp; 6837553Smckusick savacctp = NULL; 6937553Smckusick } 7045055Smckusick if (uap->fname == NULL) { 7137728Smckusick if (vp = acctp) { 7237553Smckusick acctp = NULL; 7350107Smckusick error = vn_close(vp, FWRITE, p->p_ucred, p); 7454789Storek untimeout(acctwatch, NULL); 7512788Ssam } 7650107Smckusick return (error); 7712788Ssam } 7852307Smckusick NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->fname, p); 7952307Smckusick if (error = vn_open(&nd, FWRITE, 0644)) 8044404Skarels return (error); 8147545Skarels vp = nd.ni_vp; 8249916Smckusick VOP_UNLOCK(vp); 8337728Smckusick if (vp->v_type != VREG) { 8450107Smckusick (void) vn_close(vp, FWRITE, p->p_ucred, p); 8544404Skarels return (EACCES); 8637553Smckusick } 8737728Smckusick oacctp = acctp; 8837728Smckusick acctp = vp; 8937728Smckusick if (oacctp) 9050107Smckusick error = vn_close(oacctp, FWRITE, p->p_ucred, p); 9154789Storek acctwatch(NULL); 9250107Smckusick return (error); 9312788Ssam } 9412788Ssam 9512788Ssam /* 9637728Smckusick * Periodically check the file system to see if accounting 9737728Smckusick * should be turned on or off. 9812788Ssam */ 9954789Storek /* ARGSUSED */ 10054789Storek void 10154789Storek acctwatch(a) 10254789Storek void *a; 10312788Ssam { 10437728Smckusick struct statfs sb; 10512788Ssam 10612788Ssam if (savacctp) { 10748021Smckusick (void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0); 10837728Smckusick if (sb.f_bavail > acctresume * sb.f_blocks / 100) { 10912788Ssam acctp = savacctp; 11012788Ssam savacctp = NULL; 11137728Smckusick log(LOG_NOTICE, "Accounting resumed\n"); 11258149Smckusick } 11358149Smckusick } else { 11458149Smckusick if (acctp == NULL) 11537728Smckusick return; 11658149Smckusick (void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0); 11758149Smckusick if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) { 11858149Smckusick savacctp = acctp; 11958149Smckusick acctp = NULL; 12058149Smckusick log(LOG_NOTICE, "Accounting suspended\n"); 12112788Ssam } 12212788Ssam } 12358149Smckusick timeout(acctwatch, NULL, acctchkfreq * hz); 12437728Smckusick } 12537728Smckusick 12637728Smckusick /* 12749671Smckusick * This routine calculates an accounting record for a process and, 12849671Smckusick * if accounting is enabled, writes it to the accounting file. 12937728Smckusick */ 13043379Smckusick acct(p) 13143379Smckusick register struct proc *p; 13237728Smckusick { 13337728Smckusick register struct rusage *ru; 13437728Smckusick struct vnode *vp; 13540803Smarc struct timeval t, ut, st; 13640803Smarc int i, s; 13737728Smckusick struct acct acctbuf; 13837728Smckusick register struct acct *ap = &acctbuf; 13937728Smckusick 14037728Smckusick if ((vp = acctp) == NULL) 14143379Smckusick return (0); 14240803Smarc bcopy(p->p_comm, ap->ac_comm, sizeof(ap->ac_comm)); 14347545Skarels ru = &p->p_stats->p_ru; 14454789Storek calcru(p, &ut, &st, NULL); 14540803Smarc s = splclock(); 14616715Ssam t = time; 14740803Smarc splx(s); 14840803Smarc ap->ac_utime = compress(ut.tv_sec, ut.tv_usec); 14940803Smarc ap->ac_stime = compress(st.tv_sec, st.tv_usec); 15047545Skarels timevalsub(&t, &p->p_stats->p_start); 15116715Ssam ap->ac_etime = compress(t.tv_sec, t.tv_usec); 15247545Skarels ap->ac_btime = p->p_stats->p_start.tv_sec; 15347545Skarels ap->ac_uid = p->p_cred->p_ruid; 15447545Skarels ap->ac_gid = p->p_cred->p_rgid; 15540803Smarc t = st; 15640803Smarc timevaladd(&t, &ut); 15716719Skarels if (i = t.tv_sec * hz + t.tv_usec / tick) 15847545Skarels ap->ac_mem = (ru->ru_ixrss + ru->ru_idrss + ru->ru_isrss) / i; 15916715Ssam else 16016715Ssam ap->ac_mem = 0; 16126277Skarels ap->ac_io = compress(ru->ru_inblock + ru->ru_oublock, (long)0); 16242204Smarc if (p->p_flag&SCTTY && p->p_session->s_ttyp) 16340803Smarc ap->ac_tty = p->p_session->s_ttyp->t_dev; 16412788Ssam else 16512788Ssam ap->ac_tty = NODEV; 16647545Skarels ap->ac_flag = p->p_acflag; 167*59377Smckusick LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 16848021Smckusick return (vn_rdwr(UIO_WRITE, vp, (caddr_t)ap, sizeof (acctbuf), (off_t)0, 16948021Smckusick UIO_SYSSPACE, IO_UNIT|IO_APPEND, p->p_ucred, (int *)0, 17048021Smckusick (struct proc *)0)); 17112788Ssam } 17212788Ssam 17312788Ssam /* 17412788Ssam * Produce a pseudo-floating point representation 17512788Ssam * with 3 bits base-8 exponent, 13 bits fraction. 17612788Ssam */ 17716715Ssam compress(t, ut) 17812788Ssam register long t; 17916715Ssam long ut; 18012788Ssam { 18112788Ssam register exp = 0, round = 0; 18212788Ssam 18317501Skarels t = t * AHZ; /* compiler will convert only this format to a shift */ 18416715Ssam if (ut) 18517501Skarels t += ut / (1000000 / AHZ); 18612788Ssam while (t >= 8192) { 18712788Ssam exp++; 18812788Ssam round = t&04; 18912788Ssam t >>= 3; 19012788Ssam } 19112788Ssam if (round) { 19212788Ssam t++; 19312788Ssam if (t >= 8192) { 19412788Ssam t >>= 3; 19512788Ssam exp++; 19612788Ssam } 19712788Ssam } 19812788Ssam return ((exp<<13) + t); 19912788Ssam } 200