13125Sdg/* 23125Sdg * Copyright (c) 1994 Christopher G. Demetriou 33125Sdg * All rights reserved. 43125Sdg * 53125Sdg * Redistribution and use in source and binary forms, with or without 63125Sdg * modification, are permitted provided that the following conditions 73125Sdg * are met: 83125Sdg * 1. Redistributions of source code must retain the above copyright 93125Sdg * notice, this list of conditions and the following disclaimer. 103125Sdg * 2. Redistributions in binary form must reproduce the above copyright 113125Sdg * notice, this list of conditions and the following disclaimer in the 123125Sdg * documentation and/or other materials provided with the distribution. 133125Sdg * 3. All advertising materials mentioning features or use of this software 143125Sdg * must display the following acknowledgement: 153125Sdg * This product includes software developed by Christopher G. Demetriou. 163125Sdg * 4. The name of the author may not be used to endorse or promote products 173125Sdg * derived from this software without specific prior written permission 183125Sdg * 193125Sdg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 203125Sdg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 213125Sdg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 223125Sdg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 233125Sdg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 243125Sdg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 253125Sdg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 263125Sdg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 273125Sdg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 283125Sdg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 293125Sdg */ 303125Sdg 31114601Sobrien#include <sys/cdefs.h> 32114601Sobrien__FBSDID("$FreeBSD$"); 333125Sdg 3461089Sghelmer#include <sys/param.h> 353125Sdg#include <sys/types.h> 363125Sdg#include <sys/acct.h> 373125Sdg#include <err.h> 383125Sdg#include <errno.h> 393125Sdg#include <fcntl.h> 4090878Simp#include <pwd.h> 41100107Sdes#include <stdint.h> 4213558Smpp#include <stdio.h> 4330425Scharnier#include <stdlib.h> 4413558Smpp#include <string.h> 453125Sdg#include "extern.h" 463125Sdg#include "pathnames.h" 473125Sdg 48141638Sdelphijstatic int uid_compare(const DBT *, const DBT *); 493125Sdg 503125Sdgstatic DB *usracct_db; 513125Sdg 52169857Sdds/* Legacy format in AHZV1 units. */ 53169857Sddsstruct userinfov1 { 54169857Sdds uid_t ui_uid; /* user id; for consistency */ 55169857Sdds u_quad_t ui_calls; /* number of invocations */ 56169857Sdds u_quad_t ui_utime; /* user time */ 57169857Sdds u_quad_t ui_stime; /* system time */ 58169857Sdds u_quad_t ui_mem; /* memory use */ 59169857Sdds u_quad_t ui_io; /* number of disk i/o ops */ 60169857Sdds}; 61169857Sdds 62169857Sdds/* 63169857Sdds * Convert a v1 data record into the current version. 64169857Sdds * Return 0 if OK, -1 on error, setting errno. 65169857Sdds */ 66169857Sddsstatic int 67169857Sddsv1_to_v2(DBT *key, DBT *data) 683125Sdg{ 69169857Sdds struct userinfov1 uiv1; 70169857Sdds static struct userinfo uiv2; 71169857Sdds static uid_t uid; 723125Sdg 73169857Sdds if (key->size != sizeof(u_long) || data->size != sizeof(uiv1)) { 74169857Sdds errno = EFTYPE; 753125Sdg return (-1); 76169857Sdds } 773125Sdg 78169857Sdds /* Convert key. */ 79169857Sdds key->size = sizeof(uid_t); 80169857Sdds uid = (uid_t)*(u_long *)(key->data); 81169857Sdds key->data = &uid; 823125Sdg 83169857Sdds /* Convert data. */ 84169857Sdds memcpy(&uiv1, data->data, data->size); 85169857Sdds memset(&uiv2, 0, sizeof(uiv2)); 86169857Sdds uiv2.ui_uid = uiv1.ui_uid; 87169857Sdds uiv2.ui_calls = uiv1.ui_calls; 88169857Sdds uiv2.ui_utime = ((double)uiv1.ui_utime / AHZV1) * 1000000; 89169857Sdds uiv2.ui_stime = ((double)uiv1.ui_stime / AHZV1) * 1000000; 90169857Sdds uiv2.ui_mem = uiv1.ui_mem; 91169857Sdds uiv2.ui_io = uiv1.ui_io; 92169857Sdds data->size = sizeof(uiv2); 93169857Sdds data->data = &uiv2; 943125Sdg 95169857Sdds return (0); 96169857Sdds} 973125Sdg 98169857Sdds/* Copy usrdb_file to in-memory usracct_db. */ 99169857Sddsint 100201227Sedusracct_init(void) 101169857Sdds{ 102169857Sdds BTREEINFO bti; 1033125Sdg 104169857Sdds bzero(&bti, sizeof bti); 105169857Sdds bti.compare = uid_compare; 1063125Sdg 107169857Sdds return (db_copy_in(&usracct_db, usrdb_file, "user accounting", 108169857Sdds &bti, v1_to_v2)); 1093125Sdg} 1103125Sdg 1113125Sdgvoid 112201227Sedusracct_destroy(void) 1133125Sdg{ 114169857Sdds db_destroy(usracct_db, "user accounting"); 1153125Sdg} 1163125Sdg 1173125Sdgint 118141638Sdelphijusracct_add(const struct cmdinfo *ci) 1193125Sdg{ 1203125Sdg DBT key, data; 1213125Sdg struct userinfo newui; 122169857Sdds uid_t uid; 1233125Sdg int rv; 1243125Sdg 1253125Sdg uid = ci->ci_uid; 1263125Sdg key.data = &uid; 1273125Sdg key.size = sizeof uid; 1283125Sdg 1293125Sdg rv = DB_GET(usracct_db, &key, &data, 0); 1303125Sdg if (rv < 0) { 131169857Sdds warn("get key %u from user accounting stats", uid); 1323125Sdg return (-1); 1333125Sdg } else if (rv == 0) { /* it's there; copy whole thing */ 1343125Sdg /* add the old data to the new data */ 1353125Sdg bcopy(data.data, &newui, data.size); 1363125Sdg if (newui.ui_uid != uid) { 137169857Sdds warnx("key %u != expected record number %u", 1383125Sdg newui.ui_uid, uid); 1393125Sdg warnx("inconsistent user accounting stats"); 1403125Sdg return (-1); 1413125Sdg } 1423125Sdg } else { /* it's not there; zero it and copy the key */ 1433125Sdg bzero(&newui, sizeof newui); 1443125Sdg newui.ui_uid = ci->ci_uid; 1453125Sdg } 1463125Sdg 1473125Sdg newui.ui_calls += ci->ci_calls; 1483125Sdg newui.ui_utime += ci->ci_utime; 1493125Sdg newui.ui_stime += ci->ci_stime; 1503125Sdg newui.ui_mem += ci->ci_mem; 1513125Sdg newui.ui_io += ci->ci_io; 1523125Sdg 1538857Srgrimes data.data = &newui; 1543125Sdg data.size = sizeof newui; 1553125Sdg rv = DB_PUT(usracct_db, &key, &data, 0); 1563125Sdg if (rv < 0) { 157169857Sdds warn("add key %u to user accounting stats", uid); 1583125Sdg return (-1); 1593125Sdg } else if (rv != 0) { 1603125Sdg warnx("DB_PUT returned 1"); 1613125Sdg return (-1); 1623125Sdg } 1633125Sdg 1643125Sdg return (0); 1653125Sdg} 1663125Sdg 167169857Sdds/* Copy in-memory usracct_db to usrdb_file. */ 1683125Sdgint 169201227Sedusracct_update(void) 1703125Sdg{ 1713125Sdg BTREEINFO bti; 1723125Sdg 1733125Sdg bzero(&bti, sizeof bti); 1743125Sdg bti.compare = uid_compare; 1753125Sdg 176169857Sdds return (db_copy_out(usracct_db, usrdb_file, "user accounting", 177169857Sdds &bti)); 1783125Sdg} 1793125Sdg 1803125Sdgvoid 181201227Sedusracct_print(void) 1823125Sdg{ 1833125Sdg DBT key, data; 18467642Sgallatin struct userinfo uistore, *ui = &uistore; 1853125Sdg double t; 1863125Sdg int rv; 1873125Sdg 1883125Sdg rv = DB_SEQ(usracct_db, &key, &data, R_FIRST); 1893125Sdg if (rv < 0) 1903125Sdg warn("retrieving user accounting stats"); 1913125Sdg 1923125Sdg while (rv == 0) { 19367642Sgallatin memcpy(ui, data.data, sizeof(struct userinfo)); 1943125Sdg 195100107Sdes printf("%-*s %9ju ", MAXLOGNAME - 1, 196100107Sdes user_from_uid(ui->ui_uid, 0), (uintmax_t)ui->ui_calls); 1973125Sdg 198169857Sdds t = (ui->ui_utime + ui->ui_stime) / 1000000; 199169857Sdds if (t < 0.000001) /* kill divide by zero */ 200169857Sdds t = 0.000001; 2013125Sdg 20213558Smpp printf("%12.2f%s ", t / 60.0, "cpu"); 2033125Sdg 2043125Sdg /* ui->ui_calls is always != 0 */ 2053125Sdg if (dflag) 206169857Sdds printf("%12.0f%s", 207169857Sdds ui->ui_io / ui->ui_calls, "avio"); 2083125Sdg else 209169857Sdds printf("%12.0f%s", ui->ui_io, "tio"); 2103125Sdg 211169857Sdds /* t is always >= 0.000001; see above. */ 2123125Sdg if (kflag) 21349455Sbde printf("%12.0f%s", ui->ui_mem / t, "k"); 2143125Sdg else 215169857Sdds printf("%12.0f%s", ui->ui_mem, "k*sec"); 2163125Sdg 2173125Sdg printf("\n"); 2183125Sdg 2193125Sdg rv = DB_SEQ(usracct_db, &key, &data, R_NEXT); 2203125Sdg if (rv < 0) 2213125Sdg warn("retrieving user accounting stats"); 2223125Sdg } 2233125Sdg} 2243125Sdg 2253125Sdgstatic int 226141638Sdelphijuid_compare(const DBT *k1, const DBT *k2) 2273125Sdg{ 228169857Sdds uid_t d1, d2; 2293125Sdg 2303125Sdg bcopy(k1->data, &d1, sizeof d1); 2313125Sdg bcopy(k2->data, &d2, sizeof d2); 2328857Srgrimes 2333125Sdg if (d1 < d2) 2343125Sdg return -1; 2353125Sdg else if (d1 == d2) 2363125Sdg return 0; 2373125Sdg else 2383125Sdg return 1; 2393125Sdg} 240