LCOV - code coverage report
Current view: top level - src/bin/pg_rewind - pg_rewind.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 82.9 % 415 344
Test Date: 2026-07-12 22:15:32 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 64.7 % 235 152

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

Generated by: LCOV version 2.0-1