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