]> git.saurik.com Git - redis.git/blame - src/redis-check-aof.c
Better Out of Memory handling.
[redis.git] / src / redis-check-aof.c
CommitLineData
418807d2 1#include "fmacros.h"
b4bd0524
PN
2#include <stdlib.h>
3#include <stdio.h>
4#include <string.h>
cb8ae3c8 5#include <unistd.h>
b4bd0524
PN
6#include <sys/stat.h>
7#include "config.h"
8
9#define ERROR(...) { \
10 char __buf[1024]; \
11 sprintf(__buf, __VA_ARGS__); \
4a701b38 12 sprintf(error, "0x%16llx: %s", (long long)epos, __buf); \
b4bd0524
PN
13}
14
15static char error[1024];
4a701b38 16static off_t epos;
b4bd0524
PN
17
18int 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
26int readLong(FILE *fp, char prefix, long *target) {
27 char buf[128], *eptr;
4a701b38 28 epos = ftello(fp);
b4bd0524
PN
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
40int readBytes(FILE *fp, char *target, long length) {
41 long real;
4a701b38 42 epos = ftello(fp);
b4bd0524
PN
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
51int 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)) {
b4bd0524
PN
62 return 0;
63 }
64 if (!consumeNewline(*target+len-2)) {
b4bd0524
PN
65 return 0;
66 }
67 (*target)[len-2] = '\0';
68 return 1;
69}
70
71int readArgc(FILE *fp, long *target) {
72 return readLong(fp,'*',target);
73}
74
4a701b38 75off_t process(FILE *fp) {
76 long argc;
77 off_t pos = 0;
b4bd0524
PN
78 int i, multi = 0;
79 char *str;
80
81 while(1) {
4a701b38 82 if (!multi) pos = ftello(fp);
e51fa063 83 if (!readArgc(fp, &argc)) break;
b4bd0524
PN
84
85 for (i = 0; i < argc; i++) {
e51fa063 86 if (!readString(fp,&str)) break;
b4bd0524
PN
87 if (i == 0) {
88 if (strcasecmp(str, "multi") == 0) {
89 if (multi++) {
90 ERROR("Unexpected MULTI");
91 break;
92 }
93 } else if (strcasecmp(str, "exec") == 0) {
94 if (--multi) {
95 ERROR("Unexpected EXEC");
96 break;
97 }
98 }
99 }
100 free(str);
101 }
102
e51fa063 103 /* Stop if the loop did not finish */
b4bd0524
PN
104 if (i < argc) {
105 if (str) free(str);
106 break;
107 }
108 }
109
110 if (feof(fp) && multi && strlen(error) == 0) {
111 ERROR("Reached EOF before reading EXEC for MULTI");
112 }
b4bd0524
PN
113 if (strlen(error) > 0) {
114 printf("%s\n", error);
115 }
b4bd0524
PN
116 return pos;
117}
118
119int main(int argc, char **argv) {
cb8ae3c8
PN
120 char *filename;
121 int fix = 0;
57ca68ac
PN
122
123 if (argc < 2) {
124 printf("Usage: %s [--fix] <file.aof>\n", argv[0]);
125 exit(1);
126 } else if (argc == 2) {
127 filename = argv[1];
128 } else if (argc == 3) {
cb8ae3c8
PN
129 if (strcmp(argv[1],"--fix") != 0) {
130 printf("Invalid argument: %s\n", argv[1]);
131 exit(1);
132 }
cb8ae3c8 133 filename = argv[2];
57ca68ac 134 fix = 1;
cb8ae3c8 135 } else {
57ca68ac 136 printf("Invalid arguments\n");
cb8ae3c8
PN
137 exit(1);
138 }
139
140 FILE *fp = fopen(filename,"r+");
b4bd0524 141 if (fp == NULL) {
cb8ae3c8 142 printf("Cannot open file: %s\n", filename);
b4bd0524
PN
143 exit(1);
144 }
145
146 struct redis_stat sb;
147 if (redis_fstat(fileno(fp),&sb) == -1) {
cb8ae3c8 148 printf("Cannot stat file: %s\n", filename);
b4bd0524
PN
149 exit(1);
150 }
151
4a701b38 152 off_t size = sb.st_size;
b4bd0524 153 if (size == 0) {
cb8ae3c8 154 printf("Empty file: %s\n", filename);
b4bd0524
PN
155 exit(1);
156 }
157
4a701b38 158 off_t pos = process(fp);
159 off_t diff = size-pos;
160 printf("AOF analyzed: size=%lld, ok_up_to=%lld, diff=%lld\n",
161 (long long) size, (long long) pos, (long long) diff);
81330149 162 if (diff > 0) {
cb8ae3c8 163 if (fix) {
81330149 164 char buf[2];
4a701b38 165 printf("This will shrink the AOF from %lld bytes, with %lld bytes, to %lld bytes\n",(long long)size,(long long)diff,(long long)pos);
81330149
PN
166 printf("Continue? [y/N]: ");
167 if (fgets(buf,sizeof(buf),stdin) == NULL ||
168 strncasecmp(buf,"y",1) != 0) {
169 printf("Aborting...\n");
170 exit(1);
171 }
cb8ae3c8 172 if (ftruncate(fileno(fp), pos) == -1) {
81330149 173 printf("Failed to truncate AOF\n");
cb8ae3c8
PN
174 exit(1);
175 } else {
81330149 176 printf("Successfully truncated AOF\n");
cb8ae3c8
PN
177 }
178 } else {
81330149
PN
179 printf("AOF is not valid\n");
180 exit(1);
cb8ae3c8 181 }
b4bd0524 182 } else {
cb8ae3c8 183 printf("AOF is valid\n");
b4bd0524
PN
184 }
185
186 fclose(fp);
187 return 0;
188}