usrdb.c revision 169670
1/*
2 * Copyright (c) 1994 Christopher G. Demetriou
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *      This product includes software developed by Christopher G. Demetriou.
16 * 4. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: head/usr.sbin/sa/usrdb.c 169670 2007-05-18 12:36:10Z dds $");
33
34#include <sys/param.h>
35#include <sys/types.h>
36#include <sys/acct.h>
37#include <err.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <pwd.h>
41#include <stdint.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include "extern.h"
46#include "pathnames.h"
47
48static int uid_compare(const DBT *, const DBT *);
49
50static DB	*usracct_db;
51
52int
53usracct_init()
54{
55	DB *saved_usracct_db;
56	BTREEINFO bti;
57	int error;
58
59	bzero(&bti, sizeof bti);
60	bti.compare = uid_compare;
61
62	usracct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
63	if (usracct_db == NULL)
64		return (-1);
65
66	error = 0;
67	if (!iflag) {
68		DBT key, data;
69		int serr, nerr;
70
71		saved_usracct_db = dbopen(usrdb_file, O_RDONLY, 0, DB_BTREE,
72		    &bti);
73		if (saved_usracct_db == NULL) {
74			error = (errno == ENOENT) ? 0 : -1;
75			if (error)
76				warn("retrieving user accounting summary");
77			goto out;
78		}
79
80		serr = DB_SEQ(saved_usracct_db, &key, &data, R_FIRST);
81		if (serr < 0) {
82			warn("retrieving user accounting summary");
83			error = -1;
84			goto closeout;
85		}
86		while (serr == 0) {
87			nerr = DB_PUT(usracct_db, &key, &data, 0);
88			if (nerr < 0) {
89				warn("initializing user accounting stats");
90				error = -1;
91				break;
92			}
93
94			serr = DB_SEQ(saved_usracct_db, &key, &data, R_NEXT);
95			if (serr < 0) {
96				warn("retrieving user accounting summary");
97				error = -1;
98				break;
99			}
100		}
101
102closeout:
103		if (DB_CLOSE(saved_usracct_db) < 0) {
104			warn("closing user accounting summary");
105			error = -1;
106		}
107	}
108
109out:
110	if (error != 0)
111		usracct_destroy();
112	return (error);
113}
114
115void
116usracct_destroy()
117{
118	if (DB_CLOSE(usracct_db) < 0)
119		warn("destroying user accounting stats");
120}
121
122int
123usracct_add(const struct cmdinfo *ci)
124{
125	DBT key, data;
126	struct userinfo newui;
127	u_long uid;
128	int rv;
129
130	uid = ci->ci_uid;
131	key.data = &uid;
132	key.size = sizeof uid;
133
134	rv = DB_GET(usracct_db, &key, &data, 0);
135	if (rv < 0) {
136		warn("get key %lu from user accounting stats", uid);
137		return (-1);
138	} else if (rv == 0) {	/* it's there; copy whole thing */
139		/* add the old data to the new data */
140		bcopy(data.data, &newui, data.size);
141		if (newui.ui_uid != uid) {
142			warnx("key %lu != expected record number %lu",
143			    newui.ui_uid, uid);
144			warnx("inconsistent user accounting stats");
145			return (-1);
146		}
147	} else {		/* it's not there; zero it and copy the key */
148		bzero(&newui, sizeof newui);
149		newui.ui_uid = ci->ci_uid;
150	}
151
152	newui.ui_calls += ci->ci_calls;
153	newui.ui_utime += ci->ci_utime;
154	newui.ui_stime += ci->ci_stime;
155	newui.ui_mem += ci->ci_mem;
156	newui.ui_io += ci->ci_io;
157
158	data.data = &newui;
159	data.size = sizeof newui;
160	rv = DB_PUT(usracct_db, &key, &data, 0);
161	if (rv < 0) {
162		warn("add key %lu to user accounting stats", uid);
163		return (-1);
164	} else if (rv != 0) {
165		warnx("DB_PUT returned 1");
166		return (-1);
167	}
168
169	return (0);
170}
171
172int
173usracct_update()
174{
175	DB *saved_usracct_db;
176	DBT key, data;
177	BTREEINFO bti;
178	int error, serr, nerr;
179
180	bzero(&bti, sizeof bti);
181	bti.compare = uid_compare;
182
183	saved_usracct_db = dbopen(usrdb_file, O_RDWR|O_CREAT|O_TRUNC, 0644,
184	    DB_BTREE, &bti);
185	if (saved_usracct_db == NULL) {
186		warn("creating user accounting summary");
187		return (-1);
188	}
189
190	error = 0;
191
192	serr = DB_SEQ(usracct_db, &key, &data, R_FIRST);
193	if (serr < 0) {
194		warn("retrieving user accounting stats");
195		error = -1;
196	}
197	while (serr == 0) {
198		nerr = DB_PUT(saved_usracct_db, &key, &data, 0);
199		if (nerr < 0) {
200			warn("saving user accounting summary");
201			error = -1;
202			break;
203		}
204
205		serr = DB_SEQ(usracct_db, &key, &data, R_NEXT);
206		if (serr < 0) {
207			warn("retrieving user accounting stats");
208			error = -1;
209			break;
210		}
211	}
212
213	if (DB_SYNC(saved_usracct_db, 0) < 0) {
214		warn("syncing process accounting summary");
215		error = -1;
216	}
217	if (DB_CLOSE(saved_usracct_db) < 0) {
218		warn("closing process accounting summary");
219		error = -1;
220	}
221	return error;
222}
223
224void
225usracct_print()
226{
227	DBT key, data;
228	struct userinfo uistore, *ui = &uistore;
229	double t;
230	int rv;
231
232	rv = DB_SEQ(usracct_db, &key, &data, R_FIRST);
233	if (rv < 0)
234		warn("retrieving user accounting stats");
235
236	while (rv == 0) {
237		memcpy(ui, data.data, sizeof(struct userinfo));
238
239		printf("%-*s %9ju ", MAXLOGNAME - 1,
240		    user_from_uid(ui->ui_uid, 0), (uintmax_t)ui->ui_calls);
241
242		t = (double) (ui->ui_utime + ui->ui_stime) /
243		    (double) AHZ;
244		if (t < 0.0001)		/* kill divide by zero */
245			t = 0.0001;
246
247		printf("%12.2f%s ", t / 60.0, "cpu");
248
249		/* ui->ui_calls is always != 0 */
250		if (dflag)
251			printf("%12ju%s",
252			    (uintmax_t)(ui->ui_io / ui->ui_calls), "avio");
253		else
254			printf("%12ju%s", (uintmax_t)ui->ui_io, "tio");
255
256		/* t is always >= 0.0001; see above */
257		if (kflag)
258			printf("%12.0f%s", ui->ui_mem / t, "k");
259		else
260			printf("%12ju%s", (uintmax_t)ui->ui_mem, "k*sec");
261
262		printf("\n");
263
264		rv = DB_SEQ(usracct_db, &key, &data, R_NEXT);
265		if (rv < 0)
266			warn("retrieving user accounting stats");
267	}
268}
269
270static int
271uid_compare(const DBT *k1, const DBT *k2)
272{
273	u_long d1, d2;
274
275	bcopy(k1->data, &d1, sizeof d1);
276	bcopy(k2->data, &d2, sizeof d2);
277
278	if (d1 < d2)
279		return -1;
280	else if (d1 == d2)
281		return 0;
282	else
283		return 1;
284}
285