From b425cc797090c46c3c9ab7b3052d322c4224bd76 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Fri, 30 May 2014 16:45:49 +0300 Subject: binlogsel: dry-run and incremental mode. * src/binlogsel.c (last_ts): New variable. (module_init): Change signature (incompatible change!) (interval) : New member. (interval_count): New variable. (interval_add): Take descr as argument. Fill in interval->descr. (interval_add): Increment interval_count. (interval_add_str): Construct descr. (selmem): Update last_ts (selidx_day,selidx_month,selidx_year): Silently ignore non-existing directories. Don't bail out if no matching file was found. (selidx_year): Clear the START_TIME flag after the first iteration. (read_status_fp,read_status_file) (write_status_file): New functions. (main): New option -n. Use interval_add_str to parse intervals. Pass flags to module_init. Process status file, if requested. --- src/binlogsel.c | 216 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 182 insertions(+), 34 deletions(-) (limited to 'src/binlogsel.c') diff --git a/src/binlogsel.c b/src/binlogsel.c index da04636..c448bd5 100644 --- a/src/binlogsel.c +++ b/src/binlogsel.c @@ -43,9 +43,11 @@ char *directory; char *pattern; enum binlog_index_type index_type = index_year; +time_t last_ts; + char *module_name; char *module_args; -void (*module_init)(char *, void (*)(const char *, const char *, const char *)); +void (*module_init)(int, char *, void (*)(const char *, const char *, const char *)); int (*module_open)(const char *, size_t, const char *); void (*module_close)(void); void (*module_done)(void); @@ -69,19 +71,23 @@ void selglob(const char *dir, const char *pattern); struct interval { struct interval *next; char *name; + char *descr; int timemask; time_t start; time_t end; }; static struct interval *interval_head, *interval_tail; +size_t interval_count; void -interval_add(const char *name, int tmask, time_t start, time_t end) +interval_add(const char *name, const char *descr, + int tmask, time_t start, time_t end) { struct interval *p = xmalloc(sizeof(*p)); p->next = NULL; p->name = xstrdup(name); + p->descr = xstrdup(descr); p->timemask = tmask; p->start = start; p->end = end; @@ -90,7 +96,8 @@ interval_add(const char *name, int tmask, time_t start, time_t end) else interval_head = p; interval_tail = p; - + ++interval_count; + if (tmask & START_TIME) { if (!(timemask & START_TIME) || start_time > start) { start_time = start; @@ -133,8 +140,20 @@ interval_add_str(const char *id, const char *from, const char *to) tmask |= STOP_TIME; } - if (tmask) - interval_add(id, tmask, start, end); + if (tmask) { + char *descr = xmalloc((from ? strlen(from) : 0) + + (from ? strlen(from) : 0) + 1); + descr[0] = 0; + if (from) + strcat(descr, from); + strcat(descr, ":"); + if (to) + strcat(descr, to); + + interval_add(id, descr, tmask, start, end); + + free(descr); + } } @@ -349,6 +368,9 @@ selmem(const char *name, void *base) continue; if ((ip->timemask & STOP_TIME) && ip->end < rec->ts) continue; + + last_ts = rec->ts; + if (module_record) module_record(ip->name, rec->ts, env->buf_base); else { @@ -519,6 +541,13 @@ selidx_day(const char *dir) char *dirbuf; size_t dirlen; + if (access(dir, F_OK)) { + if (errno == ENOENT) + return; + else + error("can't access %s: %s", dir, strerror(errno)); + } + if (index_type == index_month) { selglob(dir, BINLOG_GLOB_PATTERN); return; @@ -532,7 +561,7 @@ selidx_day(const char *dir) from_day = filename_to_int(gl.gl_pathv[0]); else { error("no matching files"); - exit(1); + return; } } @@ -543,7 +572,7 @@ selidx_day(const char *dir) glinit = matchnames(dir, "[0-9][0-9]", &gl); if (!glinit) { error("no matching files"); - exit(1); + return; } } to_day = filename_to_int(gl.gl_pathv[gl.gl_pathc - 1]); @@ -569,6 +598,13 @@ selidx_month(const char *dir) char *dirbuf; size_t dirlen; + if (access(dir, F_OK)) { + if (errno == ENOENT) + return; + else + error("can't access %s: %s", dir, strerror(errno)); + } + if (index_type == index_year) { selglob(dir, BINLOG_GLOB_PATTERN); return; @@ -582,7 +618,7 @@ selidx_month(const char *dir) from_month = filename_to_int(gl.gl_pathv[0]); else { error("no matching files"); - exit(1); + return; } } @@ -593,7 +629,7 @@ selidx_month(const char *dir) glinit = matchnames(dir, "[0-9][0-9]", &gl); if (!glinit) { error("no matching files"); - exit(1); + return; } } to_month = filename_to_int(gl.gl_pathv[gl.gl_pathc - 1]); @@ -619,6 +655,13 @@ selidx_year(const char *dir) char *dirbuf; size_t dirlen; + if (access(dir, F_OK)) { + if (errno == ENOENT) + return; + else + error("can't access %s: %s", dir, strerror(errno)); + } + if (timemask & START_TIME) from_year = 1900 + gmtime(&start_time)->tm_year; else { @@ -627,7 +670,7 @@ selidx_year(const char *dir) from_year = filename_to_int(gl.gl_pathv[0]); else { error("no matching files"); - exit(1); + return; } } @@ -638,7 +681,7 @@ selidx_year(const char *dir) glinit = matchnames(dir, "[0-9][0-9][0-9][0-9]", &gl); if (!glinit) { error("no matching files"); - exit(1); + return; } } to_year = filename_to_int(gl.gl_pathv[gl.gl_pathc - 1]); @@ -649,6 +692,7 @@ selidx_year(const char *dir) for (; from_year <= to_year; from_year++) { snprintf(dirbuf, dirlen, "%s/%04d", dir, from_year); selidx_month(dirbuf); + timemask &= ~START_TIME; } free(dirbuf); if (glinit) @@ -666,7 +710,7 @@ static int matchnames(const char *dir, const char *pat, glob_t *gl) { char *p = mkfilename(dir, pat); - int rc = glob(p, GLOB_ERR, globerrfunc, gl); + int rc = glob(p, GLOB_ERR|GLOB_ONLYDIR, globerrfunc, gl); free(p); switch (rc) { case 0: @@ -821,19 +865,121 @@ loader_init(void) module_done = lt_dlsym(handle, "done"); } +/* Status file format: + timestamp LF + N LF + INT-1-DESCR LF + ... + INT-N-DESCR LF +*/ + +int +read_status_fp(FILE *fp) +{ + struct interval *ip; + unsigned long i; + char buf[256]; + unsigned long n; + time_t ts; + + if (fscanf(fp, "%lu\n", &n) != 1) + return 1; + ts = (time_t) n; + + if (fscanf(fp, "%lu\n", &n) != 1) + return 1; + if (n != interval_count) + return 1; + + for (ip = interval_head; ip; ip = ip->next) { + char c; + char *p = ip->descr; + + do { + if ((c = fgetc(fp)) == EOF) + return 1; + if (c == '\n') + c = 0; + if (c != *p++) + return 1; + } while (c); + } + if (ferror(fp)) + return 1; + if (fgetc(fp) != EOF) + return 1; + + timemask |= START_TIME; + start_time = ts; + + return 0; +} + +int +read_status_file(char const *name) +{ + FILE *fp; + int rc; + + fp = fopen(name, "r"); + if (!fp) { + if (errno == ENOENT) + return 0; + error("cannot open '%s' for reading: %s", + name, strerror(errno)); + return 1; + } + + rc = read_status_fp(fp); + + fclose(fp); + + return rc; +} + +int +write_status_file(char const *name) +{ + FILE *fp; + struct interval *ip; + unsigned long i; + + fp = fopen(name, "w"); + if (!fp) { + error("cannot open '%s' for writing: %s", + name, strerror(errno)); + return 1; + } + + fprintf(fp, "%lu\n", (unsigned long) last_ts); + fprintf(fp, "%lu\n", (unsigned long) interval_count); + + /* Save intervals */ + for (ip = interval_head; ip; ip = ip->next) { + fprintf(fp, "%s\n", ip->descr); + } + + fclose(fp); +} + + +#define F_DRY_RUN 0x01 +#define F_INCREMENTAL 0x02 + int main(int argc, char **argv) { int c; struct timespec ts; const char *id; - int tmask = 0; - time_t start, stop; + char *start = NULL, *stop = NULL; char *p; + char *status_file_name = NULL; + int flags = 0; setprogname(argv[0]); add_load_path(BINLOGSEL_MODDIR, LP_APPEND); - while ((c = getopt(argc, argv, "D:dF:hi:I:L:m:p:P:T:t:nVv")) != EOF) + while ((c = getopt(argc, argv, "D:dF:hi:I:L:m:Nns:p:P:T:t:Vv")) != EOF) switch (c) { case 'D': directory = optarg; @@ -843,20 +989,15 @@ main(int argc, char **argv) timefmt = "%s"; break; case 'F': - if (!parse_datetime(&ts, optarg, NULL)) { - error("invalid timespec: %s", optarg); - exit(1); - } - start = ts.tv_sec; - tmask |= START_TIME; + start = optarg; break; case 'h': help(); return 0; case 'I': - if (tmask) - interval_add(id, tmask, start, stop); - tmask = 0; + if (start || stop) + interval_add_str(id, start, stop); + start = stop = NULL; id = optarg; break; case 'i': @@ -880,24 +1021,26 @@ main(int argc, char **argv) } module_name = optarg; break; + case 'n': + flags |= F_DRY_RUN; + break; case 'P': add_load_path(optarg, LP_PREPEND); break; case 'p': pattern = optarg; break; + case 's': + flags |= F_INCREMENTAL; + status_file_name = optarg; + break; case 'T': - if (!parse_datetime(&ts, optarg, NULL)) { - error("invalid timespec: %s", optarg); - exit(1); - } - stop = ts.tv_sec; - tmask |= STOP_TIME; + stop = optarg; break; case 't': timefmt = optarg; break; - case 'n': + case 'N': number_option = 1; break; case 'V': @@ -910,8 +1053,8 @@ main(int argc, char **argv) exit(1); } - if (tmask) - interval_add(id, tmask, start, stop); + if (start || stop) + interval_add_str(id, start, stop); argc -= optind; argv += optind; @@ -919,13 +1062,16 @@ main(int argc, char **argv) loader_init(); if (module_init) - module_init(module_args, interval_add_str); + module_init(flags, module_args, interval_add_str); if (timemask & CLEAR_START_TIME) timemask &= ~START_TIME; if (timemask & CLEAR_STOP_TIME) timemask &= ~STOP_TIME; + if (status_file_name) + read_status_file(status_file_name); + if (argc) { if (pattern) { error("either files or pattern (-p) must be given, " @@ -940,6 +1086,8 @@ main(int argc, char **argv) } if (module_done) module_done(); + if (status_file_name && !(flags & F_DRY_RUN)) + write_status_file(status_file_name); exit(0); } -- cgit v1.2.1