xref: /redis-3.2.3/src/aof.c (revision c9d0c362)
1 #include "redis.h"
2 
3 #include <signal.h>
4 #include <fcntl.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <sys/time.h>
8 #include <sys/resource.h>
9 #include <sys/wait.h>
10 
11 void aofUpdateCurrentSize(void);
12 
13 /* Called when the user switches from "appendonly yes" to "appendonly no"
14  * at runtime using the CONFIG command. */
15 void stopAppendOnly(void) {
16     flushAppendOnlyFile();
17     aof_fsync(server.appendfd);
18     close(server.appendfd);
19 
20     server.appendfd = -1;
21     server.appendseldb = -1;
22     server.appendonly = 0;
23     /* rewrite operation in progress? kill it, wait child exit */
24     if (server.bgrewritechildpid != -1) {
25         int statloc;
26 
27         if (kill(server.bgrewritechildpid,SIGKILL) != -1)
28             wait3(&statloc,0,NULL);
29         /* reset the buffer accumulating changes while the child saves */
30         sdsfree(server.bgrewritebuf);
31         server.bgrewritebuf = sdsempty();
32         server.bgrewritechildpid = -1;
33     }
34 }
35 
36 /* Called when the user switches from "appendonly no" to "appendonly yes"
37  * at runtime using the CONFIG command. */
38 int startAppendOnly(void) {
39     server.appendonly = 1;
40     server.lastfsync = time(NULL);
41     server.appendfd = open(server.appendfilename,O_WRONLY|O_APPEND|O_CREAT,0644);
42     if (server.appendfd == -1) {
43         redisLog(REDIS_WARNING,"Used tried to switch on AOF via CONFIG, but I can't open the AOF file: %s",strerror(errno));
44         return REDIS_ERR;
45     }
46     if (rewriteAppendOnlyFileBackground() == REDIS_ERR) {
47         server.appendonly = 0;
48         close(server.appendfd);
49         redisLog(REDIS_WARNING,"Used tried to switch on AOF via CONFIG, I can't trigger a background AOF rewrite operation. Check the above logs for more info about the error.",strerror(errno));
50         return REDIS_ERR;
51     }
52     return REDIS_OK;
53 }
54 
55 /* Write the append only file buffer on disk.
56  *
57  * Since we are required to write the AOF before replying to the client,
58  * and the only way the client socket can get a write is entering when the
59  * the event loop, we accumulate all the AOF writes in a memory
60  * buffer and write it on disk using this function just before entering
61  * the event loop again. */
62 void flushAppendOnlyFile(void) {
63     time_t now;
64     ssize_t nwritten;
65 
66     if (sdslen(server.aofbuf) == 0) return;
67 
68     /* We want to perform a single write. This should be guaranteed atomic
69      * at least if the filesystem we are writing is a real physical one.
70      * While this will save us against the server being killed I don't think
71      * there is much to do about the whole server stopping for power problems
72      * or alike */
73      nwritten = write(server.appendfd,server.aofbuf,sdslen(server.aofbuf));
74      if (nwritten != (signed)sdslen(server.aofbuf)) {
75         /* Ooops, we are in troubles. The best thing to do for now is
76          * aborting instead of giving the illusion that everything is
77          * working as expected. */
78          if (nwritten == -1) {
79             redisLog(REDIS_WARNING,"Exiting on error writing to the append-only file: %s",strerror(errno));
80          } else {
81             redisLog(REDIS_WARNING,"Exiting on short write while writing to the append-only file: %s",strerror(errno));
82          }
83          exit(1);
84     }
85     sdsfree(server.aofbuf);
86     server.aofbuf = sdsempty();
87     server.appendonly_current_size += nwritten;
88 
89     /* Don't Fsync if no-appendfsync-on-rewrite is set to yes and we have
90      * childs performing heavy I/O on disk. */
91     if (server.no_appendfsync_on_rewrite &&
92         (server.bgrewritechildpid != -1 || server.bgsavechildpid != -1))
93             return;
94     /* Fsync if needed */
95     now = time(NULL);
96     if (server.appendfsync == APPENDFSYNC_ALWAYS ||
97         (server.appendfsync == APPENDFSYNC_EVERYSEC &&
98          now-server.lastfsync > 1))
99     {
100         /* aof_fsync is defined as fdatasync() for Linux in order to avoid
101          * flushing metadata. */
102         aof_fsync(server.appendfd); /* Let's try to get this data on the disk */
103         server.lastfsync = now;
104     }
105 }
106 
107 sds catAppendOnlyGenericCommand(sds buf, int argc, robj **argv) {
108     int j;
109     buf = sdscatprintf(buf,"*%d\r\n",argc);
110     for (j = 0; j < argc; j++) {
111         robj *o = getDecodedObject(argv[j]);
112         buf = sdscatprintf(buf,"$%lu\r\n",(unsigned long)sdslen(o->ptr));
113         buf = sdscatlen(buf,o->ptr,sdslen(o->ptr));
114         buf = sdscatlen(buf,"\r\n",2);
115         decrRefCount(o);
116     }
117     return buf;
118 }
119 
120 sds catAppendOnlyExpireAtCommand(sds buf, robj *key, robj *seconds) {
121     int argc = 3;
122     long when;
123     robj *argv[3];
124 
125     /* Make sure we can use strtol */
126     seconds = getDecodedObject(seconds);
127     when = time(NULL)+strtol(seconds->ptr,NULL,10);
128     decrRefCount(seconds);
129 
130     argv[0] = createStringObject("EXPIREAT",8);
131     argv[1] = key;
132     argv[2] = createObject(REDIS_STRING,
133         sdscatprintf(sdsempty(),"%ld",when));
134     buf = catAppendOnlyGenericCommand(buf, argc, argv);
135     decrRefCount(argv[0]);
136     decrRefCount(argv[2]);
137     return buf;
138 }
139 
140 void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) {
141     sds buf = sdsempty();
142     robj *tmpargv[3];
143 
144     /* The DB this command was targetting is not the same as the last command
145      * we appendend. To issue a SELECT command is needed. */
146     if (dictid != server.appendseldb) {
147         char seldb[64];
148 
149         snprintf(seldb,sizeof(seldb),"%d",dictid);
150         buf = sdscatprintf(buf,"*2\r\n$6\r\nSELECT\r\n$%lu\r\n%s\r\n",
151             (unsigned long)strlen(seldb),seldb);
152         server.appendseldb = dictid;
153     }
154 
155     if (cmd->proc == expireCommand) {
156         /* Translate EXPIRE into EXPIREAT */
157         buf = catAppendOnlyExpireAtCommand(buf,argv[1],argv[2]);
158     } else if (cmd->proc == setexCommand) {
159         /* Translate SETEX to SET and EXPIREAT */
160         tmpargv[0] = createStringObject("SET",3);
161         tmpargv[1] = argv[1];
162         tmpargv[2] = argv[3];
163         buf = catAppendOnlyGenericCommand(buf,3,tmpargv);
164         decrRefCount(tmpargv[0]);
165         buf = catAppendOnlyExpireAtCommand(buf,argv[1],argv[2]);
166     } else {
167         buf = catAppendOnlyGenericCommand(buf,argc,argv);
168     }
169 
170     /* Append to the AOF buffer. This will be flushed on disk just before
171      * of re-entering the event loop, so before the client will get a
172      * positive reply about the operation performed. */
173     server.aofbuf = sdscatlen(server.aofbuf,buf,sdslen(buf));
174 
175     /* If a background append only file rewriting is in progress we want to
176      * accumulate the differences between the child DB and the current one
177      * in a buffer, so that when the child process will do its work we
178      * can append the differences to the new append only file. */
179     if (server.bgrewritechildpid != -1)
180         server.bgrewritebuf = sdscatlen(server.bgrewritebuf,buf,sdslen(buf));
181 
182     sdsfree(buf);
183 }
184 
185 /* In Redis commands are always executed in the context of a client, so in
186  * order to load the append only file we need to create a fake client. */
187 struct redisClient *createFakeClient(void) {
188     struct redisClient *c = zmalloc(sizeof(*c));
189 
190     selectDb(c,0);
191     c->fd = -1;
192     c->querybuf = sdsempty();
193     c->argc = 0;
194     c->argv = NULL;
195     c->bufpos = 0;
196     c->flags = 0;
197     /* We set the fake client as a slave waiting for the synchronization
198      * so that Redis will not try to send replies to this client. */
199     c->replstate = REDIS_REPL_WAIT_BGSAVE_START;
200     c->reply = listCreate();
201     c->watched_keys = listCreate();
202     listSetFreeMethod(c->reply,decrRefCount);
203     listSetDupMethod(c->reply,dupClientReplyValue);
204     initClientMultiState(c);
205     return c;
206 }
207 
208 void freeFakeClient(struct redisClient *c) {
209     sdsfree(c->querybuf);
210     listRelease(c->reply);
211     listRelease(c->watched_keys);
212     freeClientMultiState(c);
213     zfree(c);
214 }
215 
216 /* Replay the append log file. On error REDIS_OK is returned. On non fatal
217  * error (the append only file is zero-length) REDIS_ERR is returned. On
218  * fatal error an error message is logged and the program exists. */
219 int loadAppendOnlyFile(char *filename) {
220     struct redisClient *fakeClient;
221     FILE *fp = fopen(filename,"r");
222     struct redis_stat sb;
223     int appendonly = server.appendonly;
224     long loops = 0;
225 
226     if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {
227         server.appendonly_current_size = 0;
228         fclose(fp);
229         return REDIS_ERR;
230     }
231 
232     if (fp == NULL) {
233         redisLog(REDIS_WARNING,"Fatal error: can't open the append log file for reading: %s",strerror(errno));
234         exit(1);
235     }
236 
237     /* Temporarily disable AOF, to prevent EXEC from feeding a MULTI
238      * to the same file we're about to read. */
239     server.appendonly = 0;
240 
241     fakeClient = createFakeClient();
242     startLoading(fp);
243 
244     while(1) {
245         int argc, j;
246         unsigned long len;
247         robj **argv;
248         char buf[128];
249         sds argsds;
250         struct redisCommand *cmd;
251 
252         /* Serve the clients from time to time */
253         if (!(loops++ % 1000)) {
254             loadingProgress(ftello(fp));
255             aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
256         }
257 
258         if (fgets(buf,sizeof(buf),fp) == NULL) {
259             if (feof(fp))
260                 break;
261             else
262                 goto readerr;
263         }
264         if (buf[0] != '*') goto fmterr;
265         argc = atoi(buf+1);
266         argv = zmalloc(sizeof(robj*)*argc);
267         for (j = 0; j < argc; j++) {
268             if (fgets(buf,sizeof(buf),fp) == NULL) goto readerr;
269             if (buf[0] != '$') goto fmterr;
270             len = strtol(buf+1,NULL,10);
271             argsds = sdsnewlen(NULL,len);
272             if (len && fread(argsds,len,1,fp) == 0) goto fmterr;
273             argv[j] = createObject(REDIS_STRING,argsds);
274             if (fread(buf,2,1,fp) == 0) goto fmterr; /* discard CRLF */
275         }
276 
277         /* Command lookup */
278         cmd = lookupCommand(argv[0]->ptr);
279         if (!cmd) {
280             redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", argv[0]->ptr);
281             exit(1);
282         }
283         /* Run the command in the context of a fake client */
284         fakeClient->argc = argc;
285         fakeClient->argv = argv;
286         cmd->proc(fakeClient);
287 
288         /* The fake client should not have a reply */
289         redisAssert(fakeClient->bufpos == 0 && listLength(fakeClient->reply) == 0);
290 
291         /* Clean up. Command code may have changed argv/argc so we use the
292          * argv/argc of the client instead of the local variables. */
293         for (j = 0; j < fakeClient->argc; j++)
294             decrRefCount(fakeClient->argv[j]);
295         zfree(fakeClient->argv);
296     }
297 
298     /* This point can only be reached when EOF is reached without errors.
299      * If the client is in the middle of a MULTI/EXEC, log error and quit. */
300     if (fakeClient->flags & REDIS_MULTI) goto readerr;
301 
302     fclose(fp);
303     freeFakeClient(fakeClient);
304     server.appendonly = appendonly;
305     stopLoading();
306     aofUpdateCurrentSize();
307     server.auto_aofrewrite_base_size = server.appendonly_current_size;
308     return REDIS_OK;
309 
310 readerr:
311     if (feof(fp)) {
312         redisLog(REDIS_WARNING,"Unexpected end of file reading the append only file");
313     } else {
314         redisLog(REDIS_WARNING,"Unrecoverable error reading the append only file: %s", strerror(errno));
315     }
316     exit(1);
317 fmterr:
318     redisLog(REDIS_WARNING,"Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>");
319     exit(1);
320 }
321 
322 /* Write a sequence of commands able to fully rebuild the dataset into
323  * "filename". Used both by REWRITEAOF and BGREWRITEAOF. */
324 int rewriteAppendOnlyFile(char *filename) {
325     dictIterator *di = NULL;
326     dictEntry *de;
327     FILE *fp;
328     char tmpfile[256];
329     int j;
330     time_t now = time(NULL);
331 
332     /* Note that we have to use a different temp name here compared to the
333      * one used by rewriteAppendOnlyFileBackground() function. */
334     snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) getpid());
335     fp = fopen(tmpfile,"w");
336     if (!fp) {
337         redisLog(REDIS_WARNING, "Failed rewriting the append only file: %s", strerror(errno));
338         return REDIS_ERR;
339     }
340     for (j = 0; j < server.dbnum; j++) {
341         char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";
342         redisDb *db = server.db+j;
343         dict *d = db->dict;
344         if (dictSize(d) == 0) continue;
345         di = dictGetSafeIterator(d);
346         if (!di) {
347             fclose(fp);
348             return REDIS_ERR;
349         }
350 
351         /* SELECT the new DB */
352         if (fwrite(selectcmd,sizeof(selectcmd)-1,1,fp) == 0) goto werr;
353         if (fwriteBulkLongLong(fp,j) == 0) goto werr;
354 
355         /* Iterate this DB writing every entry */
356         while((de = dictNext(di)) != NULL) {
357             sds keystr;
358             robj key, *o;
359             time_t expiretime;
360 
361             keystr = dictGetEntryKey(de);
362             o = dictGetEntryVal(de);
363             initStaticStringObject(key,keystr);
364 
365             expiretime = getExpire(db,&key);
366 
367             /* Save the key and associated value */
368             if (o->type == REDIS_STRING) {
369                 /* Emit a SET command */
370                 char cmd[]="*3\r\n$3\r\nSET\r\n";
371                 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
372                 /* Key and value */
373                 if (fwriteBulkObject(fp,&key) == 0) goto werr;
374                 if (fwriteBulkObject(fp,o) == 0) goto werr;
375             } else if (o->type == REDIS_LIST) {
376                 /* Emit the RPUSHes needed to rebuild the list */
377                 char cmd[]="*3\r\n$5\r\nRPUSH\r\n";
378                 if (o->encoding == REDIS_ENCODING_ZIPLIST) {
379                     unsigned char *zl = o->ptr;
380                     unsigned char *p = ziplistIndex(zl,0);
381                     unsigned char *vstr;
382                     unsigned int vlen;
383                     long long vlong;
384 
385                     while(ziplistGet(p,&vstr,&vlen,&vlong)) {
386                         if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
387                         if (fwriteBulkObject(fp,&key) == 0) goto werr;
388                         if (vstr) {
389                             if (fwriteBulkString(fp,(char*)vstr,vlen) == 0)
390                                 goto werr;
391                         } else {
392                             if (fwriteBulkLongLong(fp,vlong) == 0)
393                                 goto werr;
394                         }
395                         p = ziplistNext(zl,p);
396                     }
397                 } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
398                     list *list = o->ptr;
399                     listNode *ln;
400                     listIter li;
401 
402                     listRewind(list,&li);
403                     while((ln = listNext(&li))) {
404                         robj *eleobj = listNodeValue(ln);
405 
406                         if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
407                         if (fwriteBulkObject(fp,&key) == 0) goto werr;
408                         if (fwriteBulkObject(fp,eleobj) == 0) goto werr;
409                     }
410                 } else {
411                     redisPanic("Unknown list encoding");
412                 }
413             } else if (o->type == REDIS_SET) {
414                 char cmd[]="*3\r\n$4\r\nSADD\r\n";
415 
416                 /* Emit the SADDs needed to rebuild the set */
417                 if (o->encoding == REDIS_ENCODING_INTSET) {
418                     int ii = 0;
419                     int64_t llval;
420                     while(intsetGet(o->ptr,ii++,&llval)) {
421                         if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
422                         if (fwriteBulkObject(fp,&key) == 0) goto werr;
423                         if (fwriteBulkLongLong(fp,llval) == 0) goto werr;
424                     }
425                 } else if (o->encoding == REDIS_ENCODING_HT) {
426                     dictIterator *di = dictGetIterator(o->ptr);
427                     dictEntry *de;
428                     while((de = dictNext(di)) != NULL) {
429                         robj *eleobj = dictGetEntryKey(de);
430                         if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
431                         if (fwriteBulkObject(fp,&key) == 0) goto werr;
432                         if (fwriteBulkObject(fp,eleobj) == 0) goto werr;
433                     }
434                     dictReleaseIterator(di);
435                 } else {
436                     redisPanic("Unknown set encoding");
437                 }
438             } else if (o->type == REDIS_ZSET) {
439                 /* Emit the ZADDs needed to rebuild the sorted set */
440                 char cmd[]="*4\r\n$4\r\nZADD\r\n";
441 
442                 if (o->encoding == REDIS_ENCODING_ZIPLIST) {
443                     unsigned char *zl = o->ptr;
444                     unsigned char *eptr, *sptr;
445                     unsigned char *vstr;
446                     unsigned int vlen;
447                     long long vll;
448                     double score;
449 
450                     eptr = ziplistIndex(zl,0);
451                     redisAssert(eptr != NULL);
452                     sptr = ziplistNext(zl,eptr);
453                     redisAssert(sptr != NULL);
454 
455                     while (eptr != NULL) {
456                         redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll));
457                         score = zzlGetScore(sptr);
458 
459                         if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
460                         if (fwriteBulkObject(fp,&key) == 0) goto werr;
461                         if (fwriteBulkDouble(fp,score) == 0) goto werr;
462                         if (vstr != NULL) {
463                             if (fwriteBulkString(fp,(char*)vstr,vlen) == 0)
464                                 goto werr;
465                         } else {
466                             if (fwriteBulkLongLong(fp,vll) == 0)
467                                 goto werr;
468                         }
469                         zzlNext(zl,&eptr,&sptr);
470                     }
471                 } else if (o->encoding == REDIS_ENCODING_SKIPLIST) {
472                     zset *zs = o->ptr;
473                     dictIterator *di = dictGetIterator(zs->dict);
474                     dictEntry *de;
475 
476                     while((de = dictNext(di)) != NULL) {
477                         robj *eleobj = dictGetEntryKey(de);
478                         double *score = dictGetEntryVal(de);
479 
480                         if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
481                         if (fwriteBulkObject(fp,&key) == 0) goto werr;
482                         if (fwriteBulkDouble(fp,*score) == 0) goto werr;
483                         if (fwriteBulkObject(fp,eleobj) == 0) goto werr;
484                     }
485                     dictReleaseIterator(di);
486                 } else {
487                     redisPanic("Unknown sorted set encoding");
488                 }
489             } else if (o->type == REDIS_HASH) {
490                 char cmd[]="*4\r\n$4\r\nHSET\r\n";
491 
492                 /* Emit the HSETs needed to rebuild the hash */
493                 if (o->encoding == REDIS_ENCODING_ZIPMAP) {
494                     unsigned char *p = zipmapRewind(o->ptr);
495                     unsigned char *field, *val;
496                     unsigned int flen, vlen;
497 
498                     while((p = zipmapNext(p,&field,&flen,&val,&vlen)) != NULL) {
499                         if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
500                         if (fwriteBulkObject(fp,&key) == 0) goto werr;
501                         if (fwriteBulkString(fp,(char*)field,flen) == 0)
502                             goto werr;
503                         if (fwriteBulkString(fp,(char*)val,vlen) == 0)
504                             goto werr;
505                     }
506                 } else {
507                     dictIterator *di = dictGetIterator(o->ptr);
508                     dictEntry *de;
509 
510                     while((de = dictNext(di)) != NULL) {
511                         robj *field = dictGetEntryKey(de);
512                         robj *val = dictGetEntryVal(de);
513 
514                         if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
515                         if (fwriteBulkObject(fp,&key) == 0) goto werr;
516                         if (fwriteBulkObject(fp,field) == 0) goto werr;
517                         if (fwriteBulkObject(fp,val) == 0) goto werr;
518                     }
519                     dictReleaseIterator(di);
520                 }
521             } else {
522                 redisPanic("Unknown object type");
523             }
524             /* Save the expire time */
525             if (expiretime != -1) {
526                 char cmd[]="*3\r\n$8\r\nEXPIREAT\r\n";
527                 /* If this key is already expired skip it */
528                 if (expiretime < now) continue;
529                 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
530                 if (fwriteBulkObject(fp,&key) == 0) goto werr;
531                 if (fwriteBulkLongLong(fp,expiretime) == 0) goto werr;
532             }
533         }
534         dictReleaseIterator(di);
535     }
536 
537     /* Make sure data will not remain on the OS's output buffers */
538     fflush(fp);
539     aof_fsync(fileno(fp));
540     fclose(fp);
541 
542     /* Use RENAME to make sure the DB file is changed atomically only
543      * if the generate DB file is ok. */
544     if (rename(tmpfile,filename) == -1) {
545         redisLog(REDIS_WARNING,"Error moving temp append only file on the final destination: %s", strerror(errno));
546         unlink(tmpfile);
547         return REDIS_ERR;
548     }
549     redisLog(REDIS_NOTICE,"SYNC append only file rewrite performed");
550     return REDIS_OK;
551 
552 werr:
553     fclose(fp);
554     unlink(tmpfile);
555     redisLog(REDIS_WARNING,"Write error writing append only file on disk: %s", strerror(errno));
556     if (di) dictReleaseIterator(di);
557     return REDIS_ERR;
558 }
559 
560 /* This is how rewriting of the append only file in background works:
561  *
562  * 1) The user calls BGREWRITEAOF
563  * 2) Redis calls this function, that forks():
564  *    2a) the child rewrite the append only file in a temp file.
565  *    2b) the parent accumulates differences in server.bgrewritebuf.
566  * 3) When the child finished '2a' exists.
567  * 4) The parent will trap the exit code, if it's OK, will append the
568  *    data accumulated into server.bgrewritebuf into the temp file, and
569  *    finally will rename(2) the temp file in the actual file name.
570  *    The the new file is reopened as the new append only file. Profit!
571  */
572 int rewriteAppendOnlyFileBackground(void) {
573     pid_t childpid;
574     long long start;
575 
576     if (server.bgrewritechildpid != -1) return REDIS_ERR;
577     start = ustime();
578     if ((childpid = fork()) == 0) {
579         char tmpfile[256];
580 
581         /* Child */
582         if (server.ipfd > 0) close(server.ipfd);
583         if (server.sofd > 0) close(server.sofd);
584         snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
585         if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) {
586             _exit(0);
587         } else {
588             _exit(1);
589         }
590     } else {
591         /* Parent */
592         server.stat_fork_time = ustime()-start;
593         if (childpid == -1) {
594             redisLog(REDIS_WARNING,
595                 "Can't rewrite append only file in background: fork: %s",
596                 strerror(errno));
597             return REDIS_ERR;
598         }
599         redisLog(REDIS_NOTICE,
600             "Background append only file rewriting started by pid %d",childpid);
601         server.bgrewritechildpid = childpid;
602         updateDictResizePolicy();
603         /* We set appendseldb to -1 in order to force the next call to the
604          * feedAppendOnlyFile() to issue a SELECT command, so the differences
605          * accumulated by the parent into server.bgrewritebuf will start
606          * with a SELECT statement and it will be safe to merge. */
607         server.appendseldb = -1;
608         return REDIS_OK;
609     }
610     return REDIS_OK; /* unreached */
611 }
612 
613 void bgrewriteaofCommand(redisClient *c) {
614     if (server.bgrewritechildpid != -1) {
615         addReplyError(c,"Background append only file rewriting already in progress");
616     } else if (server.bgsavechildpid != -1) {
617         server.aofrewrite_scheduled = 1;
618         addReplyStatus(c,"Background append only file rewriting scheduled");
619     } else if (rewriteAppendOnlyFileBackground() == REDIS_OK) {
620         addReplyStatus(c,"Background append only file rewriting started");
621     } else {
622         addReply(c,shared.err);
623     }
624 }
625 
626 void aofRemoveTempFile(pid_t childpid) {
627     char tmpfile[256];
628 
629     snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) childpid);
630     unlink(tmpfile);
631 }
632 
633 /* Update the server.appendonly_current_size filed explicitly using stat(2)
634  * to check the size of the file. This is useful after a rewrite or after
635  * a restart, normally the size is updated just adding the write length
636  * to the current lenght, that is much faster. */
637 void aofUpdateCurrentSize(void) {
638     struct redis_stat sb;
639 
640     if (redis_fstat(server.appendfd,&sb) == -1) {
641         redisLog(REDIS_WARNING,"Unable to check the AOF length: %s",
642             strerror(errno));
643     } else {
644         server.appendonly_current_size = sb.st_size;
645     }
646 }
647 
648 /* A background append only file rewriting (BGREWRITEAOF) terminated its work.
649  * Handle this. */
650 void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
651     if (!bysignal && exitcode == 0) {
652         int fd;
653         char tmpfile[256];
654 
655         redisLog(REDIS_NOTICE,
656             "Background append only file rewriting terminated with success");
657         /* Now it's time to flush the differences accumulated by the parent */
658         snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) server.bgrewritechildpid);
659         fd = open(tmpfile,O_WRONLY|O_APPEND);
660         if (fd == -1) {
661             redisLog(REDIS_WARNING, "Not able to open the temp append only file produced by the child: %s", strerror(errno));
662             goto cleanup;
663         }
664         /* Flush our data... */
665         if (write(fd,server.bgrewritebuf,sdslen(server.bgrewritebuf)) !=
666                 (signed) sdslen(server.bgrewritebuf)) {
667             redisLog(REDIS_WARNING, "Error or short write trying to flush the parent diff of the append log file in the child temp file: %s", strerror(errno));
668             close(fd);
669             goto cleanup;
670         }
671         redisLog(REDIS_NOTICE,"Parent diff flushed into the new append log file with success (%lu bytes)",sdslen(server.bgrewritebuf));
672         /* Now our work is to rename the temp file into the stable file. And
673          * switch the file descriptor used by the server for append only. */
674         if (rename(tmpfile,server.appendfilename) == -1) {
675             redisLog(REDIS_WARNING,"Can't rename the temp append only file into the stable one: %s", strerror(errno));
676             close(fd);
677             goto cleanup;
678         }
679         /* Mission completed... almost */
680         redisLog(REDIS_NOTICE,"Append only file successfully rewritten.");
681         if (server.appendfd != -1) {
682             /* If append only is actually enabled... */
683             close(server.appendfd);
684             server.appendfd = fd;
685             if (server.appendfsync != APPENDFSYNC_NO) aof_fsync(fd);
686             server.appendseldb = -1; /* Make sure it will issue SELECT */
687             redisLog(REDIS_NOTICE,"The new append only file was selected for future appends.");
688             aofUpdateCurrentSize();
689             server.auto_aofrewrite_base_size = server.appendonly_current_size;
690         } else {
691             /* If append only is disabled we just generate a dump in this
692              * format. Why not? */
693             close(fd);
694         }
695     } else if (!bysignal && exitcode != 0) {
696         redisLog(REDIS_WARNING, "Background append only file rewriting error");
697     } else {
698         redisLog(REDIS_WARNING,
699             "Background append only file rewriting terminated by signal %d",
700             bysignal);
701     }
702 cleanup:
703     sdsfree(server.bgrewritebuf);
704     server.bgrewritebuf = sdsempty();
705     aofRemoveTempFile(server.bgrewritechildpid);
706     server.bgrewritechildpid = -1;
707 }
708