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 */
65static int
66getacct_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
77static int
78getacct_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
100static int
101getacct_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
143static ssize_t
144getacct(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
168static int
169putacct(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	}
219out:
220	kmem_free(kbuf, bufsize);
221	return (error == 0 ? error : set_errno(error));
222}
223
224static int
225wracct_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
246static int
247wracct_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
287static int
288wracct(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
323static long
324exacct(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
351static struct sysent exacctsys_sysent = {
352	6,
353	SE_NOUNLOAD | SE_ARGC | SE_LRVAL,
354	(int (*)())(uintptr_t)exacct
355};
356
357static struct modlsys modlsys = {
358	&mod_syscallops,
359	"extended accounting facility",
360	&exacctsys_sysent
361};
362
363#ifdef _SYSCALL32_IMPL
364
365static struct sysent exacctsys_sysent32 = {
366	6,
367	SE_NOUNLOAD | SE_ARGC | SE_32RVAL1,
368	(int (*)())(uintptr_t)exacct
369};
370
371static struct modlsys modlsys32 = {
372	&mod_syscallops32,
373	"32-bit extended accounting facility",
374	&exacctsys_sysent32
375};
376
377#endif
378
379static struct modlinkage modlinkage = {
380	MODREV_1,
381	&modlsys,
382#ifdef _SYSCALL32_IMPL
383	&modlsys32,
384#endif
385	NULL
386};
387
388int
389_init(void)
390{
391	return (mod_install(&modlinkage));
392}
393
394int
395_fini(void)
396{
397	return (mod_remove(&modlinkage));
398}
399
400int
401_info(struct modinfo *mip)
402{
403	return (mod_info(&modlinkage, mip));
404}
405