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