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