1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/acctctl.h> 30 #include <sys/cmn_err.h> 31 #include <sys/cred.h> 32 #include <sys/errno.h> 33 #include <sys/exacct.h> 34 #include <sys/modctl.h> 35 #include <sys/procset.h> 36 #include <sys/sysmacros.h> 37 #include <sys/systm.h> 38 #include <sys/task.h> 39 #include <sys/types.h> 40 #include <sys/user.h> 41 #include <sys/policy.h> 42 43 /* 44 * getacct(2), putacct(2), and wracct(2) system calls 45 * 46 * The extended accounting subsystem provides three root-privileged system 47 * calls for interacting with the actual resource data associated with each 48 * task or process. getacct() copies a packed exacct record reflecting the 49 * resource usage out to the buffer provided by the user. wracct() writes a 50 * record to the appropriate extended accounting file. putacct() takes the 51 * buffer provided by the user, and appends a "tag" record associated with the 52 * specified task or project that encapsulates the user data. All three of 53 * these functions exit early if extended accounting is not active for the 54 * requested entity type. 55 * 56 * Locking 57 * Under the terminology introduced in os/task.c, all three of these system 58 * calls are task observers, when executing on an existing task. 59 */ 60 61 /* 62 * getacct_callback() is used to copyout the buffer with accounting records 63 * from the kernel back to the user. It also sets actual to the size of the 64 * kernel buffer--the required minimum size for a successful outbound copy. 65 */ 66 /* ARGSUSED */ 67 static int 68 getacct_callback(ac_info_t *unused, void *ubuf, size_t usize, void *kbuf, 69 size_t ksize, size_t *actual) 70 { 71 size_t size = MIN(usize, ksize); 72 73 if (ubuf != NULL && copyout(kbuf, ubuf, size) != 0) 74 return (EFAULT); 75 *actual = ksize; 76 return (0); 77 } 78 79 static int 80 getacct_task(ac_info_t *ac_task, taskid_t tkid, void *buf, size_t bufsize, 81 size_t *sizep) 82 { 83 task_t *tk; 84 int error; 85 86 mutex_enter(&ac_task->ac_lock); 87 if (ac_task->ac_state == AC_OFF) { 88 mutex_exit(&ac_task->ac_lock); 89 return (ENOTACTIVE); 90 } 91 mutex_exit(&ac_task->ac_lock); 92 93 if ((tk = task_hold_by_id(tkid)) == NULL) 94 return (ESRCH); 95 error = exacct_assemble_task_usage(ac_task, tk, 96 getacct_callback, buf, bufsize, sizep, EW_PARTIAL); 97 task_rele(tk); 98 99 return (error); 100 } 101 102 static int 103 getacct_proc(ac_info_t *ac_proc, pid_t pid, void *buf, size_t bufsize, 104 size_t *sizep) 105 { 106 proc_t *p; 107 proc_usage_t *pu; 108 ulong_t mask[AC_MASK_SZ]; 109 ulong_t *ac_mask = &mask[0]; 110 int error; 111 112 mutex_enter(&ac_proc->ac_lock); 113 if (ac_proc->ac_state == AC_OFF) { 114 mutex_exit(&ac_proc->ac_lock); 115 return (ENOTACTIVE); 116 } 117 bt_copy(&ac_proc->ac_mask[0], ac_mask, AC_MASK_SZ); 118 mutex_exit(&ac_proc->ac_lock); 119 120 pu = kmem_zalloc(sizeof (proc_usage_t), KM_SLEEP); 121 pu->pu_command = kmem_zalloc(MAXCOMLEN + 1, KM_SLEEP); 122 123 mutex_enter(&pidlock); 124 if ((p = prfind(pid)) == NULL) { 125 mutex_exit(&pidlock); 126 kmem_free(pu->pu_command, MAXCOMLEN + 1); 127 kmem_free(pu, sizeof (proc_usage_t)); 128 return (ESRCH); 129 } 130 mutex_enter(&p->p_lock); 131 mutex_exit(&pidlock); 132 133 exacct_calculate_proc_usage(p, pu, ac_mask, EW_PARTIAL, 0); 134 mutex_exit(&p->p_lock); 135 136 error = exacct_assemble_proc_usage(ac_proc, pu, 137 getacct_callback, buf, bufsize, sizep, EW_PARTIAL); 138 139 kmem_free(pu->pu_command, MAXCOMLEN + 1); 140 kmem_free(pu, sizeof (proc_usage_t)); 141 142 return (error); 143 } 144 145 static ssize_t 146 getacct(idtype_t idtype, id_t id, void *buf, size_t bufsize) 147 { 148 size_t size = 0; 149 int error; 150 struct exacct_globals *acg; 151 152 if (bufsize > EXACCT_MAX_BUFSIZE) 153 bufsize = EXACCT_MAX_BUFSIZE; 154 155 acg = zone_getspecific(exacct_zone_key, curproc->p_zone); 156 switch (idtype) { 157 case P_PID: 158 error = getacct_proc(&acg->ac_proc, id, buf, bufsize, &size); 159 break; 160 case P_TASKID: 161 error = getacct_task(&acg->ac_task, id, buf, bufsize, &size); 162 break; 163 default: 164 error = EINVAL; 165 break; 166 } 167 return (error == 0 ? (ssize_t)size : set_errno(error)); 168 } 169 170 static int 171 putacct(idtype_t idtype, id_t id, void *buf, size_t bufsize, int flags) 172 { 173 int error; 174 taskid_t tkid; 175 proc_t *p; 176 task_t *tk; 177 void *kbuf; 178 struct exacct_globals *acg; 179 180 if (bufsize == 0 || bufsize > EXACCT_MAX_BUFSIZE) 181 return (set_errno(EINVAL)); 182 183 kbuf = kmem_alloc(bufsize, KM_SLEEP); 184 if (copyin(buf, kbuf, bufsize) != 0) { 185 error = EFAULT; 186 goto out; 187 } 188 189 acg = zone_getspecific(exacct_zone_key, curproc->p_zone); 190 switch (idtype) { 191 case P_PID: 192 mutex_enter(&pidlock); 193 if ((p = prfind(id)) == NULL) { 194 mutex_exit(&pidlock); 195 error = ESRCH; 196 } else { 197 zone_t *zone = p->p_zone; 198 199 tkid = p->p_task->tk_tkid; 200 zone_hold(zone); 201 mutex_exit(&pidlock); 202 203 error = exacct_tag_proc(&acg->ac_proc, id, tkid, kbuf, 204 bufsize, flags, zone->zone_nodename); 205 zone_rele(zone); 206 } 207 break; 208 case P_TASKID: 209 if ((tk = task_hold_by_id(id)) != NULL) { 210 error = exacct_tag_task(&acg->ac_task, tk, kbuf, 211 bufsize, flags); 212 task_rele(tk); 213 } else { 214 error = ESRCH; 215 } 216 break; 217 default: 218 error = EINVAL; 219 break; 220 } 221 out: 222 kmem_free(kbuf, bufsize); 223 return (error == 0 ? error : set_errno(error)); 224 } 225 226 static int 227 wracct_task(ac_info_t *ac_task, taskid_t tkid, int flag, size_t *sizep) 228 { 229 task_t *tk; 230 int error; 231 232 mutex_enter(&ac_task->ac_lock); 233 if (ac_task->ac_state == AC_OFF || ac_task->ac_vnode == NULL) { 234 mutex_exit(&ac_task->ac_lock); 235 return (ENOTACTIVE); 236 } 237 mutex_exit(&ac_task->ac_lock); 238 239 if ((tk = task_hold_by_id(tkid)) == NULL) 240 return (ESRCH); 241 error = exacct_assemble_task_usage(ac_task, tk, exacct_commit_callback, 242 NULL, 0, sizep, flag); 243 task_rele(tk); 244 245 return (error); 246 } 247 248 static int 249 wracct_proc(ac_info_t *ac_proc, pid_t pid, int flag, size_t *sizep) 250 { 251 proc_t *p; 252 proc_usage_t *pu; 253 ulong_t mask[AC_MASK_SZ]; 254 ulong_t *ac_mask = &mask[0]; 255 int error; 256 257 mutex_enter(&ac_proc->ac_lock); 258 if (ac_proc->ac_state == AC_OFF || ac_proc->ac_vnode == NULL) { 259 mutex_exit(&ac_proc->ac_lock); 260 return (ENOTACTIVE); 261 } 262 bt_copy(&ac_proc->ac_mask[0], ac_mask, AC_MASK_SZ); 263 mutex_exit(&ac_proc->ac_lock); 264 265 pu = kmem_zalloc(sizeof (proc_usage_t), KM_SLEEP); 266 pu->pu_command = kmem_zalloc(MAXCOMLEN + 1, KM_SLEEP); 267 268 mutex_enter(&pidlock); 269 if ((p = prfind(pid)) == NULL) { 270 mutex_exit(&pidlock); 271 kmem_free(pu->pu_command, MAXCOMLEN + 1); 272 kmem_free(pu, sizeof (proc_usage_t)); 273 return (ESRCH); 274 } 275 mutex_enter(&p->p_lock); 276 mutex_exit(&pidlock); 277 exacct_calculate_proc_usage(p, pu, ac_mask, flag, 0); 278 mutex_exit(&p->p_lock); 279 280 error = exacct_assemble_proc_usage(ac_proc, pu, 281 exacct_commit_callback, NULL, 0, sizep, flag); 282 283 kmem_free(pu->pu_command, MAXCOMLEN + 1); 284 kmem_free(pu, sizeof (proc_usage_t)); 285 286 return (error); 287 } 288 289 static int 290 wracct(idtype_t idtype, id_t id, int flags) 291 { 292 int error; 293 size_t size = 0; 294 struct exacct_globals *acg; 295 296 /* 297 * Validate flags. 298 */ 299 switch (flags) { 300 case EW_PARTIAL: 301 case EW_INTERVAL: 302 break; 303 default: 304 return (set_errno(EINVAL)); 305 } 306 307 acg = zone_getspecific(exacct_zone_key, curproc->p_zone); 308 switch (idtype) { 309 case P_PID: 310 if (flags == EW_INTERVAL) 311 return (set_errno(ENOTSUP)); 312 error = wracct_proc(&acg->ac_proc, id, flags, &size); 313 break; 314 case P_TASKID: 315 error = wracct_task(&acg->ac_task, id, flags, &size); 316 break; 317 default: 318 error = EINVAL; 319 break; 320 } 321 322 return (error == 0 ? error : set_errno(error)); 323 } 324 325 static long 326 exacct(int code, idtype_t idtype, id_t id, void *buf, size_t bufsize, 327 int flags) 328 { 329 if (secpolicy_acct(CRED()) != 0) 330 return (set_errno(EPERM)); 331 332 if (exacct_zone_key == ZONE_KEY_UNINITIALIZED) 333 return (set_errno(ENOTACTIVE)); 334 335 switch (code) { 336 case 0: 337 return (getacct(idtype, id, buf, bufsize)); 338 case 1: 339 return (putacct(idtype, id, buf, bufsize, flags)); 340 case 2: 341 return (wracct(idtype, id, flags)); 342 default: 343 return (set_errno(EINVAL)); 344 } 345 } 346 347 #if defined(_LP64) 348 #define SE_LRVAL SE_64RVAL 349 #else 350 #define SE_LRVAL SE_32RVAL1 351 #endif 352 353 static struct sysent exacctsys_sysent = { 354 6, 355 SE_NOUNLOAD | SE_ARGC | SE_LRVAL, 356 (int (*)())exacct 357 }; 358 359 static struct modlsys modlsys = { 360 &mod_syscallops, 361 "extended accounting facility", 362 &exacctsys_sysent 363 }; 364 365 #ifdef _SYSCALL32_IMPL 366 367 static struct sysent exacctsys_sysent32 = { 368 6, 369 SE_NOUNLOAD | SE_ARGC | SE_32RVAL1, 370 (int (*)())exacct 371 }; 372 373 static struct modlsys modlsys32 = { 374 &mod_syscallops32, 375 "32-bit extended accounting facility", 376 &exacctsys_sysent32 377 }; 378 379 #endif 380 381 static struct modlinkage modlinkage = { 382 MODREV_1, 383 &modlsys, 384 #ifdef _SYSCALL32_IMPL 385 &modlsys32, 386 #endif 387 NULL 388 }; 389 390 int 391 _init(void) 392 { 393 return (mod_install(&modlinkage)); 394 } 395 396 int 397 _fini(void) 398 { 399 return (mod_remove(&modlinkage)); 400 } 401 402 int 403 _info(struct modinfo *mip) 404 { 405 return (mod_info(&modlinkage, mip)); 406 }