xref: /titanic_44/usr/src/uts/common/syscall/acctctl.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <sys/proc.h>
30*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>
31*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
32*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
33*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
34*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/user.h>
37*7c478bd9Sstevel@tonic-gate #include <sys/cred.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/file.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/acctctl.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/bitmap.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/exacct.h>
45*7c478bd9Sstevel@tonic-gate #include <sys/policy.h>
46*7c478bd9Sstevel@tonic-gate 
47*7c478bd9Sstevel@tonic-gate /*
48*7c478bd9Sstevel@tonic-gate  * acctctl(2)
49*7c478bd9Sstevel@tonic-gate  *
50*7c478bd9Sstevel@tonic-gate  *   acctctl() provides the administrative interface to the extended accounting
51*7c478bd9Sstevel@tonic-gate  *   subsystem.  The process and task accounting facilities are configurable:
52*7c478bd9Sstevel@tonic-gate  *   resources can be individually specified for recording in the appropriate
53*7c478bd9Sstevel@tonic-gate  *   accounting file.
54*7c478bd9Sstevel@tonic-gate  *
55*7c478bd9Sstevel@tonic-gate  *   The current implementation of acctctl() requires that the process and task
56*7c478bd9Sstevel@tonic-gate  *   and flow files be distinct across all zones.
57*7c478bd9Sstevel@tonic-gate  *
58*7c478bd9Sstevel@tonic-gate  * Locking
59*7c478bd9Sstevel@tonic-gate  *   Each accounting species has an ac_info_t which contains a mutex,
60*7c478bd9Sstevel@tonic-gate  *   used to protect the ac_info_t's contents, and to serialize access to the
61*7c478bd9Sstevel@tonic-gate  *   appropriate file.
62*7c478bd9Sstevel@tonic-gate  */
63*7c478bd9Sstevel@tonic-gate 
64*7c478bd9Sstevel@tonic-gate static list_t exacct_globals_list;
65*7c478bd9Sstevel@tonic-gate static kmutex_t exacct_globals_list_lock;
66*7c478bd9Sstevel@tonic-gate 
67*7c478bd9Sstevel@tonic-gate static int
68*7c478bd9Sstevel@tonic-gate ac_state_set(ac_info_t *info, void *buf, size_t bufsz)
69*7c478bd9Sstevel@tonic-gate {
70*7c478bd9Sstevel@tonic-gate 	int state;
71*7c478bd9Sstevel@tonic-gate 
72*7c478bd9Sstevel@tonic-gate 	if (buf == NULL || (bufsz != sizeof (int)))
73*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
74*7c478bd9Sstevel@tonic-gate 
75*7c478bd9Sstevel@tonic-gate 	if (copyin(buf, &state, bufsz) != 0)
76*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
77*7c478bd9Sstevel@tonic-gate 
78*7c478bd9Sstevel@tonic-gate 	if (state != AC_ON && state != AC_OFF)
79*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
80*7c478bd9Sstevel@tonic-gate 
81*7c478bd9Sstevel@tonic-gate 	mutex_enter(&info->ac_lock);
82*7c478bd9Sstevel@tonic-gate 	info->ac_state = state;
83*7c478bd9Sstevel@tonic-gate 	mutex_exit(&info->ac_lock);
84*7c478bd9Sstevel@tonic-gate 	return (0);
85*7c478bd9Sstevel@tonic-gate }
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate static int
88*7c478bd9Sstevel@tonic-gate ac_state_get(ac_info_t *info, void *buf, size_t bufsz)
89*7c478bd9Sstevel@tonic-gate {
90*7c478bd9Sstevel@tonic-gate 	if (buf == NULL || (bufsz != sizeof (int)))
91*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
92*7c478bd9Sstevel@tonic-gate 
93*7c478bd9Sstevel@tonic-gate 	mutex_enter(&info->ac_lock);
94*7c478bd9Sstevel@tonic-gate 	if (copyout(&info->ac_state, buf, bufsz) != 0) {
95*7c478bd9Sstevel@tonic-gate 		mutex_exit(&info->ac_lock);
96*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
97*7c478bd9Sstevel@tonic-gate 	}
98*7c478bd9Sstevel@tonic-gate 	mutex_exit(&info->ac_lock);
99*7c478bd9Sstevel@tonic-gate 	return (0);
100*7c478bd9Sstevel@tonic-gate }
101*7c478bd9Sstevel@tonic-gate 
102*7c478bd9Sstevel@tonic-gate static boolean_t
103*7c478bd9Sstevel@tonic-gate ac_file_in_use(vnode_t *vp)
104*7c478bd9Sstevel@tonic-gate {
105*7c478bd9Sstevel@tonic-gate 	boolean_t in_use = B_FALSE;
106*7c478bd9Sstevel@tonic-gate 	struct exacct_globals *acg;
107*7c478bd9Sstevel@tonic-gate 
108*7c478bd9Sstevel@tonic-gate 	if (vp == NULL)
109*7c478bd9Sstevel@tonic-gate 		return (B_FALSE);
110*7c478bd9Sstevel@tonic-gate 	mutex_enter(&exacct_globals_list_lock);
111*7c478bd9Sstevel@tonic-gate 	/*
112*7c478bd9Sstevel@tonic-gate 	 * Start off by grabbing all locks.
113*7c478bd9Sstevel@tonic-gate 	 */
114*7c478bd9Sstevel@tonic-gate 	for (acg = list_head(&exacct_globals_list); acg != NULL;
115*7c478bd9Sstevel@tonic-gate 	    acg = list_next(&exacct_globals_list, acg)) {
116*7c478bd9Sstevel@tonic-gate 		mutex_enter(&acg->ac_proc.ac_lock);
117*7c478bd9Sstevel@tonic-gate 		mutex_enter(&acg->ac_task.ac_lock);
118*7c478bd9Sstevel@tonic-gate 		mutex_enter(&acg->ac_flow.ac_lock);
119*7c478bd9Sstevel@tonic-gate 	}
120*7c478bd9Sstevel@tonic-gate 
121*7c478bd9Sstevel@tonic-gate 	for (acg = list_head(&exacct_globals_list); !in_use && acg != NULL;
122*7c478bd9Sstevel@tonic-gate 	    acg = list_next(&exacct_globals_list, acg)) {
123*7c478bd9Sstevel@tonic-gate 		/*
124*7c478bd9Sstevel@tonic-gate 		 * We need to verify that we aren't already using this file for
125*7c478bd9Sstevel@tonic-gate 		 * accounting in any zone.
126*7c478bd9Sstevel@tonic-gate 		 */
127*7c478bd9Sstevel@tonic-gate 		if (vn_compare(acg->ac_proc.ac_vnode, vp) ||
128*7c478bd9Sstevel@tonic-gate 		    vn_compare(acg->ac_task.ac_vnode, vp) ||
129*7c478bd9Sstevel@tonic-gate 		    vn_compare(acg->ac_flow.ac_vnode, vp))
130*7c478bd9Sstevel@tonic-gate 			in_use = B_TRUE;
131*7c478bd9Sstevel@tonic-gate 	}
132*7c478bd9Sstevel@tonic-gate 
133*7c478bd9Sstevel@tonic-gate 	/*
134*7c478bd9Sstevel@tonic-gate 	 * Drop all locks.
135*7c478bd9Sstevel@tonic-gate 	 */
136*7c478bd9Sstevel@tonic-gate 	for (acg = list_head(&exacct_globals_list); acg != NULL;
137*7c478bd9Sstevel@tonic-gate 	    acg = list_next(&exacct_globals_list, acg)) {
138*7c478bd9Sstevel@tonic-gate 		mutex_exit(&acg->ac_proc.ac_lock);
139*7c478bd9Sstevel@tonic-gate 		mutex_exit(&acg->ac_task.ac_lock);
140*7c478bd9Sstevel@tonic-gate 		mutex_exit(&acg->ac_flow.ac_lock);
141*7c478bd9Sstevel@tonic-gate 	}
142*7c478bd9Sstevel@tonic-gate 	mutex_exit(&exacct_globals_list_lock);
143*7c478bd9Sstevel@tonic-gate 	return (in_use);
144*7c478bd9Sstevel@tonic-gate }
145*7c478bd9Sstevel@tonic-gate 
146*7c478bd9Sstevel@tonic-gate static int
147*7c478bd9Sstevel@tonic-gate ac_file_set(ac_info_t *info, void *ubuf, size_t bufsz)
148*7c478bd9Sstevel@tonic-gate {
149*7c478bd9Sstevel@tonic-gate 	int error = 0;
150*7c478bd9Sstevel@tonic-gate 	void *kbuf;
151*7c478bd9Sstevel@tonic-gate 	void *namebuf;
152*7c478bd9Sstevel@tonic-gate 	int namelen;
153*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
154*7c478bd9Sstevel@tonic-gate 	void *hdr;
155*7c478bd9Sstevel@tonic-gate 	size_t hdrsize;
156*7c478bd9Sstevel@tonic-gate 
157*7c478bd9Sstevel@tonic-gate 	if (ubuf == NULL) {
158*7c478bd9Sstevel@tonic-gate 		mutex_enter(&info->ac_lock);
159*7c478bd9Sstevel@tonic-gate 
160*7c478bd9Sstevel@tonic-gate 		/*
161*7c478bd9Sstevel@tonic-gate 		 * Closing accounting file
162*7c478bd9Sstevel@tonic-gate 		 */
163*7c478bd9Sstevel@tonic-gate 		if (info->ac_vnode != NULL) {
164*7c478bd9Sstevel@tonic-gate 			error = VOP_CLOSE(info->ac_vnode, FWRITE, 1, 0, CRED());
165*7c478bd9Sstevel@tonic-gate 			if (error) {
166*7c478bd9Sstevel@tonic-gate 				mutex_exit(&info->ac_lock);
167*7c478bd9Sstevel@tonic-gate 				return (error);
168*7c478bd9Sstevel@tonic-gate 			}
169*7c478bd9Sstevel@tonic-gate 			VN_RELE(info->ac_vnode);
170*7c478bd9Sstevel@tonic-gate 			info->ac_vnode = NULL;
171*7c478bd9Sstevel@tonic-gate 		}
172*7c478bd9Sstevel@tonic-gate 		if (info->ac_file != NULL) {
173*7c478bd9Sstevel@tonic-gate 			kmem_free(info->ac_file, strlen(info->ac_file) + 1);
174*7c478bd9Sstevel@tonic-gate 			info->ac_file = NULL;
175*7c478bd9Sstevel@tonic-gate 		}
176*7c478bd9Sstevel@tonic-gate 
177*7c478bd9Sstevel@tonic-gate 		mutex_exit(&info->ac_lock);
178*7c478bd9Sstevel@tonic-gate 		return (error);
179*7c478bd9Sstevel@tonic-gate 	}
180*7c478bd9Sstevel@tonic-gate 
181*7c478bd9Sstevel@tonic-gate 	if (bufsz < 2 || bufsz > MAXPATHLEN)
182*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
183*7c478bd9Sstevel@tonic-gate 
184*7c478bd9Sstevel@tonic-gate 	/*
185*7c478bd9Sstevel@tonic-gate 	 * We have to copy in the whole buffer since we can't tell the length
186*7c478bd9Sstevel@tonic-gate 	 * of the string in user's address space.
187*7c478bd9Sstevel@tonic-gate 	 */
188*7c478bd9Sstevel@tonic-gate 	kbuf = kmem_zalloc(bufsz, KM_SLEEP);
189*7c478bd9Sstevel@tonic-gate 	if ((error = copyinstr((char *)ubuf, (char *)kbuf, bufsz, NULL)) != 0) {
190*7c478bd9Sstevel@tonic-gate 		kmem_free(kbuf, bufsz);
191*7c478bd9Sstevel@tonic-gate 		return (error);
192*7c478bd9Sstevel@tonic-gate 	}
193*7c478bd9Sstevel@tonic-gate 	if (*((char *)kbuf) != '/') {
194*7c478bd9Sstevel@tonic-gate 		kmem_free(kbuf, bufsz);
195*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
196*7c478bd9Sstevel@tonic-gate 	}
197*7c478bd9Sstevel@tonic-gate 
198*7c478bd9Sstevel@tonic-gate 	/*
199*7c478bd9Sstevel@tonic-gate 	 * Now, allocate the space where we are going to save the
200*7c478bd9Sstevel@tonic-gate 	 * name of the accounting file and kmem_free kbuf. We have to do this
201*7c478bd9Sstevel@tonic-gate 	 * now because it is not good to sleep in kmem_alloc() while
202*7c478bd9Sstevel@tonic-gate 	 * holding ac_info's lock.
203*7c478bd9Sstevel@tonic-gate 	 */
204*7c478bd9Sstevel@tonic-gate 	namelen = strlen(kbuf) + 1;
205*7c478bd9Sstevel@tonic-gate 	namebuf = kmem_alloc(namelen, KM_SLEEP);
206*7c478bd9Sstevel@tonic-gate 	(void) strcpy(namebuf, kbuf);
207*7c478bd9Sstevel@tonic-gate 	kmem_free(kbuf, bufsz);
208*7c478bd9Sstevel@tonic-gate 
209*7c478bd9Sstevel@tonic-gate 	/*
210*7c478bd9Sstevel@tonic-gate 	 * Check if this file already exists.
211*7c478bd9Sstevel@tonic-gate 	 */
212*7c478bd9Sstevel@tonic-gate 	error = lookupname(namebuf, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
213*7c478bd9Sstevel@tonic-gate 
214*7c478bd9Sstevel@tonic-gate 	/*
215*7c478bd9Sstevel@tonic-gate 	 * Check if the file is already in use.
216*7c478bd9Sstevel@tonic-gate 	 */
217*7c478bd9Sstevel@tonic-gate 	if (!error) {
218*7c478bd9Sstevel@tonic-gate 		if (ac_file_in_use(vp)) {
219*7c478bd9Sstevel@tonic-gate 			/*
220*7c478bd9Sstevel@tonic-gate 			 * If we're already using it then return EBUSY
221*7c478bd9Sstevel@tonic-gate 			 */
222*7c478bd9Sstevel@tonic-gate 			kmem_free(namebuf, namelen);
223*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
224*7c478bd9Sstevel@tonic-gate 			return (EBUSY);
225*7c478bd9Sstevel@tonic-gate 		}
226*7c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
227*7c478bd9Sstevel@tonic-gate 	}
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate 	/*
230*7c478bd9Sstevel@tonic-gate 	 * Now, grab info's ac_lock and try to set up everything.
231*7c478bd9Sstevel@tonic-gate 	 */
232*7c478bd9Sstevel@tonic-gate 	mutex_enter(&info->ac_lock);
233*7c478bd9Sstevel@tonic-gate 
234*7c478bd9Sstevel@tonic-gate 	if ((error = vn_open(namebuf, UIO_SYSSPACE,
235*7c478bd9Sstevel@tonic-gate 	    FCREAT | FWRITE | FTRUNC, 0600, &vp, CRCREAT, 0)) != 0) {
236*7c478bd9Sstevel@tonic-gate 		mutex_exit(&info->ac_lock);
237*7c478bd9Sstevel@tonic-gate 		kmem_free(namebuf, namelen);
238*7c478bd9Sstevel@tonic-gate 		return (error);
239*7c478bd9Sstevel@tonic-gate 	}
240*7c478bd9Sstevel@tonic-gate 
241*7c478bd9Sstevel@tonic-gate 	if (vp->v_type != VREG) {
242*7c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
243*7c478bd9Sstevel@tonic-gate 		mutex_exit(&info->ac_lock);
244*7c478bd9Sstevel@tonic-gate 		kmem_free(namebuf, namelen);
245*7c478bd9Sstevel@tonic-gate 		return (EACCES);
246*7c478bd9Sstevel@tonic-gate 	}
247*7c478bd9Sstevel@tonic-gate 
248*7c478bd9Sstevel@tonic-gate 	if (info->ac_vnode != NULL) {
249*7c478bd9Sstevel@tonic-gate 		/*
250*7c478bd9Sstevel@tonic-gate 		 * Switch from an old file to a new file by swapping
251*7c478bd9Sstevel@tonic-gate 		 * their vnode pointers.
252*7c478bd9Sstevel@tonic-gate 		 */
253*7c478bd9Sstevel@tonic-gate 		vnode_t *oldvp;
254*7c478bd9Sstevel@tonic-gate 		oldvp = info->ac_vnode;
255*7c478bd9Sstevel@tonic-gate 		info->ac_vnode = vp;
256*7c478bd9Sstevel@tonic-gate 		vp = oldvp;
257*7c478bd9Sstevel@tonic-gate 	} else {
258*7c478bd9Sstevel@tonic-gate 		/*
259*7c478bd9Sstevel@tonic-gate 		 * Start writing accounting records to a new file.
260*7c478bd9Sstevel@tonic-gate 		 */
261*7c478bd9Sstevel@tonic-gate 		info->ac_vnode = vp;
262*7c478bd9Sstevel@tonic-gate 		vp = NULL;
263*7c478bd9Sstevel@tonic-gate 	}
264*7c478bd9Sstevel@tonic-gate 	if (vp) {
265*7c478bd9Sstevel@tonic-gate 		/*
266*7c478bd9Sstevel@tonic-gate 		 * We still need to close the old file.
267*7c478bd9Sstevel@tonic-gate 		 */
268*7c478bd9Sstevel@tonic-gate 		if ((error = VOP_CLOSE(vp, FWRITE, 1, 0, CRED())) != 0) {
269*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
270*7c478bd9Sstevel@tonic-gate 			mutex_exit(&info->ac_lock);
271*7c478bd9Sstevel@tonic-gate 			kmem_free(namebuf, namelen);
272*7c478bd9Sstevel@tonic-gate 			return (error);
273*7c478bd9Sstevel@tonic-gate 		}
274*7c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
275*7c478bd9Sstevel@tonic-gate 		if (info->ac_file != NULL) {
276*7c478bd9Sstevel@tonic-gate 			kmem_free(info->ac_file,
277*7c478bd9Sstevel@tonic-gate 			    strlen(info->ac_file) + 1);
278*7c478bd9Sstevel@tonic-gate 			info->ac_file = NULL;
279*7c478bd9Sstevel@tonic-gate 		}
280*7c478bd9Sstevel@tonic-gate 	}
281*7c478bd9Sstevel@tonic-gate 	/*
282*7c478bd9Sstevel@tonic-gate 	 * Finally, point ac_file to the filename string and release the lock.
283*7c478bd9Sstevel@tonic-gate 	 */
284*7c478bd9Sstevel@tonic-gate 	info->ac_file = namebuf;
285*7c478bd9Sstevel@tonic-gate 	mutex_exit(&info->ac_lock);
286*7c478bd9Sstevel@tonic-gate 
287*7c478bd9Sstevel@tonic-gate 	/*
288*7c478bd9Sstevel@tonic-gate 	 * Create and write an exacct header to the file.
289*7c478bd9Sstevel@tonic-gate 	 */
290*7c478bd9Sstevel@tonic-gate 	hdr = exacct_create_header(&hdrsize);
291*7c478bd9Sstevel@tonic-gate 	error = exacct_write_header(info, hdr, hdrsize);
292*7c478bd9Sstevel@tonic-gate 
293*7c478bd9Sstevel@tonic-gate 	return (error);
294*7c478bd9Sstevel@tonic-gate }
295*7c478bd9Sstevel@tonic-gate 
296*7c478bd9Sstevel@tonic-gate static int
297*7c478bd9Sstevel@tonic-gate ac_file_get(ac_info_t *info, void *buf, size_t bufsz)
298*7c478bd9Sstevel@tonic-gate {
299*7c478bd9Sstevel@tonic-gate 	int error = 0;
300*7c478bd9Sstevel@tonic-gate 	vnode_t *vnode;
301*7c478bd9Sstevel@tonic-gate 	char *file;
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 	mutex_enter(&info->ac_lock);
304*7c478bd9Sstevel@tonic-gate 	file = info->ac_file;
305*7c478bd9Sstevel@tonic-gate 	vnode = info->ac_vnode;
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate 	if (file == NULL || vnode == NULL) {
308*7c478bd9Sstevel@tonic-gate 		mutex_exit(&info->ac_lock);
309*7c478bd9Sstevel@tonic-gate 		return (ENOTACTIVE);
310*7c478bd9Sstevel@tonic-gate 	}
311*7c478bd9Sstevel@tonic-gate 
312*7c478bd9Sstevel@tonic-gate 	if (strlen(file) >= bufsz)
313*7c478bd9Sstevel@tonic-gate 		error = ENOMEM;
314*7c478bd9Sstevel@tonic-gate 	else
315*7c478bd9Sstevel@tonic-gate 		error = copyoutstr(file, buf, MAXPATHLEN, NULL);
316*7c478bd9Sstevel@tonic-gate 
317*7c478bd9Sstevel@tonic-gate 	mutex_exit(&info->ac_lock);
318*7c478bd9Sstevel@tonic-gate 	return (error);
319*7c478bd9Sstevel@tonic-gate }
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate static int
322*7c478bd9Sstevel@tonic-gate ac_res_set(ac_info_t *info, void *buf, size_t bufsz, int maxres)
323*7c478bd9Sstevel@tonic-gate {
324*7c478bd9Sstevel@tonic-gate 	ac_res_t *res;
325*7c478bd9Sstevel@tonic-gate 	ac_res_t *tmp;
326*7c478bd9Sstevel@tonic-gate 	ulong_t *maskp;
327*7c478bd9Sstevel@tonic-gate 	int id;
328*7c478bd9Sstevel@tonic-gate 	uint_t counter = 0;
329*7c478bd9Sstevel@tonic-gate 
330*7c478bd9Sstevel@tonic-gate 	/*
331*7c478bd9Sstevel@tonic-gate 	 * Validate that a non-zero buffer, sized within limits and to an
332*7c478bd9Sstevel@tonic-gate 	 * integral number of ac_res_t's has been specified.
333*7c478bd9Sstevel@tonic-gate 	 */
334*7c478bd9Sstevel@tonic-gate 	if (bufsz == 0 ||
335*7c478bd9Sstevel@tonic-gate 	    bufsz > sizeof (ac_res_t) * (AC_MAX_RES + 1) ||
336*7c478bd9Sstevel@tonic-gate 	    (bufsz / sizeof (ac_res_t)) * sizeof (ac_res_t) != bufsz)
337*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
338*7c478bd9Sstevel@tonic-gate 
339*7c478bd9Sstevel@tonic-gate 	tmp = res = kmem_alloc(bufsz, KM_SLEEP);
340*7c478bd9Sstevel@tonic-gate 	if (copyin(buf, res, bufsz) != 0) {
341*7c478bd9Sstevel@tonic-gate 		kmem_free(res, bufsz);
342*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
343*7c478bd9Sstevel@tonic-gate 	}
344*7c478bd9Sstevel@tonic-gate 
345*7c478bd9Sstevel@tonic-gate 	maskp = (ulong_t *)&info->ac_mask;
346*7c478bd9Sstevel@tonic-gate 
347*7c478bd9Sstevel@tonic-gate 	mutex_enter(&info->ac_lock);
348*7c478bd9Sstevel@tonic-gate 	while ((id = tmp->ar_id) != AC_NONE && counter < maxres + 1) {
349*7c478bd9Sstevel@tonic-gate 		if (id > maxres || id < 0) {
350*7c478bd9Sstevel@tonic-gate 			mutex_exit(&info->ac_lock);
351*7c478bd9Sstevel@tonic-gate 			kmem_free(res, bufsz);
352*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
353*7c478bd9Sstevel@tonic-gate 		}
354*7c478bd9Sstevel@tonic-gate 		if (tmp->ar_state == AC_ON) {
355*7c478bd9Sstevel@tonic-gate 			BT_SET(maskp, id);
356*7c478bd9Sstevel@tonic-gate 		} else if (tmp->ar_state == AC_OFF) {
357*7c478bd9Sstevel@tonic-gate 			BT_CLEAR(maskp, id);
358*7c478bd9Sstevel@tonic-gate 		} else {
359*7c478bd9Sstevel@tonic-gate 			mutex_exit(&info->ac_lock);
360*7c478bd9Sstevel@tonic-gate 			kmem_free(res, bufsz);
361*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
362*7c478bd9Sstevel@tonic-gate 		}
363*7c478bd9Sstevel@tonic-gate 		tmp++;
364*7c478bd9Sstevel@tonic-gate 		counter++;
365*7c478bd9Sstevel@tonic-gate 	}
366*7c478bd9Sstevel@tonic-gate 	mutex_exit(&info->ac_lock);
367*7c478bd9Sstevel@tonic-gate 	kmem_free(res, bufsz);
368*7c478bd9Sstevel@tonic-gate 	return (0);
369*7c478bd9Sstevel@tonic-gate }
370*7c478bd9Sstevel@tonic-gate 
371*7c478bd9Sstevel@tonic-gate static int
372*7c478bd9Sstevel@tonic-gate ac_res_get(ac_info_t *info, void *buf, size_t bufsz, int maxres)
373*7c478bd9Sstevel@tonic-gate {
374*7c478bd9Sstevel@tonic-gate 	int error = 0;
375*7c478bd9Sstevel@tonic-gate 	ac_res_t *res;
376*7c478bd9Sstevel@tonic-gate 	ac_res_t *tmp;
377*7c478bd9Sstevel@tonic-gate 	size_t ressz = sizeof (ac_res_t) * (maxres + 1);
378*7c478bd9Sstevel@tonic-gate 	ulong_t *maskp;
379*7c478bd9Sstevel@tonic-gate 	int id;
380*7c478bd9Sstevel@tonic-gate 
381*7c478bd9Sstevel@tonic-gate 	if (bufsz < ressz)
382*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
383*7c478bd9Sstevel@tonic-gate 	tmp = res = kmem_alloc(ressz, KM_SLEEP);
384*7c478bd9Sstevel@tonic-gate 
385*7c478bd9Sstevel@tonic-gate 	mutex_enter(&info->ac_lock);
386*7c478bd9Sstevel@tonic-gate 	maskp = (ulong_t *)&info->ac_mask;
387*7c478bd9Sstevel@tonic-gate 	for (id = 1; id <= maxres; id++) {
388*7c478bd9Sstevel@tonic-gate 		tmp->ar_id = id;
389*7c478bd9Sstevel@tonic-gate 		tmp->ar_state = BT_TEST(maskp, id);
390*7c478bd9Sstevel@tonic-gate 		tmp++;
391*7c478bd9Sstevel@tonic-gate 	}
392*7c478bd9Sstevel@tonic-gate 	tmp->ar_id = AC_NONE;
393*7c478bd9Sstevel@tonic-gate 	tmp->ar_state = AC_OFF;
394*7c478bd9Sstevel@tonic-gate 	mutex_exit(&info->ac_lock);
395*7c478bd9Sstevel@tonic-gate 	error = copyout(res, buf, ressz);
396*7c478bd9Sstevel@tonic-gate 	kmem_free(res, ressz);
397*7c478bd9Sstevel@tonic-gate 	return (error);
398*7c478bd9Sstevel@tonic-gate }
399*7c478bd9Sstevel@tonic-gate 
400*7c478bd9Sstevel@tonic-gate /*
401*7c478bd9Sstevel@tonic-gate  * acctctl()
402*7c478bd9Sstevel@tonic-gate  *
403*7c478bd9Sstevel@tonic-gate  * Overview
404*7c478bd9Sstevel@tonic-gate  *   acctctl() is the entry point for the acctctl(2) system call.
405*7c478bd9Sstevel@tonic-gate  *
406*7c478bd9Sstevel@tonic-gate  * Return values
407*7c478bd9Sstevel@tonic-gate  *   On successful completion, return 0; otherwise -1 is returned and errno is
408*7c478bd9Sstevel@tonic-gate  *   set appropriately.
409*7c478bd9Sstevel@tonic-gate  *
410*7c478bd9Sstevel@tonic-gate  * Caller's context
411*7c478bd9Sstevel@tonic-gate  *   Called from the system call path.
412*7c478bd9Sstevel@tonic-gate  */
413*7c478bd9Sstevel@tonic-gate int
414*7c478bd9Sstevel@tonic-gate acctctl(int cmd, void *buf, size_t bufsz)
415*7c478bd9Sstevel@tonic-gate {
416*7c478bd9Sstevel@tonic-gate 	int error = 0;
417*7c478bd9Sstevel@tonic-gate 	int mode = AC_MODE(cmd);
418*7c478bd9Sstevel@tonic-gate 	int option = AC_OPTION(cmd);
419*7c478bd9Sstevel@tonic-gate 	int maxres;
420*7c478bd9Sstevel@tonic-gate 	ac_info_t *info;
421*7c478bd9Sstevel@tonic-gate 	zone_t *zone = curproc->p_zone;
422*7c478bd9Sstevel@tonic-gate 	struct exacct_globals *acg;
423*7c478bd9Sstevel@tonic-gate 
424*7c478bd9Sstevel@tonic-gate 	acg = zone_getspecific(exacct_zone_key, zone);
425*7c478bd9Sstevel@tonic-gate 	/*
426*7c478bd9Sstevel@tonic-gate 	 * exacct_zone_key and associated per-zone state were initialized when
427*7c478bd9Sstevel@tonic-gate 	 * the module was loaded.
428*7c478bd9Sstevel@tonic-gate 	 */
429*7c478bd9Sstevel@tonic-gate 	ASSERT(exacct_zone_key != ZONE_KEY_UNINITIALIZED);
430*7c478bd9Sstevel@tonic-gate 	ASSERT(acg != NULL);
431*7c478bd9Sstevel@tonic-gate 
432*7c478bd9Sstevel@tonic-gate 	switch (mode) {	/* sanity check */
433*7c478bd9Sstevel@tonic-gate 	case AC_TASK:
434*7c478bd9Sstevel@tonic-gate 		info = &acg->ac_task;
435*7c478bd9Sstevel@tonic-gate 		maxres = AC_TASK_MAX_RES;
436*7c478bd9Sstevel@tonic-gate 		break;
437*7c478bd9Sstevel@tonic-gate 	case AC_PROC:
438*7c478bd9Sstevel@tonic-gate 		info = &acg->ac_proc;
439*7c478bd9Sstevel@tonic-gate 		maxres = AC_PROC_MAX_RES;
440*7c478bd9Sstevel@tonic-gate 		break;
441*7c478bd9Sstevel@tonic-gate 	case AC_FLOW:
442*7c478bd9Sstevel@tonic-gate 		/*
443*7c478bd9Sstevel@tonic-gate 		 * Flow accounting isn't currently configurable in non-global
444*7c478bd9Sstevel@tonic-gate 		 * zones, but we have this field on a per-zone basis for future
445*7c478bd9Sstevel@tonic-gate 		 * expansion as well as the ability to return default "unset"
446*7c478bd9Sstevel@tonic-gate 		 * values for the various AC_*_GET queries.  AC_*_SET commands
447*7c478bd9Sstevel@tonic-gate 		 * fail with EPERM for AC_FLOW in non-global zones.
448*7c478bd9Sstevel@tonic-gate 		 */
449*7c478bd9Sstevel@tonic-gate 		info = &acg->ac_flow;
450*7c478bd9Sstevel@tonic-gate 		maxres = AC_FLOW_MAX_RES;
451*7c478bd9Sstevel@tonic-gate 		break;
452*7c478bd9Sstevel@tonic-gate 	default:
453*7c478bd9Sstevel@tonic-gate 		return (set_errno(EINVAL));
454*7c478bd9Sstevel@tonic-gate 	}
455*7c478bd9Sstevel@tonic-gate 
456*7c478bd9Sstevel@tonic-gate 	switch (option) {
457*7c478bd9Sstevel@tonic-gate 	case AC_STATE_SET:
458*7c478bd9Sstevel@tonic-gate 		if ((error = secpolicy_acct(CRED())) != 0)
459*7c478bd9Sstevel@tonic-gate 			break;
460*7c478bd9Sstevel@tonic-gate 		if (mode == AC_FLOW && getzoneid() != GLOBAL_ZONEID) {
461*7c478bd9Sstevel@tonic-gate 			error = EPERM;
462*7c478bd9Sstevel@tonic-gate 			break;
463*7c478bd9Sstevel@tonic-gate 		}
464*7c478bd9Sstevel@tonic-gate 		error = ac_state_set(info, buf, bufsz);
465*7c478bd9Sstevel@tonic-gate 		break;
466*7c478bd9Sstevel@tonic-gate 	case AC_STATE_GET:
467*7c478bd9Sstevel@tonic-gate 		error = ac_state_get(info, buf, bufsz);
468*7c478bd9Sstevel@tonic-gate 		break;
469*7c478bd9Sstevel@tonic-gate 	case AC_FILE_SET:
470*7c478bd9Sstevel@tonic-gate 		if ((error = secpolicy_acct(CRED())) != 0)
471*7c478bd9Sstevel@tonic-gate 			break;
472*7c478bd9Sstevel@tonic-gate 		if (mode == AC_FLOW && getzoneid() != GLOBAL_ZONEID) {
473*7c478bd9Sstevel@tonic-gate 			error = EPERM;
474*7c478bd9Sstevel@tonic-gate 			break;
475*7c478bd9Sstevel@tonic-gate 		}
476*7c478bd9Sstevel@tonic-gate 		error = ac_file_set(info, buf, bufsz);
477*7c478bd9Sstevel@tonic-gate 		break;
478*7c478bd9Sstevel@tonic-gate 	case AC_FILE_GET:
479*7c478bd9Sstevel@tonic-gate 		error = ac_file_get(info, buf, bufsz);
480*7c478bd9Sstevel@tonic-gate 		break;
481*7c478bd9Sstevel@tonic-gate 	case AC_RES_SET:
482*7c478bd9Sstevel@tonic-gate 		if ((error = secpolicy_acct(CRED())) != 0)
483*7c478bd9Sstevel@tonic-gate 			break;
484*7c478bd9Sstevel@tonic-gate 		if (mode == AC_FLOW && getzoneid() != GLOBAL_ZONEID) {
485*7c478bd9Sstevel@tonic-gate 			error = EPERM;
486*7c478bd9Sstevel@tonic-gate 			break;
487*7c478bd9Sstevel@tonic-gate 		}
488*7c478bd9Sstevel@tonic-gate 		error = ac_res_set(info, buf, bufsz, maxres);
489*7c478bd9Sstevel@tonic-gate 		break;
490*7c478bd9Sstevel@tonic-gate 	case AC_RES_GET:
491*7c478bd9Sstevel@tonic-gate 		error = ac_res_get(info, buf, bufsz, maxres);
492*7c478bd9Sstevel@tonic-gate 		break;
493*7c478bd9Sstevel@tonic-gate 	default:
494*7c478bd9Sstevel@tonic-gate 		return (set_errno(EINVAL));
495*7c478bd9Sstevel@tonic-gate 	}
496*7c478bd9Sstevel@tonic-gate 	if (error)
497*7c478bd9Sstevel@tonic-gate 		return (set_errno(error));
498*7c478bd9Sstevel@tonic-gate 	return (0);
499*7c478bd9Sstevel@tonic-gate }
500*7c478bd9Sstevel@tonic-gate 
501*7c478bd9Sstevel@tonic-gate static struct sysent ac_sysent = {
502*7c478bd9Sstevel@tonic-gate 	3,
503*7c478bd9Sstevel@tonic-gate 	SE_NOUNLOAD | SE_ARGC | SE_32RVAL1,
504*7c478bd9Sstevel@tonic-gate 	acctctl
505*7c478bd9Sstevel@tonic-gate };
506*7c478bd9Sstevel@tonic-gate 
507*7c478bd9Sstevel@tonic-gate static struct modlsys modlsys = {
508*7c478bd9Sstevel@tonic-gate 	&mod_syscallops,
509*7c478bd9Sstevel@tonic-gate 	"acctctl system call",
510*7c478bd9Sstevel@tonic-gate 	&ac_sysent
511*7c478bd9Sstevel@tonic-gate };
512*7c478bd9Sstevel@tonic-gate 
513*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
514*7c478bd9Sstevel@tonic-gate static struct modlsys modlsys32 = {
515*7c478bd9Sstevel@tonic-gate 	&mod_syscallops32,
516*7c478bd9Sstevel@tonic-gate 	"32-bit acctctl system call",
517*7c478bd9Sstevel@tonic-gate 	&ac_sysent
518*7c478bd9Sstevel@tonic-gate };
519*7c478bd9Sstevel@tonic-gate #endif
520*7c478bd9Sstevel@tonic-gate 
521*7c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
522*7c478bd9Sstevel@tonic-gate 	MODREV_1,
523*7c478bd9Sstevel@tonic-gate 	&modlsys,
524*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
525*7c478bd9Sstevel@tonic-gate 	&modlsys32,
526*7c478bd9Sstevel@tonic-gate #endif
527*7c478bd9Sstevel@tonic-gate 	NULL
528*7c478bd9Sstevel@tonic-gate };
529*7c478bd9Sstevel@tonic-gate 
530*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
531*7c478bd9Sstevel@tonic-gate static void *
532*7c478bd9Sstevel@tonic-gate exacct_zone_init(zoneid_t zoneid)
533*7c478bd9Sstevel@tonic-gate {
534*7c478bd9Sstevel@tonic-gate 	struct exacct_globals *acg;
535*7c478bd9Sstevel@tonic-gate 
536*7c478bd9Sstevel@tonic-gate 	acg = kmem_zalloc(sizeof (*acg), KM_SLEEP);
537*7c478bd9Sstevel@tonic-gate 	mutex_enter(&exacct_globals_list_lock);
538*7c478bd9Sstevel@tonic-gate 	list_insert_tail(&exacct_globals_list, acg);
539*7c478bd9Sstevel@tonic-gate 	mutex_exit(&exacct_globals_list_lock);
540*7c478bd9Sstevel@tonic-gate 	return (acg);
541*7c478bd9Sstevel@tonic-gate }
542*7c478bd9Sstevel@tonic-gate 
543*7c478bd9Sstevel@tonic-gate static void
544*7c478bd9Sstevel@tonic-gate exacct_free_info(ac_info_t *info)
545*7c478bd9Sstevel@tonic-gate {
546*7c478bd9Sstevel@tonic-gate 	mutex_enter(&info->ac_lock);
547*7c478bd9Sstevel@tonic-gate 	if (info->ac_vnode) {
548*7c478bd9Sstevel@tonic-gate 		(void) VOP_CLOSE(info->ac_vnode, FWRITE, 1, 0, kcred);
549*7c478bd9Sstevel@tonic-gate 		VN_RELE(info->ac_vnode);
550*7c478bd9Sstevel@tonic-gate 		kmem_free(info->ac_file, strlen(info->ac_file) + 1);
551*7c478bd9Sstevel@tonic-gate 	}
552*7c478bd9Sstevel@tonic-gate 	info->ac_state = AC_OFF;
553*7c478bd9Sstevel@tonic-gate 	info->ac_vnode = NULL;
554*7c478bd9Sstevel@tonic-gate 	info->ac_file = NULL;
555*7c478bd9Sstevel@tonic-gate 	mutex_exit(&info->ac_lock);
556*7c478bd9Sstevel@tonic-gate }
557*7c478bd9Sstevel@tonic-gate 
558*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
559*7c478bd9Sstevel@tonic-gate static void
560*7c478bd9Sstevel@tonic-gate exacct_zone_shutdown(zoneid_t zoneid, void *data)
561*7c478bd9Sstevel@tonic-gate {
562*7c478bd9Sstevel@tonic-gate 	struct exacct_globals *acg = data;
563*7c478bd9Sstevel@tonic-gate 
564*7c478bd9Sstevel@tonic-gate 	/*
565*7c478bd9Sstevel@tonic-gate 	 * The accounting files need to be closed during shutdown rather than
566*7c478bd9Sstevel@tonic-gate 	 * destroy, since otherwise the filesystem they reside on may fail to
567*7c478bd9Sstevel@tonic-gate 	 * unmount, thus causing the entire zone halt/reboot to fail.
568*7c478bd9Sstevel@tonic-gate 	 */
569*7c478bd9Sstevel@tonic-gate 	exacct_free_info(&acg->ac_proc);
570*7c478bd9Sstevel@tonic-gate 	exacct_free_info(&acg->ac_task);
571*7c478bd9Sstevel@tonic-gate 	exacct_free_info(&acg->ac_flow);
572*7c478bd9Sstevel@tonic-gate }
573*7c478bd9Sstevel@tonic-gate 
574*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
575*7c478bd9Sstevel@tonic-gate static void
576*7c478bd9Sstevel@tonic-gate exacct_zone_fini(zoneid_t zoneid, void *data)
577*7c478bd9Sstevel@tonic-gate {
578*7c478bd9Sstevel@tonic-gate 	struct exacct_globals *acg = data;
579*7c478bd9Sstevel@tonic-gate 
580*7c478bd9Sstevel@tonic-gate 	mutex_enter(&exacct_globals_list_lock);
581*7c478bd9Sstevel@tonic-gate 	list_remove(&exacct_globals_list, acg);
582*7c478bd9Sstevel@tonic-gate 	mutex_exit(&exacct_globals_list_lock);
583*7c478bd9Sstevel@tonic-gate 
584*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&acg->ac_proc.ac_lock);
585*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&acg->ac_task.ac_lock);
586*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&acg->ac_flow.ac_lock);
587*7c478bd9Sstevel@tonic-gate 	kmem_free(acg, sizeof (*acg));
588*7c478bd9Sstevel@tonic-gate }
589*7c478bd9Sstevel@tonic-gate 
590*7c478bd9Sstevel@tonic-gate int
591*7c478bd9Sstevel@tonic-gate _init()
592*7c478bd9Sstevel@tonic-gate {
593*7c478bd9Sstevel@tonic-gate 	int error;
594*7c478bd9Sstevel@tonic-gate 
595*7c478bd9Sstevel@tonic-gate 	mutex_init(&exacct_globals_list_lock, NULL, MUTEX_DEFAULT, NULL);
596*7c478bd9Sstevel@tonic-gate 	list_create(&exacct_globals_list, sizeof (struct exacct_globals),
597*7c478bd9Sstevel@tonic-gate 	    offsetof(struct exacct_globals, ac_link));
598*7c478bd9Sstevel@tonic-gate 	zone_key_create(&exacct_zone_key, exacct_zone_init,
599*7c478bd9Sstevel@tonic-gate 	    exacct_zone_shutdown, exacct_zone_fini);
600*7c478bd9Sstevel@tonic-gate 
601*7c478bd9Sstevel@tonic-gate 	if ((error = mod_install(&modlinkage)) != 0) {
602*7c478bd9Sstevel@tonic-gate 		(void) zone_key_delete(exacct_zone_key);
603*7c478bd9Sstevel@tonic-gate 		exacct_zone_key = ZONE_KEY_UNINITIALIZED;
604*7c478bd9Sstevel@tonic-gate 		mutex_destroy(&exacct_globals_list_lock);
605*7c478bd9Sstevel@tonic-gate 		list_destroy(&exacct_globals_list);
606*7c478bd9Sstevel@tonic-gate 	}
607*7c478bd9Sstevel@tonic-gate 	return (error);
608*7c478bd9Sstevel@tonic-gate }
609*7c478bd9Sstevel@tonic-gate 
610*7c478bd9Sstevel@tonic-gate int
611*7c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
612*7c478bd9Sstevel@tonic-gate {
613*7c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
614*7c478bd9Sstevel@tonic-gate }
615*7c478bd9Sstevel@tonic-gate 
616*7c478bd9Sstevel@tonic-gate int
617*7c478bd9Sstevel@tonic-gate _fini()
618*7c478bd9Sstevel@tonic-gate {
619*7c478bd9Sstevel@tonic-gate 	return (EBUSY);
620*7c478bd9Sstevel@tonic-gate }
621