1169857Sdds/*- 2169857Sdds * Copyright (c) 2007 Diomidis Spinellis 3169857Sdds * All rights reserved. 4169857Sdds * 5169857Sdds * Redistribution and use in source and binary forms, with or without 6169857Sdds * modification, are permitted provided that the following conditions 7169857Sdds * are met: 8169857Sdds * 1. Redistributions of source code must retain the above copyright 9169857Sdds * notice, this list of conditions and the following disclaimer. 10169857Sdds * 2. Redistributions in binary form must reproduce the above copyright 11169857Sdds * notice, this list of conditions and the following disclaimer in the 12169857Sdds * documentation and/or other materials provided with the distribution. 13169857Sdds * 14169857Sdds * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15169857Sdds * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16169857Sdds * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17169857Sdds * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18169857Sdds * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19169857Sdds * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20169857Sdds * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21169857Sdds * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22169857Sdds * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23169857Sdds * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24169857Sdds * SUCH DAMAGE. 25169857Sdds * 26169857Sdds */ 27169857Sdds 28169857Sdds#include <sys/cdefs.h> 29169857Sdds__FBSDID("$FreeBSD: releng/10.2/usr.bin/lastcomm/readrec.c 223375 2011-06-21 20:36:10Z delphij $"); 30169857Sdds 31169857Sdds#include <sys/param.h> 32169857Sdds#include <sys/stat.h> 33169857Sdds#include <sys/types.h> 34169857Sdds#include <sys/acct.h> 35169857Sdds 36169857Sdds#include <errno.h> 37169857Sdds#include <stddef.h> 38169857Sdds#include <stdio.h> 39169857Sdds#include <string.h> 40169857Sdds 41169857Sddsint readrec_forward(FILE *f, struct acctv2 *av2); 42169857Sddsint readrec_backward(FILE *f, struct acctv2 *av2); 43169857Sdds 44169857Sdds/* 45169857Sdds * Reverse offsetof: return the offset of field f 46169857Sdds * from the end of the structure s. 47169857Sdds */ 48169857Sdds#define roffsetof(s, f) (sizeof(s) - offsetof(s, f)) 49169857Sdds 50169857Sdds/* 51169857Sdds * Read exactly one record of size size from stream f into ptr. 52169857Sdds * Failure to read the complete record is considered a file format error, 53169857Sdds * and will set errno to EFTYPE. 54169857Sdds * Return 0 on success, EOF on end of file or error. 55169857Sdds */ 56169857Sddsstatic int 57169857Sddsfread_record(void *ptr, size_t size, FILE *f) 58169857Sdds{ 59169857Sdds size_t rv; 60169857Sdds 61169857Sdds if ((rv = fread(ptr, 1, size, f)) == size) 62169857Sdds return (0); 63169857Sdds else if (ferror(f) || rv == 0) 64169857Sdds return (EOF); 65169857Sdds else { 66169857Sdds /* Short read. */ 67169857Sdds errno = EFTYPE; 68169857Sdds return (EOF); 69169857Sdds } 70169857Sdds} 71169857Sdds 72169857Sdds/* 73169857Sdds * Return the value of a comp_t field. 74169857Sdds */ 75169857Sddsstatic float 76169857Sddsdecode_comp(comp_t v) 77169857Sdds{ 78169857Sdds int result, exp; 79169857Sdds 80169857Sdds result = v & 017777; 81169857Sdds for (exp = v >> 13; exp; exp--) 82169857Sdds result <<= 3; 83169857Sdds return ((double)result / AHZV1); 84169857Sdds} 85169857Sdds 86169857Sdds/* 87169857Sdds * Read a v1 accounting record stored at the current 88169857Sdds * position of stream f. 89169857Sdds * Convert the data to the current record format. 90169857Sdds * Return EOF on error or end-of-file. 91169857Sdds */ 92169857Sddsstatic int 93169857Sddsreadrec_v1(FILE *f, struct acctv2 *av2) 94169857Sdds{ 95169857Sdds struct acctv1 av1; 96169857Sdds int rv; 97169857Sdds 98169857Sdds if ((rv = fread_record(&av1, sizeof(av1), f)) == EOF) 99169857Sdds return (EOF); 100169857Sdds av2->ac_zero = 0; 101169857Sdds av2->ac_version = 2; 102169857Sdds av2->ac_len = av2->ac_len2 = sizeof(*av2); 103169857Sdds memcpy(av2->ac_comm, av1.ac_comm, AC_COMM_LEN); 104169857Sdds av2->ac_utime = decode_comp(av1.ac_utime) * 1000000; 105169857Sdds av2->ac_stime = decode_comp(av1.ac_stime) * 1000000; 106169857Sdds av2->ac_etime = decode_comp(av1.ac_etime) * 1000000; 107169857Sdds av2->ac_btime = av1.ac_btime; 108169857Sdds av2->ac_uid = av1.ac_uid; 109169857Sdds av2->ac_gid = av1.ac_gid; 110169857Sdds av2->ac_mem = av1.ac_mem; 111169857Sdds av2->ac_io = decode_comp(av1.ac_io); 112169857Sdds av2->ac_tty = av1.ac_tty; 113169857Sdds av2->ac_flagx = av1.ac_flag | ANVER; 114169857Sdds return (0); 115169857Sdds} 116169857Sdds 117169857Sdds/* 118169857Sdds * Read an v2 accounting record stored at the current 119169857Sdds * position of stream f. 120169857Sdds * Return EOF on error or end-of-file. 121169857Sdds */ 122169857Sddsstatic int 123169857Sddsreadrec_v2(FILE *f, struct acctv2 *av2) 124169857Sdds{ 125169857Sdds return (fread_record(av2, sizeof(*av2), f)); 126169857Sdds} 127169857Sdds 128169857Sdds/* 129169857Sdds * Read a new-style (post-v1) accounting record stored at 130169857Sdds * the current position of stream f. 131169857Sdds * Convert the data to the current record format. 132169857Sdds * Return EOF on error or end-of-file. 133169857Sdds */ 134169857Sddsstatic int 135169857Sddsreadrec_vx(FILE *f, struct acctv2 *av2) 136169857Sdds{ 137169857Sdds uint8_t magic, version; 138169857Sdds 139169857Sdds if (fread_record(&magic, sizeof(magic), f) == EOF || 140169857Sdds fread_record(&version, sizeof(version), f) == EOF || 141169857Sdds ungetc(version, f) == EOF || 142169857Sdds ungetc(magic, f) == EOF) 143169857Sdds return (EOF); 144169857Sdds switch (version) { 145169857Sdds case 2: 146169857Sdds return (readrec_v2(f, av2)); 147169857Sdds 148169857Sdds /* Add handling for more versions here. */ 149169857Sdds 150169857Sdds default: 151169857Sdds errno = EFTYPE; 152169857Sdds return (EOF); 153169857Sdds } 154169857Sdds} 155169857Sdds 156169857Sdds/* 157169857Sdds * Read an accounting record stored at the current 158169857Sdds * position of stream f. 159169857Sdds * Old-format records are converted to the current record 160169857Sdds * format. 161169857Sdds * Return the number of records read (1 or 0 at the end-of-file), 162169857Sdds * or EOF on error. 163169857Sdds */ 164169857Sddsint 165169857Sddsreadrec_forward(FILE *f, struct acctv2 *av2) 166169857Sdds{ 167169857Sdds int magic, rv; 168169857Sdds 169169857Sdds if ((magic = getc(f)) == EOF) 170169857Sdds return (ferror(f) ? EOF : 0); 171169857Sdds if (ungetc(magic, f) == EOF) 172169857Sdds return (EOF); 173169857Sdds if (magic != 0) 174169857Sdds /* Old record format. */ 175169857Sdds rv = readrec_v1(f, av2); 176169857Sdds else 177169857Sdds /* New record formats. */ 178169857Sdds rv = readrec_vx(f, av2); 179169857Sdds return (rv == EOF ? EOF : 1); 180169857Sdds} 181169857Sdds 182169857Sdds/* 183169857Sdds * Read an accounting record ending at the current 184169857Sdds * position of stream f. 185169857Sdds * Old-format records are converted to the current record 186169857Sdds * format. 187169857Sdds * The file pointer is positioned at the beginning of the 188169857Sdds * record read. 189169857Sdds * Return the number of records read (1 or 0 at the end-of-file), 190169857Sdds * or EOF on error. 191169857Sdds */ 192169857Sddsint 193169857Sddsreadrec_backward(FILE *f, struct acctv2 *av2) 194169857Sdds{ 195169857Sdds off_t pos; 196169857Sdds int c; 197169857Sdds uint16_t len; 198169857Sdds 199169857Sdds if ((pos = ftell(f)) == -1) 200169857Sdds return (EOF); 201169857Sdds if (pos == 0) 202169857Sdds return (0); 203169857Sdds if (fseek(f, -roffsetof(struct acctv2, ac_trailer), 204169857Sdds SEEK_CUR) == EOF || 205169857Sdds (c = getc(f)) == EOF) 206169857Sdds return (EOF); 207169857Sdds if (c & ANVER) { 208169857Sdds /* New record formats. */ 209169857Sdds if (fseeko(f, pos - roffsetof(struct acctv2, ac_len2), 210169857Sdds SEEK_SET) == EOF || 211169857Sdds fread_record(&len, sizeof(len), f) == EOF || 212169857Sdds fseeko(f, pos - len, SEEK_SET) == EOF || 213169857Sdds readrec_vx(f, av2) == EOF || 214169857Sdds fseeko(f, pos - len, SEEK_SET) == EOF) 215169857Sdds return (EOF); 216169857Sdds else 217169857Sdds return (1); 218169857Sdds } else { 219169857Sdds /* Old record format. */ 220169857Sdds if (fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF || 221169857Sdds readrec_v1(f, av2) == EOF || 222169857Sdds fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF) 223169857Sdds return (EOF); 224169857Sdds else 225169857Sdds return (1); 226169857Sdds } 227169857Sdds} 228