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*63431Sbostic * @(#)kern_acct.c 7.29 (Berkeley) 06/14/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 */ 49*63431Sbostic struct acct_args { 5054919Storek char *fname; 5154919Storek }; 52*63431Sbostic acct(p, uap, retval) 5343379Smckusick struct proc *p; 54*63431Sbostic struct acct_args *uap; 5543379Smckusick int *retval; 5612788Ssam { 5737728Smckusick register struct vnode *vp; 5854789Storek extern void acctwatch __P((void *)); 5937728Smckusick struct vnode *oacctp; 6043379Smckusick int error; 6147545Skarels struct nameidata nd; 6212788Ssam 6347545Skarels if (error = suser(p->p_ucred, &p->p_acflag)) 6444404Skarels return (error); 6537553Smckusick if (savacctp) { 6637553Smckusick acctp = savacctp; 6737553Smckusick savacctp = NULL; 6837553Smckusick } 6945055Smckusick if (uap->fname == NULL) { 7037728Smckusick if (vp = acctp) { 7137553Smckusick acctp = NULL; 7250107Smckusick error = vn_close(vp, FWRITE, p->p_ucred, p); 7354789Storek untimeout(acctwatch, NULL); 7412788Ssam } 7550107Smckusick return (error); 7612788Ssam } 7752307Smckusick NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->fname, p); 7852307Smckusick if (error = vn_open(&nd, FWRITE, 0644)) 7944404Skarels return (error); 8047545Skarels vp = nd.ni_vp; 8149916Smckusick VOP_UNLOCK(vp); 8237728Smckusick if (vp->v_type != VREG) { 8350107Smckusick (void) vn_close(vp, FWRITE, p->p_ucred, p); 8444404Skarels return (EACCES); 8537553Smckusick } 8637728Smckusick oacctp = acctp; 8737728Smckusick acctp = vp; 8837728Smckusick if (oacctp) 8950107Smckusick error = vn_close(oacctp, FWRITE, p->p_ucred, p); 9054789Storek acctwatch(NULL); 9150107Smckusick return (error); 9212788Ssam } 9312788Ssam 9412788Ssam /* 9537728Smckusick * Periodically check the file system to see if accounting 9637728Smckusick * should be turned on or off. 9712788Ssam */ 9854789Storek /* ARGSUSED */ 9954789Storek void 10054789Storek acctwatch(a) 10154789Storek void *a; 10212788Ssam { 10337728Smckusick struct statfs sb; 10412788Ssam 10512788Ssam if (savacctp) { 10648021Smckusick (void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0); 10737728Smckusick if (sb.f_bavail > acctresume * sb.f_blocks / 100) { 10812788Ssam acctp = savacctp; 10912788Ssam savacctp = NULL; 11037728Smckusick log(LOG_NOTICE, "Accounting resumed\n"); 11158149Smckusick } 11258149Smckusick } else { 11358149Smckusick if (acctp == NULL) 11437728Smckusick return; 11558149Smckusick (void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0); 11658149Smckusick if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) { 11758149Smckusick savacctp = acctp; 11858149Smckusick acctp = NULL; 11958149Smckusick log(LOG_NOTICE, "Accounting suspended\n"); 12012788Ssam } 12112788Ssam } 12258149Smckusick timeout(acctwatch, NULL, acctchkfreq * hz); 12337728Smckusick } 12437728Smckusick 12537728Smckusick /* 12649671Smckusick * This routine calculates an accounting record for a process and, 12749671Smckusick * if accounting is enabled, writes it to the accounting file. 12837728Smckusick */ 129*63431Sbostic acct_process(p) 13043379Smckusick register struct proc *p; 13137728Smckusick { 13237728Smckusick register struct rusage *ru; 13337728Smckusick struct vnode *vp; 13440803Smarc struct timeval t, ut, st; 13540803Smarc int i, s; 13637728Smckusick struct acct acctbuf; 13737728Smckusick register struct acct *ap = &acctbuf; 13837728Smckusick 13937728Smckusick if ((vp = acctp) == NULL) 14043379Smckusick return (0); 14140803Smarc bcopy(p->p_comm, ap->ac_comm, sizeof(ap->ac_comm)); 14247545Skarels ru = &p->p_stats->p_ru; 14354789Storek calcru(p, &ut, &st, NULL); 14440803Smarc s = splclock(); 14516715Ssam t = time; 14640803Smarc splx(s); 14740803Smarc ap->ac_utime = compress(ut.tv_sec, ut.tv_usec); 14840803Smarc ap->ac_stime = compress(st.tv_sec, st.tv_usec); 14947545Skarels timevalsub(&t, &p->p_stats->p_start); 15016715Ssam ap->ac_etime = compress(t.tv_sec, t.tv_usec); 15147545Skarels ap->ac_btime = p->p_stats->p_start.tv_sec; 15247545Skarels ap->ac_uid = p->p_cred->p_ruid; 15347545Skarels ap->ac_gid = p->p_cred->p_rgid; 15440803Smarc t = st; 15540803Smarc timevaladd(&t, &ut); 15616719Skarels if (i = t.tv_sec * hz + t.tv_usec / tick) 15747545Skarels ap->ac_mem = (ru->ru_ixrss + ru->ru_idrss + ru->ru_isrss) / i; 15816715Ssam else 15916715Ssam ap->ac_mem = 0; 16026277Skarels ap->ac_io = compress(ru->ru_inblock + ru->ru_oublock, (long)0); 16142204Smarc if (p->p_flag&SCTTY && p->p_session->s_ttyp) 16240803Smarc ap->ac_tty = p->p_session->s_ttyp->t_dev; 16312788Ssam else 16412788Ssam ap->ac_tty = NODEV; 16547545Skarels ap->ac_flag = p->p_acflag; 16659377Smckusick LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 16748021Smckusick return (vn_rdwr(UIO_WRITE, vp, (caddr_t)ap, sizeof (acctbuf), (off_t)0, 16848021Smckusick UIO_SYSSPACE, IO_UNIT|IO_APPEND, p->p_ucred, (int *)0, 16948021Smckusick (struct proc *)0)); 17012788Ssam } 17112788Ssam 17212788Ssam /* 17312788Ssam * Produce a pseudo-floating point representation 17412788Ssam * with 3 bits base-8 exponent, 13 bits fraction. 17512788Ssam */ 17616715Ssam compress(t, ut) 17712788Ssam register long t; 17816715Ssam long ut; 17912788Ssam { 18012788Ssam register exp = 0, round = 0; 18112788Ssam 18217501Skarels t = t * AHZ; /* compiler will convert only this format to a shift */ 18316715Ssam if (ut) 18417501Skarels t += ut / (1000000 / AHZ); 18512788Ssam while (t >= 8192) { 18612788Ssam exp++; 18712788Ssam round = t&04; 18812788Ssam t >>= 3; 18912788Ssam } 19012788Ssam if (round) { 19112788Ssam t++; 19212788Ssam if (t >= 8192) { 19312788Ssam t >>= 3; 19412788Ssam exp++; 19512788Ssam } 19612788Ssam } 19712788Ssam return ((exp<<13) + t); 19812788Ssam } 199