kern_acct.c revision 3124
150276Speter/*- 250276Speter * Copyright (c) 1994 Christopher G. Demetriou 350276Speter * Copyright (c) 1982, 1986, 1989, 1993 466963Speter * The Regents of the University of California. All rights reserved. 550276Speter * (c) UNIX System Laboratories, Inc. 650276Speter * All or some portions of this file are derived from material licensed 750276Speter * to the University of California by American Telephone and Telegraph 850276Speter * Co. or Unix System Laboratories, Inc. and are reproduced herein with 950276Speter * the permission of UNIX System Laboratories, Inc. 1050276Speter * 1150276Speter * Redistribution and use in source and binary forms, with or without 1250276Speter * modification, are permitted provided that the following conditions 1350276Speter * are met: 1450276Speter * 1. Redistributions of source code must retain the above copyright 1550276Speter * notice, this list of conditions and the following disclaimer. 1666963Speter * 2. Redistributions in binary form must reproduce the above copyright 1750276Speter * notice, this list of conditions and the following disclaimer in the 1850276Speter * documentation and/or other materials provided with the distribution. 1950276Speter * 3. All advertising materials mentioning features or use of this software 2050276Speter * must display the following acknowledgement: 2150276Speter * This product includes software developed by the University of 2250276Speter * California, Berkeley and its contributors. 2350276Speter * 4. Neither the name of the University nor the names of its contributors 2450276Speter * may be used to endorse or promote products derived from this software 2550276Speter * without specific prior written permission. 2650276Speter * 2750276Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2850276Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2950276Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3050276Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3150276Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3250276Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3350276Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3450276Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3550276Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3650276Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3750276Speter * SUCH DAMAGE. 3850276Speter * 3950276Speter * @(#)kern_acct.c 8.1 (Berkeley) 6/14/93 4050276Speter * $Id$ 4150276Speter */ 4250276Speter 4350276Speter#include <sys/param.h> 4450276Speter#include <sys/systm.h> 4550276Speter#include <sys/proc.h> 4650276Speter#include <sys/mount.h> 4750276Speter#include <sys/vnode.h> 4850276Speter#include <sys/file.h> 4950276Speter#include <sys/syslog.h> 5050276Speter#include <sys/kernel.h> 5150276Speter#include <sys/namei.h> 5250276Speter#include <sys/errno.h> 5350276Speter#include <sys/acct.h> 5450276Speter#include <sys/resourcevar.h> 5550276Speter#include <sys/ioctl.h> 5650276Speter#include <sys/tty.h> 5750276Speter 5850276Speter/* 5950276Speter * The routines implemented in this file are described in: 6050276Speter * Leffler, et al.: The Design and Implementation of the 4.3BSD 6150276Speter * UNIX Operating System (Addison Welley, 1989) 6250276Speter * on pages 62-63. 6350276Speter * 6450276Speter * Arguably, to simplify accounting operations, this mechanism should 6550276Speter * be replaced by one in which an accounting log file (similar to /dev/klog) 6650276Speter * is read by a user process, etc. However, that has its own problems. 6750276Speter */ 6850276Speter 6950276Speter/* 7050276Speter * Internal accounting functions. 7150276Speter * The former's operation is described in Leffler, et al., and the latter 7250276Speter * was provided by UCB with the 4.4BSD-Lite release 7350276Speter */ 7450276Spetercomp_t encode_comp_t __P((u_long, u_long)); 7550276Spetervoid acctwatch __P((void *)); 7650276Speter 7750276Speter/* 7850276Speter * Accounting vnode pointer, and saved vnode pointer. 7950276Speter */ 8050276Speterstruct vnode *acctp; 8150276Speterstruct vnode *savacctp; 8250276Speter 8350276Speter/* 8450276Speter * Values associated with enabling and disabling accounting 8550276Speter */ 8650276Speterint acctsuspend = 2; /* stop accounting when < 2% free space left */ 8750276Speterint acctresume = 4; /* resume when free space risen to > 4% */ 8850276Speterint acctchkfreq = 15; /* frequency (in seconds) to check space */ 8950276Speter 9050276Speter/* 9150276Speter * Accounting system call. Written based on the specification and 9250276Speter * previous implementation done by Mark Tinguely. 9350276Speter */ 9450276Speterstruct acct_args { 9550276Speter char *path; 9650276Speter}; 9750276Speter 9850276Speterint 9950276Speteracct(p, uap, retval) 10050276Speter struct proc *p; 10150276Speter struct acct_args *uap; 10250276Speter int *retval; 10350276Speter{ 10450276Speter struct nameidata nd; 10550276Speter int error; 10650276Speter 10750276Speter /* Make sure that the caller is root. */ 10850276Speter if (error = suser(p->p_ucred, &p->p_acflag)) 10950276Speter return (error); 11050276Speter 11150276Speter /* 11250276Speter * If accounting is to be started to a file, open that file for 11350276Speter * writing and make sure it's a 'normal'. 11450276Speter */ 11550276Speter if (uap->path != NULL) { 11650276Speter NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, p); 11750276Speter if (error = vn_open(&nd, FWRITE, 0)) 11850276Speter return (error); 11950276Speter VOP_UNLOCK(nd.ni_vp); 12050276Speter if (nd.ni_vp->v_type != VREG) { 12150276Speter vn_close(nd.ni_vp, FWRITE, p->p_ucred, p); 12250276Speter return (EACCES); 12350276Speter } 12450276Speter } 12550276Speter 12650276Speter /* 12750276Speter * If accounting was previously enabled, kill the old space-watcher, 12850276Speter * close the file, and (if no new file was specified, leave). 12950276Speter */ 13050276Speter if (acctp != NULLVP || savacctp != NULLVP) { 13150276Speter untimeout(acctwatch, NULL); 13250276Speter error = vn_close((acctp != NULLVP ? acctp : savacctp), FWRITE, 13350276Speter p->p_ucred, p); 13450276Speter acctp = savacctp = NULLVP; 13550276Speter } 13650276Speter if (uap->path == NULL) 13750276Speter return (error); 13850276Speter 13950276Speter /* 14066963Speter * Save the new accounting file vnode, and schedule the new 14150276Speter * free space watcher. 14250276Speter */ 14350276Speter acctp = nd.ni_vp; 14450276Speter acctwatch(NULL); 14550276Speter return (error); 14650276Speter} 14750276Speter 14850276Speter/* 14950276Speter * Write out process accounting information, on process exit. 15050276Speter * Data to be written out is specified in Leffler, et al. 15150276Speter * and are enumerated below. (They're also noted in the system 15250276Speter * "acct.h" header file.) 15350276Speter */ 15450276Speter 15550276Speterint 15650276Speteracct_process(p) 15750276Speter struct proc *p; 15850276Speter{ 15950276Speter struct acct acct; 16050276Speter struct rusage *r; 16150276Speter struct timeval ut, st, tmp; 16250276Speter int s, t; 16350276Speter struct vnode *vp; 16450276Speter 16550276Speter /* If accounting isn't enabled, don't bother */ 16650276Speter vp = acctp; 16750276Speter if (vp == NULLVP) 16850276Speter return (0); 16950276Speter 17050276Speter /* 17150276Speter * Get process accounting information. 17250276Speter */ 17350276Speter 17450276Speter /* (1) The name of the command that ran */ 17550276Speter bcopy(p->p_comm, acct.ac_comm, sizeof acct.ac_comm); 17650276Speter 17750276Speter /* (2) The amount of user and system time that was used */ 17850276Speter calcru(p, &ut, &st, NULL); 17950276Speter acct.ac_utime = encode_comp_t(ut.tv_sec, ut.tv_usec); 18050276Speter acct.ac_stime = encode_comp_t(st.tv_sec, st.tv_usec); 18150276Speter 18250276Speter /* (3) The elapsed time the commmand ran (and its starting time) */ 18350276Speter acct.ac_btime = p->p_stats->p_start.tv_sec; 18450276Speter s = splclock(); 18550276Speter tmp = time; 18650276Speter splx(s); 18750276Speter timevalsub(&tmp, &p->p_stats->p_start); 18850276Speter acct.ac_etime = encode_comp_t(tmp.tv_sec, tmp.tv_usec); 18950276Speter 19066963Speter /* (4) The average amount of memory used */ 19166963Speter r = &p->p_stats->p_ru; 19250276Speter tmp = ut; 19350276Speter timevaladd(&tmp, &st); 19450276Speter t = tmp.tv_sec * hz + tmp.tv_usec / tick; 19550276Speter if (t) 19650276Speter acct.ac_mem = (r->ru_ixrss + r->ru_idrss + r->ru_isrss) / t; 19750276Speter else 19850276Speter acct.ac_mem = 0; 19950276Speter 20050276Speter /* (5) The number of disk I/O operations done */ 20150276Speter acct.ac_io = encode_comp_t(r->ru_inblock + r->ru_oublock, 0); 20250276Speter 20350276Speter /* (6) The UID and GID of the process */ 20450276Speter acct.ac_uid = p->p_cred->p_ruid; 20550276Speter acct.ac_gid = p->p_cred->p_rgid; 20650276Speter 20750276Speter /* (7) The terminal from which the process was started */ 20850276Speter if ((p->p_flag & P_CONTROLT) && p->p_pgrp->pg_session->s_ttyp) 20950276Speter acct.ac_tty = p->p_pgrp->pg_session->s_ttyp->t_dev; 21050276Speter else 21150276Speter acct.ac_tty = NODEV; 21250276Speter 21350276Speter /* (8) The boolean flags that tell how the process terminated, etc. */ 21450276Speter acct.ac_flag = p->p_acflag; 21550276Speter 21650276Speter /* 21750276Speter * Now, just write the accounting information to the file. 21850276Speter */ 21950276Speter LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 22050276Speter return (vn_rdwr(UIO_WRITE, vp, (caddr_t)&acct, sizeof (acct), 22150276Speter (off_t)0, UIO_SYSSPACE, IO_APPEND|IO_UNIT, p->p_ucred, 22250276Speter (int *)0, p)); 22350276Speter} 22450276Speter 22550276Speter/* 22650276Speter * Encode_comp_t converts from ticks in seconds and microseconds 22750276Speter * to ticks in 1/AHZ seconds. The encoding is described in 22850276Speter * Leffler, et al., on page 63. 22950276Speter */ 23050276Speter 23150276Speter#define MANTSIZE 13 /* 13 bit mantissa. */ 23250276Speter#define EXPSIZE 3 /* Base 8 (3 bit) exponent. */ 23350276Speter#define MAXFRACT ((1 << MANTSIZE) - 1) /* Maximum fractional value. */ 23450276Speter 235comp_t 236encode_comp_t(s, us) 237 u_long s, us; 238{ 239 int exp, rnd; 240 241 exp = 0; 242 rnd = 0; 243 s *= AHZ; 244 s += us / (1000000 / AHZ); /* Maximize precision. */ 245 246 while (s > MAXFRACT) { 247 rnd = s & (1 << (EXPSIZE - 1)); /* Round up? */ 248 s >>= EXPSIZE; /* Base 8 exponent == 3 bit shift. */ 249 exp++; 250 } 251 252 /* If we need to round up, do it (and handle overflow correctly). */ 253 if (rnd && (++s > MAXFRACT)) { 254 s >>= EXPSIZE; 255 exp++; 256 } 257 258 /* Clean it up and polish it off. */ 259 exp <<= MANTSIZE; /* Shift the exponent into place */ 260 exp += s; /* and add on the mantissa. */ 261 return (exp); 262} 263 264/* 265 * Periodically check the file system to see if accounting 266 * should be turned on or off. Beware the case where the vnode 267 * has been vgone()'d out from underneath us, e.g. when the file 268 * system containing the accounting file has been forcibly unmounted. 269 */ 270/* ARGSUSED */ 271void 272acctwatch(a) 273 void *a; 274{ 275 struct statfs sb; 276 277 if (savacctp != NULLVP) { 278 if (savacctp->v_type == VBAD) { 279 (void) vn_close(savacctp, FWRITE, NOCRED, NULL); 280 savacctp = NULLVP; 281 return; 282 } 283 (void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0); 284 if (sb.f_bavail > acctresume * sb.f_blocks / 100) { 285 acctp = savacctp; 286 savacctp = NULLVP; 287 log(LOG_NOTICE, "Accounting resumed\n"); 288 } 289 } else if (acctp != NULLVP) { 290 if (acctp->v_type == VBAD) { 291 (void) vn_close(acctp, FWRITE, NOCRED, NULL); 292 acctp = NULLVP; 293 return; 294 } 295 (void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0); 296 if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) { 297 savacctp = acctp; 298 acctp = NULLVP; 299 log(LOG_NOTICE, "Accounting suspended\n"); 300 } 301 } else 302 return; 303 timeout(acctwatch, NULL, acctchkfreq * hz); 304} 305