xref: /illumos-gate/usr/src/uts/common/syscall/exacctsys.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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