usrdb.c revision 100107
1171939Sdes/*
2222176Suqs * Copyright (c) 1994 Christopher G. Demetriou
3171939Sdes * All rights reserved.
4171939Sdes *
5171939Sdes * Redistribution and use in source and binary forms, with or without
6171939Sdes * modification, are permitted provided that the following conditions
7171939Sdes * are met:
8171939Sdes * 1. Redistributions of source code must retain the above copyright
9171939Sdes *    notice, this list of conditions and the following disclaimer.
10171939Sdes * 2. Redistributions in binary form must reproduce the above copyright
11171939Sdes *    notice, this list of conditions and the following disclaimer in the
12171939Sdes *    documentation and/or other materials provided with the distribution.
13171939Sdes * 3. All advertising materials mentioning features or use of this software
14171939Sdes *    must display the following acknowledgement:
15171939Sdes *      This product includes software developed by Christopher G. Demetriou.
16171939Sdes * 4. The name of the author may not be used to endorse or promote products
17171939Sdes *    derived from this software without specific prior written permission
18171939Sdes *
19171939Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20171939Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21171939Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22171939Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23171939Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24171939Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25171939Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26171939Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27171939Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28181967Srpaulo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29171939Sdes */
30171939Sdes
31171939Sdes#ifndef lint
32171939Sdesstatic const char rcsid[] =
33171939Sdes  "$FreeBSD: head/usr.sbin/sa/usrdb.c 100107 2002-07-15 16:05:15Z des $";
34171939Sdes#endif /* not lint */
35171939Sdes
36171939Sdes#include <sys/param.h>
37171939Sdes#include <sys/types.h>
38171939Sdes#include <sys/acct.h>
39171939Sdes#include <err.h>
40171939Sdes#include <errno.h>
41171939Sdes#include <fcntl.h>
42171939Sdes#include <pwd.h>
43171939Sdes#include <stdint.h>
44171939Sdes#include <stdio.h>
45171939Sdes#include <stdlib.h>
46171939Sdes#include <string.h>
47171939Sdes#include "extern.h"
48171939Sdes#include "pathnames.h"
49171939Sdes
50171939Sdesstatic int uid_compare __P((const DBT *, const DBT *));
51171939Sdes
52171939Sdesstatic DB	*usracct_db;
53181967Srpaulo
54181967Srpauloint
55181967Srpaulousracct_init()
56181967Srpaulo{
57181967Srpaulo	DB *saved_usracct_db;
58181967Srpaulo	BTREEINFO bti;
59181967Srpaulo	int error;
60181967Srpaulo
61181967Srpaulo	bzero(&bti, sizeof bti);
62181967Srpaulo	bti.compare = uid_compare;
63181967Srpaulo
64181967Srpaulo	usracct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
65181967Srpaulo	if (usracct_db == NULL)
66171939Sdes		return (-1);
67171939Sdes
68171939Sdes	error = 0;
69198288Savg	if (!iflag) {
70181967Srpaulo		DBT key, data;
71181967Srpaulo		int serr, nerr;
72181967Srpaulo
73181967Srpaulo		saved_usracct_db = dbopen(_PATH_USRACCT, O_RDONLY, 0, DB_BTREE,
74181967Srpaulo		    &bti);
75181967Srpaulo		if (saved_usracct_db == NULL) {
76171939Sdes			error = (errno == ENOENT) ? 0 : -1;
77171939Sdes			if (error)
78171939Sdes				warn("retrieving user accounting summary");
79171939Sdes			goto out;
80171939Sdes		}
81171939Sdes
82171939Sdes		serr = DB_SEQ(saved_usracct_db, &key, &data, R_FIRST);
83171939Sdes		if (serr < 0) {
84171939Sdes			warn("retrieving user accounting summary");
85181967Srpaulo			error = -1;
86267938Sbapt			goto closeout;
87181967Srpaulo		}
88267938Sbapt		while (serr == 0) {
89171939Sdes			nerr = DB_PUT(usracct_db, &key, &data, 0);
90267938Sbapt			if (nerr < 0) {
91				warn("initializing user accounting stats");
92				error = -1;
93				break;
94			}
95
96			serr = DB_SEQ(saved_usracct_db, &key, &data, R_NEXT);
97			if (serr < 0) {
98				warn("retrieving user accounting summary");
99				error = -1;
100				break;
101			}
102		}
103
104closeout:
105		if (DB_CLOSE(saved_usracct_db) < 0) {
106			warn("closing user accounting summary");
107			error = -1;
108		}
109	}
110
111out:
112	if (error != 0)
113		usracct_destroy();
114	return (error);
115}
116
117void
118usracct_destroy()
119{
120	if (DB_CLOSE(usracct_db) < 0)
121		warn("destroying user accounting stats");
122}
123
124int
125usracct_add(ci)
126	const struct cmdinfo *ci;
127{
128	DBT key, data;
129	struct userinfo newui;
130	u_long uid;
131	int rv;
132
133	uid = ci->ci_uid;
134	key.data = &uid;
135	key.size = sizeof uid;
136
137	rv = DB_GET(usracct_db, &key, &data, 0);
138	if (rv < 0) {
139		warn("get key %lu from user accounting stats", uid);
140		return (-1);
141	} else if (rv == 0) {	/* it's there; copy whole thing */
142		/* add the old data to the new data */
143		bcopy(data.data, &newui, data.size);
144		if (newui.ui_uid != uid) {
145			warnx("key %lu != expected record number %lu",
146			    newui.ui_uid, uid);
147			warnx("inconsistent user accounting stats");
148			return (-1);
149		}
150	} else {		/* it's not there; zero it and copy the key */
151		bzero(&newui, sizeof newui);
152		newui.ui_uid = ci->ci_uid;
153	}
154
155	newui.ui_calls += ci->ci_calls;
156	newui.ui_utime += ci->ci_utime;
157	newui.ui_stime += ci->ci_stime;
158	newui.ui_mem += ci->ci_mem;
159	newui.ui_io += ci->ci_io;
160
161	data.data = &newui;
162	data.size = sizeof newui;
163	rv = DB_PUT(usracct_db, &key, &data, 0);
164	if (rv < 0) {
165		warn("add key %lu to user accounting stats", uid);
166		return (-1);
167	} else if (rv != 0) {
168		warnx("DB_PUT returned 1");
169		return (-1);
170	}
171
172	return (0);
173}
174
175int
176usracct_update()
177{
178	DB *saved_usracct_db;
179	DBT key, data;
180	BTREEINFO bti;
181	int error, serr, nerr;
182
183	bzero(&bti, sizeof bti);
184	bti.compare = uid_compare;
185
186	saved_usracct_db = dbopen(_PATH_USRACCT, O_RDWR|O_CREAT|O_TRUNC, 0644,
187	    DB_BTREE, &bti);
188	if (saved_usracct_db == NULL) {
189		warn("creating user accounting summary");
190		return (-1);
191	}
192
193	error = 0;
194
195	serr = DB_SEQ(usracct_db, &key, &data, R_FIRST);
196	if (serr < 0) {
197		warn("retrieving user accounting stats");
198		error = -1;
199	}
200	while (serr == 0) {
201		nerr = DB_PUT(saved_usracct_db, &key, &data, 0);
202		if (nerr < 0) {
203			warn("saving user accounting summary");
204			error = -1;
205			break;
206		}
207
208		serr = DB_SEQ(usracct_db, &key, &data, R_NEXT);
209		if (serr < 0) {
210			warn("retrieving user accounting stats");
211			error = -1;
212			break;
213		}
214	}
215
216	if (DB_SYNC(saved_usracct_db, 0) < 0) {
217		warn("syncing process accounting summary");
218		error = -1;
219	}
220	if (DB_CLOSE(saved_usracct_db) < 0) {
221		warn("closing process accounting summary");
222		error = -1;
223	}
224	return error;
225}
226
227void
228usracct_print()
229{
230	DBT key, data;
231	struct userinfo uistore, *ui = &uistore;
232	double t;
233	int rv;
234
235	rv = DB_SEQ(usracct_db, &key, &data, R_FIRST);
236	if (rv < 0)
237		warn("retrieving user accounting stats");
238
239	while (rv == 0) {
240		memcpy(ui, data.data, sizeof(struct userinfo));
241
242		printf("%-*s %9ju ", MAXLOGNAME - 1,
243		    user_from_uid(ui->ui_uid, 0), (uintmax_t)ui->ui_calls);
244
245		t = (double) (ui->ui_utime + ui->ui_stime) /
246		    (double) AHZ;
247		if (t < 0.0001)		/* kill divide by zero */
248			t = 0.0001;
249
250		printf("%12.2f%s ", t / 60.0, "cpu");
251
252		/* ui->ui_calls is always != 0 */
253		if (dflag)
254			printf("%12ju%s",
255			    (uintmax_t)(ui->ui_io / ui->ui_calls), "avio");
256		else
257			printf("%12ju%s", (uintmax_t)ui->ui_io, "tio");
258
259		/* t is always >= 0.0001; see above */
260		if (kflag)
261			printf("%12.0f%s", ui->ui_mem / t, "k");
262		else
263			printf("%12ju%s", (uintmax_t)ui->ui_mem, "k*sec");
264
265		printf("\n");
266
267		rv = DB_SEQ(usracct_db, &key, &data, R_NEXT);
268		if (rv < 0)
269			warn("retrieving user accounting stats");
270	}
271}
272
273static int
274uid_compare(k1, k2)
275	const DBT *k1, *k2;
276{
277	u_long d1, d2;
278
279	bcopy(k1->data, &d1, sizeof d1);
280	bcopy(k2->data, &d2, sizeof d2);
281
282	if (d1 < d2)
283		return -1;
284	else if (d1 == d2)
285		return 0;
286	else
287		return 1;
288}
289