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#include <machine/spl.h> 97#if CONFIG_MACF 98#include <security/mac_framework.h> 99#endif 100 101/* 102 * The routines implemented in this file are described in: 103 * Leffler, et al.: The Design and Implementation of the 4.3BSD 104 * UNIX Operating System (Addison Welley, 1989) 105 * on pages 62-63. 106 * 107 * Arguably, to simplify accounting operations, this mechanism should 108 * be replaced by one in which an accounting log file (similar to /dev/klog) 109 * is read by a user process, etc. However, that has its own problems. 110 */ 111 112/* 113 * Internal accounting functions. 114 * The former's operation is described in Leffler, et al., and the latter 115 * was provided by UCB with the 4.4BSD-Lite release 116 */ 117comp_t encode_comp_t(uint32_t, uint32_t); 118void acctwatch(void *); 119void acct_init(void); 120 121/* 122 * Accounting vnode pointer, and suspended accounting vnode pointer. States 123 * are as follows: 124 * 125 * acctp suspend_acctp state 126 * ------------- ------------ ------------------------------ 127 * NULL NULL Accounting disabled 128 * !NULL NULL Accounting enabled 129 * NULL !NULL Accounting enabled, but suspended 130 * !NULL !NULL <not allowed> 131 */ 132struct vnode *acctp; 133struct vnode *suspend_acctp; 134 135/* 136 * Values associated with enabling and disabling accounting 137 */ 138int acctsuspend = 2; /* stop accounting when < 2% free space left */ 139int acctresume = 4; /* resume when free space risen to > 4% */ 140int acctchkfreq = 15; /* frequency (in seconds) to check space */ 141 142 143static lck_grp_t *acct_subsys_lck_grp; 144static lck_mtx_t *acct_subsys_mutex; 145 146#define ACCT_SUBSYS_LOCK() lck_mtx_lock(acct_subsys_mutex) 147#define ACCT_SUBSYS_UNLOCK() lck_mtx_unlock(acct_subsys_mutex) 148 149void 150acct_init(void) 151{ 152 acct_subsys_lck_grp = lck_grp_alloc_init("acct", NULL); 153 acct_subsys_mutex = lck_mtx_alloc_init(acct_subsys_lck_grp, NULL); 154} 155 156 157/* 158 * Accounting system call. Written based on the specification and 159 * previous implementation done by Mark Tinguely. 160 */ 161int 162acct(proc_t p, struct acct_args *uap, __unused int *retval) 163{ 164 struct nameidata nd; 165 int error; 166 struct vfs_context *ctx; 167 168 ctx = vfs_context_current(); 169 170 /* Make sure that the caller is root. */ 171 if ((error = suser(vfs_context_ucred(ctx), &p->p_acflag))) 172 return (error); 173 174 /* 175 * If accounting is to be started to a file, open that file for 176 * writing and make sure it's a 'normal'. 177 */ 178 if (uap->path != USER_ADDR_NULL) { 179 NDINIT(&nd, LOOKUP, OP_OPEN, NOFOLLOW, UIO_USERSPACE, uap->path, ctx); 180 if ((error = vn_open(&nd, FWRITE, 0))) 181 return (error); 182#if CONFIG_MACF 183 error = mac_system_check_acct(vfs_context_ucred(ctx), nd.ni_vp); 184 if (error) { 185 vnode_put(nd.ni_vp); 186 vn_close(nd.ni_vp, FWRITE, ctx); 187 return (error); 188 } 189#endif 190 vnode_put(nd.ni_vp); 191 192 if (nd.ni_vp->v_type != VREG) { 193 vn_close(nd.ni_vp, FWRITE, ctx); 194 return (EACCES); 195 } 196 } 197#if CONFIG_MACF 198 else { 199 error = mac_system_check_acct(vfs_context_ucred(ctx), NULL); 200 if (error) 201 return (error); 202 } 203#endif 204 205 /* 206 * If accounting was previously enabled, kill the old space-watcher, 207 * close the file, and (if no new file was specified, leave). 208 */ 209 ACCT_SUBSYS_LOCK(); 210 if (acctp != NULLVP || suspend_acctp != NULLVP) { 211 untimeout(acctwatch, NULL); 212 error = vn_close((acctp != NULLVP ? acctp : suspend_acctp), 213 FWRITE, vfs_context_current()); 214 215 acctp = suspend_acctp = NULLVP; 216 } 217 if (uap->path == USER_ADDR_NULL) { 218 ACCT_SUBSYS_UNLOCK(); 219 return (error); 220 } 221 222 /* 223 * Save the new accounting file vnode, and schedule the new 224 * free space watcher. 225 */ 226 acctp = nd.ni_vp; 227 ACCT_SUBSYS_UNLOCK(); 228 229 acctwatch(NULL); 230 return (error); 231} 232 233/* 234 * Write out process accounting information, on process exit. 235 * Data to be written out is specified in Leffler, et al. 236 * and are enumerated below. (They're also noted in the system 237 * "acct.h" header file.) 238 */ 239int 240acct_process(proc_t p) 241{ 242 struct acct an_acct; 243 struct rusage rup, *r; 244 struct timeval ut, st, tmp; 245 int t; 246 int error; 247 struct vnode *vp; 248 kauth_cred_t safecred; 249 struct session * sessp; 250 struct tty *tp; 251 252 /* If accounting isn't enabled, don't bother */ 253 ACCT_SUBSYS_LOCK(); 254 vp = acctp; 255 if (vp == NULLVP) { 256 ACCT_SUBSYS_UNLOCK(); 257 return (0); 258 } 259 260 /* 261 * Get process accounting information. 262 */ 263 264 /* (1) The name of the command that ran */ 265 bcopy(p->p_comm, an_acct.ac_comm, sizeof an_acct.ac_comm); 266 267 /* (2) The amount of user and system time that was used */ 268 calcru(p, &ut, &st, NULL); 269 an_acct.ac_utime = encode_comp_t(ut.tv_sec, ut.tv_usec); 270 an_acct.ac_stime = encode_comp_t(st.tv_sec, st.tv_usec); 271 272 /* (3) The elapsed time the commmand ran (and its starting time) */ 273 an_acct.ac_btime = p->p_start.tv_sec; 274 microtime(&tmp); 275 timevalsub(&tmp, &p->p_start); 276 an_acct.ac_etime = encode_comp_t(tmp.tv_sec, tmp.tv_usec); 277 278 /* (4) The average amount of memory used */ 279 proc_lock(p); 280 rup = p->p_stats->p_ru; 281 proc_unlock(p); 282 r = &rup; 283 tmp = ut; 284 timevaladd(&tmp, &st); 285 t = tmp.tv_sec * hz + tmp.tv_usec / tick; 286 if (t) 287 an_acct.ac_mem = (r->ru_ixrss + r->ru_idrss + r->ru_isrss) / t; 288 else 289 an_acct.ac_mem = 0; 290 291 /* (5) The number of disk I/O operations done */ 292 an_acct.ac_io = encode_comp_t(r->ru_inblock + r->ru_oublock, 0); 293 294 /* (6) The UID and GID of the process */ 295 safecred = kauth_cred_proc_ref(p); 296 297 an_acct.ac_uid = kauth_cred_getruid(safecred); 298 an_acct.ac_gid = kauth_cred_getrgid(safecred); 299 300 /* (7) The terminal from which the process was started */ 301 302 sessp = proc_session(p); 303 if ((p->p_flag & P_CONTROLT) && (sessp != SESSION_NULL) && ((tp = SESSION_TP(sessp)) != TTY_NULL)) { 304 tty_lock(tp); 305 an_acct.ac_tty = tp->t_dev; 306 tty_unlock(tp); 307 }else 308 an_acct.ac_tty = NODEV; 309 310 if (sessp != SESSION_NULL) 311 session_rele(sessp); 312 313 /* (8) The boolean flags that tell how the process terminated, etc. */ 314 an_acct.ac_flag = p->p_acflag; 315 316 /* 317 * Now, just write the accounting information to the file. 318 */ 319 if ((error = vnode_getwithref(vp)) == 0) { 320 error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&an_acct, sizeof (an_acct), 321 (off_t)0, UIO_SYSSPACE, IO_APPEND|IO_UNIT, safecred, 322 (int *)0, p); 323 vnode_put(vp); 324 } 325 326 kauth_cred_unref(&safecred); 327 ACCT_SUBSYS_UNLOCK(); 328 329 return (error); 330} 331 332/* 333 * Encode_comp_t converts from ticks in seconds and microseconds 334 * to ticks in 1/AHZ seconds. The encoding is described in 335 * Leffler, et al., on page 63. 336 */ 337 338#define MANTSIZE 13 /* 13 bit mantissa. */ 339#define EXPSIZE 3 /* Base 8 (3 bit) exponent. */ 340#define MAXFRACT ((1 << MANTSIZE) - 1) /* Maximum fractional value. */ 341 342comp_t 343encode_comp_t(uint32_t s, uint32_t us) 344{ 345 int exp, rnd; 346 347 exp = 0; 348 rnd = 0; 349 s *= AHZ; 350 s += us / (1000000 / AHZ); /* Maximize precision. */ 351 352 while (s > MAXFRACT) { 353 rnd = s & (1 << (EXPSIZE - 1)); /* Round up? */ 354 s >>= EXPSIZE; /* Base 8 exponent == 3 bit shift. */ 355 exp++; 356 } 357 358 /* If we need to round up, do it (and handle overflow correctly). */ 359 if (rnd && (++s > MAXFRACT)) { 360 s >>= EXPSIZE; 361 exp++; 362 } 363 364 /* Clean it up and polish it off. */ 365 exp <<= MANTSIZE; /* Shift the exponent into place */ 366 exp += s; /* and add on the mantissa. */ 367 return (exp); 368} 369 370/* 371 * Periodically check the file system to see if accounting 372 * should be turned on or off. Beware the case where the vnode 373 * has been vgone()'d out from underneath us, e.g. when the file 374 * system containing the accounting file has been forcibly unmounted. 375 */ 376/* ARGSUSED */ 377void 378acctwatch(__unused void *a) 379{ 380 vfs_context_t ctx = vfs_context_current(); 381 struct vfs_attr va; 382 383 VFSATTR_INIT(&va); 384 VFSATTR_WANTED(&va, f_blocks); 385 VFSATTR_WANTED(&va, f_bavail); 386 387 ACCT_SUBSYS_LOCK(); 388 if (suspend_acctp != NULLVP) { 389 /* 390 * Resuming accounting when accounting is suspended, and the 391 * filesystem containing the suspended accounting file goes 392 * below a low watermark 393 */ 394 if (suspend_acctp->v_type == VBAD) { 395 (void) vn_close(suspend_acctp, FWRITE, vfs_context_kernel()); 396 suspend_acctp = NULLVP; 397 ACCT_SUBSYS_UNLOCK(); 398 return; 399 } 400 (void)vfs_getattr(suspend_acctp->v_mount, &va, ctx); 401 if (va.f_bavail > acctresume * va.f_blocks / 100) { 402 acctp = suspend_acctp; 403 suspend_acctp = NULLVP; 404 log(LOG_NOTICE, "Accounting resumed\n"); 405 } 406 } else if (acctp != NULLVP) { 407 /* 408 * Suspending accounting when accounting is currently active, 409 * and the filesystem containing the active accounting file 410 * goes over a high watermark 411 */ 412 if (acctp->v_type == VBAD) { 413 (void) vn_close(acctp, FWRITE, vfs_context_kernel()); 414 acctp = NULLVP; 415 ACCT_SUBSYS_UNLOCK(); 416 return; 417 } 418 (void)vfs_getattr(acctp->v_mount, &va, ctx); 419 if (va.f_bavail <= acctsuspend * va.f_blocks / 100) { 420 suspend_acctp = acctp; 421 acctp = NULLVP; 422 log(LOG_NOTICE, "Accounting suspended\n"); 423 } 424 } else { 425 ACCT_SUBSYS_UNLOCK(); 426 return; 427 } 428 ACCT_SUBSYS_UNLOCK(); 429 430 timeout(acctwatch, NULL, acctchkfreq * hz); 431} 432