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