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