LCOV - code coverage report
Current view: top level - src/bin/pg_rewind - pg_rewind.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 80.3 % 452 363
Test Date: 2026-10-07 10:15:49 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 66.3 % 261 173

             Branch data     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-2026, 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/restricted_token.h"
      24                 :             : #include "common/string.h"
      25                 :             : #include "fe_utils/option_utils.h"
      26                 :             : #include "fe_utils/recovery_gen.h"
      27                 :             : #include "fe_utils/string_utils.h"
      28                 :             : #include "file_ops.h"
      29                 :             : #include "filemap.h"
      30                 :             : #include "getopt_long.h"
      31                 :             : #include "pg_rewind.h"
      32                 :             : #include "rewind_source.h"
      33                 :             : #include "storage/bufpage.h"
      34                 :             : 
      35                 :             : static void usage(const char *progname);
      36                 :             : 
      37                 :             : static void perform_rewind(filemap_t *filemap, rewind_source *source,
      38                 :             :                            XLogRecPtr chkptrec,
      39                 :             :                            TimeLineID chkpttli,
      40                 :             :                            XLogRecPtr chkptredo,
      41                 :             :                            XLogRecPtr divergerec);
      42                 :             : 
      43                 :             : static void createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli,
      44                 :             :                               XLogRecPtr checkpointloc);
      45                 :             : 
      46                 :             : static void digestControlFile(ControlFileData *ControlFile,
      47                 :             :                               const char *content, size_t size);
      48                 :             : static void getRestoreCommand(const char *argv0);
      49                 :             : static void sanityChecks(void);
      50                 :             : static TimeLineHistoryEntry *getTimelineHistory(TimeLineID tli, bool is_source,
      51                 :             :                                                 int *nentries);
      52                 :             : static void findCommonAncestorTimeline(TimeLineHistoryEntry *a_history,
      53                 :             :                                        int a_nentries,
      54                 :             :                                        TimeLineHistoryEntry *b_history,
      55                 :             :                                        int b_nentries,
      56                 :             :                                        XLogRecPtr *recptr, int *tliIndex);
      57                 :             : static void ensureCleanShutdown(const char *argv0);
      58                 :             : static void disconnect_atexit(void);
      59                 :             : 
      60                 :             : static ControlFileData ControlFile_target;
      61                 :             : static ControlFileData ControlFile_source;
      62                 :             : static ControlFileData ControlFile_source_after;
      63                 :             : 
      64                 :             : static const char *progname;
      65                 :             : int         WalSegSz;
      66                 :             : 
      67                 :             : /* Configuration options */
      68                 :             : char       *datadir_target = NULL;
      69                 :             : static char *datadir_source = NULL;
      70                 :             : static char *connstr_source = NULL;
      71                 :             : static char *restore_command = NULL;
      72                 :             : static char *config_file = NULL;
      73                 :             : 
      74                 :             : static bool debug = false;
      75                 :             : bool        showprogress = false;
      76                 :             : bool        dry_run = false;
      77                 :             : bool        do_sync = true;
      78                 :             : static bool restore_wal = false;
      79                 :             : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
      80                 :             : 
      81                 :             : /* Target history */
      82                 :             : TimeLineHistoryEntry *targetHistory;
      83                 :             : int         targetNentries;
      84                 :             : 
      85                 :             : /* Progress counters */
      86                 :             : uint64      fetch_size;
      87                 :             : uint64      fetch_done;
      88                 :             : 
      89                 :             : static PGconn *conn;
      90                 :             : static rewind_source *source;
      91                 :             : 
      92                 :             : static void
      93                 :           1 : usage(const char *progname)
      94                 :             : {
      95                 :           1 :     printf(_("%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n\n"), progname);
      96                 :           1 :     printf(_("Usage:\n  %s [OPTION]...\n\n"), progname);
      97                 :           1 :     printf(_("Options:\n"));
      98                 :           1 :     printf(_("  -c, --restore-target-wal       use \"restore_command\" in target configuration to\n"
      99                 :             :              "                                 retrieve WAL files from archives\n"));
     100                 :           1 :     printf(_("  -D, --target-pgdata=DIRECTORY  existing data directory to modify\n"));
     101                 :           1 :     printf(_("      --source-pgdata=DIRECTORY  source data directory to synchronize with\n"));
     102                 :           1 :     printf(_("      --source-server=CONNSTR    source server to synchronize with\n"));
     103                 :           1 :     printf(_("  -n, --dry-run                  stop before modifying anything\n"));
     104                 :           1 :     printf(_("  -N, --no-sync                  do not wait for changes to be written\n"
     105                 :             :              "                                 safely to disk\n"));
     106                 :           1 :     printf(_("  -P, --progress                 write progress messages\n"));
     107                 :           1 :     printf(_("  -R, --write-recovery-conf      write configuration for replication\n"
     108                 :             :              "                                 (requires --source-server)\n"));
     109                 :           1 :     printf(_("      --config-file=FILENAME     use specified main server configuration\n"
     110                 :             :              "                                 file when running target cluster\n"));
     111                 :           1 :     printf(_("      --debug                    write a lot of debug messages\n"));
     112                 :           1 :     printf(_("      --no-ensure-shutdown       do not automatically fix unclean shutdown\n"));
     113                 :           1 :     printf(_("      --sync-method=METHOD       set method for syncing files to disk\n"));
     114                 :           1 :     printf(_("  -V, --version                  output version information, then exit\n"));
     115                 :           1 :     printf(_("  -?, --help                     show this help, then exit\n"));
     116                 :           1 :     printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
     117                 :           1 :     printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
     118                 :           1 : }
     119                 :             : 
     120                 :             : 
     121                 :             : int
     122                 :          28 : main(int argc, char **argv)
     123                 :             : {
     124                 :             :     static struct option long_options[] = {
     125                 :             :         {"help", no_argument, NULL, '?'},
     126                 :             :         {"target-pgdata", required_argument, NULL, 'D'},
     127                 :             :         {"write-recovery-conf", no_argument, NULL, 'R'},
     128                 :             :         {"source-pgdata", required_argument, NULL, 1},
     129                 :             :         {"source-server", required_argument, NULL, 2},
     130                 :             :         {"no-ensure-shutdown", no_argument, NULL, 4},
     131                 :             :         {"config-file", required_argument, NULL, 5},
     132                 :             :         {"version", no_argument, NULL, 'V'},
     133                 :             :         {"restore-target-wal", no_argument, NULL, 'c'},
     134                 :             :         {"dry-run", no_argument, NULL, 'n'},
     135                 :             :         {"no-sync", no_argument, NULL, 'N'},
     136                 :             :         {"progress", no_argument, NULL, 'P'},
     137                 :             :         {"debug", no_argument, NULL, 3},
     138                 :             :         {"sync-method", required_argument, NULL, 6},
     139                 :             :         {NULL, 0, NULL, 0}
     140                 :             :     };
     141                 :             :     int         option_index;
     142                 :             :     int         c;
     143                 :             :     XLogRecPtr  divergerec;
     144                 :             :     int         lastcommontliIndex;
     145                 :             :     XLogRecPtr  chkptrec;
     146                 :             :     TimeLineID  chkpttli;
     147                 :             :     XLogRecPtr  chkptredo;
     148                 :             :     uint32      chkptdatachecksums;
     149                 :             :     TimeLineID  source_tli;
     150                 :             :     TimeLineID  target_tli;
     151                 :             :     XLogRecPtr  target_wal_endrec;
     152                 :             :     XLogSegNo   last_common_segno;
     153                 :             :     size_t      size;
     154                 :             :     char       *buffer;
     155                 :          28 :     bool        no_ensure_shutdown = false;
     156                 :             :     bool        rewind_needed;
     157                 :          28 :     bool        writerecoveryconf = false;
     158                 :             :     filemap_t  *filemap;
     159                 :             : 
     160                 :          28 :     pg_logging_init(argv[0]);
     161                 :          28 :     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_rewind"));
     162                 :          28 :     progname = get_progname(argv[0]);
     163                 :             : 
     164                 :             :     /* Process command-line arguments */
     165         [ +  - ]:          28 :     if (argc > 1)
     166                 :             :     {
     167   [ +  +  -  + ]:          28 :         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
     168                 :             :         {
     169                 :           1 :             usage(progname);
     170                 :           1 :             exit(0);
     171                 :             :         }
     172   [ +  +  -  + ]:          27 :         if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
     173                 :             :         {
     174                 :           1 :             puts("pg_rewind (PostgreSQL) " PG_VERSION);
     175                 :           1 :             exit(0);
     176                 :             :         }
     177                 :             :     }
     178                 :             : 
     179         [ +  + ]:         137 :     while ((c = getopt_long(argc, argv, "cD:nNPR", long_options, &option_index)) != -1)
     180                 :             :     {
     181   [ +  -  +  +  :         112 :         switch (c)
          +  +  +  +  +  
             +  +  -  + ]
     182                 :             :         {
     183                 :           1 :             case 'c':
     184                 :           1 :                 restore_wal = true;
     185                 :           1 :                 break;
     186                 :             : 
     187                 :           0 :             case 'P':
     188                 :           0 :                 showprogress = true;
     189                 :           0 :                 break;
     190                 :             : 
     191                 :           1 :             case 'n':
     192                 :           1 :                 dry_run = true;
     193                 :           1 :                 break;
     194                 :             : 
     195                 :          18 :             case 'N':
     196                 :          18 :                 do_sync = false;
     197                 :          18 :                 break;
     198                 :             : 
     199                 :           6 :             case 'R':
     200                 :           6 :                 writerecoveryconf = true;
     201                 :           6 :                 break;
     202                 :             : 
     203                 :          22 :             case 3:
     204                 :          22 :                 debug = true;
     205                 :          22 :                 pg_logging_increase_verbosity();
     206                 :          22 :                 break;
     207                 :             : 
     208                 :          25 :             case 'D':           /* -D or --target-pgdata */
     209                 :          25 :                 datadir_target = pg_strdup(optarg);
     210                 :          25 :                 break;
     211                 :             : 
     212                 :          16 :             case 1:             /* --source-pgdata */
     213                 :          16 :                 datadir_source = pg_strdup(optarg);
     214                 :          16 :                 break;
     215                 :             : 
     216                 :           9 :             case 2:             /* --source-server */
     217                 :           9 :                 connstr_source = pg_strdup(optarg);
     218                 :           9 :                 break;
     219                 :             : 
     220                 :           3 :             case 4:
     221                 :           3 :                 no_ensure_shutdown = true;
     222                 :           3 :                 break;
     223                 :             : 
     224                 :          10 :             case 5:
     225                 :          10 :                 config_file = pg_strdup(optarg);
     226                 :          10 :                 break;
     227                 :             : 
     228                 :           0 :             case 6:
     229         [ #  # ]:           0 :                 if (!parse_sync_method(optarg, &sync_method))
     230                 :           0 :                     exit(1);
     231                 :           0 :                 break;
     232                 :             : 
     233                 :           1 :             default:
     234                 :             :                 /* getopt_long already emitted a complaint */
     235                 :           1 :                 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     236                 :           1 :                 exit(1);
     237                 :             :         }
     238                 :             :     }
     239                 :             : 
     240   [ +  +  +  + ]:          25 :     if (datadir_source == NULL && connstr_source == NULL)
     241                 :             :     {
     242                 :           1 :         pg_log_error("no source specified (--source-pgdata or --source-server)");
     243                 :           1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     244                 :           1 :         exit(1);
     245                 :             :     }
     246                 :             : 
     247   [ +  +  +  + ]:          24 :     if (datadir_source != NULL && connstr_source != NULL)
     248                 :             :     {
     249                 :           1 :         pg_log_error("only one of --source-pgdata or --source-server can be specified");
     250                 :           1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     251                 :           1 :         exit(1);
     252                 :             :     }
     253                 :             : 
     254         [ -  + ]:          23 :     if (datadir_target == NULL)
     255                 :             :     {
     256                 :           0 :         pg_log_error("no target data directory specified (--target-pgdata)");
     257                 :           0 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     258                 :           0 :         exit(1);
     259                 :             :     }
     260                 :             : 
     261   [ +  +  +  + ]:          23 :     if (writerecoveryconf && connstr_source == NULL)
     262                 :             :     {
     263                 :           1 :         pg_log_error("no source server information (--source-server) specified for --write-recovery-conf");
     264                 :           1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     265                 :           1 :         exit(1);
     266                 :             :     }
     267                 :             : 
     268         [ +  + ]:          22 :     if (optind < argc)
     269                 :             :     {
     270                 :           1 :         pg_log_error("too many command-line arguments (first is \"%s\")",
     271                 :             :                      argv[optind]);
     272                 :           1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     273                 :           1 :         exit(1);
     274                 :             :     }
     275                 :             : 
     276                 :             :     /*
     277                 :             :      * Don't allow pg_rewind to be run as root, to avoid overwriting the
     278                 :             :      * ownership of files in the data directory. We need only check for root
     279                 :             :      * -- any other user won't have sufficient permissions to modify files in
     280                 :             :      * the data directory.
     281                 :             :      */
     282                 :             : #ifndef WIN32
     283         [ -  + ]:          21 :     if (geteuid() == 0)
     284                 :             :     {
     285                 :           0 :         pg_log_error("cannot be executed by \"root\"");
     286                 :           0 :         pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
     287                 :             :                           progname);
     288                 :           0 :         exit(1);
     289                 :             :     }
     290                 :             : #endif
     291                 :             : 
     292                 :          21 :     get_restricted_token();
     293                 :             : 
     294                 :             :     /* Set mask based on PGDATA permissions */
     295         [ -  + ]:          21 :     if (!GetDataDirectoryCreatePerm(datadir_target))
     296                 :           0 :         pg_fatal("could not read permissions of directory \"%s\": %m",
     297                 :             :                  datadir_target);
     298                 :             : 
     299                 :          21 :     umask(pg_mode_mask);
     300                 :             : 
     301                 :          21 :     getRestoreCommand(argv[0]);
     302                 :             : 
     303                 :          21 :     atexit(disconnect_atexit);
     304                 :             : 
     305                 :             :     /* Ok, we have all the options and we're ready to start. */
     306         [ +  + ]:          21 :     if (dry_run)
     307                 :             :     {
     308                 :           1 :         pg_log_info("executing in dry-run mode");
     309                 :           1 :         pg_log_info_detail("The target directory will not be modified.");
     310                 :             :     }
     311                 :             : 
     312                 :             :     /* First, connect to remote server. */
     313         [ +  + ]:          21 :     if (connstr_source)
     314                 :             :     {
     315                 :           8 :         conn = PQconnectdb(connstr_source);
     316                 :             : 
     317         [ -  + ]:           8 :         if (PQstatus(conn) == CONNECTION_BAD)
     318                 :           0 :             pg_fatal("%s", PQerrorMessage(conn));
     319                 :             : 
     320         [ -  + ]:           8 :         if (showprogress)
     321                 :           0 :             pg_log_info("connected to server");
     322                 :             : 
     323                 :           8 :         source = init_libpq_source(conn);
     324                 :             :     }
     325                 :             :     else
     326                 :          13 :         source = init_local_source(datadir_source);
     327                 :             : 
     328                 :             :     /*
     329                 :             :      * Check the status of the target instance.
     330                 :             :      *
     331                 :             :      * If the target instance was not cleanly shut down, start and stop the
     332                 :             :      * target cluster once in single-user mode to enforce recovery to finish,
     333                 :             :      * ensuring that the cluster can be used by pg_rewind.  Note that if
     334                 :             :      * no_ensure_shutdown is specified, pg_rewind ignores this step, and users
     335                 :             :      * need to make sure by themselves that the target cluster is in a clean
     336                 :             :      * state.
     337                 :             :      */
     338                 :          21 :     buffer = slurpFile(datadir_target, XLOG_CONTROL_FILE, &size);
     339                 :          21 :     digestControlFile(&ControlFile_target, buffer, size);
     340                 :          21 :     pg_free(buffer);
     341                 :             : 
     342         [ +  + ]:          21 :     if (!no_ensure_shutdown &&
     343         [ +  + ]:          18 :         ControlFile_target.state != DB_SHUTDOWNED &&
     344         [ +  + ]:          11 :         ControlFile_target.state != DB_SHUTDOWNED_IN_RECOVERY)
     345                 :             :     {
     346                 :          10 :         ensureCleanShutdown(argv[0]);
     347                 :             : 
     348                 :           9 :         buffer = slurpFile(datadir_target, XLOG_CONTROL_FILE, &size);
     349                 :           9 :         digestControlFile(&ControlFile_target, buffer, size);
     350                 :           9 :         pg_free(buffer);
     351                 :             :     }
     352                 :             : 
     353                 :          20 :     buffer = source->fetch_file(source, XLOG_CONTROL_FILE, &size);
     354                 :          20 :     digestControlFile(&ControlFile_source, buffer, size);
     355                 :          20 :     pg_free(buffer);
     356                 :             : 
     357                 :          20 :     sanityChecks();
     358                 :             : 
     359                 :             :     /*
     360                 :             :      * Usually, the TLI can be found in the latest checkpoint record. But if
     361                 :             :      * the source server is just being promoted (or it's a standby that's
     362                 :             :      * following a primary that's just being promoted), and the checkpoint
     363                 :             :      * requested by the promotion hasn't completed yet, the latest timeline is
     364                 :             :      * in minRecoveryPoint. So we check which is later, the TLI of the
     365                 :             :      * minRecoveryPoint or the latest checkpoint.
     366                 :             :      */
     367                 :          18 :     source_tli = Max(ControlFile_source.minRecoveryPointTLI,
     368                 :             :                      ControlFile_source.checkPointCopy.ThisTimeLineID);
     369                 :             : 
     370                 :             :     /* Similarly for the target. */
     371                 :          18 :     target_tli = Max(ControlFile_target.minRecoveryPointTLI,
     372                 :             :                      ControlFile_target.checkPointCopy.ThisTimeLineID);
     373                 :             : 
     374                 :             :     /*
     375                 :             :      * Find the common ancestor timeline between the clusters.
     376                 :             :      *
     377                 :             :      * If both clusters are already on the same timeline, there's nothing to
     378                 :             :      * do.
     379                 :             :      */
     380         [ +  + ]:          18 :     if (target_tli == source_tli)
     381                 :             :     {
     382                 :           1 :         pg_log_info("source and target cluster are on the same timeline");
     383                 :           1 :         rewind_needed = false;
     384                 :           1 :         target_wal_endrec = InvalidXLogRecPtr;
     385                 :             :     }
     386                 :             :     else
     387                 :             :     {
     388                 :             :         XLogRecPtr  chkptendrec;
     389                 :             :         TimeLineHistoryEntry *sourceHistory;
     390                 :             :         int         sourceNentries;
     391                 :             : 
     392                 :             :         /*
     393                 :             :          * Retrieve timelines for both source and target, and find the point
     394                 :             :          * where they diverged.
     395                 :             :          */
     396                 :          17 :         sourceHistory = getTimelineHistory(source_tli, true, &sourceNentries);
     397                 :          17 :         targetHistory = getTimelineHistory(target_tli, false, &targetNentries);
     398                 :             : 
     399                 :          17 :         findCommonAncestorTimeline(sourceHistory, sourceNentries,
     400                 :             :                                    targetHistory, targetNentries,
     401                 :             :                                    &divergerec, &lastcommontliIndex);
     402                 :             : 
     403                 :          17 :         pg_log_info("servers diverged at WAL location %X/%08X on timeline %u",
     404                 :             :                     LSN_FORMAT_ARGS(divergerec),
     405                 :             :                     targetHistory[lastcommontliIndex].tli);
     406                 :             : 
     407                 :             :         /*
     408                 :             :          * Convert the divergence LSN to a segment number, that will be used
     409                 :             :          * to decide how WAL segments should be processed.
     410                 :             :          */
     411                 :          17 :         XLByteToSeg(divergerec, last_common_segno, ControlFile_target.xlog_seg_size);
     412                 :             : 
     413                 :             :         /*
     414                 :             :          * Don't need the source history anymore. The target history is still
     415                 :             :          * needed by the routines in parsexlog.c, when we read the target WAL.
     416                 :             :          */
     417                 :          17 :         pfree(sourceHistory);
     418                 :             : 
     419                 :             : 
     420                 :             :         /*
     421                 :             :          * Determine the end-of-WAL on the target.
     422                 :             :          *
     423                 :             :          * The WAL ends at the last shutdown checkpoint, or at
     424                 :             :          * minRecoveryPoint if it was a standby. (If we supported rewinding a
     425                 :             :          * server that was not shut down cleanly, we would need to replay
     426                 :             :          * until we reach the first invalid record, like crash recovery does.)
     427                 :             :          */
     428                 :             : 
     429                 :             :         /* read the checkpoint record on the target to see where it ends. */
     430                 :          17 :         chkptendrec = readOneRecord(datadir_target,
     431                 :             :                                     ControlFile_target.checkPoint,
     432                 :             :                                     targetNentries - 1,
     433                 :             :                                     restore_command);
     434                 :             : 
     435         [ +  + ]:          17 :         if (ControlFile_target.minRecoveryPoint > chkptendrec)
     436                 :             :         {
     437                 :           1 :             target_wal_endrec = ControlFile_target.minRecoveryPoint;
     438                 :             :         }
     439                 :             :         else
     440                 :             :         {
     441                 :          16 :             target_wal_endrec = chkptendrec;
     442                 :             :         }
     443                 :             : 
     444                 :             :         /*
     445                 :             :          * Check for the possibility that the target is in fact a direct
     446                 :             :          * ancestor of the source. In that case, there is no divergent history
     447                 :             :          * in the target that needs rewinding.
     448                 :             :          */
     449         [ +  - ]:          17 :         if (target_wal_endrec > divergerec)
     450                 :             :         {
     451                 :          17 :             rewind_needed = true;
     452                 :             :         }
     453                 :             :         else
     454                 :             :         {
     455                 :             :             /* the last common checkpoint record must be part of target WAL */
     456                 :             :             Assert(target_wal_endrec == divergerec);
     457                 :             : 
     458                 :           0 :             rewind_needed = false;
     459                 :             :         }
     460                 :             :     }
     461                 :             : 
     462         [ +  + ]:          18 :     if (!rewind_needed)
     463                 :             :     {
     464                 :           1 :         pg_log_info("no rewind required");
     465   [ -  +  -  - ]:           1 :         if (writerecoveryconf && !dry_run)
     466                 :           0 :             WriteRecoveryConfig(conn, datadir_target,
     467                 :             :                                 GenerateRecoveryConfig(conn, NULL,
     468                 :             :                                                        GetDbnameFromConnectionOptions(connstr_source)));
     469                 :           1 :         exit(0);
     470                 :             :     }
     471                 :             : 
     472                 :             :     /* Initialize hashtable that tracks WAL files protected from removal */
     473                 :          17 :     keepwal_init();
     474                 :             : 
     475                 :          17 :     findLastCheckpoint(datadir_target, divergerec, lastcommontliIndex,
     476                 :             :                        &chkptrec, &chkpttli, &chkptredo, &chkptdatachecksums,
     477                 :             :                        restore_command);
     478                 :          17 :     pg_log_info("rewinding from last common checkpoint at %X/%08X on timeline %u",
     479                 :             :                 LSN_FORMAT_ARGS(chkptrec), chkpttli);
     480                 :             : 
     481                 :             :     /*
     482                 :             :      * Replay on the rewound server resumes from the last common checkpoint
     483                 :             :      * and adopts the data checksum state recorded there, not the state in the
     484                 :             :      * control file installed from the source.  If checksums were enabled at
     485                 :             :      * the divergence point but the source runs without them, the rewound
     486                 :             :      * server would verify checksums while the blocks copied from the source
     487                 :             :      * have none.  The control files cannot reveal this: an offline disable on
     488                 :             :      * the source after the divergence leaves both of them saying "off".
     489                 :             :      *
     490                 :             :      * Only the fully enabled state needs checking.  An in-progress state at
     491                 :             :      * the divergence point means a transition was still running there, and
     492                 :             :      * all of its page rewrites are logged after that point, so replay brings
     493                 :             :      * the rewound server to whatever state the copied WAL ends in.
     494                 :             :      */
     495         [ +  + ]:          17 :     if (chkptdatachecksums == PG_DATA_CHECKSUM_VERSION &&
     496         [ -  + ]:          16 :         ControlFile_source.data_checksum_version != PG_DATA_CHECKSUM_VERSION)
     497                 :             :     {
     498                 :           0 :         pg_log_error("data checksums were enabled at the point of divergence but are disabled on the source server");
     499                 :           0 :         pg_log_error_detail("Blocks copied from the source would have no checksums, but the rewound server would resume with checksum verification enabled.");
     500                 :           0 :         pg_log_error_hint("Either enable data checksums on the source server or recreate the target server from a base backup.");
     501                 :           0 :         exit(1);
     502                 :             :     }
     503                 :             : 
     504                 :             :     /*
     505                 :             :      * The same comparison is needed against the target: every block the
     506                 :             :      * rewind does not copy keeps the target's content.  The control files
     507                 :             :      * cannot reveal this case either, in the other direction: a standby's
     508                 :             :      * checkpoints are written by its upstream primary, so a standby whose
     509                 :             :      * checksums were disabled offline still has "on" checkpoints in its WAL,
     510                 :             :      * while both control files may agree.
     511                 :             :      */
     512         [ +  + ]:          17 :     if (chkptdatachecksums == PG_DATA_CHECKSUM_VERSION &&
     513         [ -  + ]:          16 :         ControlFile_target.data_checksum_version != PG_DATA_CHECKSUM_VERSION)
     514                 :             :     {
     515                 :           0 :         pg_log_error("data checksums were enabled at the point of divergence but are disabled on the target server");
     516                 :           0 :         pg_log_error_detail("Blocks kept from the target would have no checksums, but the rewound server would resume with checksum verification enabled.");
     517                 :           0 :         pg_log_error_hint("Either enable data checksums on the target server with pg_checksums or recreate the target server from a base backup.");
     518                 :           0 :         exit(1);
     519                 :             :     }
     520                 :             : 
     521                 :             :     /* Initialize the hash table to track the status of each file */
     522                 :          17 :     filehash_init();
     523                 :             : 
     524                 :             :     /*
     525                 :             :      * Collect information about all files in the both data directories.
     526                 :             :      */
     527         [ -  + ]:          17 :     if (showprogress)
     528                 :           0 :         pg_log_info("reading source file list");
     529                 :          17 :     source->traverse_files(source, &process_source_file);
     530                 :             : 
     531         [ -  + ]:          17 :     if (showprogress)
     532                 :           0 :         pg_log_info("reading target file list");
     533                 :          17 :     traverse_datadir(datadir_target, &process_target_file);
     534                 :             : 
     535                 :             :     /*
     536                 :             :      * Read the target WAL from last checkpoint before the point of fork, to
     537                 :             :      * extract all the pages that were modified on the target cluster after
     538                 :             :      * the fork.
     539                 :             :      */
     540         [ -  + ]:          17 :     if (showprogress)
     541                 :           0 :         pg_log_info("reading WAL in target");
     542                 :          17 :     extractPageMap(datadir_target, chkptrec, lastcommontliIndex,
     543                 :             :                    target_wal_endrec, restore_command);
     544                 :             : 
     545                 :             :     /*
     546                 :             :      * We have collected all information we need from both systems. Decide
     547                 :             :      * what to do with each file.
     548                 :             :      */
     549                 :          17 :     filemap = decide_file_actions(last_common_segno);
     550         [ -  + ]:          17 :     if (showprogress)
     551                 :           0 :         calculate_totals(filemap);
     552                 :             : 
     553                 :             :     /* this is too verbose even for verbose mode */
     554         [ +  + ]:          17 :     if (debug)
     555                 :          15 :         print_filemap(filemap);
     556                 :             : 
     557                 :             :     /*
     558                 :             :      * Ok, we're ready to start copying things over.
     559                 :             :      */
     560         [ -  + ]:          17 :     if (showprogress)
     561                 :             :     {
     562                 :           0 :         pg_log_info("need to copy %" PRIu64 " MB (total source directory size is %" PRIu64 " MB)",
     563                 :             :                     filemap->fetch_size / (1024 * 1024),
     564                 :             :                     filemap->total_size / (1024 * 1024));
     565                 :             : 
     566                 :           0 :         fetch_size = filemap->fetch_size;
     567                 :           0 :         fetch_done = 0;
     568                 :             :     }
     569                 :             : 
     570                 :             :     /*
     571                 :             :      * We have now collected all the information we need from both systems,
     572                 :             :      * and we are ready to start modifying the target directory.
     573                 :             :      *
     574                 :             :      * This is the point of no return. Once we start copying things, there is
     575                 :             :      * no turning back!
     576                 :             :      */
     577                 :          17 :     perform_rewind(filemap, source, chkptrec, chkpttli, chkptredo,
     578                 :             :                    divergerec);
     579                 :             : 
     580         [ -  + ]:          16 :     if (showprogress)
     581                 :           0 :         pg_log_info("syncing target data directory");
     582                 :          16 :     sync_target_dir();
     583                 :             : 
     584                 :             :     /* Also update the standby configuration, if requested. */
     585   [ +  +  +  - ]:          16 :     if (writerecoveryconf && !dry_run)
     586                 :           5 :         WriteRecoveryConfig(conn, datadir_target,
     587                 :             :                             GenerateRecoveryConfig(conn, NULL,
     588                 :             :                                                    GetDbnameFromConnectionOptions(connstr_source)));
     589                 :             : 
     590                 :             :     /* don't need the source connection anymore */
     591                 :          16 :     source->destroy(source);
     592         [ +  + ]:          16 :     if (conn)
     593                 :             :     {
     594                 :           8 :         PQfinish(conn);
     595                 :           8 :         conn = NULL;
     596                 :             :     }
     597                 :             : 
     598                 :          16 :     pg_log_info("Done!");
     599                 :             : 
     600                 :          16 :     return 0;
     601                 :             : }
     602                 :             : 
     603                 :             : /*
     604                 :             :  * Perform the rewind.
     605                 :             :  *
     606                 :             :  * We have already collected all the information we need from the
     607                 :             :  * target and the source.
     608                 :             :  */
     609                 :             : static void
     610                 :          17 : perform_rewind(filemap_t *filemap, rewind_source *source,
     611                 :             :                XLogRecPtr chkptrec,
     612                 :             :                TimeLineID chkpttli,
     613                 :             :                XLogRecPtr chkptredo,
     614                 :             :                XLogRecPtr divergerec)
     615                 :             : {
     616                 :             :     XLogRecPtr  endrec;
     617                 :             :     TimeLineID  endtli;
     618                 :             :     ControlFileData ControlFile_new;
     619                 :             :     size_t      size;
     620                 :             :     char       *buffer;
     621                 :             : 
     622                 :             :     /*
     623                 :             :      * Execute the actions in the file map, fetching data from the source
     624                 :             :      * system as needed.
     625                 :             :      */
     626         [ +  + ]:       18986 :     for (int i = 0; i < filemap->nentries; i++)
     627                 :             :     {
     628                 :       18970 :         file_entry_t *entry = filemap->entries[i];
     629                 :             : 
     630                 :             :         /*
     631                 :             :          * If this is a relation file, copy the modified blocks.
     632                 :             :          *
     633                 :             :          * This is in addition to any other changes.
     634                 :             :          */
     635         [ +  + ]:       18970 :         if (entry->target_pages_to_overwrite.bitmapsize > 0)
     636                 :             :         {
     637                 :             :             datapagemap_iterator_t *iter;
     638                 :             :             BlockNumber blkno;
     639                 :             :             off_t       offset;
     640                 :             : 
     641                 :         424 :             iter = datapagemap_iterate(&entry->target_pages_to_overwrite);
     642         [ +  + ]:        2108 :             while (datapagemap_next(iter, &blkno))
     643                 :             :             {
     644                 :        1684 :                 offset = blkno * BLCKSZ;
     645                 :        1684 :                 source->queue_fetch_range(source, entry->path, offset, BLCKSZ);
     646                 :             :             }
     647                 :         424 :             pg_free(iter);
     648                 :             :         }
     649                 :             : 
     650   [ +  +  +  +  :       18970 :         switch (entry->action)
             +  +  -  - ]
     651                 :             :         {
     652                 :       13019 :             case FILE_ACTION_NONE:
     653                 :             :                 /* nothing else to do */
     654                 :       13019 :                 break;
     655                 :             : 
     656                 :        5195 :             case FILE_ACTION_COPY:
     657                 :        5195 :                 source->queue_fetch_file(source, entry->path, entry->source_size);
     658                 :        5194 :                 break;
     659                 :             : 
     660                 :           5 :             case FILE_ACTION_TRUNCATE:
     661                 :           5 :                 truncate_target_file(entry->path, entry->source_size);
     662                 :           5 :                 break;
     663                 :             : 
     664                 :           6 :             case FILE_ACTION_COPY_TAIL:
     665                 :           6 :                 source->queue_fetch_range(source, entry->path,
     666                 :           6 :                                           entry->target_size,
     667                 :           6 :                                           entry->source_size - entry->target_size);
     668                 :           6 :                 break;
     669                 :             : 
     670                 :         736 :             case FILE_ACTION_REMOVE:
     671                 :         736 :                 remove_target(entry);
     672                 :         736 :                 break;
     673                 :             : 
     674                 :           9 :             case FILE_ACTION_CREATE:
     675                 :           9 :                 create_target(entry);
     676                 :           9 :                 break;
     677                 :             : 
     678                 :           0 :             case FILE_ACTION_UNDECIDED:
     679                 :           0 :                 pg_fatal("no action decided for file \"%s\"", entry->path);
     680                 :             :                 break;
     681                 :             :         }
     682                 :             :     }
     683                 :             : 
     684                 :             :     /* Complete any remaining range-fetches that we queued up above. */
     685                 :          16 :     source->finish_fetch(source);
     686                 :             : 
     687                 :          16 :     close_target_file();
     688                 :             : 
     689                 :          16 :     progress_report(true);
     690                 :             : 
     691                 :             :     /*
     692                 :             :      * Fetch the control file from the source last. This ensures that the
     693                 :             :      * minRecoveryPoint is up-to-date.
     694                 :             :      */
     695                 :          16 :     buffer = source->fetch_file(source, XLOG_CONTROL_FILE, &size);
     696                 :          16 :     digestControlFile(&ControlFile_source_after, buffer, size);
     697                 :          16 :     pg_free(buffer);
     698                 :             : 
     699                 :             :     /*
     700                 :             :      * Sanity check: If the source is a local system, the control file should
     701                 :             :      * not have changed since we started.
     702                 :             :      *
     703                 :             :      * XXX: We assume it hasn't been modified, but actually, what could go
     704                 :             :      * wrong? The logic handles a libpq source that's modified concurrently,
     705                 :             :      * why not a local datadir?
     706                 :             :      */
     707         [ +  + ]:          16 :     if (datadir_source &&
     708         [ -  + ]:           8 :         memcmp(&ControlFile_source, &ControlFile_source_after,
     709                 :             :                sizeof(ControlFileData)) != 0)
     710                 :             :     {
     711                 :           0 :         pg_fatal("source system was modified while pg_rewind was running");
     712                 :             :     }
     713                 :             : 
     714         [ -  + ]:          16 :     if (showprogress)
     715                 :           0 :         pg_log_info("creating backup label and updating control file");
     716                 :             : 
     717                 :             :     /*
     718                 :             :      * Create a backup label file, to tell the target where to begin the WAL
     719                 :             :      * replay. Normally, from the last common checkpoint between the source
     720                 :             :      * and the target. But if the source is a standby server, it's possible
     721                 :             :      * that the last common checkpoint is *after* the standby's restartpoint.
     722                 :             :      * That implies that the source server has applied the checkpoint record,
     723                 :             :      * but hasn't performed a corresponding restartpoint yet. Make sure we
     724                 :             :      * start at the restartpoint's redo point in that case.
     725                 :             :      *
     726                 :             :      * Use the old version of the source's control file for this. The server
     727                 :             :      * might have finished the restartpoint after we started copying files,
     728                 :             :      * but we must begin from the redo point at the time that started copying.
     729                 :             :      */
     730         [ +  + ]:          16 :     if (ControlFile_source.checkPointCopy.redo < chkptredo)
     731                 :             :     {
     732                 :           1 :         chkptredo = ControlFile_source.checkPointCopy.redo;
     733                 :           1 :         chkpttli = ControlFile_source.checkPointCopy.ThisTimeLineID;
     734                 :           1 :         chkptrec = ControlFile_source.checkPoint;
     735                 :             :     }
     736                 :          16 :     createBackupLabel(chkptredo, chkpttli, chkptrec);
     737                 :             : 
     738                 :             :     /*
     739                 :             :      * Update control file of target, to tell the target how far it must
     740                 :             :      * replay the WAL (minRecoveryPoint).
     741                 :             :      */
     742         [ +  + ]:          16 :     if (connstr_source)
     743                 :             :     {
     744                 :             :         /*
     745                 :             :          * The source is a live server. Like in an online backup, it's
     746                 :             :          * important that we recover all the WAL that was generated while we
     747                 :             :          * were copying files.
     748                 :             :          */
     749         [ +  + ]:           8 :         if (ControlFile_source_after.state == DB_IN_ARCHIVE_RECOVERY)
     750                 :             :         {
     751                 :             :             /*
     752                 :             :              * Source is a standby server. We must replay to its
     753                 :             :              * minRecoveryPoint.
     754                 :             :              */
     755                 :           1 :             endrec = ControlFile_source_after.minRecoveryPoint;
     756                 :           1 :             endtli = ControlFile_source_after.minRecoveryPointTLI;
     757                 :             :         }
     758                 :             :         else
     759                 :             :         {
     760                 :             :             /*
     761                 :             :              * Source is a production, non-standby, server. We must replay to
     762                 :             :              * the last WAL insert location.
     763                 :             :              */
     764         [ -  + ]:           7 :             if (ControlFile_source_after.state != DB_IN_PRODUCTION)
     765                 :           0 :                 pg_fatal("source system was in unexpected state at end of rewind");
     766                 :             : 
     767                 :           7 :             endrec = source->get_current_wal_insert_lsn(source);
     768                 :           7 :             endtli = Max(ControlFile_source_after.checkPointCopy.ThisTimeLineID,
     769                 :             :                          ControlFile_source_after.minRecoveryPointTLI);
     770                 :             :         }
     771                 :             :     }
     772                 :             :     else
     773                 :             :     {
     774                 :             :         /*
     775                 :             :          * Source is a local data directory. It should've shut down cleanly,
     776                 :             :          * and we must replay to the latest shutdown checkpoint.
     777                 :             :          */
     778                 :           8 :         endrec = ControlFile_source_after.checkPoint;
     779                 :           8 :         endtli = ControlFile_source_after.checkPointCopy.ThisTimeLineID;
     780                 :             :     }
     781                 :             : 
     782                 :          16 :     memcpy(&ControlFile_new, &ControlFile_source_after, sizeof(ControlFileData));
     783                 :          16 :     ControlFile_new.minRecoveryPoint = endrec;
     784                 :          16 :     ControlFile_new.minRecoveryPointTLI = endtli;
     785                 :          16 :     ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;
     786                 :             : 
     787                 :             :     /*
     788                 :             :      * Keep the target's own data checksum state.  Most of the data directory
     789                 :             :      * is still the target's: only blocks it changed since the divergence were
     790                 :             :      * copied from the source, so the source's state says nothing about the
     791                 :             :      * pages that stay.  Replay from the last common checkpoint applies any
     792                 :             :      * WAL-logged transition the target has not seen (the watermark tells them
     793                 :             :      * apart), which converges the rewound server to the source's state
     794                 :             :      * whenever the WAL carries it.
     795                 :             :      */
     796                 :          16 :     ControlFile_new.data_checksum_version = ControlFile_target.data_checksum_version;
     797                 :          16 :     ControlFile_new.data_checksum_lsn = ControlFile_target.data_checksum_lsn;
     798                 :          16 :     ControlFile_new.data_checksum_is_local = ControlFile_target.data_checksum_is_local;
     799                 :             : 
     800                 :             :     /*
     801                 :             :      * The watermark is only meaningful within the history the node replays.
     802                 :             :      * Records at or below the divergence point are common to both histories
     803                 :             :      * and stay covered, but a watermark above it was set by a transition
     804                 :             :      * record on the target's own abandoned fork: numerically it can cover
     805                 :             :      * transition records the source wrote after the divergence, and replay
     806                 :             :      * would skip them as already applied.  Clamp it to the divergence point,
     807                 :             :      * so that every transition record on the source's history takes effect.
     808                 :             :      */
     809         [ -  + ]:          16 :     if (ControlFile_new.data_checksum_lsn > divergerec)
     810                 :           0 :         ControlFile_new.data_checksum_lsn = divergerec;
     811                 :             : 
     812         [ +  + ]:          16 :     if (!dry_run)
     813                 :          15 :         update_controlfile(datadir_target, &ControlFile_new, do_sync);
     814                 :          16 : }
     815                 :             : 
     816                 :             : static void
     817                 :          20 : sanityChecks(void)
     818                 :             : {
     819                 :             :     /* TODO Check that there's no backup_label in either cluster */
     820                 :             : 
     821                 :             :     /* Check system_identifier match */
     822         [ -  + ]:          20 :     if (ControlFile_target.system_identifier != ControlFile_source.system_identifier)
     823                 :           0 :         pg_fatal("source and target clusters are from different systems");
     824                 :             : 
     825                 :             :     /* check version */
     826         [ +  - ]:          20 :     if (ControlFile_target.pg_control_version != PG_CONTROL_VERSION ||
     827         [ +  - ]:          20 :         ControlFile_source.pg_control_version != PG_CONTROL_VERSION ||
     828         [ +  - ]:          20 :         ControlFile_target.catalog_version_no != CATALOG_VERSION_NO ||
     829         [ -  + ]:          20 :         ControlFile_source.catalog_version_no != CATALOG_VERSION_NO)
     830                 :             :     {
     831                 :           0 :         pg_fatal("clusters are not compatible with this version of pg_rewind");
     832                 :             :     }
     833                 :             : 
     834                 :             :     /*
     835                 :             :      * Target cluster need to use checksums or hint bit wal-logging, this to
     836                 :             :      * prevent from data corruption that could occur because of hint bits.
     837                 :             :      */
     838         [ +  + ]:          20 :     if (ControlFile_target.data_checksum_version != PG_DATA_CHECKSUM_VERSION &&
     839         [ -  + ]:           1 :         !ControlFile_target.wal_log_hints)
     840                 :             :     {
     841                 :           0 :         pg_fatal("target server needs to use either data checksums or \"wal_log_hints = on\"");
     842                 :             :     }
     843                 :             : 
     844                 :             :     /*
     845                 :             :      * The rewound target keeps its control file fields from the source, but
     846                 :             :      * every block the rewind does not copy keeps the target's content.  If
     847                 :             :      * checksums are enabled on the target and disabled on the source, the
     848                 :             :      * result would claim enabled checksums while the blocks copied from the
     849                 :             :      * source have none, and the target would fail checksum verification as
     850                 :             :      * soon as it reads them.  Refuse that combination, and refuse an
     851                 :             :      * interrupted online transition on either side, same as pg_checksums.
     852                 :             :      *
     853                 :             :      * The opposite mismatch is allowed: recovery under the backup label
     854                 :             :      * written by pg_rewind adopts the checksum state as of the divergence
     855                 :             :      * point, so a target whose own checkpoints carry no checksums stays
     856                 :             :      * without them, or converges through the replayed WAL if the source
     857                 :             :      * enabled them online.  Only warn about it, so that an offline change on
     858                 :             :      * the source is not overlooked.
     859                 :             :      *
     860                 :             :      * That reasoning does not hold when the target is a standby, whose
     861                 :             :      * checkpoints were written by its upstream primary; see the checks after
     862                 :             :      * findLastCheckpoint().
     863                 :             :      */
     864         [ +  - ]:          20 :     if (ControlFile_target.data_checksum_version == PG_DATA_CHECKSUM_INPROGRESS_ON ||
     865         [ -  + ]:          20 :         ControlFile_target.data_checksum_version == PG_DATA_CHECKSUM_INPROGRESS_OFF)
     866                 :             :     {
     867                 :           0 :         pg_log_error("an online data checksum state transition was interrupted on the target server");
     868                 :           0 :         pg_log_error_hint("Start the server and shut it down cleanly to reset the state, then retry; on a standby, let replication complete the transition first.");
     869                 :           0 :         exit(1);
     870                 :             :     }
     871         [ +  - ]:          20 :     if (ControlFile_source.data_checksum_version == PG_DATA_CHECKSUM_INPROGRESS_ON ||
     872         [ -  + ]:          20 :         ControlFile_source.data_checksum_version == PG_DATA_CHECKSUM_INPROGRESS_OFF)
     873                 :             :     {
     874                 :           0 :         pg_log_error("an online data checksum state transition is incomplete on the source server");
     875                 :           0 :         pg_log_error_hint("Let the transition complete, or reset the state with a clean restart, then retry.");
     876                 :           0 :         exit(1);
     877                 :             :     }
     878         [ +  + ]:          20 :     if (ControlFile_target.data_checksum_version == PG_DATA_CHECKSUM_VERSION &&
     879         [ -  + ]:          19 :         ControlFile_source.data_checksum_version == PG_DATA_CHECKSUM_OFF)
     880                 :             :     {
     881                 :           0 :         pg_log_error("data checksums are enabled on the target server but disabled on the source server");
     882                 :           0 :         pg_log_error_detail("Blocks copied from the source would have no checksums, and the target would fail checksum verification after the rewind.");
     883                 :           0 :         pg_log_error_hint("Either disable data checksums on the target server or enable them on the source server, then retry.");
     884                 :           0 :         exit(1);
     885                 :             :     }
     886         [ +  + ]:          20 :     if (ControlFile_target.data_checksum_version == PG_DATA_CHECKSUM_OFF &&
     887         [ +  - ]:           1 :         ControlFile_source.data_checksum_version == PG_DATA_CHECKSUM_VERSION)
     888                 :             :     {
     889                 :           1 :         pg_log_warning("data checksums are disabled on the target server but enabled on the source server");
     890                 :           1 :         pg_log_warning_detail("The rewound server will keep data checksums disabled unless the source server enabled them online after the point of divergence.");
     891                 :             :     }
     892                 :             : 
     893                 :             :     /*
     894                 :             :      * Target cluster better not be running. This doesn't guard against
     895                 :             :      * someone starting the cluster concurrently. Also, this is probably more
     896                 :             :      * strict than necessary; it's OK if the target node was not shut down
     897                 :             :      * cleanly, as long as it isn't running at the moment.
     898                 :             :      */
     899         [ +  + ]:          20 :     if (ControlFile_target.state != DB_SHUTDOWNED &&
     900         [ +  + ]:           2 :         ControlFile_target.state != DB_SHUTDOWNED_IN_RECOVERY)
     901                 :           1 :         pg_fatal("target server must be shut down cleanly");
     902                 :             : 
     903                 :             :     /*
     904                 :             :      * When the source is a data directory, also require that the source
     905                 :             :      * server is shut down. There isn't any very strong reason for this
     906                 :             :      * limitation, but better safe than sorry.
     907                 :             :      */
     908         [ +  + ]:          19 :     if (datadir_source &&
     909         [ +  + ]:          11 :         ControlFile_source.state != DB_SHUTDOWNED &&
     910         [ +  + ]:           2 :         ControlFile_source.state != DB_SHUTDOWNED_IN_RECOVERY)
     911                 :           1 :         pg_fatal("source data directory must be shut down cleanly");
     912                 :          18 : }
     913                 :             : 
     914                 :             : /*
     915                 :             :  * Print a progress report based on the fetch_size and fetch_done variables.
     916                 :             :  *
     917                 :             :  * Progress report is written at maximum once per second, except that the
     918                 :             :  * last progress report is always printed.
     919                 :             :  *
     920                 :             :  * If finished is set to true, this is the last progress report. The cursor
     921                 :             :  * is moved to the next line.
     922                 :             :  */
     923                 :             : void
     924                 :       54825 : progress_report(bool finished)
     925                 :             : {
     926                 :             :     static pg_time_t last_progress_report = 0;
     927                 :             :     int         percent;
     928                 :             :     char        fetch_done_str[32];
     929                 :             :     char        fetch_size_str[32];
     930                 :             :     pg_time_t   now;
     931                 :             : 
     932         [ +  - ]:       54825 :     if (!showprogress)
     933                 :       54825 :         return;
     934                 :             : 
     935                 :           0 :     now = time(NULL);
     936   [ #  #  #  # ]:           0 :     if (now == last_progress_report && !finished)
     937                 :           0 :         return;                 /* Max once per second */
     938                 :             : 
     939                 :           0 :     last_progress_report = now;
     940         [ #  # ]:           0 :     percent = fetch_size ? (int) ((fetch_done) * 100 / fetch_size) : 0;
     941                 :             : 
     942                 :             :     /*
     943                 :             :      * Avoid overflowing past 100% or the full size. This may make the total
     944                 :             :      * size number change as we approach the end of the backup (the estimate
     945                 :             :      * will always be wrong if WAL is included), but that's better than having
     946                 :             :      * the done column be bigger than the total.
     947                 :             :      */
     948         [ #  # ]:           0 :     if (percent > 100)
     949                 :           0 :         percent = 100;
     950         [ #  # ]:           0 :     if (fetch_done > fetch_size)
     951                 :           0 :         fetch_size = fetch_done;
     952                 :             : 
     953                 :           0 :     snprintf(fetch_done_str, sizeof(fetch_done_str), UINT64_FORMAT,
     954                 :             :              fetch_done / 1024);
     955                 :           0 :     snprintf(fetch_size_str, sizeof(fetch_size_str), UINT64_FORMAT,
     956                 :             :              fetch_size / 1024);
     957                 :             : 
     958                 :           0 :     fprintf(stderr, _("%*s/%s kB (%d%%) copied"),
     959                 :           0 :             (int) strlen(fetch_size_str), fetch_done_str, fetch_size_str,
     960                 :             :             percent);
     961                 :             : 
     962                 :             :     /*
     963                 :             :      * Stay on the same line if reporting to a terminal and we're not done
     964                 :             :      * yet.
     965                 :             :      */
     966   [ #  #  #  # ]:           0 :     fputc((!finished && isatty(fileno(stderr))) ? '\r' : '\n', stderr);
     967                 :             : }
     968                 :             : 
     969                 :             : /*
     970                 :             :  * Find minimum from two WAL locations assuming InvalidXLogRecPtr means
     971                 :             :  * infinity as src/include/access/timeline.h states. This routine should
     972                 :             :  * be used only when comparing WAL locations related to history files.
     973                 :             :  */
     974                 :             : static XLogRecPtr
     975                 :          17 : MinXLogRecPtr(XLogRecPtr a, XLogRecPtr b)
     976                 :             : {
     977         [ +  + ]:          17 :     if (!XLogRecPtrIsValid(a))
     978                 :           1 :         return b;
     979         [ +  - ]:          16 :     else if (!XLogRecPtrIsValid(b))
     980                 :          16 :         return a;
     981                 :             :     else
     982                 :           0 :         return Min(a, b);
     983                 :             : }
     984                 :             : 
     985                 :             : /*
     986                 :             :  * Retrieve timeline history for the source or target system.
     987                 :             :  */
     988                 :             : static TimeLineHistoryEntry *
     989                 :          34 : getTimelineHistory(TimeLineID tli, bool is_source, int *nentries)
     990                 :             : {
     991                 :             :     TimeLineHistoryEntry *history;
     992                 :             : 
     993                 :             :     /*
     994                 :             :      * Timeline 1 does not have a history file, so there is no need to check
     995                 :             :      * and fake an entry with infinite start and end positions.
     996                 :             :      */
     997         [ +  + ]:          34 :     if (tli == 1)
     998                 :             :     {
     999                 :          16 :         history = pg_malloc_object(TimeLineHistoryEntry);
    1000                 :          16 :         history->tli = tli;
    1001                 :          16 :         history->begin = history->end = InvalidXLogRecPtr;
    1002                 :          16 :         *nentries = 1;
    1003                 :             :     }
    1004                 :             :     else
    1005                 :             :     {
    1006                 :             :         char        path[MAXPGPATH];
    1007                 :             :         char       *histfile;
    1008                 :             : 
    1009                 :          18 :         TLHistoryFilePath(path, tli);
    1010                 :             : 
    1011                 :             :         /* Get history file from appropriate source */
    1012         [ +  + ]:          18 :         if (is_source)
    1013                 :          16 :             histfile = source->fetch_file(source, path, NULL);
    1014                 :             :         else
    1015                 :           2 :             histfile = slurpFile(datadir_target, path, NULL);
    1016                 :             : 
    1017                 :          18 :         history = rewind_parseTimeLineHistory(histfile, tli, nentries);
    1018                 :          18 :         pg_free(histfile);
    1019                 :             :     }
    1020                 :             : 
    1021                 :             :     /* In debugging mode, print what we read */
    1022         [ +  + ]:          34 :     if (debug)
    1023                 :             :     {
    1024                 :             :         int         i;
    1025                 :             : 
    1026         [ +  + ]:          30 :         if (is_source)
    1027         [ +  - ]:          15 :             pg_log_debug("Source timeline history:");
    1028                 :             :         else
    1029         [ +  - ]:          15 :             pg_log_debug("Target timeline history:");
    1030                 :             : 
    1031         [ +  + ]:          77 :         for (i = 0; i < *nentries; i++)
    1032                 :             :         {
    1033                 :             :             TimeLineHistoryEntry *entry;
    1034                 :             : 
    1035                 :          47 :             entry = &history[i];
    1036         [ +  - ]:          47 :             pg_log_debug("%u: %X/%08X - %X/%08X", entry->tli,
    1037                 :             :                          LSN_FORMAT_ARGS(entry->begin),
    1038                 :             :                          LSN_FORMAT_ARGS(entry->end));
    1039                 :             :         }
    1040                 :             :     }
    1041                 :             : 
    1042                 :          34 :     return history;
    1043                 :             : }
    1044                 :             : 
    1045                 :             : /*
    1046                 :             :  * Determine the TLI of the last common timeline in the timeline history of
    1047                 :             :  * two clusters. *tliIndex is set to the index of last common timeline in
    1048                 :             :  * the arrays, and *recptr is set to the position where the timeline history
    1049                 :             :  * diverged (ie. the first WAL record that's not the same in both clusters).
    1050                 :             :  */
    1051                 :             : static void
    1052                 :          17 : findCommonAncestorTimeline(TimeLineHistoryEntry *a_history, int a_nentries,
    1053                 :             :                            TimeLineHistoryEntry *b_history, int b_nentries,
    1054                 :             :                            XLogRecPtr *recptr, int *tliIndex)
    1055                 :             : {
    1056                 :             :     int         i,
    1057                 :             :                 n;
    1058                 :             : 
    1059                 :             :     /*
    1060                 :             :      * Trace the history forward, until we hit the timeline diverge. It may
    1061                 :             :      * still be possible that the source and target nodes used the same
    1062                 :             :      * timeline number in their history but with different start position
    1063                 :             :      * depending on the history files that each node has fetched in previous
    1064                 :             :      * recovery processes. Hence check the start position of the new timeline
    1065                 :             :      * as well and move down by one extra timeline entry if they do not match.
    1066                 :             :      */
    1067                 :          17 :     n = Min(a_nentries, b_nentries);
    1068         [ +  + ]:          35 :     for (i = 0; i < n; i++)
    1069                 :             :     {
    1070         [ +  - ]:          18 :         if (a_history[i].tli != b_history[i].tli ||
    1071         [ +  - ]:          18 :             a_history[i].begin != b_history[i].begin)
    1072                 :             :             break;
    1073                 :             :     }
    1074                 :             : 
    1075         [ +  - ]:          17 :     if (i > 0)
    1076                 :             :     {
    1077                 :          17 :         i--;
    1078                 :          17 :         *recptr = MinXLogRecPtr(a_history[i].end, b_history[i].end);
    1079                 :          17 :         *tliIndex = i;
    1080                 :          17 :         return;
    1081                 :             :     }
    1082                 :             :     else
    1083                 :             :     {
    1084                 :           0 :         pg_fatal("could not find common ancestor of the source and target cluster's timelines");
    1085                 :             :     }
    1086                 :             : }
    1087                 :             : 
    1088                 :             : 
    1089                 :             : /*
    1090                 :             :  * Create a backup_label file that forces recovery to begin at the last common
    1091                 :             :  * checkpoint.
    1092                 :             :  */
    1093                 :             : static void
    1094                 :          16 : createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli, XLogRecPtr checkpointloc)
    1095                 :             : {
    1096                 :             :     XLogSegNo   startsegno;
    1097                 :             :     time_t      stamp_time;
    1098                 :             :     char        strfbuf[128];
    1099                 :             :     char        xlogfilename[MAXFNAMELEN];
    1100                 :             :     struct tm  *tmp;
    1101                 :             :     char        buf[1000];
    1102                 :             :     int         len;
    1103                 :             : 
    1104                 :          16 :     XLByteToSeg(startpoint, startsegno, WalSegSz);
    1105                 :          16 :     XLogFileName(xlogfilename, starttli, startsegno, WalSegSz);
    1106                 :             : 
    1107                 :             :     /*
    1108                 :             :      * Construct backup label file
    1109                 :             :      */
    1110                 :          16 :     stamp_time = time(NULL);
    1111                 :          16 :     tmp = localtime(&stamp_time);
    1112                 :          16 :     strftime(strfbuf, sizeof(strfbuf), "%Y-%m-%d %H:%M:%S %Z", tmp);
    1113                 :             : 
    1114                 :          16 :     len = snprintf(buf, sizeof(buf),
    1115                 :             :                    "START WAL LOCATION: %X/%08X (file %s)\n"
    1116                 :             :                    "CHECKPOINT LOCATION: %X/%08X\n"
    1117                 :             :                    "BACKUP METHOD: pg_rewind\n"
    1118                 :             :                    "BACKUP FROM: standby\n"
    1119                 :             :                    "START TIME: %s\n",
    1120                 :             :     /* omit LABEL: line */
    1121                 :          16 :                    LSN_FORMAT_ARGS(startpoint), xlogfilename,
    1122                 :          16 :                    LSN_FORMAT_ARGS(checkpointloc),
    1123                 :             :                    strfbuf);
    1124         [ -  + ]:          16 :     if (len >= sizeof(buf))
    1125                 :           0 :         pg_fatal("backup label buffer too small");    /* shouldn't happen */
    1126                 :             : 
    1127                 :             :     /* TODO: move old file out of the way, if any. */
    1128                 :          16 :     open_target_file("backup_label", true); /* BACKUP_LABEL_FILE */
    1129                 :          16 :     write_target_range(buf, 0, len);
    1130                 :          16 :     close_target_file();
    1131                 :          16 : }
    1132                 :             : 
    1133                 :             : /*
    1134                 :             :  * Check CRC of control file
    1135                 :             :  */
    1136                 :             : static void
    1137                 :          66 : checkControlFile(ControlFileData *ControlFile)
    1138                 :             : {
    1139                 :             :     pg_crc32c   crc;
    1140                 :             : 
    1141                 :             :     /* Calculate CRC */
    1142                 :          66 :     INIT_CRC32C(crc);
    1143                 :          66 :     COMP_CRC32C(crc, ControlFile, offsetof(ControlFileData, crc));
    1144                 :          66 :     FIN_CRC32C(crc);
    1145                 :             : 
    1146                 :             :     /* And simply compare it */
    1147         [ -  + ]:          66 :     if (!EQ_CRC32C(crc, ControlFile->crc))
    1148                 :           0 :         pg_fatal("unexpected control file CRC");
    1149                 :          66 : }
    1150                 :             : 
    1151                 :             : /*
    1152                 :             :  * Verify control file contents in the buffer 'content', and copy it to
    1153                 :             :  * *ControlFile.
    1154                 :             :  */
    1155                 :             : static void
    1156                 :          66 : digestControlFile(ControlFileData *ControlFile, const char *content,
    1157                 :             :                   size_t size)
    1158                 :             : {
    1159         [ -  + ]:          66 :     if (size != PG_CONTROL_FILE_SIZE)
    1160                 :           0 :         pg_fatal("unexpected control file size %zu, expected %d",
    1161                 :             :                  size, PG_CONTROL_FILE_SIZE);
    1162                 :             : 
    1163                 :          66 :     memcpy(ControlFile, content, sizeof(ControlFileData));
    1164                 :             : 
    1165                 :             :     /* set and validate WalSegSz */
    1166                 :          66 :     WalSegSz = ControlFile->xlog_seg_size;
    1167                 :             : 
    1168   [ +  -  +  -  :          66 :     if (!IsValidWalSegSize(WalSegSz))
             +  -  -  + ]
    1169                 :             :     {
    1170                 :           0 :         pg_log_error(ngettext("invalid WAL segment size in control file (%d byte)",
    1171                 :             :                               "invalid WAL segment size in control file (%d bytes)",
    1172                 :             :                               WalSegSz),
    1173                 :             :                      WalSegSz);
    1174                 :           0 :         pg_log_error_detail("The WAL segment size must be a power of two between 1 MB and 1 GB.");
    1175                 :           0 :         exit(1);
    1176                 :             :     }
    1177                 :             : 
    1178                 :             :     /* Additional checks on control file */
    1179                 :          66 :     checkControlFile(ControlFile);
    1180                 :          66 : }
    1181                 :             : 
    1182                 :             : /*
    1183                 :             :  * Get value of GUC parameter restore_command from the target cluster.
    1184                 :             :  *
    1185                 :             :  * This uses a logic based on "postgres -C" to get the value from the
    1186                 :             :  * cluster.
    1187                 :             :  */
    1188                 :             : static void
    1189                 :          21 : getRestoreCommand(const char *argv0)
    1190                 :             : {
    1191                 :             :     int         rc;
    1192                 :             :     char        postgres_exec_path[MAXPGPATH];
    1193                 :             :     PQExpBuffer postgres_cmd;
    1194                 :             : 
    1195         [ +  + ]:          21 :     if (!restore_wal)
    1196                 :          20 :         return;
    1197                 :             : 
    1198                 :             :     /* find postgres executable */
    1199                 :           1 :     rc = find_other_exec(argv0, "postgres",
    1200                 :             :                          PG_BACKEND_VERSIONSTR,
    1201                 :             :                          postgres_exec_path);
    1202                 :             : 
    1203         [ -  + ]:           1 :     if (rc < 0)
    1204                 :             :     {
    1205                 :             :         char        full_path[MAXPGPATH];
    1206                 :             : 
    1207         [ #  # ]:           0 :         if (find_my_exec(argv0, full_path) < 0)
    1208                 :           0 :             strlcpy(full_path, progname, sizeof(full_path));
    1209                 :             : 
    1210         [ #  # ]:           0 :         if (rc == -1)
    1211                 :           0 :             pg_fatal("program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
    1212                 :             :                      "postgres", progname, full_path);
    1213                 :             :         else
    1214                 :           0 :             pg_fatal("program \"%s\" was found by \"%s\" but was not the same version as %s",
    1215                 :             :                      "postgres", full_path, progname);
    1216                 :             :     }
    1217                 :             : 
    1218                 :             :     /*
    1219                 :             :      * Build a command able to retrieve the value of GUC parameter
    1220                 :             :      * restore_command, if set.
    1221                 :             :      */
    1222                 :           1 :     postgres_cmd = createPQExpBuffer();
    1223                 :             : 
    1224                 :             :     /* path to postgres, properly quoted */
    1225                 :           1 :     appendShellString(postgres_cmd, postgres_exec_path);
    1226                 :             : 
    1227                 :             :     /* add -D switch, with properly quoted data directory */
    1228                 :           1 :     appendPQExpBufferStr(postgres_cmd, " -D ");
    1229                 :           1 :     appendShellString(postgres_cmd, datadir_target);
    1230                 :             : 
    1231                 :             :     /* add custom configuration file only if requested */
    1232         [ +  - ]:           1 :     if (config_file != NULL)
    1233                 :             :     {
    1234                 :           1 :         appendPQExpBufferStr(postgres_cmd, " -c config_file=");
    1235                 :           1 :         appendShellString(postgres_cmd, config_file);
    1236                 :             :     }
    1237                 :             : 
    1238                 :             :     /* add -C switch, for restore_command */
    1239                 :           1 :     appendPQExpBufferStr(postgres_cmd, " -C restore_command");
    1240                 :             : 
    1241                 :           1 :     restore_command = pipe_read_line(postgres_cmd->data);
    1242         [ -  + ]:           1 :     if (restore_command == NULL)
    1243                 :           0 :         pg_fatal("could not read \"restore_command\" from target cluster");
    1244                 :             : 
    1245                 :           1 :     (void) pg_strip_crlf(restore_command);
    1246                 :             : 
    1247         [ -  + ]:           1 :     if (strcmp(restore_command, "") == 0)
    1248                 :           0 :         pg_fatal("\"restore_command\" is not set in the target cluster");
    1249                 :             : 
    1250         [ +  - ]:           1 :     pg_log_debug("using for rewind \"restore_command = \'%s\'\"",
    1251                 :             :                  restore_command);
    1252                 :             : 
    1253                 :           1 :     destroyPQExpBuffer(postgres_cmd);
    1254                 :             : }
    1255                 :             : 
    1256                 :             : 
    1257                 :             : /*
    1258                 :             :  * Ensure clean shutdown of target instance by launching single-user mode
    1259                 :             :  * postgres to do crash recovery.
    1260                 :             :  */
    1261                 :             : static void
    1262                 :          10 : ensureCleanShutdown(const char *argv0)
    1263                 :             : {
    1264                 :             :     int         ret;
    1265                 :             :     char        exec_path[MAXPGPATH];
    1266                 :             :     PQExpBuffer postgres_cmd;
    1267                 :             : 
    1268                 :             :     /* locate postgres binary */
    1269         [ -  + ]:          10 :     if ((ret = find_other_exec(argv0, "postgres",
    1270                 :             :                                PG_BACKEND_VERSIONSTR,
    1271                 :             :                                exec_path)) < 0)
    1272                 :             :     {
    1273                 :             :         char        full_path[MAXPGPATH];
    1274                 :             : 
    1275         [ #  # ]:           0 :         if (find_my_exec(argv0, full_path) < 0)
    1276                 :           0 :             strlcpy(full_path, progname, sizeof(full_path));
    1277                 :             : 
    1278         [ #  # ]:           0 :         if (ret == -1)
    1279                 :           0 :             pg_fatal("program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
    1280                 :             :                      "postgres", progname, full_path);
    1281                 :             :         else
    1282                 :           0 :             pg_fatal("program \"%s\" was found by \"%s\" but was not the same version as %s",
    1283                 :             :                      "postgres", full_path, progname);
    1284                 :             :     }
    1285                 :             : 
    1286                 :          10 :     pg_log_info("executing \"%s\" for target server to complete crash recovery",
    1287                 :             :                 exec_path);
    1288                 :             : 
    1289                 :             :     /*
    1290                 :             :      * Skip processing if requested, but only after ensuring presence of
    1291                 :             :      * postgres.
    1292                 :             :      */
    1293         [ -  + ]:          10 :     if (dry_run)
    1294                 :           0 :         return;
    1295                 :             : 
    1296                 :             :     /*
    1297                 :             :      * Finally run postgres in single-user mode.  There is no need to use
    1298                 :             :      * fsync here.  This makes the recovery faster, and the target data folder
    1299                 :             :      * is synced at the end anyway.
    1300                 :             :      */
    1301                 :          10 :     postgres_cmd = createPQExpBuffer();
    1302                 :             : 
    1303                 :             :     /* path to postgres, properly quoted */
    1304                 :          10 :     appendShellString(postgres_cmd, exec_path);
    1305                 :             : 
    1306                 :             :     /* add set of options with properly quoted data directory */
    1307                 :          10 :     appendPQExpBufferStr(postgres_cmd, " --single -F -D ");
    1308                 :          10 :     appendShellString(postgres_cmd, datadir_target);
    1309                 :             : 
    1310                 :             :     /* add custom configuration file only if requested */
    1311         [ +  + ]:          10 :     if (config_file != NULL)
    1312                 :             :     {
    1313                 :           9 :         appendPQExpBufferStr(postgres_cmd, " -c config_file=");
    1314                 :           9 :         appendShellString(postgres_cmd, config_file);
    1315                 :             :     }
    1316                 :             : 
    1317                 :             :     /* finish with the database name, and a properly quoted redirection */
    1318                 :          10 :     appendPQExpBufferStr(postgres_cmd, " template1 < ");
    1319                 :          10 :     appendShellString(postgres_cmd, DEVNULL);
    1320                 :             : 
    1321                 :          10 :     fflush(NULL);
    1322         [ +  + ]:          10 :     if (system(postgres_cmd->data) != 0)
    1323                 :             :     {
    1324                 :           1 :         pg_log_error("postgres single-user mode in target cluster failed");
    1325                 :           1 :         pg_log_error_detail("Command was: %s", postgres_cmd->data);
    1326                 :           1 :         exit(1);
    1327                 :             :     }
    1328                 :             : 
    1329                 :           9 :     destroyPQExpBuffer(postgres_cmd);
    1330                 :             : }
    1331                 :             : 
    1332                 :             : static void
    1333                 :          21 : disconnect_atexit(void)
    1334                 :             : {
    1335         [ -  + ]:          21 :     if (conn != NULL)
    1336                 :           0 :         PQfinish(conn);
    1337                 :          21 : }
        

Generated by: LCOV version 2.0-1