diff options
author | phk <phk@FreeBSD.org> | 2008-03-09 19:14:36 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2008-03-09 19:14:36 +0000 |
commit | 329dcba2dfdf5d9e4878196bdfc8e7a951d13d22 (patch) | |
tree | 1ac353f6fcd504cada7e247a46746cb3f8bb97eb /usr.sbin/fifolog | |
parent | 7c7baa655e11f923a155a5367e5ec723ed0c117a (diff) | |
download | FreeBSD-src-329dcba2dfdf5d9e4878196bdfc8e7a951d13d22.zip FreeBSD-src-329dcba2dfdf5d9e4878196bdfc8e7a951d13d22.tar.gz |
Add the fifolog tools to FreeBSD.
Quoth the man-page:
Fifologs provide a compact round-robin circular storage for recording
text and binary information to permanent storage in a bounded and pre-
dictable fashion, time and space wise.
Not yet connected to the build, but feel free to test & review.
Diffstat (limited to 'usr.sbin/fifolog')
21 files changed, 3132 insertions, 0 deletions
diff --git a/usr.sbin/fifolog/Makefile b/usr.sbin/fifolog/Makefile new file mode 100644 index 0000000..596410c --- /dev/null +++ b/usr.sbin/fifolog/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +VERSION_MAJOR = 1 +VERSION_MINOR = 1 + +SUBDIR = lib fifolog_create fifolog_writer fifolog_reader + +.include <bsd.subdir.mk> + +test: _SUBDIR diff --git a/usr.sbin/fifolog/Makefile.inc b/usr.sbin/fifolog/Makefile.inc new file mode 100644 index 0000000..f62f589 --- /dev/null +++ b/usr.sbin/fifolog/Makefile.inc @@ -0,0 +1,13 @@ +# $FreeBSD$ + +LIBFIFOLOG = ${.OBJDIR}/../lib/libfifolog.a + + +WARNS ?= 6 + +# CFLAGS += -O0 -g + +# LINT = flint +# LINTFLAGS = ${.CURDIR}/../flint.lnt -I/usr/include + +.include "../Makefile.inc" diff --git a/usr.sbin/fifolog/fifolog_create/Makefile b/usr.sbin/fifolog/fifolog_create/Makefile new file mode 100644 index 0000000..0c4bd65 --- /dev/null +++ b/usr.sbin/fifolog/fifolog_create/Makefile @@ -0,0 +1,22 @@ +# $FreeBSD$ + +PROG = fifolog_create + +CFLAGS += -I${.CURDIR}/../lib + +DPADD = ${LIBFIFOLOG} ${LIBUTIL} +LDADD = ${LIBFIFOLOG} -lutil + +MAN = fifolog.1 +MLINKS += fifolog.1 fifolog_create.1 +MLINKS += fifolog.1 fifolog_reader.1 +MLINKS += fifolog.1 fifolog_writer.1 + +.include <bsd.prog.mk> + +test: ${PROG} + rm -f /tmp/fifolog.? + ./${PROG} /tmp/fifolog.0 + ./${PROG} -s 10m /tmp/fifolog.1 + ./${PROG} -l 1k /tmp/fifolog.2 + ./${PROG} -r 1k /tmp/fifolog.3 diff --git a/usr.sbin/fifolog/fifolog_create/fifolog.1 b/usr.sbin/fifolog/fifolog_create/fifolog.1 new file mode 100644 index 0000000..91d1877 --- /dev/null +++ b/usr.sbin/fifolog/fifolog_create/fifolog.1 @@ -0,0 +1,210 @@ +.\" Copyright (c) 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$ +.\" +.Dd Feb 9, 2008 +.Os FreeBSD 8.0 +.Dt FIFOLOG 1 +.Sh NAME +.Nm fifolog_create +.Nd Initialize storage for fifolog +.br +.Nm fifolog_write +.Nd Write data to fifolog +.br +.Nm fifolog_read +.Nd Seek and extract data from fifolog +.Sh SYNOPSIS +.Nm fifolog_create +.Op Fl l Ar record-size +.Op Fl r Ar record-count +.Op Fl s Ar size +.Ar file +.Nm fifolog_reader +.Op Fl t +.Op Fl b Ar tstart +.Op Fl B Ar Tstart +.Op Fl e Ar tend +.Op Fl E Ar Tend +.Op Fl o Ar ofile +.Op Fl R Ar regexp +.Op Fl T Ar timefmt +.Ar file +.Nm fifolog_writer +.Op Fl w Ar write-rate +.Op Fl s Ar sync-rate +.Op Fl z Ar compression +.Ar file +.Sh DESCRIPTION +Fifologs provide a compact round-robin circular storage for +recording text and binary information to permanent storage in a bounded +and predictable fashion, time and space wise. +.Pp +A fifolog can be stored either directly on a disk partition or in a +regular file. +.Pp +The input data stream is encoded, compressed and marked up with +timestamps before it is written to storage, such that it is possible +to seek out a particular time interval in the stored data, without +having to decompress the entire logfile. +.Pp +.Nm Fifolog_create +is used to initialize the first sector of a disk device +or filesystem file to make it a fifolog and should be called only +once. +.Pp +Running +.Nm +on an existing fifolog will reset it so that +.Nm fifolog_reader +and +.Nm fifolog_writer +will not see the previous contents. +(The previos contents is not physically erased, and with a bit +of hand-work, all but the first record can be easily recovered). +.Pp +If the file does not already exist +.Nm +will attempt to create and +.Xr ftruncate 3 +it to the specified size, defaulting to 86400 records of 512 bytes +if the +.Fl r , +.Fl l +or +.Fl s +arguments do not specify otherwise. +.Pp +.Nm Fifolog_writer +will read standard input and write it to the end of the fifolog +according to the parameters given. +.Pp +Writes happen whenever the output buffer is filled with compressed +data or when either of two timers expire, forcing a partially filled +buffer to be written. +.Pp +The first and faster timer, +.Fl w write-rate , +forces available data to be written +but does not flush and reset the compression dictionary. +This timer is intended to minimize the amount of logdata lost in RAM +in case of a crash and by default it fires 10 seconds after +the previous write. +.Pp +The second and slower timer, +.Fl s sync-rate , +forces a full flush and reset of the compression +engine and causes the next record written to be a synchronization +point with an uncompressed timestamp, making it possible to start +reading the logfile from that record. +By default this timer fires a minute after the previous sync. +.Pp +The +.Fl z compression +argument controls the +.Xr zlib 3 +compression level, legal values are zero to nine which is the default. +.Pp +.Nm Fifolog_reader +will retrieve records from the fifolog according to the specified +parameters and write them either to stdout or the file specified +with +.Fl o . +.Pp +It is possible to specify a start and end time to limit the amount +of data +.Nm fifolog_reader +will report. +The lower-case variants +.Fl b +and +.Fl e +take a +.Xr time_t +value, whereas the upper-case variants +.Fl B +and +.Fl E +take human redable specifications such as "1 hour ago". +.Pp +The +.Fl t +argument forces timestamps to be formatted as "YYYYMMDDhhmmss" instead +of as time_t, and +.Fl T +allows the specification of a +.Xr strftime 3 +formatting string. +.Pp +Finally, records can be filtered such that only records matching the +(REG_BASIC) regular expression specified with +.Fl R +is output. +.Sh IMPLEMENTATION NOTES +The data stored in the fifolog consists of three layers, an outher +layer that allows searches to synchronization points based on timestamps +without having to decompress and decode the actual contents, a +compression layer implemented with +.Xr zlib 3 +and an inner serialization and timestamping layer. +.Pp +The exact encoding is described in the fifolog.h file. +.Pp +Fifolog is particularly well suited for use on Flash based media, where +it results in much lower write-wear, than a filesystem with regular +logfiles rotated with +.Xr newsyslog 8 +etc. +.Sh EXAMPLES +Create a fifolog with 1024*1024 records of 512 bytes: +.Bd -literal +fifolog_create -r 10m /tmp/fifolog +.Ed +.Pp +Write a single record to this file: +.Bd -literal +date | fifolog_writer /tmp/fifolog +.Ed +.Pp +Read it back with human readable timestamps: +.Bd -literal +fifolog_reader -t /tmp/fifolog +.Ed +.Pp +One particular useful use of +.Nm fifolog_writer +is with +.Xr syslogd 8 +using a line such as this in +.Xr /etc/syslog.conf 5 : +.Bd -literal +*.* |fifolog_writer /var/log/syslog_fifolog +.Ed +.Sh HISTORY +The fifolog tools have been liberated from an open source SCADA applications +called "measured", which monitors and controls remote radio navigation +transmitters for the Danish Air Traffic Control system. +.Sh AUTHORS +The fifolog tools were written by Poul-Henning Kamp diff --git a/usr.sbin/fifolog/fifolog_create/fifolog_create.c b/usr.sbin/fifolog/fifolog_create/fifolog_create.c new file mode 100644 index 0000000..2b93765 --- /dev/null +++ b/usr.sbin/fifolog/fifolog_create/fifolog_create.c @@ -0,0 +1,105 @@ +/*- + * 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 <err.h> +#include <libutil.h> + +#include "libfifolog.h" + +#define DEF_RECSIZE 512 +#define DEF_RECCNT (24 * 60 * 60) + +int +main(int argc, char * const *argv) +{ + int ch; + int64_t size; + int64_t recsize; + int64_t reccnt; + const char *s; + + recsize = 0; + size = 0; + reccnt = 0; + while((ch = getopt(argc, argv, "l:r:s:")) != -1) { + switch (ch) { + case 'l': + if (expand_number(optarg, &recsize)) + err(1, "Couldn't parse -l argument"); + break; + case 'r': + if (expand_number(optarg, &reccnt)) + err(1, "Couldn't parse -r argument"); + break; + case 's': + if (expand_number(optarg, &size)) + err(1, "Couldn't parse -s argument"); + break; + default: + errx(1, "Usage"); + } + } + argc -= optind; + argv += optind; + if (argc != 1) + errx(1, "Usage"); + + if (size != 0 && reccnt != 0 && recsize != 0) { /* N N N */ + if (size != reccnt * recsize) + errx(1, "Inconsistent -l, -r and -s values"); + } else if (size != 0 && reccnt != 0 && recsize == 0) { /* N N Z */ + if (size % reccnt) + errx(1, + "Inconsistent -r and -s values (gives remainder)"); + recsize = size / reccnt; + } else if (size != 0 && reccnt == 0 && recsize != 0) { /* N Z N */ + if (size % recsize) + errx(1, "-s arg not divisible by -l arg"); + } else if (size != 0 && reccnt == 0 && recsize == 0) { /* N Z Z */ + recsize = DEF_RECSIZE; + if (size % recsize) + errx(1, "-s arg not divisible by %jd", recsize); + } else if (size == 0 && reccnt != 0 && recsize != 0) { /* Z N N */ + size = reccnt * recsize; + } else if (size == 0 && reccnt != 0 && recsize == 0) { /* Z N Z */ + recsize = DEF_RECSIZE; + size = reccnt * recsize; + } else if (size == 0 && reccnt == 0 && recsize != 0) { /* Z Z N */ + size = DEF_RECCNT * recsize; + } else if (size == 0 && reccnt == 0 && recsize == 0) { /* Z Z Z */ + recsize = DEF_RECSIZE; + size = DEF_RECCNT * recsize; + } + + s = fifolog_create(argv[0], size, recsize); + if (s == NULL) + return (0); + err(1, "%s", s); +} diff --git a/usr.sbin/fifolog/fifolog_reader/Makefile b/usr.sbin/fifolog/fifolog_reader/Makefile new file mode 100644 index 0000000..99294da --- /dev/null +++ b/usr.sbin/fifolog/fifolog_reader/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +PROG = fifolog_reader + +CFLAGS += -I${.CURDIR}/../lib -I${.CURDIR} + +NO_MAN = see ../fifolog_create/fifolog.1 + +DPADD = ${LIBFIFOLOG} ${LIBUTIL} ${LIBZ} +LDADD = ${LIBFIFOLOG} -lutil -lz + +.include <bsd.prog.mk> + +test: ${PROG} + ./${PROG} /tmp/fifolog.0 + ./${PROG} -t /tmp/fifolog.0 + ./${PROG} /tmp/fifolog.1 + ./${PROG} -B "00:00" /tmp/fifolog.1 + +t2: + ./${PROG} -t /critter/10.1.29.74.fifolog diff --git a/usr.sbin/fifolog/fifolog_reader/fifolog_reader.c b/usr.sbin/fifolog/fifolog_reader/fifolog_reader.c new file mode 100644 index 0000000..6c4dc41 --- /dev/null +++ b/usr.sbin/fifolog/fifolog_reader/fifolog_reader.c @@ -0,0 +1,170 @@ +/*- + * 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 <assert.h> +#include <unistd.h> +#include <err.h> +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include <regex.h> + +#include "libfifolog.h" + +static time_t opt_B; +static time_t opt_E; +static const char *opt_T; +static const char *opt_o; +static const char *opt_R; +static regex_t R; + +static FILE *fo; + +static void +Render(void *priv __unused, time_t now, unsigned flag __unused, const unsigned char *p, unsigned l __unused) +{ + static struct tm utc; + char buf[128]; + int i; + + if (now < opt_B || now > opt_E) + return; + + if (opt_R != NULL && regexec(&R, (const char *)p, 0, NULL, 0)) + return; + + if (opt_T != NULL) { + (void)gmtime_r(&now, &utc); + i = strftime(buf, sizeof buf, opt_T, &utc); + assert(i > 0); + fprintf(fo, "%s %s\n", buf, p); + } else { + fprintf(fo, "%12ld %s\n", (long)now, p); + } +} + +/*--------------------------------------------------------------------*/ + +static void +Usage(void) +{ + fprintf(stderr, + "Usage: fiforead [options] fifofile\n" + "\t-b <start time integer>\n" + "\t-B <start time>\n" + "\t-e <end time integer>\n" + "\t-E <end time>\n" + "\t-o <output file>\n" + "\t-R <regexp> # match regexp\n" + "\t-t # format timestamps as %%Y%%m%%d%%H%%M%%S\n" + "\t-T <timestamp format>\n" + ); + exit (2); +} + +int +main(int argc, char * const *argv) +{ + int ch, i; + off_t o; + struct fifolog_reader *fl; + const char *progname; + + progname=argv[0]; + + time(&opt_E); + opt_o = "-"; + while ((ch = getopt(argc, argv, "b:B:e:E:o:R:tT:")) != -1) { + switch (ch) { + case 'b': + opt_B = strtoul(optarg, NULL, 0); + break; + case 'B': + opt_B = get_date(optarg); + if (opt_B == -1) + errx(1, "Didn't understand \"%s\"", optarg); + break; + case 'e': + opt_E = strtoul(optarg, NULL, 0); + break; + case 'E': + opt_E = get_date(optarg); + if (opt_E == -1) + errx(1, "Didn't understand \"%s\"", optarg); + break; + case 'o': + opt_o = optarg; + break; + case 'R': + opt_R = optarg; + break; + case 't': + opt_T = "%Y%m%d%H%M%S"; + break; + case 'T': + opt_T = optarg; + break; + default: + Usage(); + } + } + argc -= optind; + argv += optind; + + if (opt_R != NULL) { + i = regcomp(&R, opt_R, REG_NOSUB); + if (i != 0) { + char buf[BUFSIZ]; + (void)regerror(i, &R, buf, sizeof buf); + fprintf(stderr, "-R argument: %s\n", buf); + exit (1); + } + } + + fprintf(stderr, "From\t%jd %s", (intmax_t)opt_B, ctime(&opt_B)); + fprintf(stderr, "To\t%jd %s", (intmax_t)opt_E, ctime(&opt_E)); + if (opt_B >= opt_E) + errx(1, "Begin time not before End time"); + + if (argv[0] == NULL) + errx(1, "Usage: %s [options] fifolog", progname); + fl = fifolog_reader_open(argv[0]); + + if (!strcmp(opt_o, "-")) + fo = stdout; + else { + fo = fopen(opt_o, "w"); + if (fo == NULL) + err(1, "Cannot open: %s", argv[1]); + } + + o = fifolog_reader_seek(fl, opt_B); + fifolog_reader_process(fl, o, Render, NULL, opt_E); + return (0); +} diff --git a/usr.sbin/fifolog/fifolog_writer/Makefile b/usr.sbin/fifolog/fifolog_writer/Makefile new file mode 100644 index 0000000..7613085 --- /dev/null +++ b/usr.sbin/fifolog/fifolog_writer/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +PROG = fifolog_writer + +CFLAGS += -I${.CURDIR}/../lib + +NO_MAN = see ../fifolog_create/fifolog.1 + +DPADD = ${LIBFIFOLOG} ${LIBUTIL} ${LIBZ} +LDADD = ${LIBFIFOLOG} -lutil -lz + +.include <bsd.prog.mk> + +test: ${PROG} + date | ./${PROG} -z 0 /tmp/fifolog.0 + lptest 65 | ./${PROG} -z 9 /tmp/fifolog.1 diff --git a/usr.sbin/fifolog/fifolog_writer/fifolog_writer.c b/usr.sbin/fifolog/fifolog_writer/fifolog_writer.c new file mode 100644 index 0000000..469d284 --- /dev/null +++ b/usr.sbin/fifolog/fifolog_writer/fifolog_writer.c @@ -0,0 +1,105 @@ +/*- + * 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 <stdlib.h> +#include <err.h> +#include <unistd.h> +#include <ctype.h> +#include <assert.h> +#include <poll.h> +#include <string.h> +#include <zlib.h> + +#include "libfifolog.h" + +int +main(int argc, char * const *argv) +{ + struct fifolog_writer *f; + const char *es; + struct pollfd pfd[1]; + char buf[BUFSIZ], *p; + int i, c; + unsigned w_opt = 10; + unsigned s_opt = 60; + unsigned z_opt = Z_BEST_COMPRESSION; + + while ((c = getopt(argc, argv, "w:s:z:")) != -1) { + switch(c) { + case 'w': + w_opt = strtoul(optarg, NULL, 0); + break; + case 's': + s_opt = strtoul(optarg, NULL, 0); + break; + case 'z': + z_opt = strtoul(optarg, NULL, 0); + break; + default: + errx(1, "Usage"); + } + } + argc -= optind; + argv += optind; + if (argc != 1) + errx(1, "Usage"); + + if (z_opt > 9) + errx(1, "Usage"); + + if (w_opt > s_opt) + errx(1, "Usage"); + + f = fifolog_write_new(); + assert(f != NULL); + + es = fifolog_write_open(f, argv[0], w_opt, s_opt, z_opt); + if (es) + err(1, "Error: %s", es); + + while (1) { + pfd[0].fd = 0; + pfd[0].events = POLLIN; + i = poll(pfd, 1, 1000); + if (i == 1) { + if (fgets(buf, sizeof buf, stdin) == NULL) + break; + p = strchr(buf, '\0'); + assert(p != NULL); + while (p > buf && isspace(p[-1])) + p--; + *p = '\0'; + if (*buf != '\0') + fifolog_write_bytes_poll(f, 0, 0, buf, 0); + } else if (i == 0) + (void)fifolog_write_poll(f, 0); + } + (void)fifolog_write_flush(f); + return (0); +} diff --git a/usr.sbin/fifolog/flint.lnt b/usr.sbin/fifolog/flint.lnt new file mode 100644 index 0000000..16ee7ad --- /dev/null +++ b/usr.sbin/fifolog/flint.lnt @@ -0,0 +1,49 @@ +// $FreeBSD$ +// FlexeLint file for fifolog tools +// + +-passes=3 +-ffc + +// GCC +-cgnu ++d__FreeBSD__=7 ++d__GNUC__=4 ++d__GNUC_MINOR__=2 ++d__FreeBSD_cc_version=700003 ++d__attribute__()= +-d__builtin_va_list=void* // used by stdarg.h +// -d__builtin_stdarg_start()=_to_semi // ditto +// -d__builtin_va_start(a,b)=((void)(b),(a)=0) // ditto +// -d__builtin_va_end()=_to_semi // ditto ++rw(__inline) // enable the (non-standard) __inline keyword ++rw(__inline__) // enable the (non-standard) __inline__ keyword + ++d"__unused=/*lint -e{715} -e{818} */" + +-e537 // Repeated include file +-elib(652) // #define of symbol '...' declared previously +-function(exit,__assert) +-function(exit,err) +-function(exit,errx) +-e716 // while(1) ... +-e717 // do ... while(0) + +// Ignore return values +-esym(534, memset) +-esym(534, memcpy) +-esym(534, strcpy) +-esym(534, printf) +-esym(534, time) +-esym(534, fprintf) +-esym(534, vfprintf) + ++libh(fifolog.h) ++libh(miniobj.h) ++libh(libfifolog.h) + +-e713 // loss of precision sign/unsigned +-e732 // loss of sign +-e734 // loss of precision assignment +-e737 // loss of sign in promotion int->unsigned +-e573 // sign/unsign mix in divide diff --git a/usr.sbin/fifolog/lib/Makefile b/usr.sbin/fifolog/lib/Makefile new file mode 100644 index 0000000..fedfc50 --- /dev/null +++ b/usr.sbin/fifolog/lib/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +LIB = fifolog +INTERNALLIB = API not published or supported. + +SRCS += fifolog_int.c +SRCS += fifolog_create.c +SRCS += fifolog_write_poll.c +SRCS += fifolog_reader.c +SRCS += getdate.y + +CFLAGS += -I${.CURDIR} + +.include <bsd.lib.mk> + +test: + echo ${HAS_EXPAND_NUMBER} diff --git a/usr.sbin/fifolog/lib/fifolog.h b/usr.sbin/fifolog/lib/fifolog.h new file mode 100644 index 0000000..d5c8297 --- /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 outher 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 outher layer + * record boundaries, from where reading can be initiated. + * + * + * The outher 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 precense 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 outher layer (apart from a natural correlation with padding) since + * zlibs data stream handles this without help. + * + * + * The inner layer: + * ---------------- + * The inner layer contains data indentification and to the second + * timestamping (the timestamp in the outherlayer 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 lenght 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..68f8cef --- /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, unsigned recsize) +{ + int i, fd; + unsigned 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 ((int)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..9093174 --- /dev/null +++ b/usr.sbin/fifolog/lib/fifolog_int.c @@ -0,0 +1,276 @@ +/*- + * 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" + +/* + * Memory handling for zlib + */ + +static voidpf +fifo_zalloc(voidpf opaque __unused, uInt items, uInt size) +{ + + return calloc(items,size); +} + +static void +fifo_zfree(voidpf opaque __unused, voidpf address) +{ + + free(address); +} + +/* + * 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; + unsigned 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); + 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"); + f->zs->zalloc = fifo_zalloc; + f->zs->zfree = fifo_zfree; + + 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); + free(f); +} + +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 (-1); + if (i != (int)ff->recsize) + return (-1); + 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..37a03b7 --- /dev/null +++ b/usr.sbin/fifolog/lib/fifolog_reader.c @@ -0,0 +1,315 @@ +/*- + * 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 while looking for SYNC"); + 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 while looking for SYNC"); + 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); + 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) + err(1, "Read error, duing binary search"); + /* 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++; + } 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"); + 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); + 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..06b3233 --- /dev/null +++ b/usr.sbin/fifolog/lib/fifolog_write.h @@ -0,0 +1,65 @@ +/*- + * 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; + + unsigned writes_since_sync; + + int cleanup; + + intmax_t cnt[FIFOLOG_NPOINT]; + + uint32_t seq; + off_t recno; + int flag; + time_t last; + + u_int ibufsize; + u_char *ibuf; + u_char *iptr; + + time_t starttime; + time_t lastwrite; + time_t lastsync; +}; 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..86615aa --- /dev/null +++ b/usr.sbin/fifolog/lib/fifolog_write_poll.c @@ -0,0 +1,416 @@ +/*- + * 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 <time.h> +#include <sys/endian.h> + +#include <zlib.h> + +#include "fifolog.h" +#include "libfifolog.h" +#include "libfifolog_int.h" +#include "fifolog_write.h" +#include "miniobj.h" + +#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->iptr == f->ff->zs->next_in + f->ff->zs->avail_in); + assert(f->ff->zs->next_out + f->ff->zs->avail_out == \ + f->ff->recbuf + f->ff->recsize); +} + +struct fifolog_writer * +fifolog_write_new(void) +{ + struct fifolog_writer *f; + + ALLOC(&f, sizeof *f); + f->magic = FIFOLOG_WRITER_MAGIC; + return (f); +} + +void +fifolog_write_destroy(struct fifolog_writer *f) +{ + CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC); + free(f); +} + +void +fifolog_write_close(struct fifolog_writer *f) +{ + + CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC); + fifolog_int_close(&f->ff); + if (f->ibuf != NULL) + free(f->ibuf); + free(f); +} + +static void +fifo_prepobuf(struct fifolog_writer *f, time_t now, int flag) +{ + + memset(f->ff->recbuf, 0, f->ff->recsize); + f->ff->zs->next_out = f->ff->recbuf + 5; + f->ff->zs->avail_out = f->ff->recsize - 5; + if (f->recno == 0 && f->seq == 0) { + srandomdev(); + do { + f->seq = random(); + } while (f->seq == 0); + } + be32enc(f->ff->recbuf, f->seq++); + f->ff->recbuf[4] = f->flag; + f->flag = 0; + if (flag) { + f->ff->recbuf[4] |= FIFOLOG_FLG_SYNC; + be32enc(f->ff->recbuf + 5, (u_int)now); + f->ff->zs->next_out += 4; + f->ff->zs->avail_out -= 4; + } + fifolog_write_assert(f); +} + +const char * +fifolog_write_open(struct fifolog_writer *f, const char *fn, unsigned writerate, unsigned syncrate, int 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_DEFAULT_COMPRESSION || + 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); + if (o == 0) { + f->seq = 0; + f->recno = 0; + } else { + i = fifolog_int_read(f->ff, o); + if (i) + return ("Read error, looking for seq"); + f->seq = be32dec(f->ff->recbuf) + 1; + f->recno = o + 1; + } + + f->ibufsize = 32768; + ALLOC(&f->ibuf, f->ibufsize); + f->iptr = f->ibuf; + f->ff->zs->next_in = f->iptr; + i = deflateInit(f->ff->zs, (int)f->compression); + assert(i == Z_OK); + + f->flag |= FIFOLOG_FLG_RESTART; + + time(&now); + fifo_prepobuf(f, now, 1); + f->starttime = now; + + fifolog_write_assert(f); + return (NULL); +} + +static void +fifo_writerec(struct fifolog_writer *f) +{ + int i; + time_t t; + + fifolog_write_assert(f); + f->writes_since_sync++; + + assert(f->recno < f->ff->logsize); + f->cnt[FIFOLOG_PT_BYTES_POST] += f->ff->recsize - f->ff->zs->avail_out; + if (f->ff->zs->avail_out == 0) { + /* nothing */ + } else if (f->ff->zs->avail_out <= 255) { + f->ff->recbuf[f->ff->recsize - 1] = + (u_char)f->ff->zs->avail_out; + f->ff->recbuf[4] |= FIFOLOG_FLG_1BYTE; + } else { + be32enc(f->ff->recbuf + f->ff->recsize - 4, + f->ff->zs->avail_out); + f->ff->recbuf[4] |= FIFOLOG_FLG_4BYTE; + } + i = pwrite(f->ff->fd, f->ff->recbuf, f->ff->recsize, + (f->recno + 1) * f->ff->recsize); + assert (i == (int)f->ff->recsize); + if (++f->recno == f->ff->logsize) + f->recno = 0; + f->cnt[FIFOLOG_PT_WRITES]++; + time(&t); + f->cnt[FIFOLOG_PT_RUNTIME] = t - f->starttime; /*lint !e776 */ + fifolog_write_assert(f); +} + +int +fifolog_write_poll(struct fifolog_writer *f, time_t now) +{ + int i, fl, bo, bf; + + if (now == 0) + time(&now); + + fifolog_write_assert(f); + if (f->cleanup || now >= (int)(f->lastsync + f->syncrate)) { + /* + * We always check the sync timer, otherwise a flood of data + * would not get any sync records at all + */ + f->cleanup = 0; + fl = Z_FINISH; + f->lastsync = now; + f->lastwrite = now; + f->cnt[FIFOLOG_PT_SYNC]++; + } else if (f->ff->zs->avail_in == 0 && + now >= (int)(f->lastwrite + f->writerate)) { + /* + * We only check for writerate timeouts when the input + * buffer is empty. It would be silly to force a write if + * pending input could cause it to happen on its own. + */ + fl = Z_SYNC_FLUSH; + f->lastwrite = now; + f->cnt[FIFOLOG_PT_FLUSH]++; + } else if (f->ff->zs->avail_in == 0) + return (0); /* nothing to do */ + else + fl = Z_NO_FLUSH; + + for (;;) { + assert(f->ff->zs->avail_out > 0); + + bf = f->ff->zs->avail_out; + + i = deflate(f->ff->zs, fl); + assert (i == Z_OK || i == Z_BUF_ERROR || i == Z_STREAM_END); + + bo = f->ff->zs->avail_out; + + /* If we have output space and not in a hurry.. */ + if (bo > 0 && fl == Z_NO_FLUSH) + break; + + /* Write output buffer, if anything in it */ + if (bo != bf) + fifo_writerec(f); + + /* If the buffer were full, we need to check again */ + if (bo == 0) { + fifo_prepobuf(f, now, 0); + continue; + } + + if (fl == Z_FINISH) { + /* Make next record a SYNC record */ + fifo_prepobuf(f, now, 1); + /* And reset the zlib engine */ + i = deflateReset(f->ff->zs); + assert(i == Z_OK); + f->writes_since_sync = 0; + } else { + fifo_prepobuf(f, now, 0); + } + break; + } + + if (f->ff->zs->avail_in == 0) { + /* Reset input buffer when empty */ + f->iptr = f->ibuf; + f->ff->zs->next_in = f->iptr; + } + + fifolog_write_assert(f); + return (1); +} + +static void +fifolog_acct(struct fifolog_writer *f, unsigned bytes) +{ + + f->ff->zs->avail_in += bytes; + f->iptr += bytes; + f->cnt[FIFOLOG_PT_BYTES_PRE] += bytes; +} + +/* + * Attempt to write an entry. + * Return zero if there is no space, one otherwise + */ + +int +fifolog_write_bytes(struct fifolog_writer *f, uint32_t id, time_t now, const void *ptr, unsigned len) +{ + u_int l; + const unsigned char *p; + + fifolog_write_assert(f); + assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH))); + assert(ptr != NULL); + + if (len == 0) { + len = strlen(ptr); + l = 4 + len; /* id */ + p = ptr; + } else { + assert(len <= 255); + p = ptr; + id |= FIFOLOG_LENGTH; + l = 5 + len; /* id + len */ + } + + l += 4; /* A timestamp may be necessary */ + + /* Now do timestamp, if needed */ + if (now == 0) + time(&now); + + assert(l < f->ibufsize); + + /* Wait until there is sufficient space without the lock */ + if (f->iptr + l > f->ibuf + f->ibufsize) + return (0); + + if (now != f->last) { + id |= FIFOLOG_TIMESTAMP; + f->last = now; + } + + /* Emit instance+flag and length */ + be32enc(f->iptr, id); + fifolog_acct(f, 4); + + if (id & FIFOLOG_TIMESTAMP) { + be32enc(f->iptr, (uint32_t)f->last); + fifolog_acct(f, 4); + } + if (id & FIFOLOG_LENGTH) { + f->iptr[0] = (u_char)len; + fifolog_acct(f, 1); + } + + assert (len > 0); + memcpy(f->iptr, p, len); + fifolog_acct(f, len); + fifolog_write_assert(f); + return (1); +} + +/* + * Write an entry, polling until success. + * Long binary entries are broken into 255 byte chunks. + */ + +void +fifolog_write_bytes_poll(struct fifolog_writer *f, uint32_t id, time_t now, const void *ptr, unsigned len) +{ + u_int l; + const unsigned char *p; + + fifolog_write_assert(f); + + assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH))); + assert(ptr != NULL); + + if (len == 0) { + while (!fifolog_write_bytes(f, id, now, ptr, len)) { + (void)fifolog_write_poll(f, now); + (void)usleep(10000); + } + } else { + p = ptr; + for (p = ptr; len > 0; len -= l, p += l) { + l = len; + if (l > 255) + l = 255; + while (!fifolog_write_bytes(f, id, now, p, l)) { + (void)fifolog_write_poll(f, now); + (void)usleep(10000); + } + } + } + fifolog_write_assert(f); +} + +int +fifolog_write_flush(struct fifolog_writer *f) +{ + int i; + + fifolog_write_assert(f); + + f->cleanup = 1; + for (i = 0; fifolog_write_poll(f, 0); i = 1) + continue; + fifolog_write_assert(f); + return (i); +} diff --git a/usr.sbin/fifolog/lib/getdate.y b/usr.sbin/fifolog/lib/getdate.y new file mode 100644 index 0000000..93b5b6b --- /dev/null +++ b/usr.sbin/fifolog/lib/getdate.y @@ -0,0 +1,889 @@ +%{ +/* +** 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 yyparse getdate_yyparse +#define yylex getdate_yylex +#define yyerror getdate_yyerror + +static int yyparse(void); +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() +{ + 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..430e72b --- /dev/null +++ b/usr.sbin/fifolog/lib/libfifolog.h @@ -0,0 +1,62 @@ +/*- + * 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, unsigned recsize); + + +/* WRITERS */ + +struct fifolog_writer; +struct fifolog_writer *fifolog_write_new(void); +const char *fifolog_write_open(struct fifolog_writer *f, const char *fn, unsigned writerate, unsigned syncrate, int compression); +int fifolog_write_bytes(struct fifolog_writer *f, uint32_t id, time_t now, const void *ptr, unsigned len); +void fifolog_write_bytes_poll(struct fifolog_writer *f, uint32_t id, time_t now, const void *ptr, unsigned len); +int fifolog_write_poll(struct fifolog_writer *f, time_t now); +void fifolog_write_close(struct fifolog_writer *f); +void fifolog_write_destroy(struct fifolog_writer *f); +int fifolog_write_flush(struct fifolog_writer *f); +extern const char *fifolog_write_statnames[]; + +/* 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); + +#if (__FreeBSD__ < 7) +int expand_number(char *_buf, int64_t *_num); +#endif + diff --git a/usr.sbin/fifolog/lib/libfifolog_int.h b/usr.sbin/fifolog/lib/libfifolog_int.h new file mode 100644 index 0000000..54ab897 --- /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 + + unsigned 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..38a907e --- /dev/null +++ b/usr.sbin/fifolog/lib/miniobj.h @@ -0,0 +1,66 @@ +/*- + * 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); \ + assert((to) != NULL); \ + (to)->magic = (type_magic); \ + } while (0) + +#define FREE_OBJ(to) \ + do { \ + (to)->magic = (0); \ + free(to); \ + } while (0) + +#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 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); + |