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