]> granicus.if.org Git - postgresql/blob - contrib/pg_standby/pg_standby.c
52f5fcaf10651dc3e688eefb8d91332298596401
[postgresql] / contrib / pg_standby / pg_standby.c
1 /*
2  * contrib/pg_standby/pg_standby.c
3  *
4  *
5  * pg_standby.c
6  *
7  * Production-ready example of how to create a Warm Standby
8  * database server using continuous archiving as a
9  * replication mechanism
10  *
11  * We separate the parameters for archive and nextWALfile
12  * so that we can check the archive exists, even if the
13  * WAL file doesn't (yet).
14  *
15  * This program will be executed once in full for each file
16  * requested by the warm standby server.
17  *
18  * It is designed to cater to a variety of needs, as well
19  * providing a customizable section.
20  *
21  * Original author:             Simon Riggs  simon@2ndquadrant.com
22  * Current maintainer:  Simon Riggs
23  */
24 #include "postgres_fe.h"
25
26 #include <ctype.h>
27 #include <dirent.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <signal.h>
31
32 #ifdef WIN32
33 int                     getopt(int argc, char *const argv[], const char *optstring);
34 #else
35 #include <sys/time.h>
36 #include <unistd.h>
37
38 #ifdef HAVE_GETOPT_H
39 #include <getopt.h>
40 #endif
41 #endif   /* ! WIN32 */
42
43 extern char *optarg;
44 extern int      optind;
45
46 const char *progname;
47
48 /* Options and defaults */
49 int                     sleeptime = 5;          /* amount of time to sleep between file checks */
50 int                     waittime = -1;          /* how long we have been waiting, -1 no wait
51                                                                  * yet */
52 int                     maxwaittime = 0;        /* how long are we prepared to wait for? */
53 int                     keepfiles = 0;          /* number of WAL files to keep, 0 keep all */
54 int                     maxretries = 3;         /* number of retries on restore command */
55 bool            debug = false;          /* are we debugging? */
56 bool            need_cleanup = false;           /* do we need to remove files from
57                                                                                  * archive? */
58
59 #ifndef WIN32
60 static volatile sig_atomic_t signaled = false;
61 #endif
62
63 char       *archiveLocation;    /* where to find the archive? */
64 char       *triggerPath;                /* where to find the trigger file? */
65 char       *xlogFilePath;               /* where we are going to restore to */
66 char       *nextWALFileName;    /* the file we need to get from archive */
67 char       *restartWALFileName; /* the file from which we can restart restore */
68 char       *priorWALFileName;   /* the file we need to get from archive */
69 char            WALFilePath[MAXPGPATH];         /* the file path including archive */
70 char            restoreCommand[MAXPGPATH];      /* run this to restore */
71 char            exclusiveCleanupFileName[MAXPGPATH];            /* the file we need to
72                                                                                                                  * get from archive */
73
74 /*
75  * Two types of failover are supported (smart and fast failover).
76  *
77  * The content of the trigger file determines the type of failover. If the
78  * trigger file contains the word "smart" (or the file is empty), smart
79  * failover is chosen: pg_standby acts as cp or ln command itself, on
80  * successful completion all the available WAL records will be applied
81  * resulting in zero data loss. But, it might take a long time to finish
82  * recovery if there's a lot of unapplied WAL.
83  *
84  * On the other hand, if the trigger file contains the word "fast", the
85  * recovery is finished immediately even if unapplied WAL files remain. Any
86  * transactions in the unapplied WAL files are lost.
87  *
88  * An empty trigger file performs smart failover. SIGUSR or SIGINT triggers
89  * fast failover. A timeout causes fast failover (smart failover would have
90  * the same effect, since if the timeout is reached there is no unapplied WAL).
91  */
92 #define NoFailover              0
93 #define SmartFailover   1
94 #define FastFailover    2
95
96 static int      Failover = NoFailover;
97
98 #define RESTORE_COMMAND_COPY 0
99 #define RESTORE_COMMAND_LINK 1
100 int                     restoreCommandType;
101
102 #define XLOG_DATA                        0
103 #define XLOG_HISTORY             1
104 #define XLOG_BACKUP_LABEL        2
105 int                     nextWALFileType;
106
107 #define SET_RESTORE_COMMAND(cmd, arg1, arg2) \
108         snprintf(restoreCommand, MAXPGPATH, cmd " \"%s\" \"%s\"", arg1, arg2)
109
110 struct stat stat_buf;
111
112 /* =====================================================================
113  *
114  *                Customizable section
115  *
116  * =====================================================================
117  *
118  *      Currently, this section assumes that the Archive is a locally
119  *      accessible directory. If you want to make other assumptions,
120  *      such as using a vendor-specific archive and access API, these
121  *      routines are the ones you'll need to change. You're
122  *      encouraged to submit any changes to pgsql-hackers@postgresql.org
123  *      or personally to the current maintainer. Those changes may be
124  *      folded in to later versions of this program.
125  */
126
127 #define XLOG_DATA_FNAME_LEN             24
128 /* Reworked from access/xlog_internal.h */
129 #define XLogFileName(fname, tli, log, seg)      \
130         snprintf(fname, XLOG_DATA_FNAME_LEN + 1, "%08X%08X%08X", tli, log, seg)
131
132 /*
133  *      Initialize allows customized commands into the warm standby program.
134  *
135  *      As an example, and probably the common case, we use either
136  *      cp/ln commands on *nix, or copy/move command on Windows.
137  */
138 static void
139 CustomizableInitialize(void)
140 {
141 #ifdef WIN32
142         snprintf(WALFilePath, MAXPGPATH, "%s\\%s", archiveLocation, nextWALFileName);
143         switch (restoreCommandType)
144         {
145                 case RESTORE_COMMAND_LINK:
146                         SET_RESTORE_COMMAND("mklink", WALFilePath, xlogFilePath);
147                         break;
148                 case RESTORE_COMMAND_COPY:
149                 default:
150                         SET_RESTORE_COMMAND("copy", WALFilePath, xlogFilePath);
151                         break;
152         }
153 #else
154         snprintf(WALFilePath, MAXPGPATH, "%s/%s", archiveLocation, nextWALFileName);
155         switch (restoreCommandType)
156         {
157                 case RESTORE_COMMAND_LINK:
158 #if HAVE_WORKING_LINK
159                         SET_RESTORE_COMMAND("ln -s -f", WALFilePath, xlogFilePath);
160                         break;
161 #endif
162                 case RESTORE_COMMAND_COPY:
163                 default:
164                         SET_RESTORE_COMMAND("cp", WALFilePath, xlogFilePath);
165                         break;
166         }
167 #endif
168
169         /*
170          * This code assumes that archiveLocation is a directory You may wish to
171          * add code to check for tape libraries, etc.. So, since it is a
172          * directory, we use stat to test if it's accessible
173          */
174         if (stat(archiveLocation, &stat_buf) != 0)
175         {
176                 fprintf(stderr, "%s: archive location \"%s\" does not exist\n", progname, archiveLocation);
177                 fflush(stderr);
178                 exit(2);
179         }
180 }
181
182 /*
183  * CustomizableNextWALFileReady()
184  *
185  *        Is the requested file ready yet?
186  */
187 static bool
188 CustomizableNextWALFileReady()
189 {
190         if (stat(WALFilePath, &stat_buf) == 0)
191         {
192                 /*
193                  * If it's a backup file, return immediately. If it's a regular file
194                  * return only if it's the right size already.
195                  */
196                 if (strlen(nextWALFileName) > 24 &&
197                         strspn(nextWALFileName, "0123456789ABCDEF") == 24 &&
198                 strcmp(nextWALFileName + strlen(nextWALFileName) - strlen(".backup"),
199                            ".backup") == 0)
200                 {
201                         nextWALFileType = XLOG_BACKUP_LABEL;
202                         return true;
203                 }
204                 else if (stat_buf.st_size == XLOG_SEG_SIZE)
205                 {
206 #ifdef WIN32
207
208                         /*
209                          * Windows 'cp' sets the final file size before the copy is
210                          * complete, and not yet ready to be opened by pg_standby. So we
211                          * wait for sleeptime secs before attempting to restore. If that
212                          * is not enough, we will rely on the retry/holdoff mechanism.
213                          * GNUWin32's cp does not have this problem.
214                          */
215                         pg_usleep(sleeptime * 1000000L);
216 #endif
217                         nextWALFileType = XLOG_DATA;
218                         return true;
219                 }
220
221                 /*
222                  * If still too small, wait until it is the correct size
223                  */
224                 if (stat_buf.st_size > XLOG_SEG_SIZE)
225                 {
226                         if (debug)
227                         {
228                                 fprintf(stderr, "file size greater than expected\n");
229                                 fflush(stderr);
230                         }
231                         exit(3);
232                 }
233         }
234
235         return false;
236 }
237
238 #define MaxSegmentsPerLogFile ( 0xFFFFFFFF / XLOG_SEG_SIZE )
239
240 static void
241 CustomizableCleanupPriorWALFiles(void)
242 {
243         /*
244          * Work out name of prior file from current filename
245          */
246         if (nextWALFileType == XLOG_DATA)
247         {
248                 int                     rc;
249                 DIR                *xldir;
250                 struct dirent *xlde;
251
252                 /*
253                  * Assume it's OK to keep failing. The failure situation may change
254                  * over time, so we'd rather keep going on the main processing than
255                  * fail because we couldn't clean up yet.
256                  */
257                 if ((xldir = opendir(archiveLocation)) != NULL)
258                 {
259                         while ((xlde = readdir(xldir)) != NULL)
260                         {
261                                 /*
262                                  * We ignore the timeline part of the XLOG segment identifiers
263                                  * in deciding whether a segment is still needed.  This
264                                  * ensures that we won't prematurely remove a segment from a
265                                  * parent timeline. We could probably be a little more
266                                  * proactive about removing segments of non-parent timelines,
267                                  * but that would be a whole lot more complicated.
268                                  *
269                                  * We use the alphanumeric sorting property of the filenames
270                                  * to decide which ones are earlier than the
271                                  * exclusiveCleanupFileName file. Note that this means files
272                                  * are not removed in the order they were originally written,
273                                  * in case this worries you.
274                                  */
275                                 if (strlen(xlde->d_name) == XLOG_DATA_FNAME_LEN &&
276                                         strspn(xlde->d_name, "0123456789ABCDEF") == XLOG_DATA_FNAME_LEN &&
277                                   strcmp(xlde->d_name + 8, exclusiveCleanupFileName + 8) < 0)
278                                 {
279 #ifdef WIN32
280                                         snprintf(WALFilePath, MAXPGPATH, "%s\\%s", archiveLocation, xlde->d_name);
281 #else
282                                         snprintf(WALFilePath, MAXPGPATH, "%s/%s", archiveLocation, xlde->d_name);
283 #endif
284
285                                         if (debug)
286                                                 fprintf(stderr, "\nremoving file \"%s\"", WALFilePath);
287
288                                         rc = unlink(WALFilePath);
289                                         if (rc != 0)
290                                         {
291                                                 fprintf(stderr, "\n%s: ERROR: could not remove file \"%s\": %s\n",
292                                                                 progname, WALFilePath, strerror(errno));
293                                                 break;
294                                         }
295                                 }
296                         }
297                         if (debug)
298                                 fprintf(stderr, "\n");
299                 }
300                 else
301                         fprintf(stderr, "%s: could not open archive location \"%s\": %s\n",
302                                         progname, archiveLocation, strerror(errno));
303
304                 closedir(xldir);
305                 fflush(stderr);
306         }
307 }
308
309 /* =====================================================================
310  *                End of Customizable section
311  * =====================================================================
312  */
313
314 /*
315  * SetWALFileNameForCleanup()
316  *
317  *        Set the earliest WAL filename that we want to keep on the archive
318  *        and decide whether we need_cleanup
319  */
320 static bool
321 SetWALFileNameForCleanup(void)
322 {
323         uint32          tli = 1,
324                                 log = 0,
325                                 seg = 0;
326         uint32          log_diff = 0,
327                                 seg_diff = 0;
328         bool            cleanup = false;
329
330         if (restartWALFileName)
331         {
332                 /*
333                  * Don't do cleanup if the restartWALFileName provided is later than
334                  * the xlog file requested. This is an error and we must not remove
335                  * these files from archive. This shouldn't happen, but better safe
336                  * than sorry.
337                  */
338                 if (strcmp(restartWALFileName, nextWALFileName) > 0)
339                         return false;
340
341                 strcpy(exclusiveCleanupFileName, restartWALFileName);
342                 return true;
343         }
344
345         if (keepfiles > 0)
346         {
347                 sscanf(nextWALFileName, "%08X%08X%08X", &tli, &log, &seg);
348                 if (tli > 0 && log >= 0 && seg > 0)
349                 {
350                         log_diff = keepfiles / MaxSegmentsPerLogFile;
351                         seg_diff = keepfiles % MaxSegmentsPerLogFile;
352                         if (seg_diff > seg)
353                         {
354                                 log_diff++;
355                                 seg = MaxSegmentsPerLogFile - (seg_diff - seg);
356                         }
357                         else
358                                 seg -= seg_diff;
359
360                         if (log >= log_diff)
361                         {
362                                 log -= log_diff;
363                                 cleanup = true;
364                         }
365                         else
366                         {
367                                 log = 0;
368                                 seg = 0;
369                         }
370                 }
371         }
372
373         XLogFileName(exclusiveCleanupFileName, tli, log, seg);
374
375         return cleanup;
376 }
377
378 /*
379  * CheckForExternalTrigger()
380  *
381  *        Is there a trigger file? Sets global 'Failover' variable to indicate
382  *        what kind of a trigger file it was. A "fast" trigger file is turned
383  *        into a "smart" file as a side-effect.
384  */
385 static void
386 CheckForExternalTrigger(void)
387 {
388         char            buf[32];
389         int                     fd;
390         int                     len;
391
392         /*
393          * Look for a trigger file, if that option has been selected
394          *
395          * We use stat() here because triggerPath is always a file rather than
396          * potentially being in an archive
397          */
398         if (!triggerPath || stat(triggerPath, &stat_buf) != 0)
399                 return;
400
401         /*
402          * An empty trigger file performs smart failover. There's a little race
403          * condition here: if the writer of the trigger file has just created the
404          * file, but not yet written anything to it, we'll treat that as smart
405          * shutdown even if the other process was just about to write "fast" to
406          * it. But that's fine: we'll restore one more WAL file, and when we're
407          * invoked next time, we'll see the word "fast" and fail over immediately.
408          */
409         if (stat_buf.st_size == 0)
410         {
411                 Failover = SmartFailover;
412                 fprintf(stderr, "trigger file found: smart failover\n");
413                 fflush(stderr);
414                 return;
415         }
416
417         if ((fd = open(triggerPath, O_RDWR, 0)) < 0)
418         {
419                 fprintf(stderr, "WARNING: could not open \"%s\": %s\n",
420                                 triggerPath, strerror(errno));
421                 fflush(stderr);
422                 return;
423         }
424
425         if ((len = read(fd, buf, sizeof(buf))) < 0)
426         {
427                 fprintf(stderr, "WARNING: could not read \"%s\": %s\n",
428                                 triggerPath, strerror(errno));
429                 fflush(stderr);
430                 close(fd);
431                 return;
432         }
433         buf[len] = '\0';
434
435         if (strncmp(buf, "smart", 5) == 0)
436         {
437                 Failover = SmartFailover;
438                 fprintf(stderr, "trigger file found: smart failover\n");
439                 fflush(stderr);
440                 close(fd);
441                 return;
442         }
443
444         if (strncmp(buf, "fast", 4) == 0)
445         {
446                 Failover = FastFailover;
447
448                 fprintf(stderr, "trigger file found: fast failover\n");
449                 fflush(stderr);
450
451                 /*
452                  * Turn it into a "smart" trigger by truncating the file. Otherwise if
453                  * the server asks us again to restore a segment that was restored
454                  * already, we would return "not found" and upset the server.
455                  */
456                 if (ftruncate(fd, 0) < 0)
457                 {
458                         fprintf(stderr, "WARNING: could not read \"%s\": %s\n",
459                                         triggerPath, strerror(errno));
460                         fflush(stderr);
461                 }
462                 close(fd);
463
464                 return;
465         }
466         close(fd);
467
468         fprintf(stderr, "WARNING: invalid content in \"%s\"\n", triggerPath);
469         fflush(stderr);
470         return;
471 }
472
473 /*
474  * RestoreWALFileForRecovery()
475  *
476  *        Perform the action required to restore the file from archive
477  */
478 static bool
479 RestoreWALFileForRecovery(void)
480 {
481         int                     rc = 0;
482         int                     numretries = 0;
483
484         if (debug)
485         {
486                 fprintf(stderr, "running restore                :");
487                 fflush(stderr);
488         }
489
490         while (numretries <= maxretries)
491         {
492                 rc = system(restoreCommand);
493                 if (rc == 0)
494                 {
495                         if (debug)
496                         {
497                                 fprintf(stderr, " OK\n");
498                                 fflush(stderr);
499                         }
500                         return true;
501                 }
502                 pg_usleep(numretries++ * sleeptime * 1000000L);
503         }
504
505         /*
506          * Allow caller to add additional info
507          */
508         if (debug)
509                 fprintf(stderr, "not restored\n");
510         return false;
511 }
512
513 static void
514 usage(void)
515 {
516         printf("%s allows PostgreSQL warm standby servers to be configured.\n\n", progname);
517         printf("Usage:\n");
518         printf("  %s [OPTION]... ARCHIVELOCATION NEXTWALFILE XLOGFILEPATH [RESTARTWALFILE]\n", progname);
519         printf("\nOptions:\n");
520         printf("  -c                 copy file from archive (default)\n");
521         printf("  -d                 generate lots of debugging output (testing only)\n");
522         printf("  -k NUMFILESTOKEEP  if RESTARTWALFILE is not used, remove files prior to limit\n"
523                    "                     (0 keeps all)\n");
524         printf("  -l                 does nothing; use of link is now deprecated\n");
525         printf("  -r MAXRETRIES      max number of times to retry, with progressive wait\n"
526                    "                     (default=3)\n");
527         printf("  -s SLEEPTIME       seconds to wait between file checks (min=1, max=60,\n"
528                    "                     default=5)\n");
529         printf("  -t TRIGGERFILE     trigger file to initiate failover (no default)\n");
530         printf("  -w MAXWAITTIME     max seconds to wait for a file (0=no limit) (default=0)\n");
531         printf("  --help             show this help, then exit\n");
532         printf("  --version          output version information, then exit\n");
533         printf("\n"
534                    "Main intended use as restore_command in recovery.conf:\n"
535                    "  restore_command = 'pg_standby [OPTION]... ARCHIVELOCATION %%f %%p %%r'\n"
536                    "e.g.\n"
537         "  restore_command = 'pg_standby /mnt/server/archiverdir %%f %%p %%r'\n");
538         printf("\nReport bugs to <pgsql-bugs@postgresql.org>.\n");
539 }
540
541 #ifndef WIN32
542 static void
543 sighandler(int sig)
544 {
545         signaled = true;
546 }
547
548 /* We don't want SIGQUIT to core dump */
549 static void
550 sigquit_handler(int sig)
551 {
552         signal(SIGINT, SIG_DFL);
553         kill(getpid(), SIGINT);
554 }
555 #endif
556
557 /*------------ MAIN ----------------------------------------*/
558 int
559 main(int argc, char **argv)
560 {
561         int                     c;
562
563         progname = get_progname(argv[0]);
564
565         if (argc > 1)
566         {
567                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
568                 {
569                         usage();
570                         exit(0);
571                 }
572                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
573                 {
574                         puts("pg_standby (PostgreSQL) " PG_VERSION);
575                         exit(0);
576                 }
577         }
578
579 #ifndef WIN32
580
581         /*
582          * You can send SIGUSR1 to trigger failover.
583          *
584          * Postmaster uses SIGQUIT to request immediate shutdown. The default
585          * action is to core dump, but we don't want that, so trap it and commit
586          * suicide without core dump.
587          *
588          * We used to use SIGINT and SIGQUIT to trigger failover, but that turned
589          * out to be a bad idea because postmaster uses SIGQUIT to request
590          * immediate shutdown. We still trap SIGINT, but that may change in a
591          * future release.
592          *
593          * There's no way to trigger failover via signal on Windows.
594          */
595         (void) signal(SIGUSR1, sighandler);
596         (void) signal(SIGINT, sighandler);      /* deprecated, use SIGUSR1 */
597         (void) signal(SIGQUIT, sigquit_handler);
598 #endif
599
600         while ((c = getopt(argc, argv, "cdk:lr:s:t:w:")) != -1)
601         {
602                 switch (c)
603                 {
604                         case 'c':                       /* Use copy */
605                                 restoreCommandType = RESTORE_COMMAND_COPY;
606                                 break;
607                         case 'd':                       /* Debug mode */
608                                 debug = true;
609                                 break;
610                         case 'k':                       /* keepfiles */
611                                 keepfiles = atoi(optarg);
612                                 if (keepfiles < 0)
613                                 {
614                                         fprintf(stderr, "%s: -k keepfiles must be >= 0\n", progname);
615                                         exit(2);
616                                 }
617                                 break;
618                         case 'l':                       /* Use link */
619
620                                 /*
621                                  * Link feature disabled, possibly permanently. Linking causes
622                                  * a problem after recovery ends that is not currently
623                                  * resolved by PostgreSQL. 25 Jun 2009
624                                  */
625 #ifdef NOT_USED
626                                 restoreCommandType = RESTORE_COMMAND_LINK;
627 #endif
628                                 break;
629                         case 'r':                       /* Retries */
630                                 maxretries = atoi(optarg);
631                                 if (maxretries < 0)
632                                 {
633                                         fprintf(stderr, "%s: -r maxretries must be >= 0\n", progname);
634                                         exit(2);
635                                 }
636                                 break;
637                         case 's':                       /* Sleep time */
638                                 sleeptime = atoi(optarg);
639                                 if (sleeptime <= 0 || sleeptime > 60)
640                                 {
641                                         fprintf(stderr, "%s: -s sleeptime incorrectly set\n", progname);
642                                         exit(2);
643                                 }
644                                 break;
645                         case 't':                       /* Trigger file */
646                                 triggerPath = optarg;
647                                 break;
648                         case 'w':                       /* Max wait time */
649                                 maxwaittime = atoi(optarg);
650                                 if (maxwaittime < 0)
651                                 {
652                                         fprintf(stderr, "%s: -w maxwaittime incorrectly set\n", progname);
653                                         exit(2);
654                                 }
655                                 break;
656                         default:
657                                 fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
658                                 exit(2);
659                                 break;
660                 }
661         }
662
663         /*
664          * Parameter checking - after checking to see if trigger file present
665          */
666         if (argc == 1)
667         {
668                 fprintf(stderr, "%s: not enough command-line arguments\n", progname);
669                 exit(2);
670         }
671
672         /*
673          * We will go to the archiveLocation to get nextWALFileName.
674          * nextWALFileName may not exist yet, which would not be an error, so we
675          * separate the archiveLocation and nextWALFileName so we can check
676          * separately whether archiveLocation exists, if not that is an error
677          */
678         if (optind < argc)
679         {
680                 archiveLocation = argv[optind];
681                 optind++;
682         }
683         else
684         {
685                 fprintf(stderr, "%s: must specify archive location\n", progname);
686                 fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
687                 exit(2);
688         }
689
690         if (optind < argc)
691         {
692                 nextWALFileName = argv[optind];
693                 optind++;
694         }
695         else
696         {
697                 fprintf(stderr, "%s: must specify WAL file name as second non-option argument (use \"%%f\")\n", progname);
698                 fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
699                 exit(2);
700         }
701
702         if (optind < argc)
703         {
704                 xlogFilePath = argv[optind];
705                 optind++;
706         }
707         else
708         {
709                 fprintf(stderr, "%s: must specify xlog destination as third non-option argument (use \"%%p\")\n", progname);
710                 fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
711                 exit(2);
712         }
713
714         if (optind < argc)
715         {
716                 restartWALFileName = argv[optind];
717                 optind++;
718         }
719
720         CustomizableInitialize();
721
722         need_cleanup = SetWALFileNameForCleanup();
723
724         if (debug)
725         {
726                 fprintf(stderr, "Trigger file:         %s\n", triggerPath ? triggerPath : "<not set>");
727                 fprintf(stderr, "Waiting for WAL file: %s\n", nextWALFileName);
728                 fprintf(stderr, "WAL file path:        %s\n", WALFilePath);
729                 fprintf(stderr, "Restoring to:         %s\n", xlogFilePath);
730                 fprintf(stderr, "Sleep interval:       %d second%s\n",
731                                 sleeptime, (sleeptime > 1 ? "s" : " "));
732                 fprintf(stderr, "Max wait interval:    %d %s\n",
733                                 maxwaittime, (maxwaittime > 0 ? "seconds" : "forever"));
734                 fprintf(stderr, "Command for restore:  %s\n", restoreCommand);
735                 fprintf(stderr, "Keep archive history: ");
736                 if (need_cleanup)
737                         fprintf(stderr, "%s and later\n", exclusiveCleanupFileName);
738                 else
739                         fprintf(stderr, "no cleanup required\n");
740                 fflush(stderr);
741         }
742
743         /*
744          * Check for initial history file: always the first file to be requested
745          * It's OK if the file isn't there - all other files need to wait
746          */
747         if (strlen(nextWALFileName) > 8 &&
748                 strspn(nextWALFileName, "0123456789ABCDEF") == 8 &&
749                 strcmp(nextWALFileName + strlen(nextWALFileName) - strlen(".history"),
750                            ".history") == 0)
751         {
752                 nextWALFileType = XLOG_HISTORY;
753                 if (RestoreWALFileForRecovery())
754                         exit(0);
755                 else
756                 {
757                         if (debug)
758                         {
759                                 fprintf(stderr, "history file not found\n");
760                                 fflush(stderr);
761                         }
762                         exit(1);
763                 }
764         }
765
766         /*
767          * Main wait loop
768          */
769         for (;;)
770         {
771                 /* Check for trigger file or signal first */
772                 CheckForExternalTrigger();
773 #ifndef WIN32
774                 if (signaled)
775                 {
776                         Failover = FastFailover;
777                         if (debug)
778                         {
779                                 fprintf(stderr, "signaled to exit: fast failover\n");
780                                 fflush(stderr);
781                         }
782                 }
783 #endif
784
785                 /*
786                  * Check for fast failover immediately, before checking if the
787                  * requested WAL file is available
788                  */
789                 if (Failover == FastFailover)
790                         exit(1);
791
792                 if (CustomizableNextWALFileReady())
793                 {
794                         /*
795                          * Once we have restored this file successfully we can remove some
796                          * prior WAL files. If this restore fails we musn't remove any
797                          * file because some of them will be requested again immediately
798                          * after the failed restore, or when we restart recovery.
799                          */
800                         if (RestoreWALFileForRecovery())
801                         {
802                                 if (need_cleanup)
803                                         CustomizableCleanupPriorWALFiles();
804
805                                 exit(0);
806                         }
807                         else
808                         {
809                                 /* Something went wrong in copying the file */
810                                 exit(1);
811                         }
812                 }
813
814                 /* Check for smart failover if the next WAL file was not available */
815                 if (Failover == SmartFailover)
816                         exit(1);
817
818                 if (sleeptime <= 60)
819                         pg_usleep(sleeptime * 1000000L);
820
821                 waittime += sleeptime;
822                 if (waittime >= maxwaittime && maxwaittime > 0)
823                 {
824                         Failover = FastFailover;
825                         if (debug)
826                         {
827                                 fprintf(stderr, "Timed out after %d seconds: fast failover\n",
828                                                 waittime);
829                                 fflush(stderr);
830                         }
831                 }
832                 if (debug)
833                 {
834                         fprintf(stderr, "WAL file not present yet.");
835                         if (triggerPath)
836                                 fprintf(stderr, " Checking for trigger file...");
837                         fprintf(stderr, "\n");
838                         fflush(stderr);
839                 }
840         }
841 }