Home | History | Annotate | Download | only in syscall
      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 }
    407