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