usrdb.c revision 201227
184865Sobrien/* 2218822Sdim * Copyright (c) 1994 Christopher G. Demetriou 384865Sobrien * All rights reserved. 484865Sobrien * 584865Sobrien * Redistribution and use in source and binary forms, with or without 684865Sobrien * modification, are permitted provided that the following conditions 784865Sobrien * are met: 884865Sobrien * 1. Redistributions of source code must retain the above copyright 984865Sobrien * notice, this list of conditions and the following disclaimer. 1084865Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1184865Sobrien * notice, this list of conditions and the following disclaimer in the 1284865Sobrien * documentation and/or other materials provided with the distribution. 1384865Sobrien * 3. All advertising materials mentioning features or use of this software 1484865Sobrien * must display the following acknowledgement: 1584865Sobrien * This product includes software developed by Christopher G. Demetriou. 1684865Sobrien * 4. The name of the author may not be used to endorse or promote products 1784865Sobrien * derived from this software without specific prior written permission 1884865Sobrien * 19218822Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20218822Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2184865Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2284865Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23218822Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2484865Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2584865Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2684865Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2784865Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28130561Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29130561Sobrien */ 30130561Sobrien 31130561Sobrien#include <sys/cdefs.h> 32130561Sobrien__FBSDID("$FreeBSD: head/usr.sbin/sa/usrdb.c 201227 2009-12-29 22:53:27Z ed $"); 33130561Sobrien 34130561Sobrien#include <sys/param.h> 35130561Sobrien#include <sys/types.h> 36130561Sobrien#include <sys/acct.h> 3789857Sobrien#include <err.h> 38130561Sobrien#include <errno.h> 3989857Sobrien#include <fcntl.h> 40130561Sobrien#include <pwd.h> 4189857Sobrien#include <stdint.h> 4284865Sobrien#include <stdio.h> 4384865Sobrien#include <stdlib.h> 4484865Sobrien#include <string.h> 4584865Sobrien#include "extern.h" 4684865Sobrien#include "pathnames.h" 4784865Sobrien 4884865Sobrienstatic int uid_compare(const DBT *, const DBT *); 4984865Sobrien 5084865Sobrienstatic DB *usracct_db; 5184865Sobrien 5284865Sobrien/* Legacy format in AHZV1 units. */ 5384865Sobrienstruct userinfov1 { 5484865Sobrien uid_t ui_uid; /* user id; for consistency */ 5584865Sobrien u_quad_t ui_calls; /* number of invocations */ 5684865Sobrien u_quad_t ui_utime; /* user time */ 5784865Sobrien u_quad_t ui_stime; /* system time */ 5884865Sobrien u_quad_t ui_mem; /* memory use */ 5984865Sobrien u_quad_t ui_io; /* number of disk i/o ops */ 6084865Sobrien}; 6184865Sobrien 6284865Sobrien/* 6384865Sobrien * Convert a v1 data record into the current version. 6484865Sobrien * Return 0 if OK, -1 on error, setting errno. 6584865Sobrien */ 6684865Sobrienstatic int 6784865Sobrienv1_to_v2(DBT *key, DBT *data) 68130561Sobrien{ 6984865Sobrien struct userinfov1 uiv1; 7084865Sobrien static struct userinfo uiv2; 7184865Sobrien static uid_t uid; 7284865Sobrien 7384865Sobrien if (key->size != sizeof(u_long) || data->size != sizeof(uiv1)) { 7484865Sobrien errno = EFTYPE; 7584865Sobrien return (-1); 7684865Sobrien } 7784865Sobrien 7884865Sobrien /* Convert key. */ 7984865Sobrien key->size = sizeof(uid_t); 8084865Sobrien uid = (uid_t)*(u_long *)(key->data); 8184865Sobrien key->data = &uid; 8284865Sobrien 8384865Sobrien /* Convert data. */ 8484865Sobrien memcpy(&uiv1, data->data, data->size); 8584865Sobrien memset(&uiv2, 0, sizeof(uiv2)); 8684865Sobrien uiv2.ui_uid = uiv1.ui_uid; 8784865Sobrien uiv2.ui_calls = uiv1.ui_calls; 8884865Sobrien uiv2.ui_utime = ((double)uiv1.ui_utime / AHZV1) * 1000000; 8984865Sobrien uiv2.ui_stime = ((double)uiv1.ui_stime / AHZV1) * 1000000; 90130561Sobrien uiv2.ui_mem = uiv1.ui_mem; 9184865Sobrien uiv2.ui_io = uiv1.ui_io; 9284865Sobrien data->size = sizeof(uiv2); 9384865Sobrien data->data = &uiv2; 9484865Sobrien 9584865Sobrien return (0); 9684865Sobrien} 9784865Sobrien 9884865Sobrien/* Copy usrdb_file to in-memory usracct_db. */ 9984865Sobrienint 10084865Sobrienusracct_init(void) 10184865Sobrien{ 10284865Sobrien BTREEINFO bti; 10384865Sobrien 10484865Sobrien bzero(&bti, sizeof bti); 10584865Sobrien bti.compare = uid_compare; 10684865Sobrien 10784865Sobrien return (db_copy_in(&usracct_db, usrdb_file, "user accounting", 10884865Sobrien &bti, v1_to_v2)); 10984865Sobrien} 11084865Sobrien 11184865Sobrienvoid 11284865Sobrienusracct_destroy(void) 11384865Sobrien{ 11484865Sobrien db_destroy(usracct_db, "user accounting"); 11584865Sobrien} 11684865Sobrien 11784865Sobrienint 11884865Sobrienusracct_add(const struct cmdinfo *ci) 11984865Sobrien{ 12084865Sobrien DBT key, data; 12184865Sobrien struct userinfo newui; 12284865Sobrien uid_t uid; 12384865Sobrien int rv; 124130561Sobrien 12584865Sobrien uid = ci->ci_uid; 12684865Sobrien key.data = &uid; 12784865Sobrien key.size = sizeof uid; 12884865Sobrien 12984865Sobrien rv = DB_GET(usracct_db, &key, &data, 0); 13084865Sobrien if (rv < 0) { 13184865Sobrien warn("get key %u from user accounting stats", uid); 13284865Sobrien return (-1); 13384865Sobrien } else if (rv == 0) { /* it's there; copy whole thing */ 13484865Sobrien /* add the old data to the new data */ 13584865Sobrien bcopy(data.data, &newui, data.size); 13684865Sobrien if (newui.ui_uid != uid) { 13784865Sobrien warnx("key %u != expected record number %u", 13884865Sobrien newui.ui_uid, uid); 13984865Sobrien warnx("inconsistent user accounting stats"); 14084865Sobrien return (-1); 14184865Sobrien } 14284865Sobrien } else { /* it's not there; zero it and copy the key */ 14384865Sobrien bzero(&newui, sizeof newui); 14484865Sobrien newui.ui_uid = ci->ci_uid; 14584865Sobrien } 14684865Sobrien 14784865Sobrien newui.ui_calls += ci->ci_calls; 14884865Sobrien newui.ui_utime += ci->ci_utime; 14984865Sobrien newui.ui_stime += ci->ci_stime; 15084865Sobrien newui.ui_mem += ci->ci_mem; 15184865Sobrien newui.ui_io += ci->ci_io; 15284865Sobrien 15384865Sobrien data.data = &newui; 15484865Sobrien data.size = sizeof newui; 15584865Sobrien rv = DB_PUT(usracct_db, &key, &data, 0); 15684865Sobrien if (rv < 0) { 15784865Sobrien warn("add key %u to user accounting stats", uid); 15884865Sobrien return (-1); 15984865Sobrien } else if (rv != 0) { 16084865Sobrien warnx("DB_PUT returned 1"); 16184865Sobrien return (-1); 16289857Sobrien } 163130561Sobrien 16484865Sobrien return (0); 16584865Sobrien} 16684865Sobrien 16784865Sobrien/* Copy in-memory usracct_db to usrdb_file. */ 16884865Sobrienint 16984865Sobrienusracct_update(void) 17084865Sobrien{ 17184865Sobrien BTREEINFO bti; 17284865Sobrien 17384865Sobrien bzero(&bti, sizeof bti); 17484865Sobrien bti.compare = uid_compare; 17584865Sobrien 17684865Sobrien return (db_copy_out(usracct_db, usrdb_file, "user accounting", 17784865Sobrien &bti)); 17884865Sobrien} 17984865Sobrien 18084865Sobrienvoid 18184865Sobrienusracct_print(void) 18284865Sobrien{ 18384865Sobrien DBT key, data; 18484865Sobrien struct userinfo uistore, *ui = &uistore; 18584865Sobrien double t; 18684865Sobrien int rv; 18784865Sobrien 18884865Sobrien rv = DB_SEQ(usracct_db, &key, &data, R_FIRST); 18984865Sobrien if (rv < 0) 19084865Sobrien warn("retrieving user accounting stats"); 19184865Sobrien 19284865Sobrien while (rv == 0) { 19384865Sobrien memcpy(ui, data.data, sizeof(struct userinfo)); 19484865Sobrien 19584865Sobrien printf("%-*s %9ju ", MAXLOGNAME - 1, 196130561Sobrien user_from_uid(ui->ui_uid, 0), (uintmax_t)ui->ui_calls); 19784865Sobrien 19884865Sobrien t = (ui->ui_utime + ui->ui_stime) / 1000000; 19984865Sobrien if (t < 0.000001) /* kill divide by zero */ 20084865Sobrien t = 0.000001; 20184865Sobrien 20284865Sobrien printf("%12.2f%s ", t / 60.0, "cpu"); 20384865Sobrien 20484865Sobrien /* ui->ui_calls is always != 0 */ 20584865Sobrien if (dflag) 20684865Sobrien printf("%12.0f%s", 20784865Sobrien ui->ui_io / ui->ui_calls, "avio"); 20884865Sobrien else 20984865Sobrien printf("%12.0f%s", ui->ui_io, "tio"); 21084865Sobrien 21184865Sobrien /* t is always >= 0.000001; see above. */ 21284865Sobrien if (kflag) 21384865Sobrien printf("%12.0f%s", ui->ui_mem / t, "k"); 214130561Sobrien else 21584865Sobrien printf("%12.0f%s", ui->ui_mem, "k*sec"); 21684865Sobrien 21784865Sobrien printf("\n"); 21884865Sobrien 21984865Sobrien rv = DB_SEQ(usracct_db, &key, &data, R_NEXT); 22084865Sobrien if (rv < 0) 22184865Sobrien warn("retrieving user accounting stats"); 22284865Sobrien } 22384865Sobrien} 22484865Sobrien 22584865Sobrienstatic int 22684865Sobrienuid_compare(const DBT *k1, const DBT *k2) 22784865Sobrien{ 22884865Sobrien uid_t d1, d2; 22984865Sobrien 23084865Sobrien bcopy(k1->data, &d1, sizeof d1); 23184865Sobrien bcopy(k2->data, &d2, sizeof d2); 23284865Sobrien 23384865Sobrien if (d1 < d2) 23484865Sobrien return -1; 23584865Sobrien else if (d1 == d2) 23684865Sobrien return 0; 23784865Sobrien else 23884865Sobrien return 1; 23984865Sobrien} 24084865Sobrien