xref: /xnu-8792.81.2/bsd/kern/kern_acct.c (revision 19c3b8c28c31cb8130e034cfb5df6bf9ba342d90)
1 /*
2  * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29 /*-
30  * Copyright (c) 1982, 1986, 1989, 1993
31  *	The Regents of the University of California.  All rights reserved.
32  * (c) UNIX System Laboratories, Inc.
33  * All or some portions of this file are derived from material licensed
34  * to the University of California by American Telephone and Telegraph
35  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
36  * the permission of UNIX System Laboratories, Inc.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  * 3. All advertising materials mentioning features or use of this software
47  *    must display the following acknowledgement:
48  *	This product includes software developed by the University of
49  *	California, Berkeley and its contributors.
50  * 4. Neither the name of the University nor the names of its contributors
51  *    may be used to endorse or promote products derived from this software
52  *    without specific prior written permission.
53  *
54  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64  * SUCH DAMAGE.
65  *
66  *	@(#)kern_acct.c	8.1 (Berkeley) 6/14/93
67  */
68 /* HISTORY
69  * 08-May-95  Mac Gillon (mgillon) at NeXT
70  *	Purged old history
71  *      New version based on 4.4
72  */
73 /*
74  * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
75  * support for mandatory and extensible security protections.  This notice
76  * is included in support of clause 2.2 (b) of the Apple Public License,
77  * Version 2.0.
78  */
79 
80 
81 #include <sys/param.h>
82 #include <sys/proc_internal.h>
83 #include <sys/kauth.h>
84 #include <sys/mount_internal.h>
85 #include <sys/vnode_internal.h>
86 #include <sys/file_internal.h>
87 #include <sys/syslog.h>
88 #include <sys/kernel.h>
89 #include <sys/namei.h>
90 #include <sys/errno.h>
91 #include <sys/acct.h>
92 #include <sys/resourcevar.h>
93 #include <sys/ioctl.h>
94 #include <sys/tty.h>
95 #include <sys/sysproto.h>
96 #if CONFIG_MACF
97 #include <security/mac_framework.h>
98 #endif
99 
100 /*
101  * The routines implemented in this file are described in:
102  *      Leffler, et al.: The Design and Implementation of the 4.3BSD
103  *	    UNIX Operating System (Addison Welley, 1989)
104  * on pages 62-63.
105  *
106  * Arguably, to simplify accounting operations, this mechanism should
107  * be replaced by one in which an accounting log file (similar to /dev/klog)
108  * is read by a user process, etc.  However, that has its own problems.
109  */
110 
111 /*
112  * Internal accounting functions.
113  * The former's operation is described in Leffler, et al., and the latter
114  * was provided by UCB with the 4.4BSD-Lite release
115  */
116 comp_t  encode_comp_t(uint32_t, uint32_t);
117 void    acctwatch(void *);
118 
119 /*
120  * Accounting vnode pointer, and suspended accounting vnode pointer.  States
121  * are as follows:
122  *
123  *	acctp		suspend_acctp	state
124  *	-------------	------------	------------------------------
125  *	NULL		NULL		Accounting disabled
126  *	!NULL		NULL		Accounting enabled
127  *	NULL		!NULL		Accounting enabled, but suspended
128  *	!NULL		!NULL		<not allowed>
129  */
130 struct  vnode *acctp;
131 struct  vnode *suspend_acctp;
132 
133 /*
134  * Values associated with enabling and disabling accounting
135  */
136 int     acctsuspend = 2;        /* stop accounting when < 2% free space left */
137 int     acctresume = 4;         /* resume when free space risen to > 4% */
138 int     acctchkfreq = 15;       /* frequency (in seconds) to check space */
139 
140 
141 static LCK_GRP_DECLARE(acct_subsys_lck_grp, "acct");
142 static LCK_MTX_DECLARE(acct_subsys_mutex, &acct_subsys_lck_grp);
143 
144 #define ACCT_SUBSYS_LOCK() lck_mtx_lock(&acct_subsys_mutex)
145 #define ACCT_SUBSYS_UNLOCK() lck_mtx_unlock(&acct_subsys_mutex)
146 
147 
148 /*
149  * Accounting system call.  Written based on the specification and
150  * previous implementation done by Mark Tinguely.
151  */
152 int
acct(proc_t p,struct acct_args * uap,__unused int * retval)153 acct(proc_t p, struct acct_args *uap, __unused int *retval)
154 {
155 	struct nameidata nd;
156 	int error;
157 	struct vfs_context *ctx;
158 
159 	ctx = vfs_context_current();
160 
161 	/* Make sure that the caller is root. */
162 	if ((error = suser(vfs_context_ucred(ctx), &p->p_acflag))) {
163 		return error;
164 	}
165 
166 	/*
167 	 * If accounting is to be started to a file, open that file for
168 	 * writing and make sure it's a 'normal'.
169 	 */
170 	if (uap->path != USER_ADDR_NULL) {
171 		NDINIT(&nd, LOOKUP, OP_OPEN, NOFOLLOW, UIO_USERSPACE, uap->path, ctx);
172 		if ((error = vn_open(&nd, FWRITE, 0))) {
173 			return error;
174 		}
175 #if CONFIG_MACF
176 		error = mac_system_check_acct(vfs_context_ucred(ctx), nd.ni_vp);
177 		if (error) {
178 			vnode_put(nd.ni_vp);
179 			vn_close(nd.ni_vp, FWRITE, ctx);
180 			return error;
181 		}
182 #endif
183 		vnode_put(nd.ni_vp);
184 
185 		if (nd.ni_vp->v_type != VREG) {
186 			vn_close(nd.ni_vp, FWRITE, ctx);
187 			return EACCES;
188 		}
189 	}
190 #if CONFIG_MACF
191 	else {
192 		error = mac_system_check_acct(vfs_context_ucred(ctx), NULL);
193 		if (error) {
194 			return error;
195 		}
196 	}
197 #endif
198 
199 	/*
200 	 * If accounting was previously enabled, kill the old space-watcher,
201 	 * close the file, and (if no new file was specified, leave).
202 	 */
203 	ACCT_SUBSYS_LOCK();
204 	if (acctp != NULLVP || suspend_acctp != NULLVP) {
205 		untimeout(acctwatch, NULL);
206 		error = vn_close((acctp != NULLVP ? acctp : suspend_acctp),
207 		    FWRITE, vfs_context_current());
208 
209 		acctp = suspend_acctp = NULLVP;
210 	}
211 	if (uap->path == USER_ADDR_NULL) {
212 		ACCT_SUBSYS_UNLOCK();
213 		return error;
214 	}
215 
216 	/*
217 	 * Save the new accounting file vnode, and schedule the new
218 	 * free space watcher.
219 	 */
220 	acctp = nd.ni_vp;
221 	ACCT_SUBSYS_UNLOCK();
222 
223 	acctwatch(NULL);
224 	return error;
225 }
226 
227 /*
228  * Write out process accounting information, on process exit.
229  * Data to be written out is specified in Leffler, et al.
230  * and are enumerated below.  (They're also noted in the system
231  * "acct.h" header file.)
232  */
233 int
acct_process(proc_t p)234 acct_process(proc_t p)
235 {
236 	struct acct an_acct;
237 	struct rusage rup, *r;
238 	struct timeval ut, st, tmp;
239 	int t;
240 	int error;
241 	struct vnode *vp;
242 	kauth_cred_t safecred;
243 	struct pgrp *pg;
244 
245 	/* If accounting isn't enabled, don't bother */
246 	ACCT_SUBSYS_LOCK();
247 	vp = acctp;
248 	if (vp == NULLVP) {
249 		ACCT_SUBSYS_UNLOCK();
250 		return 0;
251 	}
252 
253 	/*
254 	 * Get process accounting information.
255 	 */
256 
257 	/* (1) The name of the command that ran */
258 	bcopy(p->p_comm, an_acct.ac_comm, sizeof an_acct.ac_comm);
259 
260 	/* (2) The amount of user and system time that was used */
261 	calcru(p, &ut, &st, NULL);
262 	an_acct.ac_utime = encode_comp_t((uint32_t)ut.tv_sec, ut.tv_usec);
263 	an_acct.ac_stime = encode_comp_t((uint32_t)st.tv_sec, st.tv_usec);
264 
265 	/* (3) The elapsed time the commmand ran (and its starting time) */
266 	an_acct.ac_btime = (u_int32_t)p->p_start.tv_sec;
267 	microtime(&tmp);
268 	timevalsub(&tmp, &p->p_start);
269 	an_acct.ac_etime = encode_comp_t((uint32_t)tmp.tv_sec, tmp.tv_usec);
270 
271 	/* (4) The average amount of memory used */
272 	proc_lock(p);
273 	rup = p->p_stats->p_ru;
274 	proc_unlock(p);
275 	r = &rup;
276 	tmp = ut;
277 	timevaladd(&tmp, &st);
278 	t = (int)(tmp.tv_sec * hz + tmp.tv_usec / tick);
279 	if (t) {
280 		an_acct.ac_mem = (u_int16_t)((r->ru_ixrss + r->ru_idrss + r->ru_isrss) / t);
281 	} else {
282 		an_acct.ac_mem = 0;
283 	}
284 
285 	/* (5) The number of disk I/O operations done */
286 	an_acct.ac_io = encode_comp_t((uint32_t)(r->ru_inblock + r->ru_oublock), 0);
287 
288 	/* (6) The UID and GID of the process */
289 	safecred = kauth_cred_proc_ref(p);
290 
291 	an_acct.ac_uid = kauth_cred_getruid(safecred);
292 	an_acct.ac_gid = kauth_cred_getrgid(safecred);
293 
294 	/* (7) The terminal from which the process was started */
295 
296 	an_acct.ac_tty = NODEV;
297 	if ((p->p_flag & P_CONTROLT) && (pg = proc_pgrp(p, NULL))) {
298 		an_acct.ac_tty = os_atomic_load(&pg->pg_session->s_ttydev, relaxed);
299 		pgrp_rele(pg);
300 	}
301 
302 	/* (8) The boolean flags that tell how the process terminated, etc. */
303 	an_acct.ac_flag = (u_int8_t)p->p_acflag;
304 
305 	/*
306 	 * Now, just write the accounting information to the file.
307 	 */
308 	if ((error = vnode_getwithref(vp)) == 0) {
309 		error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&an_acct, sizeof(an_acct),
310 		    (off_t)0, UIO_SYSSPACE, IO_APPEND | IO_UNIT, safecred,
311 		    (int *)0, p);
312 		vnode_put(vp);
313 	}
314 
315 	kauth_cred_unref(&safecred);
316 	ACCT_SUBSYS_UNLOCK();
317 
318 	return error;
319 }
320 
321 /*
322  * Encode_comp_t converts from ticks in seconds and microseconds
323  * to ticks in 1/AHZ seconds.  The encoding is described in
324  * Leffler, et al., on page 63.
325  */
326 
327 #define MANTSIZE        13                      /* 13 bit mantissa. */
328 #define EXPSIZE         3                       /* Base 8 (3 bit) exponent. */
329 #define MAXFRACT        ((1 << MANTSIZE) - 1)   /* Maximum fractional value. */
330 
331 comp_t
encode_comp_t(uint32_t s,uint32_t us)332 encode_comp_t(uint32_t s, uint32_t us)
333 {
334 	int exp, rnd;
335 
336 	exp = 0;
337 	rnd = 0;
338 	s *= AHZ;
339 	s += us / (1000000 / AHZ);      /* Maximize precision. */
340 
341 	while (s > MAXFRACT) {
342 		rnd = s & (1 << (EXPSIZE - 1)); /* Round up? */
343 		s >>= EXPSIZE;          /* Base 8 exponent == 3 bit shift. */
344 		exp++;
345 	}
346 
347 	/* If we need to round up, do it (and handle overflow correctly). */
348 	if (rnd && (++s > MAXFRACT)) {
349 		s >>= EXPSIZE;
350 		exp++;
351 	}
352 
353 	/* Clean it up and polish it off. */
354 	exp <<= MANTSIZE;               /* Shift the exponent into place */
355 	exp += s;                       /* and add on the mantissa. */
356 	return (comp_t)exp;
357 }
358 
359 /*
360  * Periodically check the file system to see if accounting
361  * should be turned on or off.  Beware the case where the vnode
362  * has been vgone()'d out from underneath us, e.g. when the file
363  * system containing the accounting file has been forcibly unmounted.
364  */
365 /* ARGSUSED */
366 void
acctwatch(__unused void * a)367 acctwatch(__unused void *a)
368 {
369 	vfs_context_t ctx = vfs_context_current();
370 	struct vfs_attr va;
371 
372 	VFSATTR_INIT(&va);
373 	VFSATTR_WANTED(&va, f_blocks);
374 	VFSATTR_WANTED(&va, f_bavail);
375 
376 	ACCT_SUBSYS_LOCK();
377 	if (suspend_acctp != NULLVP) {
378 		/*
379 		 * Resuming accounting when accounting is suspended, and the
380 		 * filesystem containing the suspended accounting file goes
381 		 * below a low watermark
382 		 */
383 		if (suspend_acctp->v_type == VBAD) {
384 			(void) vn_close(suspend_acctp, FWRITE, vfs_context_kernel());
385 			suspend_acctp = NULLVP;
386 			ACCT_SUBSYS_UNLOCK();
387 			return;
388 		}
389 		(void)vfs_getattr(suspend_acctp->v_mount, &va, ctx);
390 		if (va.f_bavail > acctresume * va.f_blocks / 100) {
391 			acctp = suspend_acctp;
392 			suspend_acctp = NULLVP;
393 			log(LOG_NOTICE, "Accounting resumed\n");
394 		}
395 	} else if (acctp != NULLVP) {
396 		/*
397 		 * Suspending accounting when accounting is currently active,
398 		 * and the filesystem containing the active accounting file
399 		 * goes over a high watermark
400 		 */
401 		if (acctp->v_type == VBAD) {
402 			(void) vn_close(acctp, FWRITE, vfs_context_kernel());
403 			acctp = NULLVP;
404 			ACCT_SUBSYS_UNLOCK();
405 			return;
406 		}
407 		(void)vfs_getattr(acctp->v_mount, &va, ctx);
408 		if (va.f_bavail <= acctsuspend * va.f_blocks / 100) {
409 			suspend_acctp = acctp;
410 			acctp = NULLVP;
411 			log(LOG_NOTICE, "Accounting suspended\n");
412 		}
413 	} else {
414 		ACCT_SUBSYS_UNLOCK();
415 		return;
416 	}
417 	ACCT_SUBSYS_UNLOCK();
418 
419 	timeout(acctwatch, NULL, acctchkfreq * hz);
420 }
421