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