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