YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

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