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