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/param.h> 31 #include <sys/stat.h> 32 #include <sys/types.h> 33 #include <sys/acct.h> 34 35 #include <errno.h> 36 #include <stddef.h> 37 #include <stdio.h> 38 #include <string.h> 39 40 int readrec_forward(FILE *f, struct acctv3 *av2); 41 int readrec_backward(FILE *f, struct acctv3 *av2); 42 43 /* 44 * Reverse offsetof: return the offset of field f 45 * from the end of the structure s. 46 */ 47 #define roffsetof(s, f) (sizeof(s) - offsetof(s, f)) 48 49 /* 50 * Read exactly one record of size size from stream f into ptr. 51 * Failure to read the complete record is considered a file format error, 52 * and will set errno to EFTYPE. 53 * Return 0 on success, EOF on end of file or error. 54 */ 55 static int 56 fread_record(void *ptr, size_t size, FILE *f) 57 { 58 size_t rv; 59 60 if ((rv = fread(ptr, 1, size, f)) == size) 61 return (0); 62 else if (ferror(f) || rv == 0) 63 return (EOF); 64 else { 65 /* Short read. */ 66 errno = EFTYPE; 67 return (EOF); 68 } 69 } 70 71 /* 72 * Return the value of a comp_t field. 73 */ 74 static float 75 decode_comp(comp_t v) 76 { 77 int result, exp; 78 79 result = v & 017777; 80 for (exp = v >> 13; exp; exp--) 81 result <<= 3; 82 return ((double)result / AHZV1); 83 } 84 85 /* 86 * Read a v1 accounting record stored at the current 87 * position of stream f. 88 * Convert the data to the current record format. 89 * Return EOF on error or end-of-file. 90 */ 91 static int 92 readrec_v1(FILE *f, struct acctv3 *av3) 93 { 94 struct acctv1 av1; 95 int rv; 96 97 if ((rv = fread_record(&av1, sizeof(av1), f)) == EOF) 98 return (EOF); 99 av3->ac_zero = 0; 100 av3->ac_version = 3; 101 av3->ac_len = av3->ac_len2 = sizeof(*av3); 102 memcpy(av3->ac_comm, av1.ac_comm, AC_COMM_LEN); 103 av3->ac_utime = decode_comp(av1.ac_utime) * 1000000; 104 av3->ac_stime = decode_comp(av1.ac_stime) * 1000000; 105 av3->ac_etime = decode_comp(av1.ac_etime) * 1000000; 106 av3->ac_btime = av1.ac_btime; 107 av3->ac_uid = av1.ac_uid; 108 av3->ac_gid = av1.ac_gid; 109 av3->ac_mem = av1.ac_mem; 110 av3->ac_io = decode_comp(av1.ac_io); 111 av3->ac_tty = av1.ac_tty; 112 av3->ac_flagx = av1.ac_flag | ANVER; 113 return (0); 114 } 115 116 /* 117 * Read an v2 accounting record stored at the current 118 * position of stream f. 119 * Return EOF on error or end-of-file. 120 */ 121 static int 122 readrec_v2(FILE *f, struct acctv3 *av3) 123 { 124 struct acctv2 av2; 125 int rv; 126 127 if ((rv = fread_record(&av2, sizeof(av2), f)) == EOF) 128 return (EOF); 129 av3->ac_zero = 0; 130 av3->ac_version = 3; 131 av3->ac_len = av3->ac_len2 = sizeof(*av3); 132 memcpy(av3->ac_comm, av2.ac_comm, AC_COMM_LEN); 133 av3->ac_utime = av2.ac_utime; 134 av3->ac_stime = av2.ac_stime; 135 av3->ac_etime = av2.ac_etime; 136 av3->ac_btime = av2.ac_btime; 137 av3->ac_uid = av2.ac_uid; 138 av3->ac_gid = av2.ac_gid; 139 av3->ac_mem = av2.ac_mem; 140 av3->ac_io = av2.ac_io; 141 av3->ac_tty = av2.ac_tty; 142 av3->ac_flagx = av2.ac_flagx; 143 return (0); 144 } 145 146 /* 147 * Read an v2 accounting record stored at the current 148 * position of stream f. 149 * Return EOF on error or end-of-file. 150 */ 151 static int 152 readrec_v3(FILE *f, struct acctv3 *av3) 153 { 154 155 return (fread_record(av3, sizeof(*av3), f)); 156 } 157 158 /* 159 * Read a new-style (post-v1) accounting record stored at 160 * the current position of stream f. 161 * Convert the data to the current record format. 162 * Return EOF on error or end-of-file. 163 */ 164 static int 165 readrec_vx(FILE *f, struct acctv3 *av3) 166 { 167 uint8_t magic, version; 168 169 if (fread_record(&magic, sizeof(magic), f) == EOF || 170 fread_record(&version, sizeof(version), f) == EOF || 171 ungetc(version, f) == EOF || 172 ungetc(magic, f) == EOF) 173 return (EOF); 174 switch (version) { 175 case 2: 176 return (readrec_v2(f, av3)); 177 case 3: 178 return (readrec_v3(f, av3)); 179 180 /* Add handling for more versions here. */ 181 182 default: 183 errno = EFTYPE; 184 return (EOF); 185 } 186 } 187 188 /* 189 * Read an accounting record stored at the current 190 * position of stream f. 191 * Old-format records are converted to the current record 192 * format. 193 * Return the number of records read (1 or 0 at the end-of-file), 194 * or EOF on error. 195 */ 196 int 197 readrec_forward(FILE *f, struct acctv3 *av3) 198 { 199 int magic, rv; 200 201 if ((magic = getc(f)) == EOF) 202 return (ferror(f) ? EOF : 0); 203 if (ungetc(magic, f) == EOF) 204 return (EOF); 205 if (magic != 0) 206 /* Old record format. */ 207 rv = readrec_v1(f, av3); 208 else 209 /* New record formats. */ 210 rv = readrec_vx(f, av3); 211 return (rv == EOF ? EOF : 1); 212 } 213 214 /* 215 * Read an accounting record ending at the current 216 * position of stream f. 217 * Old-format records are converted to the current record 218 * format. 219 * The file pointer is positioned at the beginning of the 220 * record read. 221 * Return the number of records read (1 or 0 at the end-of-file), 222 * or EOF on error. 223 */ 224 int 225 readrec_backward(FILE *f, struct acctv3 *av3) 226 { 227 off_t pos; 228 int c; 229 uint16_t len; 230 231 if ((pos = ftell(f)) == -1) 232 return (EOF); 233 if (pos == 0) 234 return (0); 235 if (fseek(f, -roffsetof(struct acctv3, ac_trailer), 236 SEEK_CUR) == EOF || 237 (c = getc(f)) == EOF) 238 return (EOF); 239 if (c & ANVER) { 240 /* 241 * New record formats. For v2 and v3 offset from the 242 * end for ac_len2 should be same. 243 */ 244 if (fseeko(f, pos - roffsetof(struct acctv2, ac_len2), 245 SEEK_SET) == EOF || 246 fread_record(&len, sizeof(len), f) == EOF || 247 fseeko(f, pos - len, SEEK_SET) == EOF || 248 readrec_vx(f, av3) == EOF || 249 fseeko(f, pos - len, SEEK_SET) == EOF) 250 return (EOF); 251 else 252 return (1); 253 } else { 254 /* Old record format. */ 255 if (fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF || 256 readrec_v1(f, av3) == EOF || 257 fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF) 258 return (EOF); 259 else 260 return (1); 261 } 262 } 263