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