]> git.saurik.com Git - redis.git/blob - redis-check-aof.c
3be60781db5aa1f94ab39807940d5007edc86cca
[redis.git] / redis-check-aof.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <sys/stat.h>
5 #include "config.h"
6
7 #define ERROR(...) { \
8 char __buf[1024]; \
9 sprintf(__buf, __VA_ARGS__); \
10 sprintf(error, "0x%08lx: %s", epos, __buf); \
11 }
12
13 static char error[1024];
14 static long epos;
15
16 int consumeNewline(char *buf) {
17 if (strncmp(buf,"\r\n",2) != 0) {
18 ERROR("Expected \\r\\n, got: %02x%02x",buf[0],buf[1]);
19 return 0;
20 }
21 return 1;
22 }
23
24 int readLong(FILE *fp, char prefix, long *target) {
25 char buf[128], *eptr;
26 epos = ftell(fp);
27 if (fgets(buf,sizeof(buf),fp) == NULL) {
28 return 0;
29 }
30 if (buf[0] != prefix) {
31 ERROR("Expected prefix '%c', got: '%c'",buf[0],prefix);
32 return 0;
33 }
34 *target = strtol(buf+1,&eptr,10);
35 return consumeNewline(eptr);
36 }
37
38 int readBytes(FILE *fp, char *target, long length) {
39 long real;
40 epos = ftell(fp);
41 real = fread(target,1,length,fp);
42 if (real != length) {
43 ERROR("Expected to read %ld bytes, got %ld bytes",length,real);
44 return 0;
45 }
46 return 1;
47 }
48
49 int readString(FILE *fp, char** target) {
50 long len;
51 *target = NULL;
52 if (!readLong(fp,'$',&len)) {
53 return 0;
54 }
55
56 /* Increase length to also consume \r\n */
57 len += 2;
58 *target = (char*)malloc(len);
59 if (!readBytes(fp,*target,len)) {
60 free(*target);
61 *target = NULL;
62 return 0;
63 }
64 if (!consumeNewline(*target+len-2)) {
65 free(*target);
66 *target = NULL;
67 return 0;
68 }
69 (*target)[len-2] = '\0';
70 return 1;
71 }
72
73 int readArgc(FILE *fp, long *target) {
74 return readLong(fp,'*',target);
75 }
76
77 long process(FILE *fp) {
78 long argc, pos = 0;
79 int i, multi = 0;
80 char *str;
81
82 while(1) {
83 if (!multi) pos = ftell(fp);
84 if (!readArgc(fp, &argc)) {
85 break;
86 }
87
88 for (i = 0; i < argc; i++) {
89 if (!readString(fp,&str)) {
90 break;
91 }
92 if (i == 0) {
93 if (strcasecmp(str, "multi") == 0) {
94 if (multi++) {
95 ERROR("Unexpected MULTI");
96 break;
97 }
98 } else if (strcasecmp(str, "exec") == 0) {
99 if (--multi) {
100 ERROR("Unexpected EXEC");
101 break;
102 }
103 }
104 }
105 free(str);
106 }
107
108 /* Check if loop was finished */
109 if (i < argc) {
110 if (str) free(str);
111 break;
112 }
113 }
114
115 if (feof(fp) && multi && strlen(error) == 0) {
116 ERROR("Reached EOF before reading EXEC for MULTI");
117 }
118
119 if (strlen(error) > 0) {
120 printf("%s\n", error);
121 }
122
123 return pos;
124 }
125
126 int main(int argc, char **argv) {
127 /* expect the first argument to be the dump file */
128 if (argc <= 1) {
129 printf("Usage: %s <file.aof>\n", argv[0]);
130 exit(0);
131 }
132
133 FILE *fp = fopen(argv[1],"r");
134 if (fp == NULL) {
135 printf("Cannot open file: %s\n", argv[1]);
136 exit(1);
137 }
138
139 struct redis_stat sb;
140 if (redis_fstat(fileno(fp),&sb) == -1) {
141 printf("Cannot stat file: %s\n", argv[1]);
142 exit(1);
143 }
144
145 long size = sb.st_size;
146 if (size == 0) {
147 printf("Empty file: %s\n", argv[1]);
148 exit(1);
149 }
150
151 long pos = process(fp);
152 if (pos < size) {
153 printf("First invalid operation at offset %ld.\n", pos);
154 exit(1);
155 } else {
156 printf("AOF is valid.\n");
157 }
158
159 fclose(fp);
160 return 0;
161 }