xref: /freebsd/usr.sbin/sa/usrdb.c (revision 4d65a7c6951cea0333f1a0c1b32c38489cdfa6c5)
1*1de7b4b8SPedro F. Giffuni /*-
2*1de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-4-Clause
3*1de7b4b8SPedro F. Giffuni  *
4b1069b52SDavid Greenman  * Copyright (c) 1994 Christopher G. Demetriou
5b1069b52SDavid Greenman  * All rights reserved.
6b1069b52SDavid Greenman  *
7b1069b52SDavid Greenman  * Redistribution and use in source and binary forms, with or without
8b1069b52SDavid Greenman  * modification, are permitted provided that the following conditions
9b1069b52SDavid Greenman  * are met:
10b1069b52SDavid Greenman  * 1. Redistributions of source code must retain the above copyright
11b1069b52SDavid Greenman  *    notice, this list of conditions and the following disclaimer.
12b1069b52SDavid Greenman  * 2. Redistributions in binary form must reproduce the above copyright
13b1069b52SDavid Greenman  *    notice, this list of conditions and the following disclaimer in the
14b1069b52SDavid Greenman  *    documentation and/or other materials provided with the distribution.
15b1069b52SDavid Greenman  * 3. All advertising materials mentioning features or use of this software
16b1069b52SDavid Greenman  *    must display the following acknowledgement:
17b1069b52SDavid Greenman  *      This product includes software developed by Christopher G. Demetriou.
18b1069b52SDavid Greenman  * 4. The name of the author may not be used to endorse or promote products
19b1069b52SDavid Greenman  *    derived from this software without specific prior written permission
20b1069b52SDavid Greenman  *
21b1069b52SDavid Greenman  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22b1069b52SDavid Greenman  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23b1069b52SDavid Greenman  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24b1069b52SDavid Greenman  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25b1069b52SDavid Greenman  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26b1069b52SDavid Greenman  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27b1069b52SDavid Greenman  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28b1069b52SDavid Greenman  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29b1069b52SDavid Greenman  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30b1069b52SDavid Greenman  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31b1069b52SDavid Greenman  */
32b1069b52SDavid Greenman 
33e35ba769SGuy Helmer #include <sys/param.h>
34b1069b52SDavid Greenman #include <sys/types.h>
35b1069b52SDavid Greenman #include <sys/acct.h>
36b1069b52SDavid Greenman #include <err.h>
37b1069b52SDavid Greenman #include <errno.h>
38b1069b52SDavid Greenman #include <fcntl.h>
39576541a9SWarner Losh #include <pwd.h>
4072d78aeaSDag-Erling Smørgrav #include <stdint.h>
41553072f3SMike Pritchard #include <stdio.h>
4219112792SPhilippe Charnier #include <stdlib.h>
43553072f3SMike Pritchard #include <string.h>
44b1069b52SDavid Greenman #include "extern.h"
45b1069b52SDavid Greenman #include "pathnames.h"
46b1069b52SDavid Greenman 
4769315c6fSXin LI static int uid_compare(const DBT *, const DBT *);
48b1069b52SDavid Greenman 
49b1069b52SDavid Greenman static DB	*usracct_db;
50b1069b52SDavid Greenman 
51fdbe5babSDiomidis Spinellis /* Legacy format in AHZV1 units. */
52fdbe5babSDiomidis Spinellis struct userinfov1 {
53fdbe5babSDiomidis Spinellis 	uid_t		ui_uid;			/* user id; for consistency */
54fdbe5babSDiomidis Spinellis 	u_quad_t	ui_calls;		/* number of invocations */
55fdbe5babSDiomidis Spinellis 	u_quad_t	ui_utime;		/* user time */
56fdbe5babSDiomidis Spinellis 	u_quad_t	ui_stime;		/* system time */
57fdbe5babSDiomidis Spinellis 	u_quad_t	ui_mem;			/* memory use */
58fdbe5babSDiomidis Spinellis 	u_quad_t	ui_io;			/* number of disk i/o ops */
59fdbe5babSDiomidis Spinellis };
60fdbe5babSDiomidis Spinellis 
61fdbe5babSDiomidis Spinellis /*
62fdbe5babSDiomidis Spinellis  * Convert a v1 data record into the current version.
63fdbe5babSDiomidis Spinellis  * Return 0 if OK, -1 on error, setting errno.
64fdbe5babSDiomidis Spinellis  */
65fdbe5babSDiomidis Spinellis static int
v1_to_v2(DBT * key,DBT * data)66fdbe5babSDiomidis Spinellis v1_to_v2(DBT *key, DBT *data)
67fdbe5babSDiomidis Spinellis {
68fdbe5babSDiomidis Spinellis 	struct userinfov1 uiv1;
69fdbe5babSDiomidis Spinellis 	static struct userinfo uiv2;
70fdbe5babSDiomidis Spinellis 	static uid_t uid;
71fdbe5babSDiomidis Spinellis 
72fdbe5babSDiomidis Spinellis 	if (key->size != sizeof(u_long) || data->size != sizeof(uiv1)) {
73fdbe5babSDiomidis Spinellis 		errno = EFTYPE;
74fdbe5babSDiomidis Spinellis 		return (-1);
75fdbe5babSDiomidis Spinellis 	}
76fdbe5babSDiomidis Spinellis 
77fdbe5babSDiomidis Spinellis 	/* Convert key. */
78fdbe5babSDiomidis Spinellis 	key->size = sizeof(uid_t);
79fdbe5babSDiomidis Spinellis 	uid = (uid_t)*(u_long *)(key->data);
80fdbe5babSDiomidis Spinellis 	key->data = &uid;
81fdbe5babSDiomidis Spinellis 
82fdbe5babSDiomidis Spinellis 	/* Convert data. */
83fdbe5babSDiomidis Spinellis 	memcpy(&uiv1, data->data, data->size);
84fdbe5babSDiomidis Spinellis 	memset(&uiv2, 0, sizeof(uiv2));
85fdbe5babSDiomidis Spinellis 	uiv2.ui_uid = uiv1.ui_uid;
86fdbe5babSDiomidis Spinellis 	uiv2.ui_calls = uiv1.ui_calls;
87fdbe5babSDiomidis Spinellis 	uiv2.ui_utime = ((double)uiv1.ui_utime / AHZV1) * 1000000;
88fdbe5babSDiomidis Spinellis 	uiv2.ui_stime = ((double)uiv1.ui_stime / AHZV1) * 1000000;
89fdbe5babSDiomidis Spinellis 	uiv2.ui_mem = uiv1.ui_mem;
90fdbe5babSDiomidis Spinellis 	uiv2.ui_io = uiv1.ui_io;
91fdbe5babSDiomidis Spinellis 	data->size = sizeof(uiv2);
92fdbe5babSDiomidis Spinellis 	data->data = &uiv2;
93fdbe5babSDiomidis Spinellis 
94fdbe5babSDiomidis Spinellis 	return (0);
95fdbe5babSDiomidis Spinellis }
96fdbe5babSDiomidis Spinellis 
97fdbe5babSDiomidis Spinellis /* Copy usrdb_file to in-memory usracct_db. */
98b1069b52SDavid Greenman int
usracct_init(void)9910bc3a7fSEd Schouten usracct_init(void)
100b1069b52SDavid Greenman {
101b1069b52SDavid Greenman 	BTREEINFO bti;
102b1069b52SDavid Greenman 
103b1069b52SDavid Greenman 	bzero(&bti, sizeof bti);
104b1069b52SDavid Greenman 	bti.compare = uid_compare;
105b1069b52SDavid Greenman 
106fdbe5babSDiomidis Spinellis 	return (db_copy_in(&usracct_db, usrdb_file, "user accounting",
107fdbe5babSDiomidis Spinellis 	    &bti, v1_to_v2));
108b1069b52SDavid Greenman }
109b1069b52SDavid Greenman 
110b1069b52SDavid Greenman void
usracct_destroy(void)11110bc3a7fSEd Schouten usracct_destroy(void)
112b1069b52SDavid Greenman {
113fdbe5babSDiomidis Spinellis 	db_destroy(usracct_db, "user accounting");
114b1069b52SDavid Greenman }
115b1069b52SDavid Greenman 
116b1069b52SDavid Greenman int
usracct_add(const struct cmdinfo * ci)11769315c6fSXin LI usracct_add(const struct cmdinfo *ci)
118b1069b52SDavid Greenman {
119b1069b52SDavid Greenman 	DBT key, data;
120b1069b52SDavid Greenman 	struct userinfo newui;
121fdbe5babSDiomidis Spinellis 	uid_t uid;
122b1069b52SDavid Greenman 	int rv;
123b1069b52SDavid Greenman 
124b1069b52SDavid Greenman 	uid = ci->ci_uid;
125b1069b52SDavid Greenman 	key.data = &uid;
126b1069b52SDavid Greenman 	key.size = sizeof uid;
127b1069b52SDavid Greenman 
128b1069b52SDavid Greenman 	rv = DB_GET(usracct_db, &key, &data, 0);
129b1069b52SDavid Greenman 	if (rv < 0) {
130fdbe5babSDiomidis Spinellis 		warn("get key %u from user accounting stats", uid);
131b1069b52SDavid Greenman 		return (-1);
132b1069b52SDavid Greenman 	} else if (rv == 0) {	/* it's there; copy whole thing */
133b1069b52SDavid Greenman 		/* add the old data to the new data */
134b1069b52SDavid Greenman 		bcopy(data.data, &newui, data.size);
135b1069b52SDavid Greenman 		if (newui.ui_uid != uid) {
136fdbe5babSDiomidis Spinellis 			warnx("key %u != expected record number %u",
137b1069b52SDavid Greenman 			    newui.ui_uid, uid);
138b1069b52SDavid Greenman 			warnx("inconsistent user accounting stats");
139b1069b52SDavid Greenman 			return (-1);
140b1069b52SDavid Greenman 		}
141b1069b52SDavid Greenman 	} else {		/* it's not there; zero it and copy the key */
142b1069b52SDavid Greenman 		bzero(&newui, sizeof newui);
143b1069b52SDavid Greenman 		newui.ui_uid = ci->ci_uid;
144b1069b52SDavid Greenman 	}
145b1069b52SDavid Greenman 
146b1069b52SDavid Greenman 	newui.ui_calls += ci->ci_calls;
147b1069b52SDavid Greenman 	newui.ui_utime += ci->ci_utime;
148b1069b52SDavid Greenman 	newui.ui_stime += ci->ci_stime;
149b1069b52SDavid Greenman 	newui.ui_mem += ci->ci_mem;
150b1069b52SDavid Greenman 	newui.ui_io += ci->ci_io;
151b1069b52SDavid Greenman 
152b1069b52SDavid Greenman 	data.data = &newui;
153b1069b52SDavid Greenman 	data.size = sizeof newui;
154b1069b52SDavid Greenman 	rv = DB_PUT(usracct_db, &key, &data, 0);
155b1069b52SDavid Greenman 	if (rv < 0) {
156fdbe5babSDiomidis Spinellis 		warn("add key %u to user accounting stats", uid);
157b1069b52SDavid Greenman 		return (-1);
158b1069b52SDavid Greenman 	} else if (rv != 0) {
159b1069b52SDavid Greenman 		warnx("DB_PUT returned 1");
160b1069b52SDavid Greenman 		return (-1);
161b1069b52SDavid Greenman 	}
162b1069b52SDavid Greenman 
163b1069b52SDavid Greenman 	return (0);
164b1069b52SDavid Greenman }
165b1069b52SDavid Greenman 
166fdbe5babSDiomidis Spinellis /* Copy in-memory usracct_db to usrdb_file. */
167b1069b52SDavid Greenman int
usracct_update(void)16810bc3a7fSEd Schouten usracct_update(void)
169b1069b52SDavid Greenman {
170b1069b52SDavid Greenman 	BTREEINFO bti;
171b1069b52SDavid Greenman 
172b1069b52SDavid Greenman 	bzero(&bti, sizeof bti);
173b1069b52SDavid Greenman 	bti.compare = uid_compare;
174b1069b52SDavid Greenman 
175fdbe5babSDiomidis Spinellis 	return (db_copy_out(usracct_db, usrdb_file, "user accounting",
176fdbe5babSDiomidis Spinellis 	    &bti));
177b1069b52SDavid Greenman }
178b1069b52SDavid Greenman 
179b1069b52SDavid Greenman void
usracct_print(void)18010bc3a7fSEd Schouten usracct_print(void)
181b1069b52SDavid Greenman {
182b1069b52SDavid Greenman 	DBT key, data;
183e49012e8SAndrew Gallatin 	struct userinfo uistore, *ui = &uistore;
184b1069b52SDavid Greenman 	double t;
185b1069b52SDavid Greenman 	int rv;
186b1069b52SDavid Greenman 
187b1069b52SDavid Greenman 	rv = DB_SEQ(usracct_db, &key, &data, R_FIRST);
188b1069b52SDavid Greenman 	if (rv < 0)
189b1069b52SDavid Greenman 		warn("retrieving user accounting stats");
190b1069b52SDavid Greenman 
191b1069b52SDavid Greenman 	while (rv == 0) {
192e49012e8SAndrew Gallatin 		memcpy(ui, data.data, sizeof(struct userinfo));
193b1069b52SDavid Greenman 
19472d78aeaSDag-Erling Smørgrav 		printf("%-*s %9ju ", MAXLOGNAME - 1,
19572d78aeaSDag-Erling Smørgrav 		    user_from_uid(ui->ui_uid, 0), (uintmax_t)ui->ui_calls);
196b1069b52SDavid Greenman 
197fdbe5babSDiomidis Spinellis 		t = (ui->ui_utime + ui->ui_stime) / 1000000;
198fdbe5babSDiomidis Spinellis 		if (t < 0.000001)		/* kill divide by zero */
199fdbe5babSDiomidis Spinellis 			t = 0.000001;
200b1069b52SDavid Greenman 
201553072f3SMike Pritchard 		printf("%12.2f%s ", t / 60.0, "cpu");
202b1069b52SDavid Greenman 
203b1069b52SDavid Greenman 		/* ui->ui_calls is always != 0 */
204b1069b52SDavid Greenman 		if (dflag)
205fdbe5babSDiomidis Spinellis 			printf("%12.0f%s",
206fdbe5babSDiomidis Spinellis 			    ui->ui_io / ui->ui_calls, "avio");
207b1069b52SDavid Greenman 		else
208fdbe5babSDiomidis Spinellis 			printf("%12.0f%s", ui->ui_io, "tio");
209b1069b52SDavid Greenman 
210fdbe5babSDiomidis Spinellis 		/* t is always >= 0.000001; see above. */
211b1069b52SDavid Greenman 		if (kflag)
2120f854af2SBruce Evans 			printf("%12.0f%s", ui->ui_mem / t, "k");
213b1069b52SDavid Greenman 		else
214fdbe5babSDiomidis Spinellis 			printf("%12.0f%s", ui->ui_mem, "k*sec");
215b1069b52SDavid Greenman 
216b1069b52SDavid Greenman 		printf("\n");
217b1069b52SDavid Greenman 
218b1069b52SDavid Greenman 		rv = DB_SEQ(usracct_db, &key, &data, R_NEXT);
219b1069b52SDavid Greenman 		if (rv < 0)
220b1069b52SDavid Greenman 			warn("retrieving user accounting stats");
221b1069b52SDavid Greenman 	}
222b1069b52SDavid Greenman }
223b1069b52SDavid Greenman 
224b1069b52SDavid Greenman static int
uid_compare(const DBT * k1,const DBT * k2)22569315c6fSXin LI uid_compare(const DBT *k1, const DBT *k2)
226b1069b52SDavid Greenman {
227fdbe5babSDiomidis Spinellis 	uid_t d1, d2;
228b1069b52SDavid Greenman 
229b1069b52SDavid Greenman 	bcopy(k1->data, &d1, sizeof d1);
230b1069b52SDavid Greenman 	bcopy(k2->data, &d2, sizeof d2);
231b1069b52SDavid Greenman 
232b1069b52SDavid Greenman 	if (d1 < d2)
233b1069b52SDavid Greenman 		return -1;
234b1069b52SDavid Greenman 	else if (d1 == d2)
235b1069b52SDavid Greenman 		return 0;
236b1069b52SDavid Greenman 	else
237b1069b52SDavid Greenman 		return 1;
238b1069b52SDavid Greenman }
239