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