Merge branch 'check-aof' of git://github.com/pietern/redis

This commit is contained in:
antirez 2010-05-10 15:09:25 +02:00
commit 45ae650129
3 changed files with 192 additions and 2 deletions

1
.gitignore vendored
View File

@ -6,6 +6,7 @@ redis-cli
redis-server
redis-benchmark
redis-check-dump
redis-check-aof
doc-tools
mkrelease.sh
release

View File

@ -20,13 +20,15 @@ OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort
BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o linenoise.o
CHECKDUMPOBJ = redis-check-dump.o lzf_c.o lzf_d.o
CHECKAOFOBJ = redis-check-aof.o
PRGNAME = redis-server
BENCHPRGNAME = redis-benchmark
CLIPRGNAME = redis-cli
CHECKDUMPPRGNAME = redis-check-dump
CHECKAOFPRGNAME = redis-check-aof
all: redis-server redis-benchmark redis-cli redis-check-dump
all: redis-server redis-benchmark redis-cli redis-check-dump redis-check-aof
# Deps (use make dep to generate this)
adlist.o: adlist.c adlist.h zmalloc.h
@ -68,11 +70,14 @@ redis-cli: $(CLIOBJ)
redis-check-dump: $(CHECKDUMPOBJ)
$(CC) -o $(CHECKDUMPPRGNAME) $(CCOPT) $(DEBUG) $(CHECKDUMPOBJ)
redis-check-aof: $(CHECKAOFOBJ)
$(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ)
.c.o:
$(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $<
clean:
rm -rf $(PRGNAME) $(BENCHPRGNAME) $(CLIPRGNAME) $(CHECKDUMPPRGNAME) *.o *.gcda *.gcno *.gcov
rm -rf $(PRGNAME) $(BENCHPRGNAME) $(CLIPRGNAME) $(CHECKDUMPPRGNAME) $(CHECKAOFPRGNAME) *.o *.gcda *.gcno *.gcov
dep:
$(CC) -MM *.c

184
redis-check-aof.c Normal file
View File

@ -0,0 +1,184 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "config.h"
#define ERROR(...) { \
char __buf[1024]; \
sprintf(__buf, __VA_ARGS__); \
sprintf(error, "0x%08lx: %s", epos, __buf); \
}
static char error[1024];
static long epos;
int consumeNewline(char *buf) {
if (strncmp(buf,"\r\n",2) != 0) {
ERROR("Expected \\r\\n, got: %02x%02x",buf[0],buf[1]);
return 0;
}
return 1;
}
int readLong(FILE *fp, char prefix, long *target) {
char buf[128], *eptr;
epos = ftell(fp);
if (fgets(buf,sizeof(buf),fp) == NULL) {
return 0;
}
if (buf[0] != prefix) {
ERROR("Expected prefix '%c', got: '%c'",buf[0],prefix);
return 0;
}
*target = strtol(buf+1,&eptr,10);
return consumeNewline(eptr);
}
int readBytes(FILE *fp, char *target, long length) {
long real;
epos = ftell(fp);
real = fread(target,1,length,fp);
if (real != length) {
ERROR("Expected to read %ld bytes, got %ld bytes",length,real);
return 0;
}
return 1;
}
int readString(FILE *fp, char** target) {
long len;
*target = NULL;
if (!readLong(fp,'$',&len)) {
return 0;
}
/* Increase length to also consume \r\n */
len += 2;
*target = (char*)malloc(len);
if (!readBytes(fp,*target,len)) {
return 0;
}
if (!consumeNewline(*target+len-2)) {
return 0;
}
(*target)[len-2] = '\0';
return 1;
}
int readArgc(FILE *fp, long *target) {
return readLong(fp,'*',target);
}
long process(FILE *fp) {
long argc, pos = 0;
int i, multi = 0;
char *str;
while(1) {
if (!multi) pos = ftell(fp);
if (!readArgc(fp, &argc)) break;
for (i = 0; i < argc; i++) {
if (!readString(fp,&str)) break;
if (i == 0) {
if (strcasecmp(str, "multi") == 0) {
if (multi++) {
ERROR("Unexpected MULTI");
break;
}
} else if (strcasecmp(str, "exec") == 0) {
if (--multi) {
ERROR("Unexpected EXEC");
break;
}
}
}
free(str);
}
/* Stop if the loop did not finish */
if (i < argc) {
if (str) free(str);
break;
}
}
if (feof(fp) && multi && strlen(error) == 0) {
ERROR("Reached EOF before reading EXEC for MULTI");
}
if (strlen(error) > 0) {
printf("%s\n", error);
}
return pos;
}
int main(int argc, char **argv) {
char *filename;
int fix = 0;
if (argc < 2) {
printf("Usage: %s [--fix] <file.aof>\n", argv[0]);
exit(1);
} else if (argc == 2) {
filename = argv[1];
} else if (argc == 3) {
if (strcmp(argv[1],"--fix") != 0) {
printf("Invalid argument: %s\n", argv[1]);
exit(1);
}
filename = argv[2];
fix = 1;
} else {
printf("Invalid arguments\n");
exit(1);
}
FILE *fp = fopen(filename,"r+");
if (fp == NULL) {
printf("Cannot open file: %s\n", filename);
exit(1);
}
struct redis_stat sb;
if (redis_fstat(fileno(fp),&sb) == -1) {
printf("Cannot stat file: %s\n", filename);
exit(1);
}
long size = sb.st_size;
if (size == 0) {
printf("Empty file: %s\n", filename);
exit(1);
}
long pos = process(fp);
long diff = size-pos;
if (diff > 0) {
if (fix) {
char buf[2];
printf("This will shrink the AOF from %ld bytes, with %ld bytes, to %ld bytes\n",size,diff,pos);
printf("Continue? [y/N]: ");
if (fgets(buf,sizeof(buf),stdin) == NULL ||
strncasecmp(buf,"y",1) != 0) {
printf("Aborting...\n");
exit(1);
}
if (ftruncate(fileno(fp), pos) == -1) {
printf("Failed to truncate AOF\n");
exit(1);
} else {
printf("Successfully truncated AOF\n");
}
} else {
printf("AOF is not valid\n");
exit(1);
}
} else {
printf("AOF is valid\n");
}
fclose(fp);
return 0;
}