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