1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2007 Diomidis Spinellis 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/stat.h> 35 #include <sys/types.h> 36 #include <sys/acct.h> 37 38 #include <errno.h> 39 #include <stddef.h> 40 #include <stdio.h> 41 #include <string.h> 42 43 int readrec_forward(FILE *f, struct acctv3 *av2); 44 int readrec_backward(FILE *f, struct acctv3 *av2); 45 46 /* 47 * Reverse offsetof: return the offset of field f 48 * from the end of the structure s. 49 */ 50 #define roffsetof(s, f) (sizeof(s) - offsetof(s, f)) 51 52 /* 53 * Read exactly one record of size size from stream f into ptr. 54 * Failure to read the complete record is considered a file format error, 55 * and will set errno to EFTYPE. 56 * Return 0 on success, EOF on end of file or error. 57 */ 58 static int 59 fread_record(void *ptr, size_t size, FILE *f) 60 { 61 size_t rv; 62 63 if ((rv = fread(ptr, 1, size, f)) == size) 64 return (0); 65 else if (ferror(f) || rv == 0) 66 return (EOF); 67 else { 68 /* Short read. */ 69 errno = EFTYPE; 70 return (EOF); 71 } 72 } 73 74 /* 75 * Return the value of a comp_t field. 76 */ 77 static float 78 decode_comp(comp_t v) 79 { 80 int result, exp; 81 82 result = v & 017777; 83 for (exp = v >> 13; exp; exp--) 84 result <<= 3; 85 return ((double)result / AHZV1); 86 } 87 88 /* 89 * Read a v1 accounting record stored at the current 90 * position of stream f. 91 * Convert the data to the current record format. 92 * Return EOF on error or end-of-file. 93 */ 94 static int 95 readrec_v1(FILE *f, struct acctv3 *av3) 96 { 97 struct acctv1 av1; 98 int rv; 99 100 if ((rv = fread_record(&av1, sizeof(av1), f)) == EOF) 101 return (EOF); 102 av3->ac_zero = 0; 103 av3->ac_version = 3; 104 av3->ac_len = av3->ac_len2 = sizeof(*av3); 105 memcpy(av3->ac_comm, av1.ac_comm, AC_COMM_LEN); 106 av3->ac_utime = decode_comp(av1.ac_utime) * 1000000; 107 av3->ac_stime = decode_comp(av1.ac_stime) * 1000000; 108 av3->ac_etime = decode_comp(av1.ac_etime) * 1000000; 109 av3->ac_btime = av1.ac_btime; 110 av3->ac_uid = av1.ac_uid; 111 av3->ac_gid = av1.ac_gid; 112 av3->ac_mem = av1.ac_mem; 113 av3->ac_io = decode_comp(av1.ac_io); 114 av3->ac_tty = av1.ac_tty; 115 av3->ac_flagx = av1.ac_flag | ANVER; 116 return (0); 117 } 118 119 /* 120 * Read an v2 accounting record stored at the current 121 * position of stream f. 122 * Return EOF on error or end-of-file. 123 */ 124 static int 125 readrec_v2(FILE *f, struct acctv3 *av3) 126 { 127 struct acctv2 av2; 128 int rv; 129 130 if ((rv = fread_record(&av2, sizeof(av2), f)) == EOF) 131 return (EOF); 132 av3->ac_zero = 0; 133 av3->ac_version = 3; 134 av3->ac_len = av3->ac_len2 = sizeof(*av3); 135 memcpy(av3->ac_comm, av2.ac_comm, AC_COMM_LEN); 136 av3->ac_utime = av2.ac_utime; 137 av3->ac_stime = av2.ac_stime; 138 av3->ac_etime = av2.ac_etime; 139 av3->ac_btime = av2.ac_btime; 140 av3->ac_uid = av2.ac_uid; 141 av3->ac_gid = av2.ac_gid; 142 av3->ac_mem = av2.ac_mem; 143 av3->ac_io = av2.ac_io; 144 av3->ac_tty = av2.ac_tty; 145 av3->ac_flagx = av2.ac_flagx; 146 return (0); 147 } 148 149 /* 150 * Read an v2 accounting record stored at the current 151 * position of stream f. 152 * Return EOF on error or end-of-file. 153 */ 154 static int 155 readrec_v3(FILE *f, struct acctv3 *av3) 156 { 157 158 return (fread_record(av3, sizeof(*av3), f)); 159 } 160 161 /* 162 * Read a new-style (post-v1) accounting record stored at 163 * the current position of stream f. 164 * Convert the data to the current record format. 165 * Return EOF on error or end-of-file. 166 */ 167 static int 168 readrec_vx(FILE *f, struct acctv3 *av3) 169 { 170 uint8_t magic, version; 171 172 if (fread_record(&magic, sizeof(magic), f) == EOF || 173 fread_record(&version, sizeof(version), f) == EOF || 174 ungetc(version, f) == EOF || 175 ungetc(magic, f) == EOF) 176 return (EOF); 177 switch (version) { 178 case 2: 179 return (readrec_v2(f, av3)); 180 case 3: 181 return (readrec_v3(f, av3)); 182 183 /* Add handling for more versions here. */ 184 185 default: 186 errno = EFTYPE; 187 return (EOF); 188 } 189 } 190 191 /* 192 * Read an accounting record stored at the current 193 * position of stream f. 194 * Old-format records are converted to the current record 195 * format. 196 * Return the number of records read (1 or 0 at the end-of-file), 197 * or EOF on error. 198 */ 199 int 200 readrec_forward(FILE *f, struct acctv3 *av3) 201 { 202 int magic, rv; 203 204 if ((magic = getc(f)) == EOF) 205 return (ferror(f) ? EOF : 0); 206 if (ungetc(magic, f) == EOF) 207 return (EOF); 208 if (magic != 0) 209 /* Old record format. */ 210 rv = readrec_v1(f, av3); 211 else 212 /* New record formats. */ 213 rv = readrec_vx(f, av3); 214 return (rv == EOF ? EOF : 1); 215 } 216 217 /* 218 * Read an accounting record ending at the current 219 * position of stream f. 220 * Old-format records are converted to the current record 221 * format. 222 * The file pointer is positioned at the beginning of the 223 * record read. 224 * Return the number of records read (1 or 0 at the end-of-file), 225 * or EOF on error. 226 */ 227 int 228 readrec_backward(FILE *f, struct acctv3 *av3) 229 { 230 off_t pos; 231 int c; 232 uint16_t len; 233 234 if ((pos = ftell(f)) == -1) 235 return (EOF); 236 if (pos == 0) 237 return (0); 238 if (fseek(f, -roffsetof(struct acctv3, ac_trailer), 239 SEEK_CUR) == EOF || 240 (c = getc(f)) == EOF) 241 return (EOF); 242 if (c & ANVER) { 243 /* 244 * New record formats. For v2 and v3 offset from the 245 * end for ac_len2 should be same. 246 */ 247 if (fseeko(f, pos - roffsetof(struct acctv2, ac_len2), 248 SEEK_SET) == EOF || 249 fread_record(&len, sizeof(len), f) == EOF || 250 fseeko(f, pos - len, SEEK_SET) == EOF || 251 readrec_vx(f, av3) == EOF || 252 fseeko(f, pos - len, SEEK_SET) == EOF) 253 return (EOF); 254 else 255 return (1); 256 } else { 257 /* Old record format. */ 258 if (fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF || 259 readrec_v1(f, av3) == EOF || 260 fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF) 261 return (EOF); 262 else 263 return (1); 264 } 265 } 266