1df8bae1dSRodney W. Grimes /*- 2c7d893deSDavid Greenman * Copyright (c) 1994 Christopher G. Demetriou 3df8bae1dSRodney W. Grimes * Copyright (c) 1982, 1986, 1989, 1993 4df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 5df8bae1dSRodney W. Grimes * (c) UNIX System Laboratories, Inc. 6df8bae1dSRodney W. Grimes * All or some portions of this file are derived from material licensed 7df8bae1dSRodney W. Grimes * to the University of California by American Telephone and Telegraph 8df8bae1dSRodney W. Grimes * Co. or Unix System Laboratories, Inc. and are reproduced herein with 9df8bae1dSRodney W. Grimes * the permission of UNIX System Laboratories, Inc. 10df8bae1dSRodney W. Grimes * 11df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 12df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 13df8bae1dSRodney W. Grimes * are met: 14df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 15df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 16df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 17df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 18df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 19df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 20df8bae1dSRodney W. Grimes * must display the following acknowledgement: 21df8bae1dSRodney W. Grimes * This product includes software developed by the University of 22df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 23df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 24df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 25df8bae1dSRodney W. Grimes * without specific prior written permission. 26df8bae1dSRodney W. Grimes * 27df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37df8bae1dSRodney W. Grimes * SUCH DAMAGE. 38df8bae1dSRodney W. Grimes * 39c7d893deSDavid Greenman * @(#)kern_acct.c 8.1 (Berkeley) 6/14/93 40c7d893deSDavid Greenman * $Id$ 41df8bae1dSRodney W. Grimes */ 42df8bae1dSRodney W. Grimes 43df8bae1dSRodney W. Grimes #include <sys/param.h> 440ad076d5SBruce Evans #include <sys/systm.h> 45df8bae1dSRodney W. Grimes #include <sys/proc.h> 46df8bae1dSRodney W. Grimes #include <sys/mount.h> 47df8bae1dSRodney W. Grimes #include <sys/vnode.h> 48df8bae1dSRodney W. Grimes #include <sys/file.h> 49df8bae1dSRodney W. Grimes #include <sys/syslog.h> 50df8bae1dSRodney W. Grimes #include <sys/kernel.h> 51c7d893deSDavid Greenman #include <sys/namei.h> 52c7d893deSDavid Greenman #include <sys/errno.h> 53c7d893deSDavid Greenman #include <sys/acct.h> 54c7d893deSDavid Greenman #include <sys/resourcevar.h> 55c7d893deSDavid Greenman #include <sys/ioctl.h> 56c7d893deSDavid Greenman #include <sys/tty.h> 57df8bae1dSRodney W. Grimes 58df8bae1dSRodney W. Grimes /* 59c7d893deSDavid Greenman * The routines implemented in this file are described in: 60c7d893deSDavid Greenman * Leffler, et al.: The Design and Implementation of the 4.3BSD 61c7d893deSDavid Greenman * UNIX Operating System (Addison Welley, 1989) 62c7d893deSDavid Greenman * on pages 62-63. 63c7d893deSDavid Greenman * 64c7d893deSDavid Greenman * Arguably, to simplify accounting operations, this mechanism should 65c7d893deSDavid Greenman * be replaced by one in which an accounting log file (similar to /dev/klog) 66c7d893deSDavid Greenman * is read by a user process, etc. However, that has its own problems. 67df8bae1dSRodney W. Grimes */ 68df8bae1dSRodney W. Grimes 69df8bae1dSRodney W. Grimes /* 70c7d893deSDavid Greenman * Internal accounting functions. 71c7d893deSDavid Greenman * The former's operation is described in Leffler, et al., and the latter 72c7d893deSDavid Greenman * was provided by UCB with the 4.4BSD-Lite release 73df8bae1dSRodney W. Grimes */ 74c7d893deSDavid Greenman comp_t encode_comp_t __P((u_long, u_long)); 75c7d893deSDavid Greenman void acctwatch __P((void *)); 76c7d893deSDavid Greenman 77c7d893deSDavid Greenman /* 78c7d893deSDavid Greenman * Accounting vnode pointer, and saved vnode pointer. 79c7d893deSDavid Greenman */ 80c7d893deSDavid Greenman struct vnode *acctp; 81c7d893deSDavid Greenman struct vnode *savacctp; 82df8bae1dSRodney W. Grimes 83df8bae1dSRodney W. Grimes /* 84df8bae1dSRodney W. Grimes * Values associated with enabling and disabling accounting 85df8bae1dSRodney W. Grimes */ 86df8bae1dSRodney W. Grimes int acctsuspend = 2; /* stop accounting when < 2% free space left */ 87df8bae1dSRodney W. Grimes int acctresume = 4; /* resume when free space risen to > 4% */ 88df8bae1dSRodney W. Grimes int acctchkfreq = 15; /* frequency (in seconds) to check space */ 89df8bae1dSRodney W. Grimes 90df8bae1dSRodney W. Grimes /* 91c7d893deSDavid Greenman * Accounting system call. Written based on the specification and 92c7d893deSDavid Greenman * previous implementation done by Mark Tinguely. 93df8bae1dSRodney W. Grimes */ 94c7d893deSDavid Greenman struct acct_args { 95c7d893deSDavid Greenman char *path; 96c7d893deSDavid Greenman }; 97df8bae1dSRodney W. Grimes 98c7d893deSDavid Greenman int 99c7d893deSDavid Greenman acct(p, uap, retval) 100c7d893deSDavid Greenman struct proc *p; 101c7d893deSDavid Greenman struct acct_args *uap; 102c7d893deSDavid Greenman int *retval; 103c7d893deSDavid Greenman { 104c7d893deSDavid Greenman struct nameidata nd; 105c7d893deSDavid Greenman int error; 106c7d893deSDavid Greenman 107c7d893deSDavid Greenman /* Make sure that the caller is root. */ 108c7d893deSDavid Greenman if (error = suser(p->p_ucred, &p->p_acflag)) 109c7d893deSDavid Greenman return (error); 110c7d893deSDavid Greenman 111c7d893deSDavid Greenman /* 112c7d893deSDavid Greenman * If accounting is to be started to a file, open that file for 113c7d893deSDavid Greenman * writing and make sure it's a 'normal'. 114c7d893deSDavid Greenman */ 115c7d893deSDavid Greenman if (uap->path != NULL) { 116c7d893deSDavid Greenman NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, p); 117c7d893deSDavid Greenman if (error = vn_open(&nd, FWRITE, 0)) 118c7d893deSDavid Greenman return (error); 119c7d893deSDavid Greenman VOP_UNLOCK(nd.ni_vp); 120c7d893deSDavid Greenman if (nd.ni_vp->v_type != VREG) { 121c7d893deSDavid Greenman vn_close(nd.ni_vp, FWRITE, p->p_ucred, p); 122c7d893deSDavid Greenman return (EACCES); 123c7d893deSDavid Greenman } 124c7d893deSDavid Greenman } 125c7d893deSDavid Greenman 126c7d893deSDavid Greenman /* 127c7d893deSDavid Greenman * If accounting was previously enabled, kill the old space-watcher, 128c7d893deSDavid Greenman * close the file, and (if no new file was specified, leave). 129c7d893deSDavid Greenman */ 130c7d893deSDavid Greenman if (acctp != NULLVP || savacctp != NULLVP) { 131c7d893deSDavid Greenman untimeout(acctwatch, NULL); 132c7d893deSDavid Greenman error = vn_close((acctp != NULLVP ? acctp : savacctp), FWRITE, 133c7d893deSDavid Greenman p->p_ucred, p); 134c7d893deSDavid Greenman acctp = savacctp = NULLVP; 135c7d893deSDavid Greenman } 136c7d893deSDavid Greenman if (uap->path == NULL) 137c7d893deSDavid Greenman return (error); 138c7d893deSDavid Greenman 139c7d893deSDavid Greenman /* 140c7d893deSDavid Greenman * Save the new accounting file vnode, and schedule the new 141c7d893deSDavid Greenman * free space watcher. 142c7d893deSDavid Greenman */ 143c7d893deSDavid Greenman acctp = nd.ni_vp; 144c7d893deSDavid Greenman acctwatch(NULL); 145c7d893deSDavid Greenman return (error); 146c7d893deSDavid Greenman } 147c7d893deSDavid Greenman 148c7d893deSDavid Greenman /* 149c7d893deSDavid Greenman * Write out process accounting information, on process exit. 150c7d893deSDavid Greenman * Data to be written out is specified in Leffler, et al. 151c7d893deSDavid Greenman * and are enumerated below. (They're also noted in the system 152c7d893deSDavid Greenman * "acct.h" header file.) 153c7d893deSDavid Greenman */ 154c7d893deSDavid Greenman 155c7d893deSDavid Greenman int 156c7d893deSDavid Greenman acct_process(p) 157c7d893deSDavid Greenman struct proc *p; 158c7d893deSDavid Greenman { 159c7d893deSDavid Greenman struct acct acct; 160c7d893deSDavid Greenman struct rusage *r; 161c7d893deSDavid Greenman struct timeval ut, st, tmp; 162c7d893deSDavid Greenman int s, t; 163c7d893deSDavid Greenman struct vnode *vp; 164c7d893deSDavid Greenman 165c7d893deSDavid Greenman /* If accounting isn't enabled, don't bother */ 166c7d893deSDavid Greenman vp = acctp; 167c7d893deSDavid Greenman if (vp == NULLVP) 168c7d893deSDavid Greenman return (0); 169c7d893deSDavid Greenman 170c7d893deSDavid Greenman /* 171c7d893deSDavid Greenman * Get process accounting information. 172c7d893deSDavid Greenman */ 173c7d893deSDavid Greenman 174c7d893deSDavid Greenman /* (1) The name of the command that ran */ 175c7d893deSDavid Greenman bcopy(p->p_comm, acct.ac_comm, sizeof acct.ac_comm); 176c7d893deSDavid Greenman 177c7d893deSDavid Greenman /* (2) The amount of user and system time that was used */ 178c7d893deSDavid Greenman calcru(p, &ut, &st, NULL); 179c7d893deSDavid Greenman acct.ac_utime = encode_comp_t(ut.tv_sec, ut.tv_usec); 180c7d893deSDavid Greenman acct.ac_stime = encode_comp_t(st.tv_sec, st.tv_usec); 181c7d893deSDavid Greenman 182c7d893deSDavid Greenman /* (3) The elapsed time the commmand ran (and its starting time) */ 183c7d893deSDavid Greenman acct.ac_btime = p->p_stats->p_start.tv_sec; 184c7d893deSDavid Greenman s = splclock(); 185c7d893deSDavid Greenman tmp = time; 186c7d893deSDavid Greenman splx(s); 187c7d893deSDavid Greenman timevalsub(&tmp, &p->p_stats->p_start); 188c7d893deSDavid Greenman acct.ac_etime = encode_comp_t(tmp.tv_sec, tmp.tv_usec); 189c7d893deSDavid Greenman 190c7d893deSDavid Greenman /* (4) The average amount of memory used */ 191c7d893deSDavid Greenman r = &p->p_stats->p_ru; 192c7d893deSDavid Greenman tmp = ut; 193c7d893deSDavid Greenman timevaladd(&tmp, &st); 194c7d893deSDavid Greenman t = tmp.tv_sec * hz + tmp.tv_usec / tick; 195c7d893deSDavid Greenman if (t) 196c7d893deSDavid Greenman acct.ac_mem = (r->ru_ixrss + r->ru_idrss + r->ru_isrss) / t; 197c7d893deSDavid Greenman else 198c7d893deSDavid Greenman acct.ac_mem = 0; 199c7d893deSDavid Greenman 200c7d893deSDavid Greenman /* (5) The number of disk I/O operations done */ 201c7d893deSDavid Greenman acct.ac_io = encode_comp_t(r->ru_inblock + r->ru_oublock, 0); 202c7d893deSDavid Greenman 203c7d893deSDavid Greenman /* (6) The UID and GID of the process */ 204c7d893deSDavid Greenman acct.ac_uid = p->p_cred->p_ruid; 205c7d893deSDavid Greenman acct.ac_gid = p->p_cred->p_rgid; 206c7d893deSDavid Greenman 207c7d893deSDavid Greenman /* (7) The terminal from which the process was started */ 208c7d893deSDavid Greenman if ((p->p_flag & P_CONTROLT) && p->p_pgrp->pg_session->s_ttyp) 209c7d893deSDavid Greenman acct.ac_tty = p->p_pgrp->pg_session->s_ttyp->t_dev; 210c7d893deSDavid Greenman else 211c7d893deSDavid Greenman acct.ac_tty = NODEV; 212c7d893deSDavid Greenman 213c7d893deSDavid Greenman /* (8) The boolean flags that tell how the process terminated, etc. */ 214c7d893deSDavid Greenman acct.ac_flag = p->p_acflag; 215c7d893deSDavid Greenman 216c7d893deSDavid Greenman /* 217c7d893deSDavid Greenman * Now, just write the accounting information to the file. 218c7d893deSDavid Greenman */ 219c7d893deSDavid Greenman LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 220c7d893deSDavid Greenman return (vn_rdwr(UIO_WRITE, vp, (caddr_t)&acct, sizeof (acct), 221c7d893deSDavid Greenman (off_t)0, UIO_SYSSPACE, IO_APPEND|IO_UNIT, p->p_ucred, 222c7d893deSDavid Greenman (int *)0, p)); 223c7d893deSDavid Greenman } 224c7d893deSDavid Greenman 225c7d893deSDavid Greenman /* 226c7d893deSDavid Greenman * Encode_comp_t converts from ticks in seconds and microseconds 227c7d893deSDavid Greenman * to ticks in 1/AHZ seconds. The encoding is described in 228c7d893deSDavid Greenman * Leffler, et al., on page 63. 229c7d893deSDavid Greenman */ 230c7d893deSDavid Greenman 231c7d893deSDavid Greenman #define MANTSIZE 13 /* 13 bit mantissa. */ 232c7d893deSDavid Greenman #define EXPSIZE 3 /* Base 8 (3 bit) exponent. */ 233c7d893deSDavid Greenman #define MAXFRACT ((1 << MANTSIZE) - 1) /* Maximum fractional value. */ 234c7d893deSDavid Greenman 235c7d893deSDavid Greenman comp_t 236c7d893deSDavid Greenman encode_comp_t(s, us) 237c7d893deSDavid Greenman u_long s, us; 238c7d893deSDavid Greenman { 239c7d893deSDavid Greenman int exp, rnd; 240c7d893deSDavid Greenman 241c7d893deSDavid Greenman exp = 0; 242c7d893deSDavid Greenman rnd = 0; 243c7d893deSDavid Greenman s *= AHZ; 244c7d893deSDavid Greenman s += us / (1000000 / AHZ); /* Maximize precision. */ 245c7d893deSDavid Greenman 246c7d893deSDavid Greenman while (s > MAXFRACT) { 247c7d893deSDavid Greenman rnd = s & (1 << (EXPSIZE - 1)); /* Round up? */ 248c7d893deSDavid Greenman s >>= EXPSIZE; /* Base 8 exponent == 3 bit shift. */ 249c7d893deSDavid Greenman exp++; 250c7d893deSDavid Greenman } 251c7d893deSDavid Greenman 252c7d893deSDavid Greenman /* If we need to round up, do it (and handle overflow correctly). */ 253c7d893deSDavid Greenman if (rnd && (++s > MAXFRACT)) { 254c7d893deSDavid Greenman s >>= EXPSIZE; 255c7d893deSDavid Greenman exp++; 256c7d893deSDavid Greenman } 257c7d893deSDavid Greenman 258c7d893deSDavid Greenman /* Clean it up and polish it off. */ 259c7d893deSDavid Greenman exp <<= MANTSIZE; /* Shift the exponent into place */ 260c7d893deSDavid Greenman exp += s; /* and add on the mantissa. */ 261c7d893deSDavid Greenman return (exp); 262c7d893deSDavid Greenman } 263c7d893deSDavid Greenman 264c7d893deSDavid Greenman /* 265c7d893deSDavid Greenman * Periodically check the file system to see if accounting 266c7d893deSDavid Greenman * should be turned on or off. Beware the case where the vnode 267c7d893deSDavid Greenman * has been vgone()'d out from underneath us, e.g. when the file 268c7d893deSDavid Greenman * system containing the accounting file has been forcibly unmounted. 269c7d893deSDavid Greenman */ 270df8bae1dSRodney W. Grimes /* ARGSUSED */ 271df8bae1dSRodney W. Grimes void 272df8bae1dSRodney W. Grimes acctwatch(a) 273df8bae1dSRodney W. Grimes void *a; 274df8bae1dSRodney W. Grimes { 275df8bae1dSRodney W. Grimes struct statfs sb; 276df8bae1dSRodney W. Grimes 277c7d893deSDavid Greenman if (savacctp != NULLVP) { 278c7d893deSDavid Greenman if (savacctp->v_type == VBAD) { 279c7d893deSDavid Greenman (void) vn_close(savacctp, FWRITE, NOCRED, NULL); 280c7d893deSDavid Greenman savacctp = NULLVP; 281c7d893deSDavid Greenman return; 282c7d893deSDavid Greenman } 283df8bae1dSRodney W. Grimes (void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0); 284df8bae1dSRodney W. Grimes if (sb.f_bavail > acctresume * sb.f_blocks / 100) { 285df8bae1dSRodney W. Grimes acctp = savacctp; 286c7d893deSDavid Greenman savacctp = NULLVP; 287df8bae1dSRodney W. Grimes log(LOG_NOTICE, "Accounting resumed\n"); 288df8bae1dSRodney W. Grimes } 289c7d893deSDavid Greenman } else if (acctp != NULLVP) { 290c7d893deSDavid Greenman if (acctp->v_type == VBAD) { 291c7d893deSDavid Greenman (void) vn_close(acctp, FWRITE, NOCRED, NULL); 292c7d893deSDavid Greenman acctp = NULLVP; 293df8bae1dSRodney W. Grimes return; 294c7d893deSDavid Greenman } 295df8bae1dSRodney W. Grimes (void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0); 296df8bae1dSRodney W. Grimes if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) { 297df8bae1dSRodney W. Grimes savacctp = acctp; 298c7d893deSDavid Greenman acctp = NULLVP; 299df8bae1dSRodney W. Grimes log(LOG_NOTICE, "Accounting suspended\n"); 300df8bae1dSRodney W. Grimes } 301c7d893deSDavid Greenman } else 302c7d893deSDavid Greenman return; 303df8bae1dSRodney W. Grimes timeout(acctwatch, NULL, acctchkfreq * hz); 304df8bae1dSRodney W. Grimes } 305