1 /*- 2 * Copyright (c) 2007 Diomidis Spinellis 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 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 acctv2 *av2); 42 int readrec_backward(FILE *f, struct acctv2 *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 acctv2 *av2) 94 { 95 struct acctv1 av1; 96 int rv; 97 98 if ((rv = fread_record(&av1, sizeof(av1), f)) == EOF) 99 return (EOF); 100 av2->ac_zero = 0; 101 av2->ac_version = 2; 102 av2->ac_len = av2->ac_len2 = sizeof(*av2); 103 memcpy(av2->ac_comm, av1.ac_comm, AC_COMM_LEN); 104 av2->ac_utime = decode_comp(av1.ac_utime) * 1000000; 105 av2->ac_stime = decode_comp(av1.ac_stime) * 1000000; 106 av2->ac_etime = decode_comp(av1.ac_etime) * 1000000; 107 av2->ac_btime = av1.ac_btime; 108 av2->ac_uid = av1.ac_uid; 109 av2->ac_gid = av1.ac_gid; 110 av2->ac_mem = av1.ac_mem; 111 av2->ac_io = decode_comp(av1.ac_io); 112 av2->ac_tty = av1.ac_tty; 113 av2->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 acctv2 *av2) 124 { 125 return (fread_record(av2, sizeof(*av2), f)); 126 } 127 128 /* 129 * Read a new-style (post-v1) accounting record stored at 130 * the current position of stream f. 131 * Convert the data to the current record format. 132 * Return EOF on error or end-of-file. 133 */ 134 static int 135 readrec_vx(FILE *f, struct acctv2 *av2) 136 { 137 uint8_t magic, version; 138 139 if (fread_record(&magic, sizeof(magic), f) == EOF || 140 fread_record(&version, sizeof(version), f) == EOF || 141 ungetc(version, f) == EOF || 142 ungetc(magic, f) == EOF) 143 return (EOF); 144 switch (version) { 145 case 2: 146 return (readrec_v2(f, av2)); 147 148 /* Add handling for more versions here. */ 149 150 default: 151 errno = EFTYPE; 152 return (EOF); 153 } 154 } 155 156 /* 157 * Read an accounting record stored at the current 158 * position of stream f. 159 * Old-format records are converted to the current record 160 * format. 161 * Return the number of records read (1 or 0 at the end-of-file), 162 * or EOF on error. 163 */ 164 int 165 readrec_forward(FILE *f, struct acctv2 *av2) 166 { 167 int magic, rv; 168 169 if ((magic = getc(f)) == EOF) 170 return (ferror(f) ? EOF : 0); 171 if (ungetc(magic, f) == EOF) 172 return (EOF); 173 if (magic != 0) 174 /* Old record format. */ 175 rv = readrec_v1(f, av2); 176 else 177 /* New record formats. */ 178 rv = readrec_vx(f, av2); 179 return (rv == EOF ? EOF : 1); 180 } 181 182 /* 183 * Read an accounting record ending at the current 184 * position of stream f. 185 * Old-format records are converted to the current record 186 * format. 187 * The file pointer is positioned at the beginning of the 188 * record read. 189 * Return the number of records read (1 or 0 at the end-of-file), 190 * or EOF on error. 191 */ 192 int 193 readrec_backward(FILE *f, struct acctv2 *av2) 194 { 195 off_t pos; 196 int c; 197 uint16_t len; 198 199 if ((pos = ftell(f)) == -1) 200 return (EOF); 201 if (pos == 0) 202 return (0); 203 if (fseek(f, -roffsetof(struct acctv2, ac_trailer), 204 SEEK_CUR) == EOF || 205 (c = getc(f)) == EOF) 206 return (EOF); 207 if (c & ANVER) { 208 /* New record formats. */ 209 if (fseeko(f, pos - roffsetof(struct acctv2, ac_len2), 210 SEEK_SET) == EOF || 211 fread_record(&len, sizeof(len), f) == EOF || 212 fseeko(f, pos - len, SEEK_SET) == EOF || 213 readrec_vx(f, av2) == EOF || 214 fseeko(f, pos - len, SEEK_SET) == EOF) 215 return (EOF); 216 else 217 return (1); 218 } else { 219 /* Old record format. */ 220 if (fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF || 221 readrec_v1(f, av2) == EOF || 222 fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF) 223 return (EOF); 224 else 225 return (1); 226 } 227 } 228