]> git.saurik.com Git - redis.git/blame - src/redis-check-aof.c
When closing the MDB DBI, do it in a transaction.
[redis.git] / src / redis-check-aof.c
CommitLineData
d288ee65 1/*
2 * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>
3 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
418807d2 31#include "fmacros.h"
b4bd0524
PN
32#include <stdlib.h>
33#include <stdio.h>
34#include <string.h>
cb8ae3c8 35#include <unistd.h>
b4bd0524
PN
36#include <sys/stat.h>
37#include "config.h"
38
39#define ERROR(...) { \
40 char __buf[1024]; \
41 sprintf(__buf, __VA_ARGS__); \
4a701b38 42 sprintf(error, "0x%16llx: %s", (long long)epos, __buf); \
b4bd0524
PN
43}
44
45static char error[1024];
4a701b38 46static off_t epos;
b4bd0524
PN
47
48int consumeNewline(char *buf) {
49 if (strncmp(buf,"\r\n",2) != 0) {
50 ERROR("Expected \\r\\n, got: %02x%02x",buf[0],buf[1]);
51 return 0;
52 }
53 return 1;
54}
55
56int readLong(FILE *fp, char prefix, long *target) {
57 char buf[128], *eptr;
4a701b38 58 epos = ftello(fp);
b4bd0524
PN
59 if (fgets(buf,sizeof(buf),fp) == NULL) {
60 return 0;
61 }
62 if (buf[0] != prefix) {
63 ERROR("Expected prefix '%c', got: '%c'",buf[0],prefix);
64 return 0;
65 }
66 *target = strtol(buf+1,&eptr,10);
67 return consumeNewline(eptr);
68}
69
70int readBytes(FILE *fp, char *target, long length) {
71 long real;
4a701b38 72 epos = ftello(fp);
b4bd0524
PN
73 real = fread(target,1,length,fp);
74 if (real != length) {
75 ERROR("Expected to read %ld bytes, got %ld bytes",length,real);
76 return 0;
77 }
78 return 1;
79}
80
81int readString(FILE *fp, char** target) {
82 long len;
83 *target = NULL;
84 if (!readLong(fp,'$',&len)) {
85 return 0;
86 }
87
88 /* Increase length to also consume \r\n */
89 len += 2;
90 *target = (char*)malloc(len);
91 if (!readBytes(fp,*target,len)) {
b4bd0524
PN
92 return 0;
93 }
94 if (!consumeNewline(*target+len-2)) {
b4bd0524
PN
95 return 0;
96 }
97 (*target)[len-2] = '\0';
98 return 1;
99}
100
101int readArgc(FILE *fp, long *target) {
102 return readLong(fp,'*',target);
103}
104
4a701b38 105off_t process(FILE *fp) {
106 long argc;
107 off_t pos = 0;
b4bd0524
PN
108 int i, multi = 0;
109 char *str;
110
111 while(1) {
4a701b38 112 if (!multi) pos = ftello(fp);
e51fa063 113 if (!readArgc(fp, &argc)) break;
b4bd0524
PN
114
115 for (i = 0; i < argc; i++) {
e51fa063 116 if (!readString(fp,&str)) break;
b4bd0524
PN
117 if (i == 0) {
118 if (strcasecmp(str, "multi") == 0) {
119 if (multi++) {
120 ERROR("Unexpected MULTI");
121 break;
122 }
123 } else if (strcasecmp(str, "exec") == 0) {
124 if (--multi) {
125 ERROR("Unexpected EXEC");
126 break;
127 }
128 }
129 }
130 free(str);
131 }
132
e51fa063 133 /* Stop if the loop did not finish */
b4bd0524
PN
134 if (i < argc) {
135 if (str) free(str);
136 break;
137 }
138 }
139
140 if (feof(fp) && multi && strlen(error) == 0) {
141 ERROR("Reached EOF before reading EXEC for MULTI");
142 }
b4bd0524
PN
143 if (strlen(error) > 0) {
144 printf("%s\n", error);
145 }
b4bd0524
PN
146 return pos;
147}
148
149int main(int argc, char **argv) {
cb8ae3c8
PN
150 char *filename;
151 int fix = 0;
57ca68ac
PN
152
153 if (argc < 2) {
154 printf("Usage: %s [--fix] <file.aof>\n", argv[0]);
155 exit(1);
156 } else if (argc == 2) {
157 filename = argv[1];
158 } else if (argc == 3) {
cb8ae3c8
PN
159 if (strcmp(argv[1],"--fix") != 0) {
160 printf("Invalid argument: %s\n", argv[1]);
161 exit(1);
162 }
cb8ae3c8 163 filename = argv[2];
57ca68ac 164 fix = 1;
cb8ae3c8 165 } else {
57ca68ac 166 printf("Invalid arguments\n");
cb8ae3c8
PN
167 exit(1);
168 }
169
170 FILE *fp = fopen(filename,"r+");
b4bd0524 171 if (fp == NULL) {
cb8ae3c8 172 printf("Cannot open file: %s\n", filename);
b4bd0524
PN
173 exit(1);
174 }
175
176 struct redis_stat sb;
177 if (redis_fstat(fileno(fp),&sb) == -1) {
cb8ae3c8 178 printf("Cannot stat file: %s\n", filename);
b4bd0524
PN
179 exit(1);
180 }
181
4a701b38 182 off_t size = sb.st_size;
b4bd0524 183 if (size == 0) {
cb8ae3c8 184 printf("Empty file: %s\n", filename);
b4bd0524
PN
185 exit(1);
186 }
187
4a701b38 188 off_t pos = process(fp);
189 off_t diff = size-pos;
190 printf("AOF analyzed: size=%lld, ok_up_to=%lld, diff=%lld\n",
191 (long long) size, (long long) pos, (long long) diff);
81330149 192 if (diff > 0) {
cb8ae3c8 193 if (fix) {
81330149 194 char buf[2];
4a701b38 195 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
196 printf("Continue? [y/N]: ");
197 if (fgets(buf,sizeof(buf),stdin) == NULL ||
198 strncasecmp(buf,"y",1) != 0) {
199 printf("Aborting...\n");
200 exit(1);
201 }
cb8ae3c8 202 if (ftruncate(fileno(fp), pos) == -1) {
81330149 203 printf("Failed to truncate AOF\n");
cb8ae3c8
PN
204 exit(1);
205 } else {
81330149 206 printf("Successfully truncated AOF\n");
cb8ae3c8
PN
207 }
208 } else {
81330149
PN
209 printf("AOF is not valid\n");
210 exit(1);
cb8ae3c8 211 }
b4bd0524 212 } else {
cb8ae3c8 213 printf("AOF is valid\n");
b4bd0524
PN
214 }
215
216 fclose(fp);
217 return 0;
218}