kern_acct.c revision 76117
1139804Simp/*- 2139013Sdavidxu * Copyright (c) 1994 Christopher G. Demetriou 3112904Sjeff * Copyright (c) 1982, 1986, 1989, 1993 4112904Sjeff * The Regents of the University of California. All rights reserved. 5112904Sjeff * (c) UNIX System Laboratories, Inc. 6112904Sjeff * All or some portions of this file are derived from material licensed 7112904Sjeff * to the University of California by American Telephone and Telegraph 8112904Sjeff * Co. or Unix System Laboratories, Inc. and are reproduced herein with 9112904Sjeff * the permission of UNIX System Laboratories, Inc. 10112904Sjeff * 11112904Sjeff * Redistribution and use in source and binary forms, with or without 12112904Sjeff * modification, are permitted provided that the following conditions 13112904Sjeff * are met: 14112904Sjeff * 1. Redistributions of source code must retain the above copyright 15112904Sjeff * notice, this list of conditions and the following disclaimer. 16112904Sjeff * 2. Redistributions in binary form must reproduce the above copyright 17112904Sjeff * notice, this list of conditions and the following disclaimer in the 18112904Sjeff * documentation and/or other materials provided with the distribution. 19112904Sjeff * 3. All advertising materials mentioning features or use of this software 20112904Sjeff * must display the following acknowledgement: 21112904Sjeff * This product includes software developed by the University of 22112904Sjeff * California, Berkeley and its contributors. 23112904Sjeff * 4. Neither the name of the University nor the names of its contributors 24112904Sjeff * may be used to endorse or promote products derived from this software 25112904Sjeff * without specific prior written permission. 26112904Sjeff * 27112904Sjeff * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28116182Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29116182Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30116182Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31162536Sdavidxu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32112904Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33112904Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34131431Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35112904Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36115765Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37112904Sjeff * SUCH DAMAGE. 38164033Srwatson * 39112904Sjeff * @(#)kern_acct.c 8.1 (Berkeley) 6/14/93 40161678Sdavidxu * $FreeBSD: head/sys/kern/kern_acct.c 76117 2001-04-29 02:45:39Z grog $ 41165369Sdavidxu */ 42161678Sdavidxu 43112904Sjeff#include <sys/param.h> 44112904Sjeff#include <sys/systm.h> 45112904Sjeff#include <sys/sysproto.h> 46216641Sdavidxu#include <sys/proc.h> 47139013Sdavidxu#include <sys/mount.h> 48112904Sjeff#include <sys/vnode.h> 49112904Sjeff#include <sys/fcntl.h> 50139013Sdavidxu#include <sys/syslog.h> 51139013Sdavidxu#include <sys/kernel.h> 52139013Sdavidxu#include <sys/sysent.h> 53139013Sdavidxu#include <sys/sysctl.h> 54139013Sdavidxu#include <sys/namei.h> 55139013Sdavidxu#include <sys/acct.h> 56165369Sdavidxu#include <sys/resourcevar.h> 57165369Sdavidxu#include <sys/tty.h> 58205014Snwhitehorn 59162536Sdavidxu 60162536Sdavidxu/* 61162536Sdavidxu * The routines implemented in this file are described in: 62179970Sdavidxu * Leffler, et al.: The Design and Implementation of the 4.3BSD 63179970Sdavidxu * UNIX Operating System (Addison Welley, 1989) 64179970Sdavidxu * on pages 62-63. 65161678Sdavidxu * 66161678Sdavidxu * Arguably, to simplify accounting operations, this mechanism should 67161678Sdavidxu * be replaced by one in which an accounting log file (similar to /dev/klog) 68161678Sdavidxu * is read by a user process, etc. However, that has its own problems. 69161678Sdavidxu */ 70161678Sdavidxu 71161678Sdavidxu/* 72161678Sdavidxu * Internal accounting functions. 73161678Sdavidxu * The former's operation is described in Leffler, et al., and the latter 74161678Sdavidxu * was provided by UCB with the 4.4BSD-Lite release 75161678Sdavidxu */ 76161678Sdavidxustatic comp_t encode_comp_t __P((u_long, u_long)); 77161678Sdavidxustatic void acctwatch __P((void *)); 78161678Sdavidxu 79161678Sdavidxu/* 80161678Sdavidxu * Accounting callout used for periodic scheduling of acctwatch. 81161678Sdavidxu */ 82161678Sdavidxustatic struct callout acctwatch_callout; 83161678Sdavidxu 84161678Sdavidxu/* 85161678Sdavidxu * Accounting vnode pointer, and saved vnode pointer. 86161678Sdavidxu */ 87115765Sjeffstatic struct vnode *acctp; 88161678Sdavidxustatic struct vnode *savacctp; 89161678Sdavidxu 90161678Sdavidxu/* 91161678Sdavidxu * Values associated with enabling and disabling accounting 92161678Sdavidxu */ 93161678Sdavidxustatic int acctsuspend = 2; /* stop accounting when < 2% free space left */ 94161678SdavidxuSYSCTL_INT(_kern, OID_AUTO, acct_suspend, CTLFLAG_RW, 95161678Sdavidxu &acctsuspend, 0, "percentage of free disk space below which accounting stops"); 96161678Sdavidxu 97161678Sdavidxustatic int acctresume = 4; /* resume when free space risen to > 4% */ 98161678SdavidxuSYSCTL_INT(_kern, OID_AUTO, acct_resume, CTLFLAG_RW, 99161678Sdavidxu &acctresume, 0, "percentage of free disk space above which accounting resumes"); 100161678Sdavidxu 101161678Sdavidxustatic int acctchkfreq = 15; /* frequency (in seconds) to check space */ 102161678SdavidxuSYSCTL_INT(_kern, OID_AUTO, acct_chkfreq, CTLFLAG_RW, 103170300Sjeff &acctchkfreq, 0, "frequency for checking the free space"); 104170300Sjeff 105161678Sdavidxu/* 106161678Sdavidxu * Accounting system call. Written based on the specification and 107161678Sdavidxu * previous implementation done by Mark Tinguely. 108161678Sdavidxu */ 109161678Sdavidxuint 110161678Sdavidxuacct(a1, uap) 111161678Sdavidxu struct proc *a1; 112161678Sdavidxu struct acct_args /* { 113161678Sdavidxu syscallarg(char *) path; 114161742Sdavidxu } */ *uap; 115161678Sdavidxu{ 116201991Sdavidxu struct proc *p = curproc; /* XXX */ 117201991Sdavidxu struct nameidata nd; 118201991Sdavidxu int error, flags; 119201991Sdavidxu 120201991Sdavidxu /* Make sure that the caller is root. */ 121201991Sdavidxu error = suser(p); 122115765Sjeff if (error) 123115765Sjeff return (error); 124161678Sdavidxu 125161678Sdavidxu /* 126201991Sdavidxu * If accounting is to be started to a file, open that file for 127201991Sdavidxu * writing and make sure it's a 'normal'. 128201991Sdavidxu */ 129201991Sdavidxu if (SCARG(uap, path) != NULL) { 130201991Sdavidxu NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), 131201991Sdavidxu p); 132201991Sdavidxu flags = FWRITE; 133201991Sdavidxu error = vn_open(&nd, &flags, 0); 134201991Sdavidxu if (error) 135201991Sdavidxu return (error); 136161678Sdavidxu NDFREE(&nd, NDF_ONLY_PNBUF); 137138224Sdavidxu VOP_UNLOCK(nd.ni_vp, 0, p); 138161678Sdavidxu if (nd.ni_vp->v_type != VREG) { 139161678Sdavidxu vn_close(nd.ni_vp, FWRITE, p->p_ucred, p); 140161678Sdavidxu return (EACCES); 141161678Sdavidxu } 142201991Sdavidxu } 143177848Sdavidxu 144177848Sdavidxu /* 145161678Sdavidxu * If accounting was previously enabled, kill the old space-watcher, 146201991Sdavidxu * close the file, and (if no new file was specified, leave). 147201991Sdavidxu */ 148161678Sdavidxu if (acctp != NULLVP || savacctp != NULLVP) { 149161678Sdavidxu callout_stop(&acctwatch_callout); 150161678Sdavidxu error = vn_close((acctp != NULLVP ? acctp : savacctp), FWRITE, 151161678Sdavidxu p->p_ucred, p); 152158377Sdavidxu acctp = savacctp = NULLVP; 153161678Sdavidxu } 154161678Sdavidxu if (SCARG(uap, path) == NULL) 155161678Sdavidxu return (error); 156201991Sdavidxu 157138224Sdavidxu /* 158115765Sjeff * Save the new accounting file vnode, and schedule the new 159161678Sdavidxu * free space watcher. 160189756Sdavidxu */ 161161678Sdavidxu acctp = nd.ni_vp; 162161678Sdavidxu callout_init(&acctwatch_callout, 0); 163161678Sdavidxu acctwatch(NULL); 164161678Sdavidxu return (error); 165161678Sdavidxu} 166161678Sdavidxu 167161678Sdavidxu/* 168161678Sdavidxu * Write out process accounting information, on process exit. 169161678Sdavidxu * Data to be written out is specified in Leffler, et al. 170161678Sdavidxu * and are enumerated below. (They're also noted in the system 171163709Sjb * "acct.h" header file.) 172163709Sjb */ 173163709Sjb 174161678Sdavidxuint 175138224Sdavidxuacct_process(p) 176216678Sdavidxu struct proc *p; 177216678Sdavidxu{ 178115765Sjeff struct acct acct; 179161678Sdavidxu struct rusage *r; 180161678Sdavidxu struct timeval ut, st, tmp; 181161678Sdavidxu int t; 182177848Sdavidxu struct vnode *vp; 183177848Sdavidxu 184161678Sdavidxu /* If accounting isn't enabled, don't bother */ 185179421Sdavidxu vp = acctp; 186138224Sdavidxu if (vp == NULLVP) 187161678Sdavidxu return (0); 188115310Sjeff 189227309Sed /* 190161678Sdavidxu * Get process accounting information. 191161678Sdavidxu */ 192161678Sdavidxu 193161678Sdavidxu /* (1) The name of the command that ran */ 194161678Sdavidxu bcopy(p->p_comm, acct.ac_comm, sizeof acct.ac_comm); 195161678Sdavidxu 196139013Sdavidxu /* (2) The amount of user and system time that was used */ 197139013Sdavidxu mtx_lock_spin(&sched_lock); 198139257Sdavidxu calcru(p, &ut, &st, NULL); 199139257Sdavidxu mtx_unlock_spin(&sched_lock); 200177848Sdavidxu acct.ac_utime = encode_comp_t(ut.tv_sec, ut.tv_usec); 201177848Sdavidxu acct.ac_stime = encode_comp_t(st.tv_sec, st.tv_usec); 202161678Sdavidxu 203139257Sdavidxu /* (3) The elapsed time the commmand ran (and its starting time) */ 204163697Sdavidxu acct.ac_btime = p->p_stats->p_start.tv_sec; 205161678Sdavidxu microtime(&tmp); 206161678Sdavidxu timevalsub(&tmp, &p->p_stats->p_start); 207161678Sdavidxu acct.ac_etime = encode_comp_t(tmp.tv_sec, tmp.tv_usec); 208161678Sdavidxu 209161678Sdavidxu /* (4) The average amount of memory used */ 210161678Sdavidxu r = &p->p_stats->p_ru; 211115310Sjeff tmp = ut; 212177848Sdavidxu timevaladd(&tmp, &st); 213177848Sdavidxu t = tmp.tv_sec * hz + tmp.tv_usec / tick; 214177848Sdavidxu if (t) 215177848Sdavidxu acct.ac_mem = (r->ru_ixrss + r->ru_idrss + r->ru_isrss) / t; 216170300Sjeff else 217170300Sjeff acct.ac_mem = 0; 218161678Sdavidxu 219161678Sdavidxu /* (5) The number of disk I/O operations done */ 220161678Sdavidxu acct.ac_io = encode_comp_t(r->ru_inblock + r->ru_oublock, 0); 221179421Sdavidxu 222138224Sdavidxu /* (6) The UID and GID of the process */ 223161678Sdavidxu acct.ac_uid = p->p_cred->p_ruid; 224161678Sdavidxu acct.ac_gid = p->p_cred->p_rgid; 225179421Sdavidxu 226179421Sdavidxu /* (7) The terminal from which the process was started */ 227179421Sdavidxu if ((p->p_flag & P_CONTROLT) && p->p_pgrp->pg_session->s_ttyp) 228179421Sdavidxu acct.ac_tty = dev2udev(p->p_pgrp->pg_session->s_ttyp->t_dev); 229201991Sdavidxu else 230201991Sdavidxu acct.ac_tty = NOUDEV; 231201991Sdavidxu 232179421Sdavidxu /* (8) The boolean flags that tell how the process terminated, etc. */ 233179421Sdavidxu acct.ac_flag = p->p_acflag; 234179421Sdavidxu 235179421Sdavidxu /* 236161678Sdavidxu * Eliminate any file size rlimit. 237170300Sjeff */ 238161678Sdavidxu if (p->p_limit->p_refcnt > 1 && 239161678Sdavidxu (p->p_limit->p_lflags & PL_SHAREMOD) == 0) { 240161678Sdavidxu p->p_limit->p_refcnt--; 241161678Sdavidxu p->p_limit = limcopy(p->p_limit); 242143149Sdavidxu } 243143149Sdavidxu p->p_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; 244143149Sdavidxu 245161678Sdavidxu /* 246161678Sdavidxu * Write the accounting information to the file. 247161678Sdavidxu */ 248201991Sdavidxu VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); 249201991Sdavidxu return (vn_rdwr(UIO_WRITE, vp, (caddr_t)&acct, sizeof (acct), 250161678Sdavidxu (off_t)0, UIO_SYSSPACE, IO_APPEND|IO_UNIT, p->p_ucred, 251161678Sdavidxu (int *)0, p)); 252161678Sdavidxu} 253143149Sdavidxu 254143149Sdavidxu/* 255143149Sdavidxu * Encode_comp_t converts from ticks in seconds and microseconds 256143149Sdavidxu * to ticks in 1/AHZ seconds. The encoding is described in 257143149Sdavidxu * Leffler, et al., on page 63. 258201991Sdavidxu */ 259201991Sdavidxu 260143149Sdavidxu#define MANTSIZE 13 /* 13 bit mantissa. */ 261143149Sdavidxu#define EXPSIZE 3 /* Base 8 (3 bit) exponent. */ 262143149Sdavidxu#define MAXFRACT ((1 << MANTSIZE) - 1) /* Maximum fractional value. */ 263161678Sdavidxu 264139013Sdavidxustatic comp_t 265138224Sdavidxuencode_comp_t(s, us) 266161678Sdavidxu u_long s, us; 267161678Sdavidxu{ 268138224Sdavidxu int exp, rnd; 269138224Sdavidxu 270161678Sdavidxu exp = 0; 271161678Sdavidxu rnd = 0; 272139013Sdavidxu s *= AHZ; 273201886Sdavidxu s += us / (1000000 / AHZ); /* Maximize precision. */ 274179421Sdavidxu 275179421Sdavidxu while (s > MAXFRACT) { 276139013Sdavidxu rnd = s & (1 << (EXPSIZE - 1)); /* Round up? */ 277139013Sdavidxu s >>= EXPSIZE; /* Base 8 exponent == 3 bit shift. */ 278161678Sdavidxu exp++; 279177848Sdavidxu } 280161678Sdavidxu 281138224Sdavidxu /* If we need to round up, do it (and handle overflow correctly). */ 282177848Sdavidxu if (rnd && (++s > MAXFRACT)) { 283139257Sdavidxu s >>= EXPSIZE; 284161678Sdavidxu exp++; 285139257Sdavidxu } 286161678Sdavidxu 287177848Sdavidxu /* Clean it up and polish it off. */ 288139257Sdavidxu exp <<= MANTSIZE; /* Shift the exponent into place */ 289139257Sdavidxu exp += s; /* and add on the mantissa. */ 290161678Sdavidxu return (exp); 291177848Sdavidxu} 292161678Sdavidxu 293139257Sdavidxu/* 294177848Sdavidxu * Periodically check the file system to see if accounting 295139257Sdavidxu * should be turned on or off. Beware the case where the vnode 296161678Sdavidxu * has been vgone()'d out from underneath us, e.g. when the file 297139257Sdavidxu * system containing the accounting file has been forcibly unmounted. 298161678Sdavidxu */ 299177848Sdavidxu/* ARGSUSED */ 300139257Sdavidxustatic void 301139257Sdavidxuacctwatch(a) 302161678Sdavidxu void *a; 303177848Sdavidxu{ 304177848Sdavidxu struct statfs sb; 305161678Sdavidxu 306139257Sdavidxu if (savacctp != NULLVP) { 307177848Sdavidxu if (savacctp->v_type == VBAD) { 308138224Sdavidxu (void) vn_close(savacctp, FWRITE, NOCRED, NULL); 309161678Sdavidxu savacctp = NULLVP; 310161678Sdavidxu return; 311161678Sdavidxu } 312177848Sdavidxu (void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0); 313177848Sdavidxu if (sb.f_bavail > acctresume * sb.f_blocks / 100) { 314177880Sdavidxu acctp = savacctp; 315177880Sdavidxu savacctp = NULLVP; 316177880Sdavidxu log(LOG_NOTICE, "Accounting resumed\n"); 317177880Sdavidxu } 318177880Sdavidxu } else { 319177880Sdavidxu if (acctp == NULLVP) 320177880Sdavidxu return; 321177880Sdavidxu if (acctp->v_type == VBAD) { 322177880Sdavidxu (void) vn_close(acctp, FWRITE, NOCRED, NULL); 323177848Sdavidxu acctp = NULLVP; 324177880Sdavidxu return; 325177880Sdavidxu } 326177848Sdavidxu (void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0); 327177848Sdavidxu if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) { 328177848Sdavidxu savacctp = acctp; 329177848Sdavidxu acctp = NULLVP; 330177848Sdavidxu log(LOG_NOTICE, "Accounting suspended\n"); 331177848Sdavidxu } 332138224Sdavidxu } 333138224Sdavidxu callout_reset(&acctwatch_callout, acctchkfreq * hz, acctwatch, NULL); 334161678Sdavidxu} 335177848Sdavidxu