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 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 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 111 usracct_destroy(void) 112 { 113 db_destroy(usracct_db, "user accounting"); 114 } 115 116 int 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 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 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 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