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
getacct_callback(ac_info_t * unused,void * ubuf,size_t usize,void * kbuf,size_t ksize,size_t * actual)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
getacct_task(ac_info_t * ac_task,taskid_t tkid,void * buf,size_t bufsize,size_t * sizep)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
getacct_proc(ac_info_t * ac_proc,pid_t pid,void * buf,size_t bufsize,size_t * sizep)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
getacct(idtype_t idtype,id_t id,void * buf,size_t bufsize)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
putacct(idtype_t idtype,id_t id,void * buf,size_t bufsize,int flags)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
wracct_task(ac_info_t * ac_task,taskid_t tkid,int flag,size_t * sizep)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
wracct_proc(ac_info_t * ac_proc,pid_t pid,int flag,size_t * sizep)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
wracct(idtype_t idtype,id_t id,int flags)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
exacct(int code,idtype_t idtype,id_t id,void * buf,size_t bufsize,int flags)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
_init(void)391 _init(void)
392 {
393 return (mod_install(&modlinkage));
394 }
395
396 int
_fini(void)397 _fini(void)
398 {
399 return (mod_remove(&modlinkage));
400 }
401
402 int
_info(struct modinfo * mip)403 _info(struct modinfo *mip)
404 {
405 return (mod_info(&modlinkage, mip));
406 }
407