LCOV - code coverage report
Current view: top level - src/bin/pg_rewind - pg_rewind.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 498 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 14 0
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.3.2-1