aboutsummaryrefslogtreecommitdiff
path: root/src/binlogsel.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-10-14 15:28:51 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2013-10-14 17:54:41 +0300
commitf035194d7d1b6cc0846ad7a5d86e0d6fa9463c67 (patch)
tree494d180cee9a078727c8d4965caa65cb2052f33d /src/binlogsel.c
parent58f7dbc0658b3d73816a1bc91b75c2bdac733510 (diff)
downloadvmod-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.c474
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);
+}
+

Return to:

Send suggestions and report system problems to the System administrator.