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
  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
  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
 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
 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
 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
 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
 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
 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
 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
 391 _init(void)
 392 {
 393         return (mod_install(&modlinkage));
 394 }
 395 
 396 int
 397 _fini(void)
 398 {
 399         return (mod_remove(&modlinkage));
 400 }
 401 
 402 int
 403 _info(struct modinfo *mip)
 404 {
 405         return (mod_info(&modlinkage, mip));
 406 }