LCOV - code coverage report
Current view: top level - src/bin/pg_rewind - pg_rewind.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 341 412 82.8 %
Date: 2025-11-17 09:18:22 Functions: 14 14 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.16