LCOV - code coverage report
Current view: top level - contrib/pg_standby - pg_standby.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 0 309 0.0 %
Date: 2019-11-21 13:06:38 Functions: 0 11 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13