136108Sjb/*- 250472Speter * Copyright (c) 2007 Diomidis Spinellis 336108Sjb * All rights reserved. 436108Sjb * 543818Swes * Redistribution and use in source and binary forms, with or without 643818Swes * modification, are permitted provided that the following conditions 750177Shoek * are met: 843818Swes * 1. Redistributions of source code must retain the above copyright 943818Swes * notice, this list of conditions and the following disclaimer. 1043818Swes * 2. Redistributions in binary form must reproduce the above copyright 1143818Swes * notice, this list of conditions and the following disclaimer in the 1243818Swes * documentation and/or other materials provided with the distribution. 1350177Shoek * 1443818Swes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1543818Swes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1643818Swes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1750177Shoek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18199243Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19220154Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20220154Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2143818Swes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2243818Swes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2350177Shoek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2450177Shoek * SUCH DAMAGE. 2543818Swes * 2636108Sjb */ 2736108Sjb 2850177Shoek#include <sys/cdefs.h> 2950177Shoek__FBSDID("$FreeBSD$"); 3056805Sobrien 3136108Sjb#include <sys/param.h> 32199243Sed#include <sys/stat.h> 3336108Sjb#include <sys/types.h> 34199243Sed#include <sys/acct.h> 35199243Sed 36199243Sed#include <errno.h> 37199243Sed#include <stddef.h> 38199243Sed#include <stdio.h> 39199243Sed#include <string.h> 40199243Sed 41170088Sdougbint readrec_forward(FILE *f, struct acctv2 *av2); 4236108Sjbint readrec_backward(FILE *f, struct acctv2 *av2); 4336108Sjb 44267236Snwhitehorn/* 45180487Sed * Reverse offsetof: return the offset of field f 46180487Sed * from the end of the structure s. 47180487Sed */ 48121468Ssimokawa#define roffsetof(s, f) (sizeof(s) - offsetof(s, f)) 49121468Ssimokawa 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 */ 56static int 57fread_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 */ 75static float 76decode_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 */ 92static int 93readrec_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 */ 122static int 123readrec_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 */ 134static int 135readrec_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 */ 164int 165readrec_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 */ 192int 193readrec_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