xref: /original-bsd/sys/kern/kern_acct.c (revision 0ac4996f)
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