diff options
Diffstat (limited to 'usr.bin/lastcomm/readrec.c')
-rw-r--r-- | usr.bin/lastcomm/readrec.c | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/usr.bin/lastcomm/readrec.c b/usr.bin/lastcomm/readrec.c new file mode 100644 index 0000000..5de7c5c --- /dev/null +++ b/usr.bin/lastcomm/readrec.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 2007 Diomidis Spinellis + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/acct.h> + +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +int readrec_forward(FILE *f, struct acctv2 *av2); +int readrec_backward(FILE *f, struct acctv2 *av2); + +/* + * Reverse offsetof: return the offset of field f + * from the end of the structure s. + */ +#define roffsetof(s, f) (sizeof(s) - offsetof(s, f)) + +/* + * Read exactly one record of size size from stream f into ptr. + * Failure to read the complete record is considered a file format error, + * and will set errno to EFTYPE. + * Return 0 on success, EOF on end of file or error. + */ +static int +fread_record(void *ptr, size_t size, FILE *f) +{ + size_t rv; + + if ((rv = fread(ptr, 1, size, f)) == size) + return (0); + else if (ferror(f) || rv == 0) + return (EOF); + else { + /* Short read. */ + errno = EFTYPE; + return (EOF); + } +} + +/* + * Return the value of a comp_t field. + */ +static float +decode_comp(comp_t v) +{ + int result, exp; + + result = v & 017777; + for (exp = v >> 13; exp; exp--) + result <<= 3; + return ((double)result / AHZV1); +} + +/* + * Read a v1 accounting record stored at the current + * position of stream f. + * Convert the data to the current record format. + * Return EOF on error or end-of-file. + */ +static int +readrec_v1(FILE *f, struct acctv2 *av2) +{ + struct acctv1 av1; + int rv; + + if ((rv = fread_record(&av1, sizeof(av1), f)) == EOF) + return (EOF); + av2->ac_zero = 0; + av2->ac_version = 2; + av2->ac_len = av2->ac_len2 = sizeof(*av2); + memcpy(av2->ac_comm, av1.ac_comm, AC_COMM_LEN); + av2->ac_utime = decode_comp(av1.ac_utime) * 1000000; + av2->ac_stime = decode_comp(av1.ac_stime) * 1000000; + av2->ac_etime = decode_comp(av1.ac_etime) * 1000000; + av2->ac_btime = av1.ac_btime; + av2->ac_uid = av1.ac_uid; + av2->ac_gid = av1.ac_gid; + av2->ac_mem = av1.ac_mem; + av2->ac_io = decode_comp(av1.ac_io); + av2->ac_tty = av1.ac_tty; + av2->ac_flagx = av1.ac_flag | ANVER; + return (0); +} + +/* + * Read an v2 accounting record stored at the current + * position of stream f. + * Return EOF on error or end-of-file. + */ +static int +readrec_v2(FILE *f, struct acctv2 *av2) +{ + return (fread_record(av2, sizeof(*av2), f)); +} + +/* + * Read a new-style (post-v1) accounting record stored at + * the current position of stream f. + * Convert the data to the current record format. + * Return EOF on error or end-of-file. + */ +static int +readrec_vx(FILE *f, struct acctv2 *av2) +{ + uint8_t magic, version; + + if (fread_record(&magic, sizeof(magic), f) == EOF || + fread_record(&version, sizeof(version), f) == EOF || + ungetc(version, f) == EOF || + ungetc(magic, f) == EOF) + return (EOF); + switch (version) { + case 2: + return (readrec_v2(f, av2)); + + /* Add handling for more versions here. */ + + default: + errno = EFTYPE; + return (EOF); + } +} + +/* + * Read an accounting record stored at the current + * position of stream f. + * Old-format records are converted to the current record + * format. + * Return the number of records read (1 or 0 at the end-of-file), + * or EOF on error. + */ +int +readrec_forward(FILE *f, struct acctv2 *av2) +{ + int magic, rv; + + if ((magic = getc(f)) == EOF) + return (ferror(f) ? EOF : 0); + if (ungetc(magic, f) == EOF) + return (EOF); + if (magic != 0) + /* Old record format. */ + rv = readrec_v1(f, av2); + else + /* New record formats. */ + rv = readrec_vx(f, av2); + return (rv == EOF ? EOF : 1); +} + +/* + * Read an accounting record ending at the current + * position of stream f. + * Old-format records are converted to the current record + * format. + * The file pointer is positioned at the beginning of the + * record read. + * Return the number of records read (1 or 0 at the end-of-file), + * or EOF on error. + */ +int +readrec_backward(FILE *f, struct acctv2 *av2) +{ + off_t pos; + int c; + uint16_t len; + + if ((pos = ftell(f)) == -1) + return (EOF); + if (pos == 0) + return (0); + if (fseek(f, -roffsetof(struct acctv2, ac_trailer), + SEEK_CUR) == EOF || + (c = getc(f)) == EOF) + return (EOF); + if (c & ANVER) { + /* New record formats. */ + if (fseeko(f, pos - roffsetof(struct acctv2, ac_len2), + SEEK_SET) == EOF || + fread_record(&len, sizeof(len), f) == EOF || + fseeko(f, pos - len, SEEK_SET) == EOF || + readrec_vx(f, av2) == EOF || + fseeko(f, pos - len, SEEK_SET) == EOF) + return (EOF); + else + return (1); + } else { + /* Old record format. */ + if (fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF || + readrec_v1(f, av2) == EOF || + fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF) + return (EOF); + else + return (1); + } +} |