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