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
acctwatch(a)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 */
acct_process(p)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 */
compress(t,ut)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