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