kern_acct.c revision 114434
1116742Ssam/*- 2116904Ssam * Copyright (c) 1994 Christopher G. Demetriou 3186904Ssam * Copyright (c) 1982, 1986, 1989, 1993 4116742Ssam * The Regents of the University of California. All rights reserved. 5116742Ssam * (c) UNIX System Laboratories, Inc. 6116742Ssam * All or some portions of this file are derived from material licensed 7116742Ssam * to the University of California by American Telephone and Telegraph 8116742Ssam * Co. or Unix System Laboratories, Inc. and are reproduced herein with 9116742Ssam * the permission of UNIX System Laboratories, Inc. 10116742Ssam * 11116742Ssam * Redistribution and use in source and binary forms, with or without 12116742Ssam * modification, are permitted provided that the following conditions 13116742Ssam * are met: 14116742Ssam * 1. Redistributions of source code must retain the above copyright 15116904Ssam * notice, this list of conditions and the following disclaimer. 16116904Ssam * 2. Redistributions in binary form must reproduce the above copyright 17116904Ssam * notice, this list of conditions and the following disclaimer in the 18116904Ssam * documentation and/or other materials provided with the distribution. 19116904Ssam * 3. All advertising materials mentioning features or use of this software 20116904Ssam * must display the following acknowledgement: 21116904Ssam * This product includes software developed by the University of 22116904Ssam * California, Berkeley and its contributors. 23116904Ssam * 4. Neither the name of the University nor the names of its contributors 24116904Ssam * may be used to endorse or promote products derived from this software 25116904Ssam * without specific prior written permission. 26116742Ssam * 27116742Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28116742Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29116742Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30116742Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31116742Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32116742Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33116742Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34138568Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35201793Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36138568Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37201793Strasz * SUCH DAMAGE. 38138568Ssam * 39201793Strasz * @(#)kern_acct.c 8.1 (Berkeley) 6/14/93 40138568Ssam * $FreeBSD: head/sys/kern/kern_acct.c 114434 2003-05-01 16:59:23Z des $ 41138568Ssam */ 42138568Ssam 43138568Ssam#include "opt_mac.h" 44138568Ssam 45138568Ssam#include <sys/param.h> 46116742Ssam#include <sys/systm.h> 47195379Ssam#include <sys/lock.h> 48116742Ssam#include <sys/mutex.h> 49178354Ssam#include <sys/sysproto.h> 50121180Ssam#include <sys/proc.h> 51190532Ssam#include <sys/mac.h> 52184288Ssam#include <sys/mount.h> 53116742Ssam#include <sys/vnode.h> 54116742Ssam#include <sys/fcntl.h> 55192468Ssam#include <sys/syslog.h> 56170530Ssam#include <sys/kernel.h> 57116742Ssam#include <sys/sysent.h> 58138568Ssam#include <sys/sysctl.h> 59138568Ssam#include <sys/namei.h> 60116742Ssam#include <sys/acct.h> 61138568Ssam#include <sys/resourcevar.h> 62140754Ssam#include <sys/tty.h> 63116742Ssam 64153349Ssam/* 65155688Ssam * The routines implemented in this file are described in: 66153349Ssam * Leffler, et al.: The Design and Implementation of the 4.3BSD 67170530Ssam * UNIX Operating System (Addison Welley, 1989) 68170530Ssam * on pages 62-63. 69170530Ssam * 70170530Ssam * Arguably, to simplify accounting operations, this mechanism should 71170530Ssam * be replaced by one in which an accounting log file (similar to /dev/klog) 72170530Ssam * is read by a user process, etc. However, that has its own problems. 73170530Ssam */ 74170530Ssam 75170530Ssam/* 76138568Ssam * Internal accounting functions. 77138568Ssam * The former's operation is described in Leffler, et al., and the latter 78116742Ssam * was provided by UCB with the 4.4BSD-Lite release 79178354Ssam */ 80178354Ssamstatic comp_t encode_comp_t(u_long, u_long); 81148290Ssamstatic void acctwatch(void *); 82148291Ssam 83148291Ssam/* 84148291Ssam * Accounting callout used for periodic scheduling of acctwatch. 85167242Ssam */ 86167242Ssamstatic struct callout acctwatch_callout; 87167242Ssam 88140754Ssam/* 89178354Ssam * Accounting vnode pointer, saved vnode pointer, and flags for each. 90178354Ssam */ 91178354Ssamstatic struct vnode *acctp; 92178354Ssamstatic struct ucred *acctcred; 93178354Ssamstatic int acctflags; 94178354Ssamstatic struct vnode *savacctp; 95178354Ssamstatic struct ucred *savacctcred; 96178354Ssamstatic int savacctflags; 97116742Ssam 98178354Ssamstatic struct mtx acct_mtx; 99178354SsamMTX_SYSINIT(acct, &acct_mtx, "accounting", MTX_DEF); 100178354Ssam 101178354Ssam/* 102178354Ssam * Values associated with enabling and disabling accounting 103178354Ssam */ 104178354Ssamstatic int acctsuspend = 2; /* stop accounting when < 2% free space left */ 105178354SsamSYSCTL_INT(_kern, OID_AUTO, acct_suspend, CTLFLAG_RW, 106178354Ssam &acctsuspend, 0, "percentage of free disk space below which accounting stops"); 107178354Ssam 108178354Ssamstatic int acctresume = 4; /* resume when free space risen to > 4% */ 109178354SsamSYSCTL_INT(_kern, OID_AUTO, acct_resume, CTLFLAG_RW, 110178354Ssam &acctresume, 0, "percentage of free disk space above which accounting resumes"); 111186904Ssam 112190579Ssamstatic int acctchkfreq = 15; /* frequency (in seconds) to check space */ 113191554SsamSYSCTL_INT(_kern, OID_AUTO, acct_chkfreq, CTLFLAG_RW, 114191554Ssam &acctchkfreq, 0, "frequency for checking the free space"); 115191753Ssam 116195377Ssam/* 117186904Ssam * Accounting system call. Written based on the specification and 118138568Ssam * previous implementation done by Mark Tinguely. 119138568Ssam * 120170530Ssam * MPSAFE 121178354Ssam */ 122170530Ssamint 123170530Ssamacct(td, uap) 124170530Ssam struct thread *td; 125170530Ssam struct acct_args /* { 126178354Ssam char *path; 127191746Sthompsa } */ *uap; 128178354Ssam{ 129191746Sthompsa struct nameidata nd; 130191746Sthompsa int error, flags; 131191746Sthompsa 132191746Sthompsa /* Make sure that the caller is root. */ 133127648Ssam error = suser(td); 134170530Ssam if (error) 135170530Ssam return (error); 136193655Ssam 137173273Ssam mtx_lock(&Giant); 138170530Ssam /* 139173273Ssam * If accounting is to be started to a file, open that file for 140205513Srpaulo * appending and make sure it's a 'normal'. 141178354Ssam */ 142170530Ssam if (uap->path != NULL) { 143178354Ssam NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); 144178354Ssam flags = FWRITE | O_APPEND; 145178354Ssam error = vn_open(&nd, &flags, 0); 146178354Ssam if (error) 147170530Ssam goto done2; 148170530Ssam NDFREE(&nd, NDF_ONLY_PNBUF); 149170530Ssam#ifdef MAC 150170530Ssam error = mac_check_system_acct(td->td_ucred, nd.ni_vp); 151178354Ssam if (error) { 152170530Ssam vn_close(nd.ni_vp, flags, td->td_ucred, td); 153170530Ssam goto done2; 154170530Ssam } 155170530Ssam#endif 156170530Ssam VOP_UNLOCK(nd.ni_vp, 0, td); 157170530Ssam if (nd.ni_vp->v_type != VREG) { 158170530Ssam vn_close(nd.ni_vp, flags, td->td_ucred, td); 159170530Ssam error = EACCES; 160170530Ssam goto done2; 161170530Ssam } 162170530Ssam#ifdef MAC 163170530Ssam } else { 164170530Ssam error = mac_check_system_acct(td->td_ucred, NULL); 165170530Ssam if (error) 166170530Ssam goto done2; 167170530Ssam#endif 168170530Ssam } 169170530Ssam 170170530Ssam /* 171170530Ssam * If accounting was previously enabled, kill the old space-watcher, 172170530Ssam * close the file, and (if no new file was specified, leave). 173170530Ssam */ 174186107Ssam 175170530Ssam /* 176170530Ssam * XXX arr: Should not hold lock over vnode operation. 177170530Ssam */ 178170530Ssam 179190532Ssam mtx_lock(&acct_mtx); 180170530Ssam if (acctp != NULLVP || savacctp != NULLVP) { 181170530Ssam callout_stop(&acctwatch_callout); 182178354Ssam error = vn_close((acctp != NULLVP ? acctp : savacctp), 183178354Ssam (acctp != NULLVP ? acctflags : savacctflags), 184178354Ssam (acctcred != NOCRED ? acctcred : savacctcred), td); 185170530Ssam acctp = savacctp = NULLVP; 186178354Ssam crfree(acctcred != NOCRED ? acctcred : savacctcred); 187178354Ssam acctcred = savacctcred = NOCRED; 188193439Ssam } 189193439Ssam if (uap->path == NULL) { 190178354Ssam mtx_unlock(&acct_mtx); 191178354Ssam goto done2; 192170530Ssam } 193170530Ssam 194170530Ssam /* 195170530Ssam * Save the new accounting file vnode, and schedule the new 196178354Ssam * free space watcher. 197178354Ssam */ 198170530Ssam acctp = nd.ni_vp; 199195379Ssam acctcred = crhold(td->td_ucred); 200195379Ssam acctflags = flags; 201170530Ssam callout_init(&acctwatch_callout, 0); 202178354Ssam mtx_unlock(&acct_mtx); 203170530Ssam acctwatch(NULL); 204170530Ssamdone2: 205178354Ssam mtx_unlock(&Giant); 206170530Ssam return (error); 207170530Ssam} 208170530Ssam 209170530Ssam/* 210173273Ssam * Write out process accounting information, on process exit. 211173273Ssam * Data to be written out is specified in Leffler, et al. 212172211Ssam * and are enumerated below. (They're also noted in the system 213173273Ssam * "acct.h" header file.) 214172211Ssam */ 215173273Ssam 216207326Srpauloint 217207326Srpauloacct_process(td) 218170530Ssam struct thread *td; 219191753Ssam{ 220191753Ssam struct proc *p = td->td_proc; 221190579Ssam struct acct acct; 222192468Ssam struct rusage *r; 223192468Ssam struct timeval ut, st, tmp; 224192468Ssam int t, ret; 225192468Ssam struct vnode *vp; 226192468Ssam struct ucred *uc; 227193292Ssam 228192468Ssam mtx_lock(&acct_mtx); 229178354Ssam 230178354Ssam /* If accounting isn't enabled, don't bother */ 231178354Ssam vp = acctp; 232178354Ssam if (vp == NULLVP) { 233178354Ssam mtx_unlock(&acct_mtx); 234178354Ssam return (0); 235178354Ssam } 236178354Ssam 237178354Ssam /* 238178354Ssam * Get process accounting information. 239178354Ssam */ 240187800Ssam 241178354Ssam PROC_LOCK(p); 242178354Ssam /* (1) The name of the command that ran */ 243178354Ssam bcopy(p->p_comm, acct.ac_comm, sizeof acct.ac_comm); 244178354Ssam 245170530Ssam /* (2) The amount of user and system time that was used */ 246178354Ssam mtx_lock_spin(&sched_lock); 247178354Ssam calcru(p, &ut, &st, NULL); 248170530Ssam mtx_unlock_spin(&sched_lock); 249170530Ssam acct.ac_utime = encode_comp_t(ut.tv_sec, ut.tv_usec); 250170530Ssam acct.ac_stime = encode_comp_t(st.tv_sec, st.tv_usec); 251170530Ssam 252170530Ssam /* (3) The elapsed time the command ran (and its starting time) */ 253170530Ssam tmp = boottime; 254178354Ssam timevaladd(&tmp, &p->p_stats->p_start); 255178354Ssam acct.ac_btime = tmp.tv_sec; 256178354Ssam microuptime(&tmp); 257178354Ssam timevalsub(&tmp, &p->p_stats->p_start); 258170530Ssam acct.ac_etime = encode_comp_t(tmp.tv_sec, tmp.tv_usec); 259170530Ssam 260186904Ssam /* (4) The average amount of memory used */ 261186904Ssam r = &p->p_stats->p_ru; 262189980Ssam tmp = ut; 263170530Ssam timevaladd(&tmp, &st); 264179643Ssam t = tmp.tv_sec * hz + tmp.tv_usec / tick; 265179643Ssam if (t) 266170530Ssam acct.ac_mem = (r->ru_ixrss + r->ru_idrss + r->ru_isrss) / t; 267170530Ssam else 268178354Ssam acct.ac_mem = 0; 269178354Ssam 270170530Ssam /* (5) The number of disk I/O operations done */ 271170530Ssam acct.ac_io = encode_comp_t(r->ru_inblock + r->ru_oublock, 0); 272170530Ssam 273178354Ssam /* (6) The UID and GID of the process */ 274178354Ssam acct.ac_uid = p->p_ucred->cr_ruid; 275178354Ssam acct.ac_gid = p->p_ucred->cr_rgid; 276170530Ssam 277170530Ssam /* (7) The terminal from which the process was started */ 278170530Ssam SESS_LOCK(p->p_session); 279170530Ssam if ((p->p_flag & P_CONTROLT) && p->p_pgrp->pg_session->s_ttyp) 280178354Ssam acct.ac_tty = dev2udev(p->p_pgrp->pg_session->s_ttyp->t_dev); 281170530Ssam else 282178354Ssam acct.ac_tty = NOUDEV; 283170530Ssam SESS_UNLOCK(p->p_session); 284138568Ssam 285170530Ssam /* (8) The boolean flags that tell how the process terminated, etc. */ 286170530Ssam acct.ac_flag = p->p_acflag; 287170530Ssam PROC_UNLOCK(p); 288170530Ssam 289170530Ssam /* 290170530Ssam * Write the accounting information to the file. 291138568Ssam */ 292195377Ssam uc = crhold(acctcred); 293195377Ssam vref(vp); 294170530Ssam mtx_unlock(&acct_mtx); 295170530Ssam 296195377Ssam /* 297178354Ssam * Eliminate any file size rlimit. 298178354Ssam */ 299178354Ssam if (p->p_limit->p_refcnt > 1) { 300170530Ssam p->p_limit->p_refcnt--; 301170530Ssam p->p_limit = limcopy(p->p_limit); 302170530Ssam } 303170530Ssam p->p_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; 304170530Ssam 305170530Ssam VOP_LEASE(vp, td, uc, LEASE_WRITE); 306170530Ssam ret = vn_rdwr(UIO_WRITE, vp, (caddr_t)&acct, sizeof (acct), 307170530Ssam (off_t)0, UIO_SYSSPACE, IO_APPEND|IO_UNIT, uc, NOCRED, 308170530Ssam (int *)0, td); 309170530Ssam vrele(vp); 310184280Ssam crfree(uc); 311184280Ssam return (ret); 312184280Ssam} 313191552Ssam 314191552Ssam/* 315191552Ssam * Encode_comp_t converts from ticks in seconds and microseconds 316191552Ssam * to ticks in 1/AHZ seconds. The encoding is described in 317191552Ssam * Leffler, et al., on page 63. 318191552Ssam */ 319205513Srpaulo 320116742Ssam#define MANTSIZE 13 /* 13 bit mantissa. */ 321116742Ssam#define EXPSIZE 3 /* Base 8 (3 bit) exponent. */ 322178354Ssam#define MAXFRACT ((1 << MANTSIZE) - 1) /* Maximum fractional value. */ 323186904Ssam 324195618Srpaulostatic comp_t 325195618Srpauloencode_comp_t(s, us) 326178354Ssam u_long s, us; 327178354Ssam{ 328178354Ssam int exp, rnd; 329178354Ssam 330178354Ssam exp = 0; 331178354Ssam rnd = 0; 332178354Ssam s *= AHZ; 333178354Ssam s += us / (1000000 / AHZ); /* Maximize precision. */ 334178354Ssam 335178354Ssam while (s > MAXFRACT) { 336178354Ssam rnd = s & (1 << (EXPSIZE - 1)); /* Round up? */ 337178354Ssam s >>= EXPSIZE; /* Base 8 exponent == 3 bit shift. */ 338178354Ssam exp++; 339178354Ssam } 340178354Ssam 341178354Ssam /* If we need to round up, do it (and handle overflow correctly). */ 342193655Ssam if (rnd && (++s > MAXFRACT)) { 343178354Ssam s >>= EXPSIZE; 344178354Ssam exp++; 345178354Ssam } 346205513Srpaulo 347178354Ssam /* Clean it up and polish it off. */ 348178354Ssam exp <<= MANTSIZE; /* Shift the exponent into place */ 349191746Sthompsa exp += s; /* and add on the mantissa. */ 350191746Sthompsa return (exp); 351191746Sthompsa} 352191746Sthompsa 353178354Ssam/* 354178354Ssam * Periodically check the filesystem to see if accounting 355178354Ssam * should be turned on or off. Beware the case where the vnode 356178354Ssam * has been vgone()'d out from underneath us, e.g. when the file 357178354Ssam * system containing the accounting file has been forcibly unmounted. 358178354Ssam */ 359178354Ssam/* ARGSUSED */ 360178354Ssamstatic void 361178354Ssamacctwatch(a) 362178354Ssam void *a; 363178354Ssam{ 364178354Ssam struct statfs sb; 365178354Ssam 366178354Ssam mtx_lock(&acct_mtx); 367178354Ssam 368178354Ssam /* 369178354Ssam * XXX arr: Need to fix the issue of holding acct_mtx over 370178354Ssam * the below vnode operations. 371178354Ssam */ 372178354Ssam 373178354Ssam if (savacctp != NULLVP) { 374178354Ssam if (savacctp->v_type == VBAD) { 375178354Ssam (void) vn_close(savacctp, savacctflags, savacctcred, 376178354Ssam NULL); 377178354Ssam savacctp = NULLVP; 378178354Ssam savacctcred = NOCRED; 379178354Ssam mtx_unlock(&acct_mtx); 380178354Ssam return; 381178354Ssam } 382178354Ssam (void)VFS_STATFS(savacctp->v_mount, &sb, (struct thread *)0); 383178354Ssam if (sb.f_bavail > acctresume * sb.f_blocks / 100) { 384178354Ssam acctp = savacctp; 385178354Ssam acctcred = savacctcred; 386178354Ssam acctflags = savacctflags; 387178354Ssam savacctp = NULLVP; 388178354Ssam savacctcred = NOCRED; 389178354Ssam log(LOG_NOTICE, "Accounting resumed\n"); 390178354Ssam } 391178354Ssam } else { 392178354Ssam if (acctp == NULLVP) { 393178354Ssam mtx_unlock(&acct_mtx); 394178354Ssam return; 395178354Ssam } 396178354Ssam if (acctp->v_type == VBAD) { 397178354Ssam (void) vn_close(acctp, acctflags, acctcred, NULL); 398178354Ssam acctp = NULLVP; 399178354Ssam crfree(acctcred); 400178354Ssam acctcred = NOCRED; 401178354Ssam mtx_unlock(&acct_mtx); 402178354Ssam return; 403178354Ssam } 404178354Ssam (void)VFS_STATFS(acctp->v_mount, &sb, (struct thread *)0); 405178354Ssam if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) { 406178354Ssam savacctp = acctp; 407178354Ssam savacctflags = acctflags; 408178354Ssam savacctcred = acctcred; 409178354Ssam acctp = NULLVP; 410178354Ssam acctcred = NOCRED; 411178354Ssam log(LOG_NOTICE, "Accounting suspended\n"); 412178354Ssam } 413178354Ssam } 414178354Ssam callout_reset(&acctwatch_callout, acctchkfreq * hz, acctwatch, NULL); 415178354Ssam mtx_unlock(&acct_mtx); 416178354Ssam} 417178354Ssam