LCOV - code coverage report
Current view: top level - src/bin/pg_rewind - pg_rewind.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 262 331 79.2 %
Date: 2020-06-01 07:06:57 Functions: 14 14 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pg_rewind.c
       4             :  *    Synchronizes a PostgreSQL data directory to a new timeline
       5             :  *
       6             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
       7             :  *
       8             :  *-------------------------------------------------------------------------
       9             :  */
      10             : #include "postgres_fe.h"
      11             : 
      12             : #include <sys/stat.h>
      13             : #include <fcntl.h>
      14             : #include <time.h>
      15             : #include <unistd.h>
      16             : 
      17             : #include "access/timeline.h"
      18             : #include "access/xlog_internal.h"
      19             : #include "catalog/catversion.h"
      20             : #include "catalog/pg_control.h"
      21             : #include "common/controldata_utils.h"
      22             : #include "common/file_perm.h"
      23             : #include "common/file_utils.h"
      24             : #include "common/restricted_token.h"
      25             : #include "common/string.h"
      26             : #include "fe_utils/recovery_gen.h"
      27             : #include "fetch.h"
      28             : #include "file_ops.h"
      29             : #include "filemap.h"
      30             : #include "getopt_long.h"
      31             : #include "pg_rewind.h"
      32             : #include "storage/bufpage.h"
      33             : 
      34             : static void usage(const char *progname);
      35             : 
      36             : static void createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli,
      37             :                               XLogRecPtr checkpointloc);
      38             : 
      39             : static void digestControlFile(ControlFileData *ControlFile, char *source,
      40             :                               size_t size);
      41             : static void syncTargetDirectory(void);
      42             : static void getRestoreCommand(const char *argv0);
      43             : static void sanityChecks(void);
      44             : static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex);
      45             : static void ensureCleanShutdown(const char *argv0);
      46             : static void disconnect_atexit(void);
      47             : 
      48             : static ControlFileData ControlFile_target;
      49             : static ControlFileData ControlFile_source;
      50             : 
      51             : const char *progname;
      52             : int         WalSegSz;
      53             : 
      54             : /* Configuration options */
      55             : char       *datadir_target = NULL;
      56             : char       *datadir_source = NULL;
      57             : char       *connstr_source = NULL;
      58             : char       *restore_command = NULL;
      59             : 
      60             : static bool debug = false;
      61             : bool        showprogress = false;
      62             : bool        dry_run = false;
      63             : bool        do_sync = true;
      64             : bool        restore_wal = false;
      65             : 
      66             : /* Target history */
      67             : TimeLineHistoryEntry *targetHistory;
      68             : int         targetNentries;
      69             : 
      70             : /* Progress counters */
      71             : uint64      fetch_size;
      72             : uint64      fetch_done;
      73             : 
      74             : 
      75             : static void
      76           2 : usage(const char *progname)
      77             : {
      78           2 :     printf(_("%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n\n"), progname);
      79           2 :     printf(_("Usage:\n  %s [OPTION]...\n\n"), progname);
      80           2 :     printf(_("Options:\n"));
      81           2 :     printf(_("  -c, --restore-target-wal       use restore_command in target configuration to\n"
      82             :              "                                 retrieve WAL files from archives\n"));
      83           2 :     printf(_("  -D, --target-pgdata=DIRECTORY  existing data directory to modify\n"));
      84           2 :     printf(_("      --source-pgdata=DIRECTORY  source data directory to synchronize with\n"));
      85           2 :     printf(_("      --source-server=CONNSTR    source server to synchronize with\n"));
      86           2 :     printf(_("  -n, --dry-run                  stop before modifying anything\n"));
      87           2 :     printf(_("  -N, --no-sync                  do not wait for changes to be written\n"
      88             :              "                                 safely to disk\n"));
      89           2 :     printf(_("  -P, --progress                 write progress messages\n"));
      90           2 :     printf(_("  -R, --write-recovery-conf      write configuration for replication\n"
      91             :              "                                 (requires --source-server)\n"));
      92           2 :     printf(_("      --debug                    write a lot of debug messages\n"));
      93           2 :     printf(_("      --no-ensure-shutdown       do not automatically fix unclean shutdown\n"));
      94           2 :     printf(_("  -V, --version                  output version information, then exit\n"));
      95           2 :     printf(_("  -?, --help                     show this help, then exit\n"));
      96           2 :     printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
      97           2 :     printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
      98           2 : }
      99             : 
     100             : 
     101             : int
     102          42 : main(int argc, char **argv)
     103             : {
     104             :     static struct option long_options[] = {
     105             :         {"help", no_argument, NULL, '?'},
     106             :         {"target-pgdata", required_argument, NULL, 'D'},
     107             :         {"write-recovery-conf", no_argument, NULL, 'R'},
     108             :         {"source-pgdata", required_argument, NULL, 1},
     109             :         {"source-server", required_argument, NULL, 2},
     110             :         {"no-ensure-shutdown", no_argument, NULL, 4},
     111             :         {"version", no_argument, NULL, 'V'},
     112             :         {"restore-target-wal", no_argument, NULL, 'c'},
     113             :         {"dry-run", no_argument, NULL, 'n'},
     114             :         {"no-sync", no_argument, NULL, 'N'},
     115             :         {"progress", no_argument, NULL, 'P'},
     116             :         {"debug", no_argument, NULL, 3},
     117             :         {NULL, 0, NULL, 0}
     118             :     };
     119             :     int         option_index;
     120             :     int         c;
     121             :     XLogRecPtr  divergerec;
     122             :     int         lastcommontliIndex;
     123             :     XLogRecPtr  chkptrec;
     124             :     TimeLineID  chkpttli;
     125             :     XLogRecPtr  chkptredo;
     126             :     size_t      size;
     127             :     char       *buffer;
     128          42 :     bool        no_ensure_shutdown = false;
     129             :     bool        rewind_needed;
     130             :     XLogRecPtr  endrec;
     131             :     TimeLineID  endtli;
     132             :     ControlFileData ControlFile_new;
     133          42 :     bool        writerecoveryconf = false;
     134             : 
     135          42 :     pg_logging_init(argv[0]);
     136          42 :     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_rewind"));
     137          42 :     progname = get_progname(argv[0]);
     138             : 
     139             :     /* Process command-line arguments */
     140          42 :     if (argc > 1)
     141             :     {
     142          42 :         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
     143             :         {
     144           2 :             usage(progname);
     145           2 :             exit(0);
     146             :         }
     147          40 :         if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
     148             :         {
     149           2 :             puts("pg_rewind (PostgreSQL) " PG_VERSION);
     150           2 :             exit(0);
     151             :         }
     152             :     }
     153             : 
     154         230 :     while ((c = getopt_long(argc, argv, "cD:nNPR", long_options, &option_index)) != -1)
     155             :     {
     156         156 :         switch (c)
     157             :         {
     158           2 :             case '?':
     159           2 :                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
     160           2 :                 exit(1);
     161             : 
     162           2 :             case 'c':
     163           2 :                 restore_wal = true;
     164           2 :                 break;
     165             : 
     166           0 :             case 'P':
     167           0 :                 showprogress = true;
     168           0 :                 break;
     169             : 
     170           2 :             case 'n':
     171           2 :                 dry_run = true;
     172           2 :                 break;
     173             : 
     174          28 :             case 'N':
     175          28 :                 do_sync = false;
     176          28 :                 break;
     177             : 
     178          10 :             case 'R':
     179          10 :                 writerecoveryconf = true;
     180          10 :                 break;
     181             : 
     182          34 :             case 3:
     183          34 :                 debug = true;
     184          34 :                 pg_logging_set_level(PG_LOG_DEBUG);
     185          34 :                 break;
     186             : 
     187          36 :             case 'D':           /* -D or --target-pgdata */
     188          36 :                 datadir_target = pg_strdup(optarg);
     189          36 :                 break;
     190             : 
     191          26 :             case 1:             /* --source-pgdata */
     192          26 :                 datadir_source = pg_strdup(optarg);
     193          26 :                 break;
     194             : 
     195          10 :             case 2:             /* --source-server */
     196          10 :                 connstr_source = pg_strdup(optarg);
     197          10 :                 break;
     198             : 
     199           6 :             case 4:
     200           6 :                 no_ensure_shutdown = true;
     201           6 :                 break;
     202             :         }
     203         192 :     }
     204             : 
     205          36 :     if (datadir_source == NULL && connstr_source == NULL)
     206             :     {
     207           2 :         pg_log_error("no source specified (--source-pgdata or --source-server)");
     208           2 :         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
     209           2 :         exit(1);
     210             :     }
     211             : 
     212          34 :     if (datadir_source != NULL && connstr_source != NULL)
     213             :     {
     214           2 :         pg_log_error("only one of --source-pgdata or --source-server can be specified");
     215           2 :         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
     216           2 :         exit(1);
     217             :     }
     218             : 
     219          32 :     if (datadir_target == NULL)
     220             :     {
     221           0 :         pg_log_error("no target data directory specified (--target-pgdata)");
     222           0 :         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
     223           0 :         exit(1);
     224             :     }
     225             : 
     226          32 :     if (writerecoveryconf && connstr_source == NULL)
     227             :     {
     228           2 :         pg_log_error("no source server information (--source-server) specified for --write-recovery-conf");
     229           2 :         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
     230           2 :         exit(1);
     231             :     }
     232             : 
     233          30 :     if (optind < argc)
     234             :     {
     235           2 :         pg_log_error("too many command-line arguments (first is \"%s\")",
     236             :                      argv[optind]);
     237           2 :         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
     238           2 :         exit(1);
     239             :     }
     240             : 
     241             :     /*
     242             :      * Don't allow pg_rewind to be run as root, to avoid overwriting the
     243             :      * ownership of files in the data directory. We need only check for root
     244             :      * -- any other user won't have sufficient permissions to modify files in
     245             :      * the data directory.
     246             :      */
     247             : #ifndef WIN32
     248          28 :     if (geteuid() == 0)
     249             :     {
     250           0 :         pg_log_error("cannot be executed by \"root\"");
     251           0 :         fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
     252             :                 progname);
     253           0 :         exit(1);
     254             :     }
     255             : #endif
     256             : 
     257          28 :     get_restricted_token();
     258             : 
     259             :     /* Set mask based on PGDATA permissions */
     260          28 :     if (!GetDataDirectoryCreatePerm(datadir_target))
     261             :     {
     262           0 :         pg_log_error("could not read permissions of directory \"%s\": %m",
     263             :                      datadir_target);
     264           0 :         exit(1);
     265             :     }
     266             : 
     267          28 :     umask(pg_mode_mask);
     268             : 
     269          28 :     getRestoreCommand(argv[0]);
     270             : 
     271          28 :     atexit(disconnect_atexit);
     272             : 
     273             :     /* Connect to remote server */
     274          28 :     if (connstr_source)
     275           8 :         libpqConnect(connstr_source);
     276             : 
     277             :     /*
     278             :      * Ok, we have all the options and we're ready to start. Read in all the
     279             :      * information we need from both clusters.
     280             :      */
     281          28 :     buffer = slurpFile(datadir_target, "global/pg_control", &size);
     282          28 :     digestControlFile(&ControlFile_target, buffer, size);
     283          28 :     pg_free(buffer);
     284             : 
     285             :     /*
     286             :      * If the target instance was not cleanly shut down, start and stop the
     287             :      * target cluster once in single-user mode to enforce recovery to finish,
     288             :      * ensuring that the cluster can be used by pg_rewind.  Note that if
     289             :      * no_ensure_shutdown is specified, pg_rewind ignores this step, and users
     290             :      * need to make sure by themselves that the target cluster is in a clean
     291             :      * state.
     292             :      */
     293          28 :     if (!no_ensure_shutdown &&
     294          22 :         ControlFile_target.state != DB_SHUTDOWNED &&
     295          20 :         ControlFile_target.state != DB_SHUTDOWNED_IN_RECOVERY)
     296             :     {
     297          20 :         ensureCleanShutdown(argv[0]);
     298             : 
     299          18 :         buffer = slurpFile(datadir_target, "global/pg_control", &size);
     300          18 :         digestControlFile(&ControlFile_target, buffer, size);
     301          18 :         pg_free(buffer);
     302             :     }
     303             : 
     304          26 :     buffer = fetchFile("global/pg_control", &size);
     305          26 :     digestControlFile(&ControlFile_source, buffer, size);
     306          26 :     pg_free(buffer);
     307             : 
     308          26 :     sanityChecks();
     309             : 
     310             :     /*
     311             :      * If both clusters are already on the same timeline, there's nothing to
     312             :      * do.
     313             :      */
     314          22 :     if (ControlFile_target.checkPointCopy.ThisTimeLineID == ControlFile_source.checkPointCopy.ThisTimeLineID)
     315             :     {
     316           2 :         pg_log_info("source and target cluster are on the same timeline");
     317           2 :         rewind_needed = false;
     318             :     }
     319             :     else
     320             :     {
     321          20 :         findCommonAncestorTimeline(&divergerec, &lastcommontliIndex);
     322          20 :         pg_log_info("servers diverged at WAL location %X/%X on timeline %u",
     323             :                     (uint32) (divergerec >> 32), (uint32) divergerec,
     324             :                     targetHistory[lastcommontliIndex].tli);
     325             : 
     326             :         /*
     327             :          * Check for the possibility that the target is in fact a direct
     328             :          * ancestor of the source. In that case, there is no divergent history
     329             :          * in the target that needs rewinding.
     330             :          */
     331          20 :         if (ControlFile_target.checkPoint >= divergerec)
     332             :         {
     333          20 :             rewind_needed = true;
     334             :         }
     335             :         else
     336             :         {
     337             :             XLogRecPtr  chkptendrec;
     338             : 
     339             :             /* Read the checkpoint record on the target to see where it ends. */
     340           0 :             chkptendrec = readOneRecord(datadir_target,
     341             :                                         ControlFile_target.checkPoint,
     342             :                                         targetNentries - 1,
     343             :                                         restore_command);
     344             : 
     345             :             /*
     346             :              * If the histories diverged exactly at the end of the shutdown
     347             :              * checkpoint record on the target, there are no WAL records in
     348             :              * the target that don't belong in the source's history, and no
     349             :              * rewind is needed.
     350             :              */
     351           0 :             if (chkptendrec == divergerec)
     352           0 :                 rewind_needed = false;
     353             :             else
     354           0 :                 rewind_needed = true;
     355             :         }
     356             :     }
     357             : 
     358          22 :     if (!rewind_needed)
     359             :     {
     360           2 :         pg_log_info("no rewind required");
     361           2 :         if (writerecoveryconf && !dry_run)
     362           0 :             WriteRecoveryConfig(conn, datadir_target,
     363             :                                 GenerateRecoveryConfig(conn, NULL));
     364           2 :         exit(0);
     365             :     }
     366             : 
     367          20 :     findLastCheckpoint(datadir_target, divergerec, lastcommontliIndex,
     368             :                        &chkptrec, &chkpttli, &chkptredo, restore_command);
     369          20 :     pg_log_info("rewinding from last common checkpoint at %X/%X on timeline %u",
     370             :                 (uint32) (chkptrec >> 32), (uint32) chkptrec,
     371             :                 chkpttli);
     372             : 
     373             :     /*
     374             :      * Build the filemap, by comparing the source and target data directories.
     375             :      */
     376          20 :     filemap_create();
     377          20 :     if (showprogress)
     378           0 :         pg_log_info("reading source file list");
     379          20 :     fetchSourceFileList();
     380          20 :     if (showprogress)
     381           0 :         pg_log_info("reading target file list");
     382          20 :     traverse_datadir(datadir_target, &process_target_file);
     383             : 
     384             :     /*
     385             :      * Read the target WAL from last checkpoint before the point of fork, to
     386             :      * extract all the pages that were modified on the target cluster after
     387             :      * the fork. We can stop reading after reaching the final shutdown record.
     388             :      * XXX: If we supported rewinding a server that was not shut down cleanly,
     389             :      * we would need to replay until the end of WAL here.
     390             :      */
     391          20 :     if (showprogress)
     392           0 :         pg_log_info("reading WAL in target");
     393          20 :     extractPageMap(datadir_target, chkptrec, lastcommontliIndex,
     394             :                    ControlFile_target.checkPoint, restore_command);
     395          20 :     filemap_finalize();
     396             : 
     397          20 :     if (showprogress)
     398           0 :         calculate_totals();
     399             : 
     400             :     /* this is too verbose even for verbose mode */
     401          20 :     if (debug)
     402          20 :         print_filemap();
     403             : 
     404             :     /*
     405             :      * Ok, we're ready to start copying things over.
     406             :      */
     407          20 :     if (showprogress)
     408             :     {
     409           0 :         pg_log_info("need to copy %lu MB (total source directory size is %lu MB)",
     410             :                     (unsigned long) (filemap->fetch_size / (1024 * 1024)),
     411             :                     (unsigned long) (filemap->total_size / (1024 * 1024)));
     412             : 
     413           0 :         fetch_size = filemap->fetch_size;
     414           0 :         fetch_done = 0;
     415             :     }
     416             : 
     417             :     /*
     418             :      * This is the point of no return. Once we start copying things, we have
     419             :      * modified the target directory and there is no turning back!
     420             :      */
     421             : 
     422          20 :     executeFileMap();
     423             : 
     424          20 :     progress_report(true);
     425          20 :     printf("\n");
     426             : 
     427          20 :     if (showprogress)
     428           0 :         pg_log_info("creating backup label and updating control file");
     429          20 :     createBackupLabel(chkptredo, chkpttli, chkptrec);
     430             : 
     431             :     /*
     432             :      * Update control file of target. Make it ready to perform archive
     433             :      * recovery when restarting.
     434             :      *
     435             :      * minRecoveryPoint is set to the current WAL insert location in the
     436             :      * source server. Like in an online backup, it's important that we recover
     437             :      * all the WAL that was generated while we copied the files over.
     438             :      */
     439          20 :     memcpy(&ControlFile_new, &ControlFile_source, sizeof(ControlFileData));
     440             : 
     441          20 :     if (connstr_source)
     442             :     {
     443           8 :         endrec = libpqGetCurrentXlogInsertLocation();
     444           8 :         endtli = ControlFile_source.checkPointCopy.ThisTimeLineID;
     445             :     }
     446             :     else
     447             :     {
     448          12 :         endrec = ControlFile_source.checkPoint;
     449          12 :         endtli = ControlFile_source.checkPointCopy.ThisTimeLineID;
     450             :     }
     451          20 :     ControlFile_new.minRecoveryPoint = endrec;
     452          20 :     ControlFile_new.minRecoveryPointTLI = endtli;
     453          20 :     ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;
     454          20 :     if (!dry_run)
     455          18 :         update_controlfile(datadir_target, &ControlFile_new, do_sync);
     456             : 
     457          20 :     if (showprogress)
     458           0 :         pg_log_info("syncing target data directory");
     459          20 :     syncTargetDirectory();
     460             : 
     461          20 :     if (writerecoveryconf && !dry_run)
     462           8 :         WriteRecoveryConfig(conn, datadir_target,
     463             :                             GenerateRecoveryConfig(conn, NULL));
     464             : 
     465          20 :     pg_log_info("Done!");
     466             : 
     467          20 :     return 0;
     468             : }
     469             : 
     470             : static void
     471          26 : sanityChecks(void)
     472             : {
     473             :     /* TODO Check that there's no backup_label in either cluster */
     474             : 
     475             :     /* Check system_identifier match */
     476          26 :     if (ControlFile_target.system_identifier != ControlFile_source.system_identifier)
     477           0 :         pg_fatal("source and target clusters are from different systems");
     478             : 
     479             :     /* check version */
     480          26 :     if (ControlFile_target.pg_control_version != PG_CONTROL_VERSION ||
     481          26 :         ControlFile_source.pg_control_version != PG_CONTROL_VERSION ||
     482          26 :         ControlFile_target.catalog_version_no != CATALOG_VERSION_NO ||
     483          26 :         ControlFile_source.catalog_version_no != CATALOG_VERSION_NO)
     484             :     {
     485           0 :         pg_fatal("clusters are not compatible with this version of pg_rewind");
     486             :     }
     487             : 
     488             :     /*
     489             :      * Target cluster need to use checksums or hint bit wal-logging, this to
     490             :      * prevent from data corruption that could occur because of hint bits.
     491             :      */
     492          26 :     if (ControlFile_target.data_checksum_version != PG_DATA_CHECKSUM_VERSION &&
     493          26 :         !ControlFile_target.wal_log_hints)
     494             :     {
     495           0 :         pg_fatal("target server needs to use either data checksums or \"wal_log_hints = on\"");
     496             :     }
     497             : 
     498             :     /*
     499             :      * Target cluster better not be running. This doesn't guard against
     500             :      * someone starting the cluster concurrently. Also, this is probably more
     501             :      * strict than necessary; it's OK if the target node was not shut down
     502             :      * cleanly, as long as it isn't running at the moment.
     503             :      */
     504          26 :     if (ControlFile_target.state != DB_SHUTDOWNED &&
     505           2 :         ControlFile_target.state != DB_SHUTDOWNED_IN_RECOVERY)
     506           2 :         pg_fatal("target server must be shut down cleanly");
     507             : 
     508             :     /*
     509             :      * When the source is a data directory, also require that the source
     510             :      * server is shut down. There isn't any very strong reason for this
     511             :      * limitation, but better safe than sorry.
     512             :      */
     513          24 :     if (datadir_source &&
     514          16 :         ControlFile_source.state != DB_SHUTDOWNED &&
     515           4 :         ControlFile_source.state != DB_SHUTDOWNED_IN_RECOVERY)
     516           2 :         pg_fatal("source data directory must be shut down cleanly");
     517          22 : }
     518             : 
     519             : /*
     520             :  * Print a progress report based on the fetch_size and fetch_done variables.
     521             :  *
     522             :  * Progress report is written at maximum once per second, unless the
     523             :  * force parameter is set to true.
     524             :  */
     525             : void
     526       81618 : progress_report(bool force)
     527             : {
     528             :     static pg_time_t last_progress_report = 0;
     529             :     int         percent;
     530             :     char        fetch_done_str[32];
     531             :     char        fetch_size_str[32];
     532             :     pg_time_t   now;
     533             : 
     534       81618 :     if (!showprogress)
     535       81618 :         return;
     536             : 
     537           0 :     now = time(NULL);
     538           0 :     if (now == last_progress_report && !force)
     539           0 :         return;                 /* Max once per second */
     540             : 
     541           0 :     last_progress_report = now;
     542           0 :     percent = fetch_size ? (int) ((fetch_done) * 100 / fetch_size) : 0;
     543             : 
     544             :     /*
     545             :      * Avoid overflowing past 100% or the full size. This may make the total
     546             :      * size number change as we approach the end of the backup (the estimate
     547             :      * will always be wrong if WAL is included), but that's better than having
     548             :      * the done column be bigger than the total.
     549             :      */
     550           0 :     if (percent > 100)
     551           0 :         percent = 100;
     552           0 :     if (fetch_done > fetch_size)
     553           0 :         fetch_size = fetch_done;
     554             : 
     555             :     /*
     556             :      * Separate step to keep platform-dependent format code out of
     557             :      * translatable strings.  And we only test for INT64_FORMAT availability
     558             :      * in snprintf, not fprintf.
     559             :      */
     560           0 :     snprintf(fetch_done_str, sizeof(fetch_done_str), INT64_FORMAT,
     561             :              fetch_done / 1024);
     562           0 :     snprintf(fetch_size_str, sizeof(fetch_size_str), INT64_FORMAT,
     563             :              fetch_size / 1024);
     564             : 
     565           0 :     fprintf(stderr, _("%*s/%s kB (%d%%) copied"),
     566           0 :             (int) strlen(fetch_size_str), fetch_done_str, fetch_size_str,
     567             :             percent);
     568           0 :     if (isatty(fileno(stderr)))
     569           0 :         fprintf(stderr, "\r");
     570             :     else
     571           0 :         fprintf(stderr, "\n");
     572             : }
     573             : 
     574             : /*
     575             :  * Find minimum from two WAL locations assuming InvalidXLogRecPtr means
     576             :  * infinity as src/include/access/timeline.h states. This routine should
     577             :  * be used only when comparing WAL locations related to history files.
     578             :  */
     579             : static XLogRecPtr
     580          20 : MinXLogRecPtr(XLogRecPtr a, XLogRecPtr b)
     581             : {
     582          20 :     if (XLogRecPtrIsInvalid(a))
     583           0 :         return b;
     584          20 :     else if (XLogRecPtrIsInvalid(b))
     585          20 :         return a;
     586             :     else
     587           0 :         return Min(a, b);
     588             : }
     589             : 
     590             : /*
     591             :  * Retrieve timeline history for given control file which should behold
     592             :  * either source or target.
     593             :  */
     594             : static TimeLineHistoryEntry *
     595          40 : getTimelineHistory(ControlFileData *controlFile, int *nentries)
     596             : {
     597             :     TimeLineHistoryEntry *history;
     598             :     TimeLineID  tli;
     599             : 
     600          40 :     tli = controlFile->checkPointCopy.ThisTimeLineID;
     601             : 
     602             :     /*
     603             :      * Timeline 1 does not have a history file, so there is no need to check
     604             :      * and fake an entry with infinite start and end positions.
     605             :      */
     606          40 :     if (tli == 1)
     607             :     {
     608          20 :         history = (TimeLineHistoryEntry *) pg_malloc(sizeof(TimeLineHistoryEntry));
     609          20 :         history->tli = tli;
     610          20 :         history->begin = history->end = InvalidXLogRecPtr;
     611          20 :         *nentries = 1;
     612             :     }
     613             :     else
     614             :     {
     615             :         char        path[MAXPGPATH];
     616             :         char       *histfile;
     617             : 
     618          20 :         TLHistoryFilePath(path, tli);
     619             : 
     620             :         /* Get history file from appropriate source */
     621          20 :         if (controlFile == &ControlFile_source)
     622          20 :             histfile = fetchFile(path, NULL);
     623           0 :         else if (controlFile == &ControlFile_target)
     624           0 :             histfile = slurpFile(datadir_target, path, NULL);
     625             :         else
     626           0 :             pg_fatal("invalid control file");
     627             : 
     628          20 :         history = rewind_parseTimeLineHistory(histfile, tli, nentries);
     629          20 :         pg_free(histfile);
     630             :     }
     631             : 
     632          40 :     if (debug)
     633             :     {
     634             :         int         i;
     635             : 
     636          40 :         if (controlFile == &ControlFile_source)
     637          20 :             pg_log_debug("Source timeline history:");
     638          20 :         else if (controlFile == &ControlFile_target)
     639          20 :             pg_log_debug("Target timeline history:");
     640             :         else
     641             :             Assert(false);
     642             : 
     643             :         /*
     644             :          * Print the target timeline history.
     645             :          */
     646          60 :         for (i = 0; i < targetNentries; i++)
     647             :         {
     648             :             TimeLineHistoryEntry *entry;
     649             : 
     650          20 :             entry = &history[i];
     651          20 :             pg_log_debug("%d: %X/%X - %X/%X", entry->tli,
     652             :                          (uint32) (entry->begin >> 32), (uint32) (entry->begin),
     653             :                          (uint32) (entry->end >> 32), (uint32) (entry->end));
     654             :         }
     655             :     }
     656             : 
     657          40 :     return history;
     658             : }
     659             : 
     660             : /*
     661             :  * Determine the TLI of the last common timeline in the timeline history of the
     662             :  * two clusters. targetHistory is filled with target timeline history and
     663             :  * targetNentries is number of items in targetHistory. *tliIndex is set to the
     664             :  * index of last common timeline in targetHistory array, and *recptr is set to
     665             :  * the position where the timeline history diverged (ie. the first WAL record
     666             :  * that's not the same in both clusters).
     667             :  *
     668             :  * Control files of both clusters must be read into ControlFile_target/source
     669             :  * before calling this routine.
     670             :  */
     671             : static void
     672          20 : findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex)
     673             : {
     674             :     TimeLineHistoryEntry *sourceHistory;
     675             :     int         sourceNentries;
     676             :     int         i,
     677             :                 n;
     678             : 
     679             :     /* Retrieve timelines for both source and target */
     680          20 :     sourceHistory = getTimelineHistory(&ControlFile_source, &sourceNentries);
     681          20 :     targetHistory = getTimelineHistory(&ControlFile_target, &targetNentries);
     682             : 
     683             :     /*
     684             :      * Trace the history forward, until we hit the timeline diverge. It may
     685             :      * still be possible that the source and target nodes used the same
     686             :      * timeline number in their history but with different start position
     687             :      * depending on the history files that each node has fetched in previous
     688             :      * recovery processes. Hence check the start position of the new timeline
     689             :      * as well and move down by one extra timeline entry if they do not match.
     690             :      */
     691          20 :     n = Min(sourceNentries, targetNentries);
     692          40 :     for (i = 0; i < n; i++)
     693             :     {
     694          20 :         if (sourceHistory[i].tli != targetHistory[i].tli ||
     695          20 :             sourceHistory[i].begin != targetHistory[i].begin)
     696             :             break;
     697             :     }
     698             : 
     699          20 :     if (i > 0)
     700             :     {
     701          20 :         i--;
     702          20 :         *recptr = MinXLogRecPtr(sourceHistory[i].end, targetHistory[i].end);
     703          20 :         *tliIndex = i;
     704             : 
     705          20 :         pg_free(sourceHistory);
     706          20 :         return;
     707             :     }
     708             :     else
     709             :     {
     710           0 :         pg_fatal("could not find common ancestor of the source and target cluster's timelines");
     711             :     }
     712             : }
     713             : 
     714             : 
     715             : /*
     716             :  * Create a backup_label file that forces recovery to begin at the last common
     717             :  * checkpoint.
     718             :  */
     719             : static void
     720          20 : createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli, XLogRecPtr checkpointloc)
     721             : {
     722             :     XLogSegNo   startsegno;
     723             :     time_t      stamp_time;
     724             :     char        strfbuf[128];
     725             :     char        xlogfilename[MAXFNAMELEN];
     726             :     struct tm  *tmp;
     727             :     char        buf[1000];
     728             :     int         len;
     729             : 
     730          20 :     XLByteToSeg(startpoint, startsegno, WalSegSz);
     731          20 :     XLogFileName(xlogfilename, starttli, startsegno, WalSegSz);
     732             : 
     733             :     /*
     734             :      * Construct backup label file
     735             :      */
     736          20 :     stamp_time = time(NULL);
     737          20 :     tmp = localtime(&stamp_time);
     738          20 :     strftime(strfbuf, sizeof(strfbuf), "%Y-%m-%d %H:%M:%S %Z", tmp);
     739             : 
     740          60 :     len = snprintf(buf, sizeof(buf),
     741             :                    "START WAL LOCATION: %X/%X (file %s)\n"
     742             :                    "CHECKPOINT LOCATION: %X/%X\n"
     743             :                    "BACKUP METHOD: pg_rewind\n"
     744             :                    "BACKUP FROM: standby\n"
     745             :                    "START TIME: %s\n",
     746             :     /* omit LABEL: line */
     747          20 :                    (uint32) (startpoint >> 32), (uint32) startpoint, xlogfilename,
     748          20 :                    (uint32) (checkpointloc >> 32), (uint32) checkpointloc,
     749             :                    strfbuf);
     750          20 :     if (len >= sizeof(buf))
     751           0 :         pg_fatal("backup label buffer too small");    /* shouldn't happen */
     752             : 
     753             :     /* TODO: move old file out of the way, if any. */
     754          20 :     open_target_file("backup_label", true); /* BACKUP_LABEL_FILE */
     755          20 :     write_target_range(buf, 0, len);
     756          20 :     close_target_file();
     757          20 : }
     758             : 
     759             : /*
     760             :  * Check CRC of control file
     761             :  */
     762             : static void
     763          72 : checkControlFile(ControlFileData *ControlFile)
     764             : {
     765             :     pg_crc32c   crc;
     766             : 
     767             :     /* Calculate CRC */
     768          72 :     INIT_CRC32C(crc);
     769          72 :     COMP_CRC32C(crc, (char *) ControlFile, offsetof(ControlFileData, crc));
     770          72 :     FIN_CRC32C(crc);
     771             : 
     772             :     /* And simply compare it */
     773          72 :     if (!EQ_CRC32C(crc, ControlFile->crc))
     774           0 :         pg_fatal("unexpected control file CRC");
     775          72 : }
     776             : 
     777             : /*
     778             :  * Verify control file contents in the buffer src, and copy it to *ControlFile.
     779             :  */
     780             : static void
     781          72 : digestControlFile(ControlFileData *ControlFile, char *src, size_t size)
     782             : {
     783          72 :     if (size != PG_CONTROL_FILE_SIZE)
     784           0 :         pg_fatal("unexpected control file size %d, expected %d",
     785             :                  (int) size, PG_CONTROL_FILE_SIZE);
     786             : 
     787          72 :     memcpy(ControlFile, src, sizeof(ControlFileData));
     788             : 
     789             :     /* set and validate WalSegSz */
     790          72 :     WalSegSz = ControlFile->xlog_seg_size;
     791             : 
     792          72 :     if (!IsValidWalSegSize(WalSegSz))
     793           0 :         pg_fatal(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte",
     794             :                           "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes",
     795             :                           WalSegSz),
     796             :                  WalSegSz);
     797             : 
     798             :     /* Additional checks on control file */
     799          72 :     checkControlFile(ControlFile);
     800          72 : }
     801             : 
     802             : /*
     803             :  * Sync target data directory to ensure that modifications are safely on disk.
     804             :  *
     805             :  * We do this once, for the whole data directory, for performance reasons.  At
     806             :  * the end of pg_rewind's run, the kernel is likely to already have flushed
     807             :  * most dirty buffers to disk.  Additionally fsync_pgdata uses a two-pass
     808             :  * approach (only initiating writeback in the first pass), which often reduces
     809             :  * the overall amount of IO noticeably.
     810             :  */
     811             : static void
     812          20 : syncTargetDirectory(void)
     813             : {
     814          20 :     if (!do_sync || dry_run)
     815          20 :         return;
     816             : 
     817           0 :     fsync_pgdata(datadir_target, PG_VERSION_NUM);
     818             : }
     819             : 
     820             : /*
     821             :  * Get value of GUC parameter restore_command from the target cluster.
     822             :  *
     823             :  * This uses a logic based on "postgres -C" to get the value from the
     824             :  * cluster.
     825             :  */
     826             : static void
     827          28 : getRestoreCommand(const char *argv0)
     828             : {
     829             :     int         rc;
     830             :     char        postgres_exec_path[MAXPGPATH],
     831             :                 postgres_cmd[MAXPGPATH],
     832             :                 cmd_output[MAXPGPATH];
     833             : 
     834          28 :     if (!restore_wal)
     835          26 :         return;
     836             : 
     837             :     /* find postgres executable */
     838           2 :     rc = find_other_exec(argv0, "postgres",
     839             :                          PG_BACKEND_VERSIONSTR,
     840             :                          postgres_exec_path);
     841             : 
     842           2 :     if (rc < 0)
     843             :     {
     844             :         char        full_path[MAXPGPATH];
     845             : 
     846           0 :         if (find_my_exec(argv0, full_path) < 0)
     847           0 :             strlcpy(full_path, progname, sizeof(full_path));
     848             : 
     849           0 :         if (rc == -1)
     850           0 :             pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
     851             :                          "same directory as \"%s\".\n"
     852             :                          "Check your installation.",
     853             :                          "postgres", progname, full_path);
     854             :         else
     855           0 :             pg_log_error("The program \"%s\" was found by \"%s\"\n"
     856             :                          "but was not the same version as %s.\n"
     857             :                          "Check your installation.",
     858             :                          "postgres", full_path, progname);
     859           0 :         exit(1);
     860             :     }
     861             : 
     862             :     /*
     863             :      * Build a command able to retrieve the value of GUC parameter
     864             :      * restore_command, if set.
     865             :      */
     866           2 :     snprintf(postgres_cmd, sizeof(postgres_cmd),
     867             :              "\"%s\" -D \"%s\" -C restore_command",
     868             :              postgres_exec_path, datadir_target);
     869             : 
     870           2 :     if (!pipe_read_line(postgres_cmd, cmd_output, sizeof(cmd_output)))
     871           0 :         exit(1);
     872             : 
     873           2 :     (void) pg_strip_crlf(cmd_output);
     874             : 
     875           2 :     if (strcmp(cmd_output, "") == 0)
     876           0 :         pg_fatal("restore_command is not set in the target cluster");
     877             : 
     878           2 :     restore_command = pg_strdup(cmd_output);
     879             : 
     880           2 :     pg_log_debug("using for rewind restore_command = \'%s\'",
     881             :                  restore_command);
     882             : }
     883             : 
     884             : 
     885             : /*
     886             :  * Ensure clean shutdown of target instance by launching single-user mode
     887             :  * postgres to do crash recovery.
     888             :  */
     889             : static void
     890          20 : ensureCleanShutdown(const char *argv0)
     891             : {
     892             :     int         ret;
     893             : #define MAXCMDLEN (2 * MAXPGPATH)
     894             :     char        exec_path[MAXPGPATH];
     895             :     char        cmd[MAXCMDLEN];
     896             : 
     897             :     /* locate postgres binary */
     898          20 :     if ((ret = find_other_exec(argv0, "postgres",
     899             :                                PG_BACKEND_VERSIONSTR,
     900             :                                exec_path)) < 0)
     901             :     {
     902             :         char        full_path[MAXPGPATH];
     903             : 
     904           0 :         if (find_my_exec(argv0, full_path) < 0)
     905           0 :             strlcpy(full_path, progname, sizeof(full_path));
     906             : 
     907           0 :         if (ret == -1)
     908           0 :             pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
     909             :                      "same directory as \"%s\".\n"
     910             :                      "Check your installation.",
     911             :                      "postgres", progname, full_path);
     912             :         else
     913           0 :             pg_fatal("The program \"%s\" was found by \"%s\"\n"
     914             :                      "but was not the same version as %s.\n"
     915             :                      "Check your installation.",
     916             :                      "postgres", full_path, progname);
     917             :     }
     918             : 
     919          20 :     pg_log_info("executing \"%s\" for target server to complete crash recovery",
     920             :                 exec_path);
     921             : 
     922             :     /*
     923             :      * Skip processing if requested, but only after ensuring presence of
     924             :      * postgres.
     925             :      */
     926          20 :     if (dry_run)
     927           0 :         return;
     928             : 
     929             :     /*
     930             :      * Finally run postgres in single-user mode.  There is no need to use
     931             :      * fsync here.  This makes the recovery faster, and the target data folder
     932             :      * is synced at the end anyway.
     933             :      */
     934          20 :     snprintf(cmd, MAXCMDLEN, "\"%s\" --single -F -D \"%s\" template1 < \"%s\"",
     935             :              exec_path, datadir_target, DEVNULL);
     936             : 
     937          20 :     if (system(cmd) != 0)
     938             :     {
     939           2 :         pg_log_error("postgres single-user mode in target cluster failed");
     940           2 :         pg_fatal("Command was: %s", cmd);
     941             :     }
     942             : }
     943             : 
     944             : static void
     945          28 : disconnect_atexit(void)
     946             : {
     947          28 :     if (conn != NULL)
     948           8 :         PQfinish(conn);
     949          28 : }

Generated by: LCOV version 1.13