diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-10-14 15:28:51 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-10-14 17:54:41 +0300 |
commit | f035194d7d1b6cc0846ad7a5d86e0d6fa9463c67 (patch) | |
tree | 494d180cee9a078727c8d4965caa65cb2052f33d /src/binlogsel.c | |
parent | 58f7dbc0658b3d73816a1bc91b75c2bdac733510 (diff) | |
download | vmod-binlog-f035194d7d1b6cc0846ad7a5d86e0d6fa9463c67.tar.gz vmod-binlog-f035194d7d1b6cc0846ad7a5d86e0d6fa9463c67.tar.bz2 |
New utility binlogsel
* configure.ac: Check for yacc.
* src/.gitignore: Update.
* src/Makefile.am (libbinlog_a_SOURCES): Add new files.
Build binlogsel.
* src/binlogcat.c: Use xmalloc.
* src/binlogsel.c: New file.
* src/parse-datetime.h: New file.
* src/parse-datetime.y: New file.
* src/xalloc.c: New file.
* src/xalloc.h: New file.
Diffstat (limited to 'src/binlogsel.c')
-rw-r--r-- | src/binlogsel.c | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/src/binlogsel.c b/src/binlogsel.c new file mode 100644 index 0000000..62299e5 --- /dev/null +++ b/src/binlogsel.c @@ -0,0 +1,474 @@ +/* This file is part of vmod-binlog + Copyright (C) 2013 Sergey Poznyakoff + + Vmod-binlog is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Vmod-binlog is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with vmod-binlog. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <time.h> +#include <string.h> +#include <glob.h> +#include "vmod-binlog.h" +#include "pack.h" +#include "err.h" +#include "xalloc.h" +#include "parse-datetime.h" + +char *timefmt = "%c"; +int number_option; +int verbose_option; +int timediff_option; +char *pattern; + +#define FROM_TIME 0x01 +#define TO_TIME 0x02 +int timemask; +time_t from_time, to_time; + +void +help() +{ + printf("usage: %s [-dhnv] [-t FORMAT] [-F FROMTIME] [-T TOTIME] [-p PATTERN] [-D DIR] [FILE...]\n", progname); +} + +/* Convert strftime-like pattern into globbing pattern */ +void +convpattern(const char *dir) +{ + char *p, *q; + char *newpat; + size_t size = strlen(pattern) + 1; + + if (dir) + size += strlen(dir) + 1; + + newpat = xmalloc(size); + + p = newpat; + if (dir) { + strcpy(p, dir); + p += strlen(dir); + *p++ = '/'; + } + + for (q = pattern; *q; ) { + if (*q == '%') { + *p++ = '*'; + q += 2; + } else + *p++ = *q++; + } + *p = 0; + pattern = newpat; +} + +#define getrec(base, recsize, n) \ + ((struct binlog_record*)((char *)(base) + (n) * (recsize))) + +int +searchts(void *base, size_t recsize, size_t from, size_t to, time_t ts, + size_t *ret) +{ + struct binlog_record *rec; + size_t middle; + + rec = getrec(base, recsize, from); + + if (ts < rec->ts) { + *ret = from; + return -1; + } + + if (to == from) { + *ret = from; + return 0; + } + + rec = getrec(base, recsize, to); + if (ts > rec->ts) { + *ret = from; + return 1; + } + + middle = (to + from) / 2; + + rec = getrec(base, recsize, middle); + + if (ts == rec->ts) { + *ret = middle; + return 0; + } + + if (ts > rec->ts) + return searchts(base, recsize, middle + 1, to, ts, ret); + + return searchts(base, recsize, from, middle, ts, ret); +} + +void +selmem(const char *name, void *base) +{ + struct binlog_file_header *hdr; + struct binlog_record *rec; + struct packenv *env; + struct packinst *inst; + char *p; + size_t i, start; + time_t start_ts; + char timebuf[128]; + char *dataspec; + + hdr = base; + + if (memcmp(hdr->magic, BINLOG_MAGIC_STR, BINLOG_MAGIC_LEN)) { + error("%s is not a binlog file", name); + return; + } + + if (hdr->version != BINLOG_VERSION) { + error("%s: unknown version", name); + return; + } + + dataspec = (char*)(hdr + 1); + + if (verbose_option) + printf("# %s; format=%s; recsize=%lu; recnum=%lu\n", + name, dataspec, hdr->recsize, hdr->recnum); + + inst = packcomp(dataspec, &p); + if (!inst) { + if (errno == EINVAL) { + error("%s: %s: bad dataspec near %s", name, + dataspec, p); + return; + } + + error("%s", strerror(errno)); + return; + } + + env = packenv_create(hdr->recsize - + offsetof(struct binlog_record,data)); + env->fp = stdout; + + base = (char*)base + hdr->hdrsize; + + if (timemask & FROM_TIME) { + switch (searchts(base, hdr->recsize, 0, hdr->recnum - 1, + from_time, &start)) { + case 0: + break; + case -1: + for (; start < hdr->recnum; start++) { + if (getrec(base, hdr->recsize, start)->ts > from_time) + break; + } + break; + case 1: + for (; start + 1 >= 0; start--) { + if (from_time < getrec(base, hdr->recsize, start)->ts) + break; + } + ++start; + } + } else + start = 0; + + for (i = 0; start < hdr->recnum; i++, start++) { + rec = getrec(base, hdr->recsize, start); + + if ((timemask & TO_TIME) && rec->ts > to_time) + break; + + if (timediff_option) { + if (i == 0) + start_ts = rec->ts; + rec->ts -= start_ts; + } + strftime(timebuf, sizeof timebuf, timefmt, + localtime(&rec->ts)); + if (number_option) + printf("%lu ", (unsigned long) start); + printf("%s ", timebuf); + memcpy(env->buf_base, rec->data, env->buf_size); + env->buf_pos = 0; + + packout(inst, env); + fputc('\n', stdout); + } +} + +static int +fchecktime(FILE *fp, const char *fname, time_t *ts) +{ + struct binlog_file_header header; + struct binlog_record rec; + time_t start, end; + + if (fread(&header, sizeof(header), 1, fp) != 1) { + error("error reading header of %s: %s", + fname, strerror(errno)); + return -1; + } + + if (memcmp(header.magic, BINLOG_MAGIC_STR, BINLOG_MAGIC_LEN)) { + error("%s is not a binlog file", fname); + return -1; + } + + if (header.version != BINLOG_VERSION) { + error("%s: unknown version", fname); + return -1; + } + + if (fseek(fp, header.hdrsize, SEEK_SET)) { + error("%s: seek error: %s", fname, strerror(errno)); + return -1; + } + + if (fread(&rec, sizeof(rec), 1, fp) != 1) { + error("%s: %s", fname, strerror(errno)); + return -1; + } + start = rec.ts; + + if (fseek(fp, header.hdrsize + + header.recsize * (header.recnum - 1), + SEEK_SET)) { + error("%s: seek error: %s", fname, strerror(errno)); + return -1; + } + if (fread(&rec, sizeof(rec), 1, fp) != 1) { + error("%s: %s", fname, strerror(errno)); + return -1; + } + end = rec.ts; + + if ((timemask & TO_TIME) && header.recnum > 1) { + if (to_time < start) + return 1; + + if (timemask & FROM_TIME) { + if (from_time > end) + return 1; + } + } else if ((timemask & FROM_TIME) && from_time > end) + return 1; + + *ts = start; + return 0; +} + +static int +checktime(const char *name, time_t *ts) +{ + int rc; + FILE *fp = fopen(name, "r"); + + if (!fp) { + error("can't open %s: %s", name, strerror(errno)); + return -1; + } + rc = fchecktime(fp, name, ts); + fclose(fp); + return rc; +} + +void +selfile(char *name) +{ + int fd; + struct stat st; + void *base; + + fd = open(name, O_RDONLY); + if (fd == -1) { + error("can't open %s: %s", name, strerror(errno)); + exit(1); + } + + if (fstat(fd, &st)) { + error("can't stat %s: %s", name, strerror(errno)); + exit(1); + } + + base = mmap((caddr_t)0, st.st_size, + PROT_READ, MAP_SHARED, + fd, 0); + if (base == MAP_FAILED) { + error("mmap(%s): %s", name, strerror(errno)); + exit(1); + } + + selmem(name, base); + munmap(base, st.st_size); + close(fd); +} + +void +selfilelist(char **argv) +{ + for (;*argv;++argv) + selfile(*argv); +} + +int +globerrfunc (const char *epath, int eerrno) +{ + error("%s: %s", strerror(eerrno)); + return 0; +} + +struct logfile { + char *name; + time_t start; +}; + +static int +tsort(const void *a, const void *b) +{ + struct logfile const *la = a; + struct logfile const *lb = b; + if (la->start > lb->start) + return 1; + if (la->start < lb->start) + return -1; + return 0; +} + +void +selpattern(void) +{ + size_t i, j; + glob_t gl; + struct logfile *logfiles; + + switch (glob(pattern, GLOB_ERR|GLOB_NOSORT, globerrfunc, &gl)) { + case 0: + break; + case GLOB_NOSPACE: + error("out of memory"); + exit(1); + + case GLOB_ABORTED: + error("read error"); + exit(1); + + case GLOB_NOMATCH: + error("no files matched pattern"); + exit(1); + } + + logfiles = xcalloc(gl.gl_pathc, sizeof(*logfiles)); + + for (i = j = 0; i < gl.gl_pathc; i++) { + time_t t; + if (checktime(gl.gl_pathv[i], &t) == 0) { + logfiles[j].name = gl.gl_pathv[i]; + logfiles[j].start = t; + ++j; + } + } + + /* sort the returned array */ + qsort(logfiles, j, sizeof(*logfiles), tsort); + + for (i = 0; i < j; i++) + selfile(logfiles[i].name); + + free(logfiles); + globfree(&gl); +} + +int +main(int argc, char **argv) +{ + int c; + struct timespec ts; + char *directory; + + setprogname(argv[0]); + while ((c = getopt(argc, argv, "D:dF:hp:T:t:nv")) != EOF) + switch (c) { + case 'D': + directory = optarg; + break; + case 'd': + timediff_option = 1; + timefmt = "%s"; + break; + case 'F': + if (!parse_datetime(&ts, optarg, NULL)) { + error("invalid timespec: %s", optarg); + exit(1); + } + from_time = ts.tv_sec; + timemask |= FROM_TIME; + break; + case 'h': + help(); + return 0; + case 'p': + pattern = optarg; + break; + case 'T': + if (!parse_datetime(&ts, optarg, NULL)) { + error("invalid timespec: %s", optarg); + exit(1); + } + to_time = ts.tv_sec; + timemask |= TO_TIME; + break; + case 't': + timefmt = optarg; + break; + case 'n': + number_option = 1; + break; + case 'v': + verbose_option = 1; + break; + default: + exit(1); + } + + argc -= optind; + argv += optind; + + if (argc) { + if (pattern) { + error("either files or pattern (-p) must be given, " + "but not both"); + exit(1); + } + selfilelist(argv); + } else { + if (!pattern) + pattern = BINLOG_PATTERN; + convpattern(directory); + selpattern(); + } + exit(0); +} + |