diff options
Diffstat (limited to 'usr.sbin/fifolog/lib')
-rw-r--r-- | usr.sbin/fifolog/lib/Makefile | 14 | ||||
-rw-r--r-- | usr.sbin/fifolog/lib/Makefile.depend | 17 | ||||
-rw-r--r-- | usr.sbin/fifolog/lib/fifolog.h | 138 | ||||
-rw-r--r-- | usr.sbin/fifolog/lib/fifolog_create.c | 122 | ||||
-rw-r--r-- | usr.sbin/fifolog/lib/fifolog_int.c | 256 | ||||
-rw-r--r-- | usr.sbin/fifolog/lib/fifolog_reader.c | 322 | ||||
-rw-r--r-- | usr.sbin/fifolog/lib/fifolog_write.h | 75 | ||||
-rw-r--r-- | usr.sbin/fifolog/lib/fifolog_write_poll.c | 412 | ||||
-rw-r--r-- | usr.sbin/fifolog/lib/getdate.y | 887 | ||||
-rw-r--r-- | usr.sbin/fifolog/lib/libfifolog.h | 47 | ||||
-rw-r--r-- | usr.sbin/fifolog/lib/libfifolog_int.h | 45 | ||||
-rw-r--r-- | usr.sbin/fifolog/lib/miniobj.h | 74 |
12 files changed, 2409 insertions, 0 deletions
diff --git a/usr.sbin/fifolog/lib/Makefile b/usr.sbin/fifolog/lib/Makefile new file mode 100644 index 0000000..f60529f --- /dev/null +++ b/usr.sbin/fifolog/lib/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD$ + +LIB= fifolog +INTERNALLIB= # API not published or supported. + +SRCS= fifolog_int.c fifolog_create.c fifolog_write_poll.c fifolog_reader.c +SRCS+= getdate.y + +CFLAGS+= -I${.CURDIR} +LIBADD= z + +NO_WMISSING_VARIABLE_DECLARATIONS= + +.include <bsd.lib.mk> diff --git a/usr.sbin/fifolog/lib/Makefile.depend b/usr.sbin/fifolog/lib/Makefile.depend new file mode 100644 index 0000000..f91d860 --- /dev/null +++ b/usr.sbin/fifolog/lib/Makefile.depend @@ -0,0 +1,17 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/libz \ + usr.bin/yacc.host \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +getdate.o: getdate.c +getdate.po: getdate.c +.endif diff --git a/usr.sbin/fifolog/lib/fifolog.h b/usr.sbin/fifolog/lib/fifolog.h new file mode 100644 index 0000000..0575a46 --- /dev/null +++ b/usr.sbin/fifolog/lib/fifolog.h @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 2005-2008 Poul-Henning Kamp + * 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. + * + * $FreeBSD$ + */ + +#ifndef __LOCAL_FIFOLOG_H_ +#define __LOCAL_FIFOLOG_H_ + +/* + * Definitions for fifolog "protocol": the on-media layout. + * + * The fifolog on-media record has three layers: + * The outer timestamping and synchronization layer. + * The zlib implemented data compression. + * The inner sequencing and identification layer. + * + * All three layers are synchronized at a subset of the outer layer + * record boundaries, from where reading can be initiated. + * + * + * The outer layer: + * ----------------- + * The first record in a fifolog contains a magic string and version + * information along with a 32be encoded recordsize for all records + * in the fifolog, including the first. + * The recordsize is explicit to avoid ambiguities when a media is + * moved from one machine to another. + * + * Each record in the fifolog has the following contents: + * offset type contents + * -------------------------------------------------------------- + * 0 32be sequence_number + * The sequence number is randomly chosen for the + * fifolog and increments once for each record written. + * It's presence allow quick identification of the next + * record to be written using a binary search for the + * first place where a discontinuity in the sequence + * numbers occur. + * 4 8 flags (FIFOLOG_FLG_*) + * + * If (flags & (FIFOLOG_FLG_SYNC)) the record is a synchronization point + * at which the inner layers are aligned so that reading can be started + * at this point. + * To enable seeks into the file based on timestamps, a third field is + * present in these records as well: + * 5 32be time_t containing POSIX's understanding of UTC. + * + * These fields are immediately followed by the inner layer payload as + * described below, which has variable length. + * + * If the inner layer payload is shorter than the available space in + * the record, it is padded with zero bytes, and the number of unused + * bytes, including the encoded length thereof is recorded at the end + * of the record as follows: + * + * If (flags & FIFOLOG_FLG_1BYTE) + * n-1 8 number of unused bytes + * else if (flags & FIFOLOG_FLG_4BYTE) + * n-4 32be number of unused bytes + * + * + * The gzip layer + * -------------- + * Is just output from zlib, nothing special here. FIFOLOG_FLG_SYNC + * corresponds to Z_FINISH flags to zlib. + * In most cases, the timer will expire before zlib has filled an entire + * record in which case Z_SYNC_FLUSH will be used to force as much as + * possible into the buffer before it is written. This is not marked + * in outer layer (apart from a natural correlation with padding) since + * zlibs data stream handles this without help. + * + * + * The inner layer: + * ---------------- + * The inner layer contains data identification and to the second + * timestamping (the timestamp in the outer layer only marks the + * first possible timestamp for content in the SYNC record). + * + * offset type contents + * -------------------------------------------------------------- + * 0 32be ident + * + * The bottom 30 bits of the identification word are application defined, + * presently unused in the stand-alone fifolog tools, but used in the + * original "measured" application that originated the fifolog format. + * Should for instance syslogd(8) grow native support for fifolog format, + * it could store the message priority here. + * + * If (ident & FIFOLOG_TIMESTAMP) the record is prefixed by: + * 4 32be time_t containing POSIX's understanding of UTC. + * + * Then follows the content, either as a NUL terminated string or as + * a length encoded binary sequence: + * + * If (ident & FIFOLOG_LENGTH) the record is prefixed by: + * {0|4} 8 length of binary data + * + */ + +/* Magic identification string */ +#define FIFOLOG_FMT_MAGIC "Measured FIFOLOG Ver 1.01\n" + +/* Offset of the 32be encoded recordsize in the first sector */ +#define FIFOLOG_OFF_BS 0x20 + +#define FIFOLOG_FLG_1BYTE 0x01 +#define FIFOLOG_FLG_4BYTE 0x02 +#define FIFOLOG_FLG_RESTART 0x40 +#define FIFOLOG_FLG_SYNC 0x80 + +#define FIFOLOG_TIMESTAMP 0x80000000 +#define FIFOLOG_LENGTH 0x40000000 +#define FIFOLOG_IDENT 0x3fffffff + +#endif /* __LOCAL_FIFOLOG_H_ */ diff --git a/usr.sbin/fifolog/lib/fifolog_create.c b/usr.sbin/fifolog/lib/fifolog_create.c new file mode 100644 index 0000000..4a7c333 --- /dev/null +++ b/usr.sbin/fifolog/lib/fifolog_create.c @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 2005-2008 Poul-Henning Kamp + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <sys/endian.h> +#include <sys/stat.h> +#include <sys/disk.h> + +#include "fifolog.h" +#include "libfifolog.h" + +const char * +fifolog_create(const char *fn, off_t size, ssize_t recsize) +{ + int i, fd; + ssize_t u; + off_t ms; + struct stat st; + char *buf; + int created; + + fd = open(fn, O_WRONLY | O_TRUNC | O_EXCL | O_CREAT, 0644); + if (fd < 0) { + created = 0; + fd = open(fn, O_WRONLY); + if (fd < 0) + return ("Could not open"); + } else + created = 1; + + /* Default sectorsize is 512 */ + if (recsize == 0) + recsize = 512; + + /* See what we got... */ + i = fstat(fd, &st); + assert(i == 0); + if (!S_ISBLK(st.st_mode) && + !S_ISCHR(st.st_mode) && + !S_ISREG(st.st_mode)) { + assert(!close (fd)); + return ("Wrong file type"); + } + + if(!created && S_ISREG(st.st_mode)) { + assert(!close (fd)); + return ("Wrong file type"); + } + + /* For raw disk with larger sectors: use 1 sector */ + i = ioctl(fd, DIOCGSECTORSIZE, &u); + if (i == 0 && (u > recsize || (recsize % u) != 0)) + recsize = u; + + /* If no configured size, or too large for disk, use device size */ + i = ioctl(fd, DIOCGMEDIASIZE, &ms); + if (i == 0 && (size == 0 || size > ms)) + size = ms; + + if (size == 0 && S_ISREG(st.st_mode)) + size = st.st_size; + + if (size == 0) + size = recsize * (off_t)(24*60*60); + + if (S_ISREG(st.st_mode) && ftruncate(fd, size) < 0) + return ("Could not ftrunc"); + + buf = calloc(recsize, 1); + if (buf == NULL) + return ("Could not malloc"); + + strcpy(buf, FIFOLOG_FMT_MAGIC); /*lint !e64 */ + be32enc(buf + FIFOLOG_OFF_BS, recsize); + if (recsize != pwrite(fd, buf, recsize, 0)) { + i = errno; + free(buf); + errno = i; + return ("Could not write first sector"); + } + memset(buf, 0, recsize); + if ((int)recsize != pwrite(fd, buf, recsize, recsize)) { + i = errno; + free(buf); + errno = i; + return ("Could not write second sector"); + } + free(buf); + assert(0 == close(fd)); + return (NULL); +} diff --git a/usr.sbin/fifolog/lib/fifolog_int.c b/usr.sbin/fifolog/lib/fifolog_int.c new file mode 100644 index 0000000..9d3b437 --- /dev/null +++ b/usr.sbin/fifolog/lib/fifolog_int.c @@ -0,0 +1,256 @@ +/*- + * Copyright (c) 2005-2008 Poul-Henning Kamp + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <zlib.h> + +#include <sys/disk.h> +#include <sys/endian.h> +#include <sys/stat.h> + +#include "miniobj.h" +#include "fifolog.h" +#include "libfifolog_int.h" + +/* + * Open a fifolog file or partition for reading or writing. + * + * Return value is NULL for success or a error description string to + * be augmented by errno if non-zero. + * + * The second function is just an error-handling wrapper around the + * first which, does the actual work. + */ + +static const char * +fifolog_int_open_i(struct fifolog_file *f, const char *fname, int mode) +{ + struct stat st; + ssize_t u; + int i; + + f->fd = open(fname, mode ? O_RDWR : O_RDONLY); + if (f->fd < 0) + return ("Cannot open"); + + /* Determine initial record size guesstimate */ + i = ioctl(f->fd, DIOCGSECTORSIZE, &f->recsize); + if (i != 0 && errno != ENOTTY) + return ("ioctl(DIOCGSECTORSIZE) failed"); + + if (i != 0) { + i = fstat(f->fd, &st); + assert(i == 0); + if (!S_ISREG(st.st_mode)) + return ("Neither disk nor regular file"); + f->recsize = 512; + f->logsize = st.st_size; + } else if (f->recsize < 64) { + return ("Disk device sectorsize smaller than 64"); + } else { + i = ioctl(f->fd, DIOCGMEDIASIZE, &f->logsize); + if (i < 0 && errno != ENOTTY) + return ("ioctl(DIOCGMEDIASIZE) failed"); + } + + /* Allocate a record buffer */ + f->recbuf = malloc(f->recsize); + if (f->recbuf == NULL) + return ("Cannot malloc"); + + /* Read and validate the label sector */ + i = pread(f->fd, f->recbuf, f->recsize, 0); + if (i < 0 || i < (int)f->recsize) + return ("Read error, first sector"); + + errno = 0; + if (memcmp(f->recbuf, FIFOLOG_FMT_MAGIC, strlen(FIFOLOG_FMT_MAGIC) + 1)) + return ("Wrong or missing magic string"); + + u = be32dec(f->recbuf + FIFOLOG_OFF_BS); + if (u < 64) + return ("Wrong record size in header (<64)"); + + if ((off_t)u >= f->logsize) + return ("Record size in header bigger than fifolog"); + + f->recsize = u; + + /* Reallocate the buffer to correct size if necessary */ + if (u != f->recsize) { + free(f->recbuf); + f->recbuf = NULL; + f->recsize = u; + f->recbuf = malloc(f->recsize); + if (f->recbuf == NULL) + return ("Cannot malloc"); + } + + /* Calculate number of records in fifolog */ + f->logsize /= u; + if (f->logsize < 10) + return ("less than 10 records in fifolog"); + + f->logsize--; /* the label record */ + + /* Initialize zlib handling */ + + f->zs = calloc(sizeof *f->zs, 1); + if (f->zs == NULL) + return ("cannot malloc"); + + return (NULL); +} + +const char * +fifolog_int_open(struct fifolog_file **ff, const char *fname, int mode) +{ + struct fifolog_file fs, *f; + const char *retval; + int e; + + f = &fs; + memset(f, 0, sizeof *f); + f->fd = -1; + retval = fifolog_int_open_i(f, fname, mode); + e = errno; + if (retval == NULL) { + *ff = malloc(sizeof *f); + if (*ff != NULL) { + memcpy(*ff, f, sizeof *f); + (*ff)->magic = FIFOLOG_FILE_MAGIC; + return (retval); + } + } + fifolog_int_close(&f); + errno = e; + return (retval); +} + +void +fifolog_int_close(struct fifolog_file **ff) +{ + struct fifolog_file *f; + + f = *ff; + *ff = NULL; + if (f == NULL) + return; + + if (f->fd >= 0) + (void)close(f->fd); + if (f->zs != NULL) + free(f->zs); + if (f->recbuf != NULL) + free(f->recbuf); +} + +static void +fifolog_int_file_assert(const struct fifolog_file *ff) +{ + + CHECK_OBJ_NOTNULL(ff, FIFOLOG_FILE_MAGIC); + assert(ff->fd >= 0); + assert(ff->recbuf != NULL); +} + + +/* + * Read a record. + * + * Return zero on success + */ + +int +fifolog_int_read(const struct fifolog_file *ff, off_t recno) +{ + int i; + + fifolog_int_file_assert(ff); + if (recno >= ff->logsize) + return (-1); + recno++; /* label sector */ + i = pread(ff->fd, ff->recbuf, ff->recsize, recno * ff->recsize); + if (i < 0) + return (-2); + if (i != (int)ff->recsize) + return (-3); + return (0); +} + +/* + * Find the last written record in the fifolog. + * + * Return is error string or NULL on success + */ + +const char * +fifolog_int_findend(const struct fifolog_file *ff, off_t *last) +{ + off_t o, s; + int e; + unsigned seq0, seq; + + fifolog_int_file_assert(ff); + + o = 0; + e = fifolog_int_read(ff, o); + if (e) + return("Read error, first record"); + + seq0 = be32dec(ff->recbuf); + + /* If the first records sequence is zero, the fifolog is empty */ + if (seq0 == 0) { + *last = o; + return (NULL); + } + + /* Do a binary search for a discontinuity in the sequence numbers */ + s = ff->logsize / 2; + do { + e = fifolog_int_read(ff, o + s); + if (e) + return ("Read error while searching"); + seq = be32dec(ff->recbuf); + if (seq == seq0 + s) { + o += s; + seq0 = seq; + } + s /= 2; + assert(o < ff->logsize); + } while (s > 0); + + *last = o; + return (NULL); +} diff --git a/usr.sbin/fifolog/lib/fifolog_reader.c b/usr.sbin/fifolog/lib/fifolog_reader.c new file mode 100644 index 0000000..5843a81 --- /dev/null +++ b/usr.sbin/fifolog/lib/fifolog_reader.c @@ -0,0 +1,322 @@ +/*- + * Copyright (c) 2005-2008 Poul-Henning Kamp + * 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. + * + * $FreeBSD$ + */ + +#include <stdio.h> +#include <unistd.h> +#include <assert.h> +#include <err.h> +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include <zlib.h> +#include <sys/endian.h> + +#include "fifolog.h" +#include "libfifolog.h" +#include "libfifolog_int.h" +#include "miniobj.h" + +/*--------------------------------------------------------------------*/ + +struct fifolog_reader { + unsigned magic; +#define FIFOLOG_READER_MAGIC 0x1036d139 + struct fifolog_file *ff; + unsigned olen; + unsigned char *obuf; + time_t now; +}; + +struct fifolog_reader * +fifolog_reader_open(const char *fname) +{ + const char *retval; + struct fifolog_reader *fr; + int i; + + fr = calloc(sizeof *fr, 1); + if (fr == NULL) + err(1, "Cannot malloc"); + + retval = fifolog_int_open(&fr->ff, fname, 0); + if (retval != NULL) + err(1, "%s", retval); + + fr->olen = fr->ff->recsize * 16; + fr->obuf = calloc(fr->olen, 1); + if (fr->obuf == NULL) + err(1, "Cannot malloc"); + + i = inflateInit(fr->ff->zs); + assert(i == Z_OK); + + fr->magic = FIFOLOG_READER_MAGIC; + return (fr); +} + +/* + * Find the next SYNC block + * + * Return: + * 0 - empty fifolog + * 1 - found sync block + * 2 - would have wrapped around + * 3 - End of written log. + */ + +static int +fifolog_reader_findsync(const struct fifolog_file *ff, off_t *o) +{ + int e; + unsigned seq, seqs; + + assert(*o < ff->logsize); + e = fifolog_int_read(ff, *o); + if (e) + err(1, "Read error (%d) while looking for SYNC", e); + seq = be32dec(ff->recbuf); + if (*o == 0 && seq == 0) + return (0); + + if (ff->recbuf[4] & FIFOLOG_FLG_SYNC) + return (1); /* That was easy... */ + while(1) { + assert(*o < ff->logsize); + (*o)++; + seq++; + if (*o == ff->logsize) + return (2); /* wraparound */ + e = fifolog_int_read(ff, *o); + if (e) + err(1, "Read error (%d) while looking for SYNC", e); + seqs = be32dec(ff->recbuf); + if (seqs != seq) + return (3); /* End of log */ + if (ff->recbuf[4] & FIFOLOG_FLG_SYNC) + return (1); /* Bingo! */ + } +} + +/* + * Seek out a given timestamp + */ + +off_t +fifolog_reader_seek(const struct fifolog_reader *fr, time_t t0) +{ + off_t o, s, st; + time_t t, tt; + unsigned seq, seqs; + const char *retval; + int e; + + CHECK_OBJ_NOTNULL(fr, FIFOLOG_READER_MAGIC); + + /* + * First, find the first SYNC block + */ + o = 0; + e = fifolog_reader_findsync(fr->ff, &o); + if (e == 0) + return (0); /* empty fifolog */ + assert(e == 1); + + assert(fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC); + seq = be32dec(fr->ff->recbuf); + t = be32dec(fr->ff->recbuf + 5); + + if (t > t0) { + /* Check if there is a second older part we can use */ + retval = fifolog_int_findend(fr->ff, &s); + if (retval != NULL) + err(1, "%s", retval); + s++; + e = fifolog_reader_findsync(fr->ff, &s); + if (e == 0) + return (0); /* empty fifolog */ + if (e == 1) { + o = s; + seq = be32dec(fr->ff->recbuf); + t = be32dec(fr->ff->recbuf + 5); + } + } + + /* Now do a binary search to find the sync block right before t0 */ + s = st = (fr->ff->logsize - o) / 2; + while (s > 1) { + /* We know we shouldn't wrap */ + if (o + st > fr->ff->logsize + 1) { + s = st = s / 2; + continue; + } + e = fifolog_int_read(fr->ff, o + st); + if (e) { + s = st = s / 2; + continue; + } + /* If not in same part, sequence won't match */ + seqs = be32dec(fr->ff->recbuf); + if (seqs != seq + st) { + s = st = s / 2; + continue; + } + /* If not sync block, try next */ + if (!(fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC)) { + st++; + continue; + } + /* Check timestamp */ + tt = be32dec(fr->ff->recbuf + 5); + if (tt >= t0) { + s = st = s / 2; + continue; + } + o += st; + seq = seqs; + } + fprintf(stderr, "Read from %jx\n", o * fr->ff->recsize); + return (o); +} + +static unsigned char * +fifolog_reader_chop(struct fifolog_reader *fr, fifolog_reader_render_t *func, void *priv) +{ + u_char *p, *q; + uint32_t v, w, u; + + p = fr->obuf; + q = fr->obuf + (fr->olen - fr->ff->zs->avail_out); + + while (1) { + /* Make sure we have a complete header */ + if (p + 5 >= q) + return (p); + w = 4; + u = be32dec(p); + if (u & FIFOLOG_TIMESTAMP) { + fr->now = be32dec(p + 4); + w += 4; + } + if (u & FIFOLOG_LENGTH) { + v = p[w]; + w++; + if (p + w + v >= q) + return (p); + } else { + for (v = 0; p + v + w < q && p[v + w] != '\0'; v++) + continue; + if (p + v + w >= q) + return (p); + v++; + } + func(priv, fr->now, u, p + w, v); + p += w + v; + } +} + +/* + * Process fifolog until end of written log or provided timestamp + */ + +void +fifolog_reader_process(struct fifolog_reader *fr, off_t from, fifolog_reader_render_t *func, void *priv, time_t end) +{ + uint32_t seq, lseq; + off_t o = from; + int i, e; + time_t t; + u_char *p, *q; + z_stream *zs; + + CHECK_OBJ_NOTNULL(fr, FIFOLOG_READER_MAGIC); + zs = fr->ff->zs; + lseq = 0; + while (1) { + e = fifolog_int_read(fr->ff, o); + if (e) + err(1, "Read error (%d)", e); + if (++o >= fr->ff->logsize) + o = 0; + seq = be32dec(fr->ff->recbuf); + if (lseq != 0 && seq != lseq + 1) + break; + lseq = seq; + zs->avail_in = fr->ff->recsize - 5; + zs->next_in = fr->ff->recbuf + 5; + if (fr->ff->recbuf[4] & FIFOLOG_FLG_1BYTE) + zs->avail_in -= fr->ff->recbuf[fr->ff->recsize - 1]; + if (fr->ff->recbuf[4] & FIFOLOG_FLG_4BYTE) + zs->avail_in -= + be32dec(fr->ff->recbuf + fr->ff->recsize - 4); + if (fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC) { + i = inflateReset(zs); + assert(i == Z_OK); + zs->next_out = fr->obuf; + zs->avail_out = fr->olen; + t = be32dec(fr->ff->recbuf + 5); + if (t > end) + break; + zs->next_in += 4; + zs->avail_in -= 4; + } + + while(zs->avail_in > 0) { + i = inflate(zs, 0); + if (i == Z_BUF_ERROR) { +#if 1 + fprintf(stderr, + "Z_BUF_ERROR [%d,%d] [%d,%d,%d]\n", + (int)(zs->next_in - fr->ff->recbuf), + zs->avail_in, + (int)(zs->next_out - fr->obuf), + zs->avail_out, fr->olen); + exit (250); +#else + + i = Z_OK; +#endif + } + if (i == Z_STREAM_END) { + i = inflateReset(zs); + } + if (i != Z_OK) { + fprintf(stderr, "inflate = %d\n", i); + exit (250); + } + assert(i == Z_OK); + if (zs->avail_out != fr->olen) { + q = fr->obuf + (fr->olen - zs->avail_out); + p = fifolog_reader_chop(fr, func, priv); + if (p < q) + (void)memmove(fr->obuf, p, q - p); + zs->avail_out = fr->olen - (q - p); + zs->next_out = fr->obuf + (q - p); + } + } + } +} diff --git a/usr.sbin/fifolog/lib/fifolog_write.h b/usr.sbin/fifolog/lib/fifolog_write.h new file mode 100644 index 0000000..957ced8 --- /dev/null +++ b/usr.sbin/fifolog/lib/fifolog_write.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2005-2008 Poul-Henning Kamp + * 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. + * + * $FreeBSD$ + */ + +#define FIFOLOG_PT_BYTES_PRE 0 +#define FIFOLOG_PT_BYTES_POST 1 +#define FIFOLOG_PT_WRITES 2 +#define FIFOLOG_PT_FLUSH 3 +#define FIFOLOG_PT_SYNC 4 +#define FIFOLOG_PT_RUNTIME 5 +#define FIFOLOG_NPOINT 6 + +struct fifolog_writer { + unsigned magic; +#define FIFOLOG_WRITER_MAGIC 0xf1f0706 + + struct fifolog_file *ff; + + unsigned writerate; + unsigned syncrate; + unsigned compression; + + int cleanup; + + intmax_t cnt[FIFOLOG_NPOINT]; + + uint32_t seq; + off_t recno; + uint8_t flag; + time_t last; + + ssize_t obufsize; + u_char *obuf; + + ssize_t ibufsize; + ssize_t ibufptr; + u_char *ibuf; + + time_t starttime; + time_t lastwrite; + time_t lastsync; +}; + +struct fifolog_writer *fifolog_write_new(void); +const char *fifolog_write_open(struct fifolog_writer *f, const char *fn, unsigned writerate, unsigned syncrate, unsigned compression); +int fifolog_write_record(struct fifolog_writer *f, uint32_t id, time_t now, const void *ptr, ssize_t len); +int fifolog_write_poll(struct fifolog_writer *f, time_t now); +int fifolog_write_record_poll(struct fifolog_writer *f, uint32_t id, time_t now, const void *ptr, ssize_t len); +void fifolog_write_close(struct fifolog_writer *f); +void fifolog_write_destroy(struct fifolog_writer *f); +extern const char *fifolog_write_statnames[]; diff --git a/usr.sbin/fifolog/lib/fifolog_write_poll.c b/usr.sbin/fifolog/lib/fifolog_write_poll.c new file mode 100644 index 0000000..9ebd5be --- /dev/null +++ b/usr.sbin/fifolog/lib/fifolog_write_poll.c @@ -0,0 +1,412 @@ +/*- + * Copyright (c) 2005-2008 Poul-Henning Kamp + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdint.h> +#include <time.h> +#include <sys/endian.h> + +#include <zlib.h> + +#include "fifolog.h" +#include "libfifolog_int.h" +#include "fifolog_write.h" +#include "miniobj.h" + +static int fifolog_write_gzip(struct fifolog_writer *f, time_t now); + +#define ALLOC(ptr, size) do { \ + (*(ptr)) = calloc(size, 1); \ + assert(*(ptr) != NULL); \ +} while (0) + + +const char *fifolog_write_statnames[] = { + [FIFOLOG_PT_BYTES_PRE] = "Bytes before compression", + [FIFOLOG_PT_BYTES_POST] = "Bytes after compression", + [FIFOLOG_PT_WRITES] = "Writes", + [FIFOLOG_PT_FLUSH] = "Flushes", + [FIFOLOG_PT_SYNC] = "Syncs", + [FIFOLOG_PT_RUNTIME] = "Runtime" +}; + +/********************************************************************** + * Check that everything is all right + */ +static void +fifolog_write_assert(const struct fifolog_writer *f) +{ + + CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC); + assert(f->ff->zs->next_out + f->ff->zs->avail_out == \ + f->obuf + f->obufsize); +} + +/********************************************************************** + * Allocate/Destroy a new fifolog writer instance + */ + +struct fifolog_writer * +fifolog_write_new(void) +{ + struct fifolog_writer *f; + + ALLOC_OBJ(f, FIFOLOG_WRITER_MAGIC); + assert(f != NULL); + return (f); +} + +void +fifolog_write_destroy(struct fifolog_writer *f) +{ + + free(f->obuf); + free(f->ibuf); + FREE_OBJ(f); +} + +/********************************************************************** + * Open/Close the fifolog + */ + +void +fifolog_write_close(struct fifolog_writer *f) +{ + time_t now; + + CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC); + fifolog_write_assert(f); + + f->cleanup = 1; + time(&now); + fifolog_write_gzip(f, now); + fifolog_write_assert(f); + fifolog_int_close(&f->ff); + free(f->ff); +} + +const char * +fifolog_write_open(struct fifolog_writer *f, const char *fn, + unsigned writerate, unsigned syncrate, unsigned compression) +{ + const char *es; + int i; + time_t now; + off_t o; + + CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC); + + /* Check for legal compression value */ + if (compression > Z_BEST_COMPRESSION) + return ("Illegal compression value"); + + f->writerate = writerate; + f->syncrate = syncrate; + f->compression = compression; + + /* Reset statistics */ + memset(f->cnt, 0, sizeof f->cnt); + + es = fifolog_int_open(&f->ff, fn, 1); + if (es != NULL) + return (es); + es = fifolog_int_findend(f->ff, &o); + if (es != NULL) + return (es); + i = fifolog_int_read(f->ff, o); + if (i) + return ("Read error, looking for seq"); + f->seq = be32dec(f->ff->recbuf); + if (f->seq == 0) { + /* Empty fifolog */ + f->seq = random(); + } else { + f->recno = o + 1; + f->seq++; + } + + f->obufsize = f->ff->recsize; + ALLOC(&f->obuf, f->obufsize); + + f->ibufsize = f->obufsize * 10; + ALLOC(&f->ibuf, f->ibufsize); + f->ibufptr = 0; + + i = deflateInit(f->ff->zs, (int)f->compression); + assert(i == Z_OK); + + f->flag |= FIFOLOG_FLG_RESTART; + f->flag |= FIFOLOG_FLG_SYNC; + f->ff->zs->next_out = f->obuf + 9; + f->ff->zs->avail_out = f->obufsize - 9; + + time(&now); + f->starttime = now; + f->lastsync = now; + f->lastwrite = now; + + fifolog_write_assert(f); + return (NULL); +} + +/********************************************************************** + * Write an output record + * Returns -1 if there are trouble writing data + */ + +static int +fifolog_write_output(struct fifolog_writer *f, int fl, time_t now) +{ + long h, l = f->ff->zs->next_out - f->obuf; + ssize_t i, w; + int retval = 0; + + h = 4; /* seq */ + be32enc(f->obuf, f->seq); + f->obuf[h] = f->flag; + h += 1; /* flag */ + if (f->flag & FIFOLOG_FLG_SYNC) { + be32enc(f->obuf + h, now); + h += 4; /* timestamp */ + } + + assert(l <= (long)f->ff->recsize); /* NB: l includes h */ + assert(l >= h); + + /* We will never write an entirely empty buffer */ + if (l == h) + return (0); + + if (l < (long)f->ff->recsize && fl == Z_NO_FLUSH) + return (0); + + w = f->ff->recsize - l; + if (w > 255) { + be32enc(f->obuf + f->ff->recsize - 4, w); + f->obuf[4] |= FIFOLOG_FLG_4BYTE; + } else if (w > 0) { + f->obuf[f->ff->recsize - 1] = (uint8_t)w; + f->obuf[4] |= FIFOLOG_FLG_1BYTE; + } + + f->cnt[FIFOLOG_PT_BYTES_POST] += l - h; + + i = pwrite(f->ff->fd, f->obuf, f->ff->recsize, + (f->recno + 1) * f->ff->recsize); + if (i != f->ff->recsize) + retval = -1; + else + retval = 1; + + f->cnt[FIFOLOG_PT_WRITES]++; + f->cnt[FIFOLOG_PT_RUNTIME] = now - f->starttime; + + f->lastwrite = now; + /* + * We increment these even on error, so as to properly skip bad, + * sectors or other light trouble. + */ + f->seq++; + f->recno++; + f->flag = 0; + + memset(f->obuf, 0, f->obufsize); + f->ff->zs->next_out = f->obuf + 5; + f->ff->zs->avail_out = f->obufsize - 5; + return (retval); +} + +/********************************************************************** + * Run the compression engine + * Returns -1 if there are trouble writing data + */ + +static int +fifolog_write_gzip(struct fifolog_writer *f, time_t now) +{ + int i, fl, retval = 0; + + assert(now != 0); + if (f->cleanup || now >= (int)(f->lastsync + f->syncrate)) { + f->cleanup = 0; + fl = Z_FINISH; + f->cnt[FIFOLOG_PT_SYNC]++; + } else if (now >= (int)(f->lastwrite + f->writerate)) { + fl = Z_SYNC_FLUSH; + f->cnt[FIFOLOG_PT_FLUSH]++; + } else if (f->ibufptr == 0) + return (0); + else + fl = Z_NO_FLUSH; + + f->ff->zs->avail_in = f->ibufptr; + f->ff->zs->next_in = f->ibuf; + + while (1) { + i = deflate(f->ff->zs, fl); + assert(i == Z_OK || i == Z_BUF_ERROR || i == Z_STREAM_END); + + i = fifolog_write_output(f, fl, now); + if (i == 0) + break; + if (i < 0) + retval = -1; + } + assert(f->ff->zs->avail_in == 0); + f->ibufptr = 0; + if (fl == Z_FINISH) { + f->flag |= FIFOLOG_FLG_SYNC; + f->ff->zs->next_out = f->obuf + 9; + f->ff->zs->avail_out = f->obufsize - 9; + f->lastsync = now; + assert(Z_OK == deflateReset(f->ff->zs)); + } + return (retval); +} + +/********************************************************************** + * Poll to see if we need to flush out a record + * Returns -1 if there are trouble writing data + */ + +int +fifolog_write_poll(struct fifolog_writer *f, time_t now) +{ + + if (now == 0) + time(&now); + return (fifolog_write_gzip(f, now)); +} + +/********************************************************************** + * Attempt to write an entry into the ibuf. + * Return zero if there is no space, one otherwise + */ + +int +fifolog_write_record(struct fifolog_writer *f, uint32_t id, time_t now, + const void *ptr, ssize_t len) +{ + const unsigned char *p; + uint8_t buf[9]; + ssize_t bufl; + + fifolog_write_assert(f); + assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH))); + assert(ptr != NULL); + + p = ptr; + if (len == 0) { + len = strlen(ptr); + len++; + } else { + assert(len <= 255); + id |= FIFOLOG_LENGTH; + } + assert (len > 0); + + /* Do a timestamp, if needed */ + if (now == 0) + time(&now); + + if (now != f->last) + id |= FIFOLOG_TIMESTAMP; + + /* Emit instance+flag */ + be32enc(buf, id); + bufl = 4; + + if (id & FIFOLOG_TIMESTAMP) { + be32enc(buf + bufl, (uint32_t)now); + bufl += 4; + } + if (id & FIFOLOG_LENGTH) + buf[bufl++] = (u_char)len; + + if (bufl + len + f->ibufptr > f->ibufsize) + return (0); + + memcpy(f->ibuf + f->ibufptr, buf, bufl); + f->ibufptr += bufl; + memcpy(f->ibuf + f->ibufptr, p, len); + f->ibufptr += len; + f->cnt[FIFOLOG_PT_BYTES_PRE] += bufl + len; + + if (id & FIFOLOG_TIMESTAMP) + f->last = now; + return (1); +} + +/********************************************************************** + * Write an entry, polling the gzip/writer until success. + * Long binary entries are broken into 255 byte chunks. + * Returns -1 if there are problems writing data + */ + +int +fifolog_write_record_poll(struct fifolog_writer *f, uint32_t id, time_t now, + const void *ptr, ssize_t len) +{ + u_int l; + const unsigned char *p; + int retval = 0; + + if (now == 0) + time(&now); + fifolog_write_assert(f); + + assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH))); + assert(ptr != NULL); + + if (len == 0) { + if (!fifolog_write_record(f, id, now, ptr, len)) { + if (fifolog_write_gzip(f, now) < 0) + retval = -1; + /* The string could be too long for the ibuf, so... */ + if (!fifolog_write_record(f, id, now, ptr, len)) + retval = -1; + } + } else { + for (p = ptr; len > 0; len -= l, p += l) { + l = len; + if (l > 255) + l = 255; + while (!fifolog_write_record(f, id, now, p, l)) + if (fifolog_write_gzip(f, now) < 0) + retval = -1; + } + } + if (fifolog_write_gzip(f, now) < 0) + retval = -1; + fifolog_write_assert(f); + return (retval); +} diff --git a/usr.sbin/fifolog/lib/getdate.y b/usr.sbin/fifolog/lib/getdate.y new file mode 100644 index 0000000..53a515c --- /dev/null +++ b/usr.sbin/fifolog/lib/getdate.y @@ -0,0 +1,887 @@ +%{ +/* +** Originally written by Steven M. Bellovin <smb@research.att.com> while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; +** +** This grammar has 10 shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +** +** Picked up from CVS and slightly cleaned up by to WARNS=5 level by +** Poul-Henning Kamp <phk@FreeBSD.org> +** +** $FreeBSD$ +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> + +#include "libfifolog.h" + +#define yylex getdate_yylex +#define yyerror getdate_yyerror + +static int yylex(void); +static int yyerror(const char *); + +#define EPOCH 1970 +#define HOUR(x) ((time_t)(x) * 60) +#define SECSPERDAY (24L * 60L * 60L) + + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + const char *name; + int type; + time_t value; +} TABLE; + + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSToff, DSTon, DSTmaybe +} DSTMODE; + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static char *yyInput; +static DSTMODE yyDSTmode; +static time_t yyDayOrdinal; +static time_t yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static time_t yyTimezone; +static time_t yyDay; +static time_t yyHour; +static time_t yyMinutes; +static time_t yyMonth; +static time_t yySeconds; +static time_t yyYear; +static MERIDIAN yyMeridian; +static time_t yyRelMonth; +static time_t yyRelSeconds; + +%} + +%union { + time_t Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST + +%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type <Meridian> tMERIDIAN o_merid + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { + yyHaveTime++; + } + | zone { + yyHaveZone++; + } + | date { + yyHaveDate++; + } + | day { + yyHaveDay++; + } + | rel { + yyHaveRel++; + } + | cvsstamp { + yyHaveTime++; + yyHaveDate++; + yyHaveZone++; + } + | number + ; + +cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER { + yyYear = $1; + if (yyYear < 100) yyYear += 1900; + yyMonth = $3; + yyDay = $5; + yyHour = $7; + yyMinutes = $9; + yySeconds = $11; + yyDSTmode = DSToff; + yyTimezone = 0; + } + ; + +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($4 % 100 + ($4 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($6 % 100 + ($6 / 100) * 60); + } + ; + +zone : tZONE { + yyTimezone = $1; + yyDSTmode = DSToff; + } + | tDAYZONE { + yyTimezone = $1; + yyDSTmode = DSTon; + } + | + tZONE tDST { + yyTimezone = $1; + yyDSTmode = DSTon; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + if ($1 >= 100) { + yyYear = $1; + yyMonth = $3; + yyDay = $5; + } else { + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; + } + | tMONTH tUNUMBER { + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + yyMonth = $2; + yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMonth = -yyRelMonth; + } + | relunit + ; + +relunit : tUNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tSNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tMINUTE_UNIT { + yyRelSeconds += $1 * 60L; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tSEC_UNIT { + yyRelSeconds++; + } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tUNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth += $1; + } + ; + +number : tUNUMBER { + if (yyHaveTime && yyHaveDate && !yyHaveRel) + yyYear = $1; + else { + if($1>10000) { + yyHaveDate++; + yyDay= ($1)%100; + yyMonth= ($1/100)%100; + yyYear = $1/10000; + } + else { + yyHaveTime++; + if ($1 < 100) { + yyHour = $1; + yyMinutes = 0; + } + else { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } + ; + +o_merid : /* NULL */ { + $$ = MER24; + } + | tMERIDIAN { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL, 0, 0 } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tMONTH_UNIT, 12 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, + { "week", tMINUTE_UNIT, 7 * 24 * 60 }, + { "day", tMINUTE_UNIT, 1 * 24 * 60 }, + { "hour", tMINUTE_UNIT, 60 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL, 0, 0 } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL, 0, 0 } +}; + +/* The timezone table. */ +/* Some of these are commented out because a time_t can't store a float. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR( 0) }, + { "wet", tZONE, HOUR( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "wat", tZONE, HOUR( 1) }, /* West Africa */ + { "at", tZONE, HOUR( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR(11) }, /* Nome */ + { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR(1) }, /* Central European */ + { "met", tZONE, -HOUR(1) }, /* Middle European */ + { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR(3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Stanard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ +#if 0 + { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ + { NULL, 0, 0 } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR( 1) }, + { "b", tZONE, HOUR( 2) }, + { "c", tZONE, HOUR( 3) }, + { "d", tZONE, HOUR( 4) }, + { "e", tZONE, HOUR( 5) }, + { "f", tZONE, HOUR( 6) }, + { "g", tZONE, HOUR( 7) }, + { "h", tZONE, HOUR( 8) }, + { "i", tZONE, HOUR( 9) }, + { "k", tZONE, HOUR( 10) }, + { "l", tZONE, HOUR( 11) }, + { "m", tZONE, HOUR( 12) }, + { "n", tZONE, HOUR(- 1) }, + { "o", tZONE, HOUR(- 2) }, + { "p", tZONE, HOUR(- 3) }, + { "q", tZONE, HOUR(- 4) }, + { "r", tZONE, HOUR(- 5) }, + { "s", tZONE, HOUR(- 6) }, + { "t", tZONE, HOUR(- 7) }, + { "u", tZONE, HOUR(- 8) }, + { "v", tZONE, HOUR(- 9) }, + { "w", tZONE, HOUR(-10) }, + { "x", tZONE, HOUR(-11) }, + { "y", tZONE, HOUR(-12) }, + { "z", tZONE, HOUR( 0) }, + { NULL, 0, 0 } +}; + + + + +/* ARGSUSED */ +static int +yyerror(const char *s __unused) +{ + return 0; +} + + +static time_t +ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian) +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + switch (Meridian) { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; + default: + abort (); + } + /* NOTREACHED */ +} + + +/* Year is either + * A negative number, which means to use its absolute value (why?) + * A number from 0 to 99, which means a year from 1900 to 1999, or + * The actual year (>=100). */ +static time_t +Convert(time_t Month, time_t Day, time_t Year, + time_t Hours, time_t Minutes, time_t Seconds, + MERIDIAN Meridian, DSTMODE DSTmode) +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + struct tm *ltm; + + if (Year < 0) + Year = -Year; + if (Year < 69) + Year += 2000; + else if (Year < 100) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + /* Checking for 2038 bogusly assumes that time_t is 32 bits. But + I'm too lazy to try to check for time_t overflow in another way. */ + if (Year < EPOCH || Year > 2038 + || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + /* FIXME: + * It would be nice to set a global error string here. + * "February 30 is not a valid date" is much more informative than + * "Can't parse date/time: 100 months" when the user input was + * "100 months" and addition resolved that to February 30, for + * example. See rcs2-7 in src/sanity.sh for more. */ + return -1; + + for (Julian = Day - 1, i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + (i % 4 == 0); + Julian *= SECSPERDAY; + Julian += yyTimezone * 60L; + if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) + return -1; + Julian += tod; + ltm = localtime(&Julian); +fprintf(stderr, "DST %d TZ %s %d\n", DSTmode, ltm->tm_zone, ltm->tm_isdst); + if (DSTmode == DSTon + || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + Julian -= 60 * 60; + return Julian; +} + + +static time_t +DSTcorrect(time_t Start, time_t Future) +{ + time_t StartDay; + time_t FutureDay; + + StartDay = (localtime(&Start)->tm_hour + 1) % 24; + FutureDay = (localtime(&Future)->tm_hour + 1) % 24; + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber) +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now); +} + + +static time_t +RelativeMonth(time_t Start, time_t RelMonth) +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + MER24, DSTmaybe)); +} + + +static int +LookupWord(char *buff) +{ + char *p; + char *q; + const TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + + if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen(buff) == 3) + abbrev = 1; + else if (strlen(buff) == 4 && buff[3] == '.') { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) { + if (abbrev) { + if (strncmp(buff, tp->name, 3) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp(buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen(buff) - 1; + if (buff[i] == 's') { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && isalpha(*buff)) { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + + +static int +yylex(void) +{ + char c; + char *p; + char buff[20]; + int Count; + int sign; + + for ( ; ; ) { + while (isspace(*yyInput)) + yyInput++; + + if (isdigit(c = *yyInput) || c == '-' || c == '+') { + if (c == '-' || c == '+') { + sign = c == '-' ? -1 : 1; + if (!isdigit(*++yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; isdigit(c = *yyInput++); ) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (isalpha(c)) { + for (p = buff; isalpha(c = *yyInput++) || c == '.'; ) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord(buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + } +} + +#define TM_YEAR_ORIGIN 1900 + +time_t +get_date(char *p) +{ + struct tm *tm, gmt; + time_t Start; + time_t tod; + time_t nowtime; + struct tm *gmt_ptr; + + yyInput = p; + + (void)time (&nowtime); + + gmt_ptr = gmtime (&nowtime); + if (gmt_ptr != NULL) + { + /* Make a copy, in case localtime modifies *tm (I think + that comment now applies to *gmt_ptr, but I am too + lazy to dig into how gmtime and locatime allocate the + structures they return pointers to). */ + gmt = *gmt_ptr; + } + + if (! (tm = localtime (&nowtime))) + return -1; + + tm = localtime(&nowtime); + yyYear = tm->tm_year + 1900; + yyMonth = tm->tm_mon + 1; + yyDay = tm->tm_mday; + yyTimezone = tm->tm_gmtoff; + yyDSTmode = DSTmaybe; + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMonth = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse() + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + if (yyHaveDate || yyHaveTime || yyHaveDay) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (Start < 0) + return -1; + } + else { + Start = nowtime; + if (!yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + Start += yyRelSeconds; + Start += RelativeMonth(Start, yyRelMonth); + + if (yyHaveDay && !yyHaveDate) { + tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); + Start += tod; + } + + /* Have to do *something* with a legitimate -1 so it's distinguishable + * from the error return value. (Alternately could set errno on error.) */ + return Start == -1 ? 0 : Start; +} diff --git a/usr.sbin/fifolog/lib/libfifolog.h b/usr.sbin/fifolog/lib/libfifolog.h new file mode 100644 index 0000000..eb5cc58 --- /dev/null +++ b/usr.sbin/fifolog/lib/libfifolog.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2005-2008 Poul-Henning Kamp + * 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. + * + * $FreeBSD$ + */ + +#include <sys/stdint.h> + +/* CREATORS */ +const char *fifolog_create(const char *fn, off_t size, ssize_t recsize); + +/* WRITERS */ + +#include "fifolog_write.h" + +/* READERS */ + +typedef void fifolog_reader_render_t(void *priv, time_t when, unsigned flag, const unsigned char *p, unsigned l); +struct fifolog_reader; +struct fifolog_reader *fifolog_reader_open(const char *fname); +off_t fifolog_reader_seek(const struct fifolog_reader *fl, time_t t0); +void fifolog_reader_process(struct fifolog_reader *fl, off_t from, fifolog_reader_render_t *func, void *priv, time_t end); + +/* UTILS */ +time_t get_date(char *p); diff --git a/usr.sbin/fifolog/lib/libfifolog_int.h b/usr.sbin/fifolog/lib/libfifolog_int.h new file mode 100644 index 0000000..2b82241 --- /dev/null +++ b/usr.sbin/fifolog/lib/libfifolog_int.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2005-2008 + * 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. + * + * $FreeBSD$ + */ + +struct fifolog_file { + unsigned magic; +#define FIFOLOG_FILE_MAGIC 0x307ea50d + + ssize_t recsize; + off_t logsize; + int fd; + + z_stream *zs; + + unsigned char *recbuf; +}; + +const char *fifolog_int_open(struct fifolog_file **ff, const char *fname, int mode); +void fifolog_int_close(struct fifolog_file **ff); +int fifolog_int_read(const struct fifolog_file *ff, off_t recno); +const char *fifolog_int_findend(const struct fifolog_file *ff, off_t *last); diff --git a/usr.sbin/fifolog/lib/miniobj.h b/usr.sbin/fifolog/lib/miniobj.h new file mode 100644 index 0000000..dd79be1 --- /dev/null +++ b/usr.sbin/fifolog/lib/miniobj.h @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2005-2008 Poul-Henning Kamp + * 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. + * + * $FreeBSD$ + */ + +#define ALLOC_OBJ(to, type_magic) \ + do { \ + (to) = calloc(sizeof *(to), 1); \ + if ((to) != NULL) \ + (to)->magic = (type_magic); \ + } while (0) + +#define FREE_OBJ(to) \ + do { \ + (to)->magic = (0); \ + free(to); \ + } while (0) + +#define VALID_OBJ(ptr, type_magic) \ + ((ptr) != NULL && (ptr)->magic == (type_magic)) + +#define CHECK_OBJ(ptr, type_magic) \ + do { \ + assert((ptr)->magic == type_magic); \ + } while (0) + +#define CHECK_OBJ_NOTNULL(ptr, type_magic) \ + do { \ + assert((ptr) != NULL); \ + assert((ptr)->magic == type_magic); \ + } while (0) + +#define CHECK_OBJ_ORNULL(ptr, type_magic) \ + do { \ + if ((ptr) != NULL) \ + assert((ptr)->magic == type_magic); \ + } while (0) + +#define CAST_OBJ(to, from, type_magic) \ + do { \ + (to) = (from); \ + if ((to) != NULL) \ + CHECK_OBJ((to), (type_magic)); \ + } while (0) + +#define CAST_OBJ_NOTNULL(to, from, type_magic) \ + do { \ + (to) = (from); \ + assert((to) != NULL); \ + CHECK_OBJ((to), (type_magic)); \ + } while (0) |