1 /*- 2 * Copyright (c) 1982, 1986, 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * (c) UNIX System Laboratories, Inc. 5 * All or some portions of this file are derived from material licensed 6 * to the University of California by American Telephone and Telegraph 7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8 * the permission of UNIX System Laboratories, Inc. 9 * 10 * %sccs.include.proprietary.c% 11 * 12 * @(#)kern_acct.c 8.8 (Berkeley) 05/14/95 13 */ 14 15 #include <sys/param.h> 16 #include <sys/systm.h> 17 #include <sys/namei.h> 18 #include <sys/resourcevar.h> 19 #include <sys/proc.h> 20 #include <sys/ioctl.h> 21 #include <sys/termios.h> 22 #include <sys/tty.h> 23 #include <sys/vnode.h> 24 #include <sys/mount.h> 25 #include <sys/kernel.h> 26 #include <sys/file.h> 27 #include <sys/acct.h> 28 #include <sys/syslog.h> 29 #include <sys/syscallargs.h> 30 31 /* 32 * Values associated with enabling and disabling accounting 33 */ 34 int acctsuspend = 2; /* stop accounting when < 2% free space left */ 35 int acctresume = 4; /* resume when free space risen to > 4% */ 36 int acctchkfreq = 15; /* frequency (in seconds) to check space */ 37 38 /* 39 * SHOULD REPLACE THIS WITH A DRIVER THAT CAN BE READ TO SIMPLIFY. 40 */ 41 struct vnode *acctp; 42 struct vnode *savacctp; 43 44 /* 45 * Enable or disable process accounting. 46 * 47 * If a non-null filename is given, that file is used to store accounting 48 * records on process exit. If a null filename is given process accounting 49 * is suspended. If accounting is enabled, the system checks the amount 50 * of freespace on the filesystem at timeval intervals. If the amount of 51 * freespace is below acctsuspend percent, accounting is suspended. If 52 * accounting has been suspended, and freespace rises above acctresume, 53 * accounting is resumed. 54 */ 55 acct(p, uap, retval) 56 struct proc *p; 57 struct acct_args /* { 58 syscallarg(char *) path; 59 } */ *uap; 60 register_t *retval; 61 { 62 register struct vnode *vp; 63 extern void acctwatch __P((void *)); 64 struct vnode *oacctp; 65 int error; 66 struct nameidata nd; 67 68 if (error = suser(p->p_ucred, &p->p_acflag)) 69 return (error); 70 if (savacctp) { 71 acctp = savacctp; 72 savacctp = NULL; 73 } 74 if (SCARG(uap, path) == NULL) { 75 if (vp = acctp) { 76 acctp = NULL; 77 error = vn_close(vp, FWRITE, p->p_ucred, p); 78 untimeout(acctwatch, NULL); 79 } 80 return (error); 81 } 82 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); 83 if (error = vn_open(&nd, FWRITE, 0644)) 84 return (error); 85 vp = nd.ni_vp; 86 VOP_UNLOCK(vp, 0, p); 87 if (vp->v_type != VREG) { 88 (void) vn_close(vp, FWRITE, p->p_ucred, p); 89 return (EACCES); 90 } 91 oacctp = acctp; 92 acctp = vp; 93 if (oacctp) 94 error = vn_close(oacctp, FWRITE, p->p_ucred, p); 95 acctwatch(NULL); 96 return (error); 97 } 98 99 /* 100 * Periodically check the file system to see if accounting 101 * should be turned on or off. Beware the case where the vnode 102 * has been vgone()'d out from underneath us, e.g. when the file 103 * system containing the accounting file has been forcibly unmounted. 104 */ 105 /* ARGSUSED */ 106 void 107 acctwatch(a) 108 void *a; 109 { 110 struct statfs sb; 111 112 if (savacctp) { 113 if (savacctp->v_type == VBAD) { 114 (void) vn_close(savacctp, FWRITE, NOCRED, NULL); 115 savacctp = NULL; 116 return; 117 } 118 (void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0); 119 if (sb.f_bavail > acctresume * sb.f_blocks / 100) { 120 acctp = savacctp; 121 savacctp = NULL; 122 log(LOG_NOTICE, "Accounting resumed\n"); 123 } 124 } else { 125 if (acctp == NULL) 126 return; 127 if (acctp->v_type == VBAD) { 128 (void) vn_close(acctp, FWRITE, NOCRED, NULL); 129 acctp = NULL; 130 return; 131 } 132 (void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0); 133 if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) { 134 savacctp = acctp; 135 acctp = NULL; 136 log(LOG_NOTICE, "Accounting suspended\n"); 137 } 138 } 139 timeout(acctwatch, NULL, acctchkfreq * hz); 140 } 141 142 /* 143 * This routine calculates an accounting record for a process and, 144 * if accounting is enabled, writes it to the accounting file. 145 */ 146 acct_process(p) 147 register struct proc *p; 148 { 149 register struct rusage *ru; 150 struct vnode *vp; 151 struct timeval t, ut, st; 152 int error, i, s; 153 struct acct acctbuf; 154 register struct acct *ap = &acctbuf; 155 156 s = splclock(); 157 if ((vp = acctp) == NULL) { 158 splx(s); 159 return (0); 160 } 161 if (vp->v_type == VBAD) { 162 (void) vn_close(vp, FWRITE, NOCRED, NULL); 163 acctp = NULL; 164 splx(s); 165 return (0); 166 } 167 bcopy(p->p_comm, ap->ac_comm, sizeof(ap->ac_comm)); 168 ru = &p->p_stats->p_ru; 169 calcru(p, &ut, &st, NULL); 170 t = time; 171 ap->ac_utime = compress(ut.tv_sec, ut.tv_usec); 172 ap->ac_stime = compress(st.tv_sec, st.tv_usec); 173 timevalsub(&t, &p->p_stats->p_start); 174 ap->ac_etime = compress(t.tv_sec, t.tv_usec); 175 ap->ac_btime = p->p_stats->p_start.tv_sec; 176 ap->ac_uid = p->p_cred->p_ruid; 177 ap->ac_gid = p->p_cred->p_rgid; 178 t = st; 179 timevaladd(&t, &ut); 180 if (i = t.tv_sec * hz + t.tv_usec / tick) 181 ap->ac_mem = (ru->ru_ixrss + ru->ru_idrss + ru->ru_isrss) / i; 182 else 183 ap->ac_mem = 0; 184 ap->ac_io = compress(ru->ru_inblock + ru->ru_oublock, (long)0); 185 if (p->p_flag & P_CONTROLT && p->p_session->s_ttyp) 186 ap->ac_tty = p->p_session->s_ttyp->t_dev; 187 else 188 ap->ac_tty = NODEV; 189 ap->ac_flag = p->p_acflag; 190 VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); 191 error = vn_rdwr(UIO_WRITE, vp, (caddr_t)ap, sizeof (acctbuf), (off_t)0, 192 UIO_SYSSPACE, IO_UNIT|IO_APPEND, p->p_ucred, (int *)0, 193 (struct proc *)0); 194 splx(s); 195 return (error); 196 } 197 198 /* 199 * Produce a pseudo-floating point representation 200 * with 3 bits base-8 exponent, 13 bits fraction. 201 */ 202 compress(t, ut) 203 register long t; 204 long ut; 205 { 206 register exp = 0, round = 0; 207 208 t = t * AHZ; /* compiler will convert only this format to a shift */ 209 if (ut) 210 t += ut / (1000000 / AHZ); 211 while (t >= 8192) { 212 exp++; 213 round = t&04; 214 t >>= 3; 215 } 216 if (round) { 217 t++; 218 if (t >= 8192) { 219 t >>= 3; 220 exp++; 221 } 222 } 223 return ((exp<<13) + t); 224 } 225