diff options
Diffstat (limited to 'src/binlog.c')
-rw-r--r-- | src/binlog.c | 159 |
1 files changed, 153 insertions, 6 deletions
diff --git a/src/binlog.c b/src/binlog.c index 765b945..e7ac7e5 100644 --- a/src/binlog.c +++ b/src/binlog.c @@ -38,6 +38,7 @@ #endif #define BLF_ROUNDTS 0x01 +#define BLF_TRUNCATE 0x02 enum binlog_state { state_init, @@ -183,6 +184,29 @@ getinterval(char *p, char **endp) } } +static struct indexdef { + char *name; + char *pat; +} indextab[] = { + { "year", "%Y" }, + { "0", "%Y" }, + { "month", "%Y/%m" }, + { "1", "%Y/%m" }, + { "day", "%Y/%m/%d" }, + { "2", "%Y/%m/%d" }, + { NULL } +}; + +static char * +getindexpat(const char *name) +{ + struct indexdef *p; + for (p = indextab; p->name; p++) + if (strcmp(p->name, name) == 0) + return p->pat; + return NULL; +} + void vmod_init(struct sess *sp, struct vmod_priv *priv, const char *dir, const char *dataspec, const char *param) @@ -191,6 +215,7 @@ vmod_init(struct sess *sp, struct vmod_priv *priv, struct stat st; char *p, *q; unsigned long n; + int user_pattern = 0; p = findparam(param, "debug"); if (p) { @@ -238,8 +263,32 @@ vmod_init(struct sess *sp, struct vmod_priv *priv, if (!p) { p = strdup(BINLOG_PATTERN); AN(p); + } else + user_pattern = 1; + conf->pattern = p; + + p = findparam(param, "index"); + if (p) { + q = getindexpat(p); + if (!q) { + binlog_error("invalid index type"); + abort(); } + } else if (!user_pattern) { + q = getindexpat(BINLOG_INDEX); + AN(q); + } else + q = NULL; + + if (q) { + p = malloc(strlen(q) + strlen(conf->pattern) + 2); + AN(p); + strcpy(p, q); + strcat(p, "/"); + strcat(p, conf->pattern); + free(conf->pattern); conf->pattern = p; + } p = findparam(param, "size"); if (p) { @@ -306,6 +355,15 @@ vmod_init(struct sess *sp, struct vmod_priv *priv, free(p); } + p = findparam(param, "reuselog"); + if (p) { + if (atoi(p)) + conf->flags &= ~BLF_TRUNCATE; + else + conf->flags |= BLF_TRUNCATE; + free(p); + } + conf->fd = -1; conf->base = NULL; conf->stoptime = time(NULL); @@ -384,7 +442,7 @@ createfile(struct sess *sp, struct binlog_config *conf) return -1; } - fd = openat(conf->dd, fname, O_CREAT|O_RDWR|O_TRUNC, + fd = openat(conf->dd, fname, O_CREAT|O_RDWR, 0666 & ~conf->umask); if (fd == -1) { binlog_error("cannot create log file %s/%s: %s", @@ -420,19 +478,103 @@ setstoptime(struct binlog_config *conf) (((conf)->size - (conf)->base->hdrsize) / (conf)->base->recsize) static int +checkheader(struct binlog_config *conf, size_t hdrsize) +{ + struct binlog_file_header header; + int c; + ssize_t rc; + char *p; + + rc = read(conf->fd, &header, sizeof(header)); + if (rc == -1) { + binlog_error("error reading header of %s/%s: %s", + conf->dir, conf->fname, strerror(errno)); + return -1; + } else if (rc != sizeof(header)) { + binlog_error("error reading header of %s/%s: %s", + conf->dir, conf->fname, "hit eof"); + return -1; + } + + if (memcmp(header.magic, BINLOG_MAGIC_STR, BINLOG_MAGIC_LEN)) { + binlog_error("%s/%s is not a binlog file", + conf->dir, conf->fname); + return -1; + } + + if (header.version != BINLOG_VERSION) { + binlog_error("%s/%s: unknown version", conf->dir, conf->fname); + return -1; + } + + if (header.hdrsize != hdrsize) { + debug(conf,1,("%s/%s: header size mismatch", + conf->dir, conf->fname)); + return 1; + } + if (header.recsize != conf->recsize) { + debug(conf,1,("%s/%s: record size mismatch", + conf->dir, conf->fname)); + return 1; + } + + p = conf->dataspec; + while (*p) { + if (read(conf->fd, &c, 1) != 1 || c != *p) { + debug(conf,1,("%s/%s: dataspec mismatch near %s: %c", + conf->dir, conf->fname, p, c)); + return 1; + } + ++p; + } + if (read(conf->fd, &c, 1) != 1 || c != 0) { + debug(conf,1,("%s/%s: dataspec mismatch at the end: %c", + conf->dir, conf->fname, c)); + return 1; + } + return 0; +} + +static int newfile(struct sess *sp, struct binlog_config *conf) { int c; void *base; - size_t n; + size_t hdrsize; + struct stat st; + int reuse = 0; setstoptime(conf); if (createfile(sp, conf)) return -1; + + hdrsize = ((sizeof(struct binlog_file_header) + + strlen(conf->dataspec) + + conf->recsize - 1) / conf->recsize) * conf->recsize; + + if (fstat(conf->fd, &st) == 0) { + /* File already exists */ + if (st.st_size > 0 && + !(conf->flags & BLF_TRUNCATE) && + checkheader(conf, hdrsize) == 0) { + reuse = 1; + } else { + binlog_error("truncating existing file %s/%s", + conf->dir, conf->fname); + ftruncate(conf->fd, 0); + } + } else { + binlog_error("can't stat %s/%s: %s", + conf->dir, conf->fname, strerror(errno)); + /* try to continue anyway */ + } + conf->flags |= BLF_TRUNCATE; + if (lseek(conf->fd, conf->size, SEEK_SET) == -1) { binlog_error("seek in log file %s/%s failed: %s", conf->dir, conf->fname, strerror(errno)); + if (!reuse) unlinkat(conf->dd, conf->fname, 0); close(conf->fd); free(conf->fname); @@ -446,6 +588,7 @@ newfile(struct sess *sp, struct binlog_config *conf) conf->fd, 0); if (base == MAP_FAILED) { binlog_error("mmap: %s", strerror(errno)); + if (!reuse) unlinkat(conf->dd, conf->fname, 0); close(conf->fd); free(conf->fname); @@ -454,20 +597,24 @@ newfile(struct sess *sp, struct binlog_config *conf) } conf->base = base; + + if (reuse) { + debug(conf,1,("reusing log file %s, recnum=%lu", + conf->fname, (unsigned long)conf->base->recnum)); + } else { + debug(conf,1,("created new log file %s",conf->fname)); memcpy(conf->base->magic, BINLOG_MAGIC_STR, BINLOG_MAGIC_LEN); conf->base->version = BINLOG_VERSION; conf->base->recsize = conf->recsize; conf->base->recnum = 0; strcpy((char*)(conf->base + 1), conf->dataspec); - n = (sizeof(struct binlog_file_header) + strlen(conf->dataspec) + - conf->recsize - 1) / conf->recsize; - conf->base->hdrsize = n * conf->recsize; + conf->base->hdrsize = hdrsize; + } conf->recbase = (char *) conf->base + conf->base->hdrsize; conf->recnum = binlog_recnum(conf); - debug(conf,1,("created new log file %s",conf->fname)); return 0; } |