PostgreSQL Source Code  git master
pg_rewind.c File Reference
#include "postgres_fe.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include "access/timeline.h"
#include "access/xlog_internal.h"
#include "catalog/catversion.h"
#include "catalog/pg_control.h"
#include "common/controldata_utils.h"
#include "common/file_perm.h"
#include "common/restricted_token.h"
#include "common/string.h"
#include "fe_utils/recovery_gen.h"
#include "file_ops.h"
#include "filemap.h"
#include "getopt_long.h"
#include "pg_rewind.h"
#include "rewind_source.h"
#include "storage/bufpage.h"
Include dependency graph for pg_rewind.c:

Go to the source code of this file.

Macros

#define MAXCMDLEN   (2 * MAXPGPATH)
 

Functions

static void usage (const char *progname)
 
static void perform_rewind (filemap_t *filemap, rewind_source *source, XLogRecPtr chkptrec, TimeLineID chkpttli, XLogRecPtr chkptredo)
 
static void createBackupLabel (XLogRecPtr startpoint, TimeLineID starttli, XLogRecPtr checkpointloc)
 
static void digestControlFile (ControlFileData *ControlFile, const char *content, size_t size)
 
static void getRestoreCommand (const char *argv0)
 
static void sanityChecks (void)
 
static void findCommonAncestorTimeline (XLogRecPtr *recptr, int *tliIndex)
 
static void ensureCleanShutdown (const char *argv0)
 
static void disconnect_atexit (void)
 
int main (int argc, char **argv)
 
void progress_report (bool finished)
 
static XLogRecPtr MinXLogRecPtr (XLogRecPtr a, XLogRecPtr b)
 
static TimeLineHistoryEntrygetTimelineHistory (ControlFileData *controlFile, int *nentries)
 
static void checkControlFile (ControlFileData *ControlFile)
 

Variables

static ControlFileData ControlFile_target
 
static ControlFileData ControlFile_source
 
static ControlFileData ControlFile_source_after
 
const char * progname
 
int WalSegSz
 
char * datadir_target = NULL
 
char * datadir_source = NULL
 
char * connstr_source = NULL
 
char * restore_command = NULL
 
static bool debug = false
 
bool showprogress = false
 
bool dry_run = false
 
bool do_sync = true
 
bool restore_wal = false
 
TimeLineHistoryEntrytargetHistory
 
int targetNentries
 
uint64 fetch_size
 
uint64 fetch_done
 
static PGconnconn
 
static rewind_sourcesource
 

Macro Definition Documentation

◆ MAXCMDLEN

#define MAXCMDLEN   (2 * MAXPGPATH)

Referenced by ensureCleanShutdown().

Function Documentation

◆ checkControlFile()

static void checkControlFile ( ControlFileData ControlFile)
static

Definition at line 972 of file pg_rewind.c.

References COMP_CRC32C, EQ_CRC32C, FIN_CRC32C, INIT_CRC32C, offsetof, and pg_fatal.

Referenced by digestControlFile().

973 {
974  pg_crc32c crc;
975 
976  /* Calculate CRC */
977  INIT_CRC32C(crc);
978  COMP_CRC32C(crc, (char *) ControlFile, offsetof(ControlFileData, crc));
979  FIN_CRC32C(crc);
980 
981  /* And simply compare it */
982  if (!EQ_CRC32C(crc, ControlFile->crc))
983  pg_fatal("unexpected control file CRC");
984 }
#define INIT_CRC32C(crc)
Definition: pg_crc32c.h:41
uint32 pg_crc32c
Definition: pg_crc32c.h:38
#define pg_fatal(...)
Definition: pg_rewind.h:37
#define EQ_CRC32C(c1, c2)
Definition: pg_crc32c.h:42
#define COMP_CRC32C(crc, data, len)
Definition: pg_crc32c.h:89
#define FIN_CRC32C(crc)
Definition: pg_crc32c.h:94
#define offsetof(type, field)
Definition: c.h:727

◆ createBackupLabel()

static void createBackupLabel ( XLogRecPtr  startpoint,
TimeLineID  starttli,
XLogRecPtr  checkpointloc 
)
static

Definition at line 929 of file pg_rewind.c.

References buf, close_target_file(), LSN_FORMAT_ARGS, MAXFNAMELEN, open_target_file(), pg_fatal, snprintf, tm, WalSegSz, write_target_range(), XLByteToSeg, and XLogFileName.

Referenced by perform_rewind().

930 {
931  XLogSegNo startsegno;
932  time_t stamp_time;
933  char strfbuf[128];
934  char xlogfilename[MAXFNAMELEN];
935  struct tm *tmp;
936  char buf[1000];
937  int len;
938 
939  XLByteToSeg(startpoint, startsegno, WalSegSz);
940  XLogFileName(xlogfilename, starttli, startsegno, WalSegSz);
941 
942  /*
943  * Construct backup label file
944  */
945  stamp_time = time(NULL);
946  tmp = localtime(&stamp_time);
947  strftime(strfbuf, sizeof(strfbuf), "%Y-%m-%d %H:%M:%S %Z", tmp);
948 
949  len = snprintf(buf, sizeof(buf),
950  "START WAL LOCATION: %X/%X (file %s)\n"
951  "CHECKPOINT LOCATION: %X/%X\n"
952  "BACKUP METHOD: pg_rewind\n"
953  "BACKUP FROM: standby\n"
954  "START TIME: %s\n",
955  /* omit LABEL: line */
956  LSN_FORMAT_ARGS(startpoint), xlogfilename,
957  LSN_FORMAT_ARGS(checkpointloc),
958  strfbuf);
959  if (len >= sizeof(buf))
960  pg_fatal("backup label buffer too small"); /* shouldn't happen */
961 
962  /* TODO: move old file out of the way, if any. */
963  open_target_file("backup_label", true); /* BACKUP_LABEL_FILE */
964  write_target_range(buf, 0, len);
966 }
void open_target_file(const char *path, bool trunc)
Definition: file_ops.c:47
void write_target_range(char *buf, off_t begin, size_t size)
Definition: file_ops.c:88
#define pg_fatal(...)
Definition: pg_rewind.h:37
static struct pg_tm tm
Definition: localtime.c:102
#define LSN_FORMAT_ARGS(lsn)
Definition: xlogdefs.h:43
static char * buf
Definition: pg_test_fsync.c:68
uint64 XLogSegNo
Definition: xlogdefs.h:48
#define MAXFNAMELEN
int WalSegSz
Definition: pg_rewind.c:56
#define XLogFileName(fname, tli, logSegNo, wal_segsz_bytes)
void close_target_file(void)
Definition: file_ops.c:75
#define snprintf
Definition: port.h:216
#define XLByteToSeg(xlrp, logSegNo, wal_segsz_bytes)

◆ digestControlFile()

static void digestControlFile ( ControlFileData ControlFile,
const char *  content,
size_t  size 
)
static

Definition at line 991 of file pg_rewind.c.

References checkControlFile(), IsValidWalSegSize, ngettext, PG_CONTROL_FILE_SIZE, pg_fatal, WalSegSz, and ControlFileData::xlog_seg_size.

Referenced by main(), and perform_rewind().

993 {
994  if (size != PG_CONTROL_FILE_SIZE)
995  pg_fatal("unexpected control file size %d, expected %d",
996  (int) size, PG_CONTROL_FILE_SIZE);
997 
998  memcpy(ControlFile, content, sizeof(ControlFileData));
999 
1000  /* set and validate WalSegSz */
1001  WalSegSz = ControlFile->xlog_seg_size;
1002 
1004  pg_fatal(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte",
1005  "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes",
1006  WalSegSz),
1007  WalSegSz);
1008 
1009  /* Additional checks on control file */
1010  checkControlFile(ControlFile);
1011 }
#define IsValidWalSegSize(size)
Definition: xlog_internal.h:94
#define pg_fatal(...)
Definition: pg_rewind.h:37
uint32 xlog_seg_size
Definition: pg_control.h:209
static void checkControlFile(ControlFileData *ControlFile)
Definition: pg_rewind.c:972
int WalSegSz
Definition: pg_rewind.c:56
#define ngettext(s, p, n)
Definition: c.h:1182
#define PG_CONTROL_FILE_SIZE
Definition: pg_control.h:248

◆ disconnect_atexit()

static void disconnect_atexit ( void  )
static

Definition at line 1138 of file pg_rewind.c.

References PQfinish().

Referenced by main().

1139 {
1140  if (conn != NULL)
1141  PQfinish(conn);
1142 }
void PQfinish(PGconn *conn)
Definition: fe-connect.c:4224
static PGconn * conn
Definition: pg_rewind.c:78

◆ ensureCleanShutdown()

static void ensureCleanShutdown ( const char *  argv0)
static

Definition at line 1083 of file pg_rewind.c.

References datadir_target, DEVNULL, dry_run, exec_path, find_my_exec(), find_other_exec(), MAXCMDLEN, MAXPGPATH, PG_BACKEND_VERSIONSTR, pg_fatal, pg_log_error, pg_log_info, progname, snprintf, and strlcpy().

Referenced by main().

1084 {
1085  int ret;
1086 #define MAXCMDLEN (2 * MAXPGPATH)
1087  char exec_path[MAXPGPATH];
1088  char cmd[MAXCMDLEN];
1089 
1090  /* locate postgres binary */
1091  if ((ret = find_other_exec(argv0, "postgres",
1093  exec_path)) < 0)
1094  {
1095  char full_path[MAXPGPATH];
1096 
1097  if (find_my_exec(argv0, full_path) < 0)
1098  strlcpy(full_path, progname, sizeof(full_path));
1099 
1100  if (ret == -1)
1101  pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
1102  "same directory as \"%s\".\n"
1103  "Check your installation.",
1104  "postgres", progname, full_path);
1105  else
1106  pg_fatal("The program \"%s\" was found by \"%s\"\n"
1107  "but was not the same version as %s.\n"
1108  "Check your installation.",
1109  "postgres", full_path, progname);
1110  }
1111 
1112  pg_log_info("executing \"%s\" for target server to complete crash recovery",
1113  exec_path);
1114 
1115  /*
1116  * Skip processing if requested, but only after ensuring presence of
1117  * postgres.
1118  */
1119  if (dry_run)
1120  return;
1121 
1122  /*
1123  * Finally run postgres in single-user mode. There is no need to use
1124  * fsync here. This makes the recovery faster, and the target data folder
1125  * is synced at the end anyway.
1126  */
1127  snprintf(cmd, MAXCMDLEN, "\"%s\" --single -F -D \"%s\" template1 < \"%s\"",
1128  exec_path, datadir_target, DEVNULL);
1129 
1130  if (system(cmd) != 0)
1131  {
1132  pg_log_error("postgres single-user mode in target cluster failed");
1133  pg_fatal("Command was: %s", cmd);
1134  }
1135 }
int find_other_exec(const char *argv0, const char *target, const char *versionstr, char *retpath)
Definition: exec.c:322
char * datadir_target
Definition: pg_rewind.c:59
static char * argv0
Definition: pg_ctl.c:97
#define MAXCMDLEN
bool dry_run
Definition: pg_rewind.c:66
#define pg_log_error(...)
Definition: logging.h:80
#define pg_fatal(...)
Definition: pg_rewind.h:37
#define MAXPGPATH
int find_my_exec(const char *argv0, char *retpath)
Definition: exec.c:127
#define DEVNULL
Definition: port.h:147
#define PG_BACKEND_VERSIONSTR
Definition: port.h:135
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
static char * exec_path
Definition: pg_ctl.c:92
#define snprintf
Definition: port.h:216
#define pg_log_info(...)
Definition: logging.h:88
const char * progname
Definition: pg_rewind.c:55

◆ findCommonAncestorTimeline()

static void findCommonAncestorTimeline ( XLogRecPtr recptr,
int *  tliIndex 
)
static

Definition at line 881 of file pg_rewind.c.

References getTimelineHistory(), i, Min, MinXLogRecPtr(), pg_fatal, pg_free(), and targetNentries.

Referenced by main().

882 {
883  TimeLineHistoryEntry *sourceHistory;
884  int sourceNentries;
885  int i,
886  n;
887 
888  /* Retrieve timelines for both source and target */
889  sourceHistory = getTimelineHistory(&ControlFile_source, &sourceNentries);
891 
892  /*
893  * Trace the history forward, until we hit the timeline diverge. It may
894  * still be possible that the source and target nodes used the same
895  * timeline number in their history but with different start position
896  * depending on the history files that each node has fetched in previous
897  * recovery processes. Hence check the start position of the new timeline
898  * as well and move down by one extra timeline entry if they do not match.
899  */
900  n = Min(sourceNentries, targetNentries);
901  for (i = 0; i < n; i++)
902  {
903  if (sourceHistory[i].tli != targetHistory[i].tli ||
904  sourceHistory[i].begin != targetHistory[i].begin)
905  break;
906  }
907 
908  if (i > 0)
909  {
910  i--;
911  *recptr = MinXLogRecPtr(sourceHistory[i].end, targetHistory[i].end);
912  *tliIndex = i;
913 
914  pg_free(sourceHistory);
915  return;
916  }
917  else
918  {
919  pg_fatal("could not find common ancestor of the source and target cluster's timelines");
920  }
921 }
TimeLineHistoryEntry * targetHistory
Definition: pg_rewind.c:71
#define Min(x, y)
Definition: c.h:986
static XLogRecPtr MinXLogRecPtr(XLogRecPtr a, XLogRecPtr b)
Definition: pg_rewind.c:789
#define pg_fatal(...)
Definition: pg_rewind.h:37
int targetNentries
Definition: pg_rewind.c:72
void pg_free(void *ptr)
Definition: fe_memutils.c:105
static TimeLineHistoryEntry * getTimelineHistory(ControlFileData *controlFile, int *nentries)
Definition: pg_rewind.c:804
int i
static ControlFileData ControlFile_target
Definition: pg_rewind.c:51
static ControlFileData ControlFile_source
Definition: pg_rewind.c:52

◆ getRestoreCommand()

static void getRestoreCommand ( const char *  argv0)
static

Definition at line 1020 of file pg_rewind.c.

References datadir_target, find_my_exec(), find_other_exec(), MAXPGPATH, PG_BACKEND_VERSIONSTR, pg_fatal, pg_log_debug, pg_log_error, pg_strdup(), pg_strip_crlf(), pipe_read_line(), progname, restore_command, restore_wal, snprintf, and strlcpy().

Referenced by main().

1021 {
1022  int rc;
1023  char postgres_exec_path[MAXPGPATH],
1024  postgres_cmd[MAXPGPATH],
1025  cmd_output[MAXPGPATH];
1026 
1027  if (!restore_wal)
1028  return;
1029 
1030  /* find postgres executable */
1031  rc = find_other_exec(argv0, "postgres",
1033  postgres_exec_path);
1034 
1035  if (rc < 0)
1036  {
1037  char full_path[MAXPGPATH];
1038 
1039  if (find_my_exec(argv0, full_path) < 0)
1040  strlcpy(full_path, progname, sizeof(full_path));
1041 
1042  if (rc == -1)
1043  pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
1044  "same directory as \"%s\".\n"
1045  "Check your installation.",
1046  "postgres", progname, full_path);
1047  else
1048  pg_log_error("The program \"%s\" was found by \"%s\"\n"
1049  "but was not the same version as %s.\n"
1050  "Check your installation.",
1051  "postgres", full_path, progname);
1052  exit(1);
1053  }
1054 
1055  /*
1056  * Build a command able to retrieve the value of GUC parameter
1057  * restore_command, if set.
1058  */
1059  snprintf(postgres_cmd, sizeof(postgres_cmd),
1060  "\"%s\" -D \"%s\" -C restore_command",
1061  postgres_exec_path, datadir_target);
1062 
1063  if (!pipe_read_line(postgres_cmd, cmd_output, sizeof(cmd_output)))
1064  exit(1);
1065 
1066  (void) pg_strip_crlf(cmd_output);
1067 
1068  if (strcmp(cmd_output, "") == 0)
1069  pg_fatal("restore_command is not set in the target cluster");
1070 
1071  restore_command = pg_strdup(cmd_output);
1072 
1073  pg_log_debug("using for rewind restore_command = \'%s\'",
1074  restore_command);
1075 }
int find_other_exec(const char *argv0, const char *target, const char *versionstr, char *retpath)
Definition: exec.c:322
char * datadir_target
Definition: pg_rewind.c:59
static char * argv0
Definition: pg_ctl.c:97
int pg_strip_crlf(char *str)
Definition: string.c:121
#define pg_log_error(...)
Definition: logging.h:80
#define pg_fatal(...)
Definition: pg_rewind.h:37
bool restore_wal
Definition: pg_rewind.c:68
#define pg_log_debug(...)
Definition: logging.h:92
#define MAXPGPATH
int find_my_exec(const char *argv0, char *retpath)
Definition: exec.c:127
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
#define PG_BACKEND_VERSIONSTR
Definition: port.h:135
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
char * restore_command
Definition: pg_rewind.c:62
char * pipe_read_line(char *cmd, char *line, int maxsize)
Definition: exec.c:358
#define snprintf
Definition: port.h:216
const char * progname
Definition: pg_rewind.c:55

◆ getTimelineHistory()

static TimeLineHistoryEntry* getTimelineHistory ( ControlFileData controlFile,
int *  nentries 
)
static

Definition at line 804 of file pg_rewind.c.

References Assert, TimeLineHistoryEntry::begin, ControlFileData::checkPointCopy, datadir_target, debug, TimeLineHistoryEntry::end, rewind_source::fetch_file, i, InvalidXLogRecPtr, LSN_FORMAT_ARGS, MAXPGPATH, pg_fatal, pg_free(), pg_log_debug, pg_malloc(), rewind_parseTimeLineHistory(), slurpFile(), targetNentries, CheckPoint::ThisTimeLineID, TLHistoryFilePath, and TimeLineHistoryEntry::tli.

Referenced by findCommonAncestorTimeline().

805 {
806  TimeLineHistoryEntry *history;
807  TimeLineID tli;
808 
809  tli = controlFile->checkPointCopy.ThisTimeLineID;
810 
811  /*
812  * Timeline 1 does not have a history file, so there is no need to check
813  * and fake an entry with infinite start and end positions.
814  */
815  if (tli == 1)
816  {
817  history = (TimeLineHistoryEntry *) pg_malloc(sizeof(TimeLineHistoryEntry));
818  history->tli = tli;
819  history->begin = history->end = InvalidXLogRecPtr;
820  *nentries = 1;
821  }
822  else
823  {
824  char path[MAXPGPATH];
825  char *histfile;
826 
827  TLHistoryFilePath(path, tli);
828 
829  /* Get history file from appropriate source */
830  if (controlFile == &ControlFile_source)
831  histfile = source->fetch_file(source, path, NULL);
832  else if (controlFile == &ControlFile_target)
833  histfile = slurpFile(datadir_target, path, NULL);
834  else
835  pg_fatal("invalid control file");
836 
837  history = rewind_parseTimeLineHistory(histfile, tli, nentries);
838  pg_free(histfile);
839  }
840 
841  if (debug)
842  {
843  int i;
844 
845  if (controlFile == &ControlFile_source)
846  pg_log_debug("Source timeline history:");
847  else if (controlFile == &ControlFile_target)
848  pg_log_debug("Target timeline history:");
849  else
850  Assert(false);
851 
852  /*
853  * Print the target timeline history.
854  */
855  for (i = 0; i < targetNentries; i++)
856  {
857  TimeLineHistoryEntry *entry;
858 
859  entry = &history[i];
860  pg_log_debug("%u: %X/%X - %X/%X", entry->tli,
861  LSN_FORMAT_ARGS(entry->begin),
862  LSN_FORMAT_ARGS(entry->end));
863  }
864  }
865 
866  return history;
867 }
char * datadir_target
Definition: pg_rewind.c:59
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
uint32 TimeLineID
Definition: xlogdefs.h:59
static bool debug
Definition: pg_rewind.c:64
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
char *(* fetch_file)(struct rewind_source *, const char *path, size_t *filesize)
Definition: rewind_source.h:37
#define pg_fatal(...)
Definition: pg_rewind.h:37
TimeLineID tli
Definition: timeline.h:27
CheckPoint checkPointCopy
Definition: pg_control.h:131
#define LSN_FORMAT_ARGS(lsn)
Definition: xlogdefs.h:43
#define pg_log_debug(...)
Definition: logging.h:92
#define MAXPGPATH
int targetNentries
Definition: pg_rewind.c:72
XLogRecPtr end
Definition: timeline.h:29
#define Assert(condition)
Definition: c.h:804
static rewind_source * source
Definition: pg_rewind.c:79
void pg_free(void *ptr)
Definition: fe_memutils.c:105
TimeLineID ThisTimeLineID
Definition: pg_control.h:39
XLogRecPtr begin
Definition: timeline.h:28
int i
TimeLineHistoryEntry * rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
Definition: timeline.c:29
static ControlFileData ControlFile_target
Definition: pg_rewind.c:51
static ControlFileData ControlFile_source
Definition: pg_rewind.c:52
char * slurpFile(const char *datadir, const char *path, size_t *filesize)
Definition: file_ops.c:314
#define TLHistoryFilePath(path, tli)

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 108 of file pg_rewind.c.

References _, Assert, calculate_totals(), ControlFileData::checkPoint, ControlFileData::checkPointCopy, CONNECTION_BAD, connstr_source, datadir_source, datadir_target, DB_SHUTDOWNED, DB_SHUTDOWNED_IN_RECOVERY, debug, decide_file_actions(), rewind_source::destroy, digestControlFile(), disconnect_atexit(), do_sync, dry_run, ensureCleanShutdown(), extractPageMap(), fetch_done, rewind_source::fetch_file, fetch_size, filemap_t::fetch_size, filehash_init(), findCommonAncestorTimeline(), findLastCheckpoint(), fprintf, GenerateRecoveryConfig(), get_progname(), get_restricted_token(), GetDataDirectoryCreatePerm(), getopt_long(), getRestoreCommand(), init_libpq_source(), init_local_source(), LSN_FORMAT_ARGS, ControlFileData::minRecoveryPoint, no_argument, optarg, optind, perform_rewind(), pg_fatal, pg_free(), pg_log_error, pg_log_info, pg_logging_increase_verbosity(), pg_logging_init(), pg_mode_mask, pg_strdup(), PG_TEXTDOMAIN, PQconnectdb(), PQerrorMessage(), PQfinish(), PQstatus(), print_filemap(), process_source_file(), process_target_file(), progname, readOneRecord(), required_argument, restore_command, restore_wal, sanityChecks(), set_pglocale_pgservice(), showprogress, slurpFile(), ControlFileData::state, sync_target_dir(), targetNentries, CheckPoint::ThisTimeLineID, filemap_t::total_size, traverse_datadir(), rewind_source::traverse_files, usage(), writerecoveryconf, and WriteRecoveryConfig().

109 {
110  static struct option long_options[] = {
111  {"help", no_argument, NULL, '?'},
112  {"target-pgdata", required_argument, NULL, 'D'},
113  {"write-recovery-conf", no_argument, NULL, 'R'},
114  {"source-pgdata", required_argument, NULL, 1},
115  {"source-server", required_argument, NULL, 2},
116  {"no-ensure-shutdown", no_argument, NULL, 4},
117  {"version", no_argument, NULL, 'V'},
118  {"restore-target-wal", no_argument, NULL, 'c'},
119  {"dry-run", no_argument, NULL, 'n'},
120  {"no-sync", no_argument, NULL, 'N'},
121  {"progress", no_argument, NULL, 'P'},
122  {"debug", no_argument, NULL, 3},
123  {NULL, 0, NULL, 0}
124  };
125  int option_index;
126  int c;
127  XLogRecPtr divergerec;
128  int lastcommontliIndex;
129  XLogRecPtr chkptrec;
130  TimeLineID chkpttli;
131  XLogRecPtr chkptredo;
132  XLogRecPtr target_wal_endrec;
133  size_t size;
134  char *buffer;
135  bool no_ensure_shutdown = false;
136  bool rewind_needed;
137  bool writerecoveryconf = false;
138  filemap_t *filemap;
139 
140  pg_logging_init(argv[0]);
141  set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_rewind"));
142  progname = get_progname(argv[0]);
143 
144  /* Process command-line arguments */
145  if (argc > 1)
146  {
147  if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
148  {
149  usage(progname);
150  exit(0);
151  }
152  if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
153  {
154  puts("pg_rewind (PostgreSQL) " PG_VERSION);
155  exit(0);
156  }
157  }
158 
159  while ((c = getopt_long(argc, argv, "cD:nNPR", long_options, &option_index)) != -1)
160  {
161  switch (c)
162  {
163  case '?':
164  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
165  exit(1);
166 
167  case 'c':
168  restore_wal = true;
169  break;
170 
171  case 'P':
172  showprogress = true;
173  break;
174 
175  case 'n':
176  dry_run = true;
177  break;
178 
179  case 'N':
180  do_sync = false;
181  break;
182 
183  case 'R':
184  writerecoveryconf = true;
185  break;
186 
187  case 3:
188  debug = true;
190  break;
191 
192  case 'D': /* -D or --target-pgdata */
194  break;
195 
196  case 1: /* --source-pgdata */
198  break;
199 
200  case 2: /* --source-server */
202  break;
203 
204  case 4:
205  no_ensure_shutdown = true;
206  break;
207  }
208  }
209 
210  if (datadir_source == NULL && connstr_source == NULL)
211  {
212  pg_log_error("no source specified (--source-pgdata or --source-server)");
213  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
214  exit(1);
215  }
216 
217  if (datadir_source != NULL && connstr_source != NULL)
218  {
219  pg_log_error("only one of --source-pgdata or --source-server can be specified");
220  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
221  exit(1);
222  }
223 
224  if (datadir_target == NULL)
225  {
226  pg_log_error("no target data directory specified (--target-pgdata)");
227  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
228  exit(1);
229  }
230 
231  if (writerecoveryconf && connstr_source == NULL)
232  {
233  pg_log_error("no source server information (--source-server) specified for --write-recovery-conf");
234  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
235  exit(1);
236  }
237 
238  if (optind < argc)
239  {
240  pg_log_error("too many command-line arguments (first is \"%s\")",
241  argv[optind]);
242  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
243  exit(1);
244  }
245 
246  /*
247  * Don't allow pg_rewind to be run as root, to avoid overwriting the
248  * ownership of files in the data directory. We need only check for root
249  * -- any other user won't have sufficient permissions to modify files in
250  * the data directory.
251  */
252 #ifndef WIN32
253  if (geteuid() == 0)
254  {
255  pg_log_error("cannot be executed by \"root\"");
256  fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
257  progname);
258  exit(1);
259  }
260 #endif
261 
263 
264  /* Set mask based on PGDATA permissions */
266  {
267  pg_log_error("could not read permissions of directory \"%s\": %m",
269  exit(1);
270  }
271 
272  umask(pg_mode_mask);
273 
274  getRestoreCommand(argv[0]);
275 
276  atexit(disconnect_atexit);
277 
278  /*
279  * Ok, we have all the options and we're ready to start. First, connect to
280  * remote server.
281  */
282  if (connstr_source)
283  {
285 
286  if (PQstatus(conn) == CONNECTION_BAD)
287  pg_fatal("%s", PQerrorMessage(conn));
288 
289  if (showprogress)
290  pg_log_info("connected to server");
291 
293  }
294  else
296 
297  /*
298  * Check the status of the target instance.
299  *
300  * If the target instance was not cleanly shut down, start and stop the
301  * target cluster once in single-user mode to enforce recovery to finish,
302  * ensuring that the cluster can be used by pg_rewind. Note that if
303  * no_ensure_shutdown is specified, pg_rewind ignores this step, and users
304  * need to make sure by themselves that the target cluster is in a clean
305  * state.
306  */
307  buffer = slurpFile(datadir_target, "global/pg_control", &size);
308  digestControlFile(&ControlFile_target, buffer, size);
309  pg_free(buffer);
310 
311  if (!no_ensure_shutdown &&
314  {
315  ensureCleanShutdown(argv[0]);
316 
317  buffer = slurpFile(datadir_target, "global/pg_control", &size);
318  digestControlFile(&ControlFile_target, buffer, size);
319  pg_free(buffer);
320  }
321 
322  buffer = source->fetch_file(source, "global/pg_control", &size);
323  digestControlFile(&ControlFile_source, buffer, size);
324  pg_free(buffer);
325 
326  sanityChecks();
327 
328  /*
329  * Find the common ancestor timeline between the clusters.
330  *
331  * If both clusters are already on the same timeline, there's nothing to
332  * do.
333  */
336  {
337  pg_log_info("source and target cluster are on the same timeline");
338  rewind_needed = false;
339  target_wal_endrec = 0;
340  }
341  else
342  {
343  XLogRecPtr chkptendrec;
344 
345  findCommonAncestorTimeline(&divergerec, &lastcommontliIndex);
346  pg_log_info("servers diverged at WAL location %X/%X on timeline %u",
347  LSN_FORMAT_ARGS(divergerec),
348  targetHistory[lastcommontliIndex].tli);
349 
350  /*
351  * Determine the end-of-WAL on the target.
352  *
353  * The WAL ends at the last shutdown checkpoint, or at
354  * minRecoveryPoint if it was a standby. (If we supported rewinding a
355  * server that was not shut down cleanly, we would need to replay
356  * until we reach the first invalid record, like crash recovery does.)
357  */
358 
359  /* read the checkpoint record on the target to see where it ends. */
360  chkptendrec = readOneRecord(datadir_target,
362  targetNentries - 1,
364 
365  if (ControlFile_target.minRecoveryPoint > chkptendrec)
366  {
367  target_wal_endrec = ControlFile_target.minRecoveryPoint;
368  }
369  else
370  {
371  target_wal_endrec = chkptendrec;
372  }
373 
374  /*
375  * Check for the possibility that the target is in fact a direct
376  * ancestor of the source. In that case, there is no divergent history
377  * in the target that needs rewinding.
378  */
379  if (target_wal_endrec > divergerec)
380  {
381  rewind_needed = true;
382  }
383  else
384  {
385  /* the last common checkpoint record must be part of target WAL */
386  Assert(target_wal_endrec == divergerec);
387 
388  rewind_needed = false;
389  }
390  }
391 
392  if (!rewind_needed)
393  {
394  pg_log_info("no rewind required");
395  if (writerecoveryconf && !dry_run)
398  exit(0);
399  }
400 
401  findLastCheckpoint(datadir_target, divergerec, lastcommontliIndex,
402  &chkptrec, &chkpttli, &chkptredo, restore_command);
403  pg_log_info("rewinding from last common checkpoint at %X/%X on timeline %u",
404  LSN_FORMAT_ARGS(chkptrec), chkpttli);
405 
406  /* Initialize the hash table to track the status of each file */
407  filehash_init();
408 
409  /*
410  * Collect information about all files in the both data directories.
411  */
412  if (showprogress)
413  pg_log_info("reading source file list");
415 
416  if (showprogress)
417  pg_log_info("reading target file list");
419 
420  /*
421  * Read the target WAL from last checkpoint before the point of fork, to
422  * extract all the pages that were modified on the target cluster after
423  * the fork.
424  */
425  if (showprogress)
426  pg_log_info("reading WAL in target");
427  extractPageMap(datadir_target, chkptrec, lastcommontliIndex,
428  target_wal_endrec, restore_command);
429 
430  /*
431  * We have collected all information we need from both systems. Decide
432  * what to do with each file.
433  */
434  filemap = decide_file_actions();
435  if (showprogress)
436  calculate_totals(filemap);
437 
438  /* this is too verbose even for verbose mode */
439  if (debug)
440  print_filemap(filemap);
441 
442  /*
443  * Ok, we're ready to start copying things over.
444  */
445  if (showprogress)
446  {
447  pg_log_info("need to copy %lu MB (total source directory size is %lu MB)",
448  (unsigned long) (filemap->fetch_size / (1024 * 1024)),
449  (unsigned long) (filemap->total_size / (1024 * 1024)));
450 
451  fetch_size = filemap->fetch_size;
452  fetch_done = 0;
453  }
454 
455  /*
456  * We have now collected all the information we need from both systems,
457  * and we are ready to start modifying the target directory.
458  *
459  * This is the point of no return. Once we start copying things, there is
460  * no turning back!
461  */
462  perform_rewind(filemap, source, chkptrec, chkpttli, chkptredo);
463 
464  if (showprogress)
465  pg_log_info("syncing target data directory");
466  sync_target_dir();
467 
468  /* Also update the standby configuration, if requested. */
469  if (writerecoveryconf && !dry_run)
472 
473  /* don't need the source connection anymore */
475  if (conn)
476  {
477  PQfinish(conn);
478  conn = NULL;
479  }
480 
481  pg_log_info("Done!");
482 
483  return 0;
484 }
void extractPageMap(const char *datadir, XLogRecPtr startpoint, int tliIndex, XLogRecPtr endpoint, const char *restoreCommand)
Definition: parsexlog.c:63
char * datadir_target
Definition: pg_rewind.c:59
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6737
static void perform_rewind(filemap_t *filemap, rewind_source *source, XLogRecPtr chkptrec, TimeLineID chkpttli, XLogRecPtr chkptredo)
Definition: pg_rewind.c:493
uint32 TimeLineID
Definition: xlogdefs.h:59
static bool debug
Definition: pg_rewind.c:64
TimeLineHistoryEntry * targetHistory
Definition: pg_rewind.c:71
bool dry_run
Definition: pg_rewind.c:66
const char * get_progname(const char *argv0)
Definition: path.c:453
#define pg_log_error(...)
Definition: logging.h:80
rewind_source * init_local_source(const char *datadir)
Definition: local_source.c:38
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition: getopt_long.c:57
void get_restricted_token(void)
void traverse_datadir(const char *datadir, process_file_callback_t callback)
Definition: file_ops.c:362
void pg_logging_init(const char *argv0)
Definition: logging.c:81
void WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
Definition: recovery_gen.c:117
void(* traverse_files)(struct rewind_source *, process_file_callback_t callback)
Definition: rewind_source.h:29
static void ensureCleanShutdown(const char *argv0)
Definition: pg_rewind.c:1083
char *(* fetch_file)(struct rewind_source *, const char *path, size_t *filesize)
Definition: rewind_source.h:37
static bool writerecoveryconf
void findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex, XLogRecPtr *lastchkptrec, TimeLineID *lastchkpttli, XLogRecPtr *lastchkptredo, const char *restoreCommand)
Definition: parsexlog.c:164
#define pg_fatal(...)
Definition: pg_rewind.h:37
void PQfinish(PGconn *conn)
Definition: fe-connect.c:4224
CheckPoint checkPointCopy
Definition: pg_control.h:131
#define fprintf
Definition: port.h:220
void filehash_init(void)
Definition: filemap.c:169
void process_target_file(const char *path, file_type_t type, size_t size, const char *link_target)
Definition: filemap.c:255
uint64 fetch_size
Definition: filemap.h:93
bool showprogress
Definition: pg_rewind.c:65
#define LSN_FORMAT_ARGS(lsn)
Definition: xlogdefs.h:43
#define required_argument
Definition: getopt_long.h:25
int optind
Definition: getopt.c:50
rewind_source * init_libpq_source(PGconn *conn)
Definition: libpq_source.c:81
bool restore_wal
Definition: pg_rewind.c:68
uint64 fetch_size
Definition: pg_rewind.c:75
char * c
static void usage(const char *progname)
Definition: pg_rewind.c:82
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
uint64 total_size
Definition: filemap.h:92
filemap_t * decide_file_actions(void)
Definition: filemap.c:789
static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex)
Definition: pg_rewind.c:881
PQExpBuffer GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
Definition: recovery_gen.c:23
char * connstr_source
Definition: pg_rewind.c:61
#define no_argument
Definition: getopt_long.h:24
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1215
void print_filemap(filemap_t *filemap)
Definition: filemap.c:480
static void disconnect_atexit(void)
Definition: pg_rewind.c:1138
int targetNentries
Definition: pg_rewind.c:72
void sync_target_dir(void)
Definition: file_ops.c:294
void(* destroy)(struct rewind_source *)
Definition: rewind_source.h:63
char * datadir_source
Definition: pg_rewind.c:60
void calculate_totals(filemap_t *filemap)
Definition: filemap.c:439
bool do_sync
Definition: pg_rewind.c:67
char * restore_command
Definition: pg_rewind.c:62
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
static rewind_source * source
Definition: pg_rewind.c:79
void pg_logging_increase_verbosity(void)
Definition: logging.c:174
XLogRecPtr readOneRecord(const char *datadir, XLogRecPtr ptr, int tliIndex, const char *restoreCommand)
Definition: parsexlog.c:120
void pg_free(void *ptr)
Definition: fe_memutils.c:105
void process_source_file(const char *path, file_type_t type, size_t size, const char *link_target)
Definition: filemap.c:219
static void getRestoreCommand(const char *argv0)
Definition: pg_rewind.c:1020
static void sanityChecks(void)
Definition: pg_rewind.c:675
static PGconn * conn
Definition: pg_rewind.c:78
bool GetDataDirectoryCreatePerm(const char *dataDir)
TimeLineID ThisTimeLineID
Definition: pg_control.h:39
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:433
char * optarg
Definition: getopt.c:52
static void digestControlFile(ControlFileData *ControlFile, const char *content, size_t size)
Definition: pg_rewind.c:991
static ControlFileData ControlFile_target
Definition: pg_rewind.c:51
uint64 fetch_done
Definition: pg_rewind.c:76
static ControlFileData ControlFile_source
Definition: pg_rewind.c:52
ConnStatusType PQstatus(const PGconn *conn)
Definition: fe-connect.c:6684
#define _(x)
Definition: elog.c:89
int pg_mode_mask
Definition: file_perm.c:25
XLogRecPtr checkPoint
Definition: pg_control.h:129
#define pg_log_info(...)
Definition: logging.h:88
PGconn * PQconnectdb(const char *conninfo)
Definition: fe-connect.c:713
const char * progname
Definition: pg_rewind.c:55
XLogRecPtr minRecoveryPoint
Definition: pg_control.h:166
char * slurpFile(const char *datadir, const char *path, size_t *filesize)
Definition: file_ops.c:314

◆ MinXLogRecPtr()

static XLogRecPtr MinXLogRecPtr ( XLogRecPtr  a,
XLogRecPtr  b 
)
static

Definition at line 789 of file pg_rewind.c.

References Min, and XLogRecPtrIsInvalid.

Referenced by findCommonAncestorTimeline().

790 {
791  if (XLogRecPtrIsInvalid(a))
792  return b;
793  else if (XLogRecPtrIsInvalid(b))
794  return a;
795  else
796  return Min(a, b);
797 }
#define Min(x, y)
Definition: c.h:986
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29

◆ perform_rewind()

static void perform_rewind ( filemap_t filemap,
rewind_source source,
XLogRecPtr  chkptrec,
TimeLineID  chkpttli,
XLogRecPtr  chkptredo 
)
static

Definition at line 493 of file pg_rewind.c.

References file_entry_t::action, datapagemap::bitmapsize, ControlFileData::checkPoint, ControlFileData::checkPointCopy, close_target_file(), connstr_source, create_target(), createBackupLabel(), datadir_source, datadir_target, datapagemap_iterate(), datapagemap_next(), DB_IN_ARCHIVE_RECOVERY, DB_IN_PRODUCTION, digestControlFile(), do_sync, dry_run, filemap_t::entries, rewind_source::fetch_file, FILE_ACTION_COPY, FILE_ACTION_COPY_TAIL, FILE_ACTION_CREATE, FILE_ACTION_NONE, FILE_ACTION_REMOVE, FILE_ACTION_TRUNCATE, FILE_ACTION_UNDECIDED, rewind_source::finish_fetch, rewind_source::get_current_wal_insert_lsn, i, ControlFileData::minRecoveryPoint, ControlFileData::minRecoveryPointTLI, filemap_t::nentries, open_target_file(), file_entry_t::path, pg_fatal, pg_free(), pg_log_info, progress_report(), rewind_source::queue_fetch_range, CheckPoint::redo, remove_target(), showprogress, file_entry_t::source_size, ControlFileData::state, file_entry_t::target_pages_to_overwrite, file_entry_t::target_size, CheckPoint::ThisTimeLineID, truncate_target_file(), and update_controlfile().

Referenced by main().

497 {
498  XLogRecPtr endrec;
499  TimeLineID endtli;
500  ControlFileData ControlFile_new;
501  size_t size;
502  char *buffer;
503 
504  /*
505  * Execute the actions in the file map, fetching data from the source
506  * system as needed.
507  */
508  for (int i = 0; i < filemap->nentries; i++)
509  {
510  file_entry_t *entry = filemap->entries[i];
511 
512  /*
513  * If this is a relation file, copy the modified blocks.
514  *
515  * This is in addition to any other changes.
516  */
517  if (entry->target_pages_to_overwrite.bitmapsize > 0)
518  {
520  BlockNumber blkno;
521  off_t offset;
522 
524  while (datapagemap_next(iter, &blkno))
525  {
526  offset = blkno * BLCKSZ;
527  source->queue_fetch_range(source, entry->path, offset, BLCKSZ);
528  }
529  pg_free(iter);
530  }
531 
532  switch (entry->action)
533  {
534  case FILE_ACTION_NONE:
535  /* nothing else to do */
536  break;
537 
538  case FILE_ACTION_COPY:
539  /* Truncate the old file out of the way, if any */
540  open_target_file(entry->path, true);
541  source->queue_fetch_range(source, entry->path,
542  0, entry->source_size);
543  break;
544 
546  truncate_target_file(entry->path, entry->source_size);
547  break;
548 
550  source->queue_fetch_range(source, entry->path,
551  entry->target_size,
552  entry->source_size - entry->target_size);
553  break;
554 
555  case FILE_ACTION_REMOVE:
556  remove_target(entry);
557  break;
558 
559  case FILE_ACTION_CREATE:
560  create_target(entry);
561  break;
562 
564  pg_fatal("no action decided for file \"%s\"", entry->path);
565  break;
566  }
567  }
568 
569  /* Complete any remaining range-fetches that we queued up above. */
570  source->finish_fetch(source);
571 
573 
574  progress_report(true);
575 
576  /*
577  * Fetch the control file from the source last. This ensures that the
578  * minRecoveryPoint is up-to-date.
579  */
580  buffer = source->fetch_file(source, "global/pg_control", &size);
582  pg_free(buffer);
583 
584  /*
585  * Sanity check: If the source is a local system, the control file should
586  * not have changed since we started.
587  *
588  * XXX: We assume it hasn't been modified, but actually, what could go
589  * wrong? The logic handles a libpq source that's modified concurrently,
590  * why not a local datadir?
591  */
592  if (datadir_source &&
594  sizeof(ControlFileData)) != 0)
595  {
596  pg_fatal("source system was modified while pg_rewind was running");
597  }
598 
599  if (showprogress)
600  pg_log_info("creating backup label and updating control file");
601 
602  /*
603  * Create a backup label file, to tell the target where to begin the WAL
604  * replay. Normally, from the last common checkpoint between the source
605  * and the target. But if the source is a standby server, it's possible
606  * that the last common checkpoint is *after* the standby's restartpoint.
607  * That implies that the source server has applied the checkpoint record,
608  * but hasn't performed a corresponding restartpoint yet. Make sure we
609  * start at the restartpoint's redo point in that case.
610  *
611  * Use the old version of the source's control file for this. The server
612  * might have finished the restartpoint after we started copying files,
613  * but we must begin from the redo point at the time that started copying.
614  */
615  if (ControlFile_source.checkPointCopy.redo < chkptredo)
616  {
619  chkptrec = ControlFile_source.checkPoint;
620  }
621  createBackupLabel(chkptredo, chkpttli, chkptrec);
622 
623  /*
624  * Update control file of target, to tell the target how far it must
625  * replay the WAL (minRecoveryPoint).
626  */
627  if (connstr_source)
628  {
629  /*
630  * The source is a live server. Like in an online backup, it's
631  * important that we recover all the WAL that was generated while we
632  * were copying files.
633  */
635  {
636  /*
637  * Source is a standby server. We must replay to its
638  * minRecoveryPoint.
639  */
642  }
643  else
644  {
645  /*
646  * Source is a production, non-standby, server. We must replay to
647  * the last WAL insert location.
648  */
650  pg_fatal("source system was in unexpected state at end of rewind");
651 
652  endrec = source->get_current_wal_insert_lsn(source);
654  }
655  }
656  else
657  {
658  /*
659  * Source is a local data directory. It should've shut down cleanly,
660  * and we must replay to the latest shutdown checkpoint.
661  */
664  }
665 
666  memcpy(&ControlFile_new, &ControlFile_source_after, sizeof(ControlFileData));
667  ControlFile_new.minRecoveryPoint = endrec;
668  ControlFile_new.minRecoveryPointTLI = endtli;
669  ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;
670  if (!dry_run)
671  update_controlfile(datadir_target, &ControlFile_new, do_sync);
672 }
char * datadir_target
Definition: pg_rewind.c:59
void open_target_file(const char *path, bool trunc)
Definition: file_ops.c:47
void(* finish_fetch)(struct rewind_source *)
Definition: rewind_source.h:53
uint32 TimeLineID
Definition: xlogdefs.h:59
TimeLineID minRecoveryPointTLI
Definition: pg_control.h:167
bool dry_run
Definition: pg_rewind.c:66
char *(* fetch_file)(struct rewind_source *, const char *path, size_t *filesize)
Definition: rewind_source.h:37
#define pg_fatal(...)
Definition: pg_rewind.h:37
uint32 BlockNumber
Definition: block.h:31
CheckPoint checkPointCopy
Definition: pg_control.h:131
static ControlFileData ControlFile_source_after
Definition: pg_rewind.c:53
void truncate_target_file(const char *path, off_t newsize)
Definition: file_ops.c:206
size_t target_size
Definition: filemap.h:61
bool showprogress
Definition: pg_rewind.c:65
void update_controlfile(const char *DataDir, ControlFileData *ControlFile, bool do_sync)
bool datapagemap_next(datapagemap_iterator_t *iter, BlockNumber *blkno)
Definition: datapagemap.c:87
XLogRecPtr(* get_current_wal_insert_lsn)(struct rewind_source *)
Definition: rewind_source.h:58
file_action_t action
Definition: filemap.h:81
size_t source_size
Definition: filemap.h:75
const char * path
Definition: filemap.h:53
void(* queue_fetch_range)(struct rewind_source *, const char *path, off_t offset, size_t len)
Definition: rewind_source.h:47
char * connstr_source
Definition: pg_rewind.c:61
datapagemap_t target_pages_to_overwrite
Definition: filemap.h:68
char * datadir_source
Definition: pg_rewind.c:60
int bitmapsize
Definition: datapagemap.h:18
bool do_sync
Definition: pg_rewind.c:67
void remove_target(file_entry_t *entry)
Definition: file_ops.c:130
uint64 XLogRecPtr
Definition: xlogdefs.h:21
void progress_report(bool finished)
Definition: pg_rewind.c:733
datapagemap_iterator_t * datapagemap_iterate(datapagemap_t *map)
Definition: datapagemap.c:75
void pg_free(void *ptr)
Definition: fe_memutils.c:105
void close_target_file(void)
Definition: file_ops.c:75
int nentries
Definition: filemap.h:95
TimeLineID ThisTimeLineID
Definition: pg_control.h:39
int i
Definition: filemap.h:49
static void digestControlFile(ControlFileData *ControlFile, const char *content, size_t size)
Definition: pg_rewind.c:991
file_entry_t * entries[FLEXIBLE_ARRAY_MEMBER]
Definition: filemap.h:96
void create_target(file_entry_t *entry)
Definition: file_ops.c:156
static ControlFileData ControlFile_source
Definition: pg_rewind.c:52
static void createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli, XLogRecPtr checkpointloc)
Definition: pg_rewind.c:929
XLogRecPtr checkPoint
Definition: pg_control.h:129
XLogRecPtr redo
Definition: pg_control.h:37
#define pg_log_info(...)
Definition: logging.h:88
XLogRecPtr minRecoveryPoint
Definition: pg_control.h:166

◆ progress_report()

void progress_report ( bool  finished)

Definition at line 733 of file pg_rewind.c.

References _, fetch_done, fetch_size, fprintf, INT64_FORMAT, last_progress_report, now(), showprogress, and snprintf.

Referenced by perform_rewind().

734 {
735  static pg_time_t last_progress_report = 0;
736  int percent;
737  char fetch_done_str[32];
738  char fetch_size_str[32];
739  pg_time_t now;
740 
741  if (!showprogress)
742  return;
743 
744  now = time(NULL);
745  if (now == last_progress_report && !finished)
746  return; /* Max once per second */
747 
748  last_progress_report = now;
749  percent = fetch_size ? (int) ((fetch_done) * 100 / fetch_size) : 0;
750 
751  /*
752  * Avoid overflowing past 100% or the full size. This may make the total
753  * size number change as we approach the end of the backup (the estimate
754  * will always be wrong if WAL is included), but that's better than having
755  * the done column be bigger than the total.
756  */
757  if (percent > 100)
758  percent = 100;
759  if (fetch_done > fetch_size)
761 
762  /*
763  * Separate step to keep platform-dependent format code out of
764  * translatable strings. And we only test for INT64_FORMAT availability
765  * in snprintf, not fprintf.
766  */
767  snprintf(fetch_done_str, sizeof(fetch_done_str), INT64_FORMAT,
768  fetch_done / 1024);
769  snprintf(fetch_size_str, sizeof(fetch_size_str), INT64_FORMAT,
770  fetch_size / 1024);
771 
772  fprintf(stderr, _("%*s/%s kB (%d%%) copied"),
773  (int) strlen(fetch_size_str), fetch_done_str, fetch_size_str,
774  percent);
775 
776  /*
777  * Stay on the same line if reporting to a terminal and we're not done
778  * yet.
779  */
780  fputc((!finished && isatty(fileno(stderr))) ? '\r' : '\n', stderr);
781 }
int64 pg_time_t
Definition: pgtime.h:23
#define fprintf
Definition: port.h:220
bool showprogress
Definition: pg_rewind.c:65
uint64 fetch_size
Definition: pg_rewind.c:75
static pg_time_t last_progress_report
Definition: pg_amcheck.c:146
#define INT64_FORMAT
Definition: c.h:483
uint64 fetch_done
Definition: pg_rewind.c:76
#define snprintf
Definition: port.h:216
#define _(x)
Definition: elog.c:89
Datum now(PG_FUNCTION_ARGS)
Definition: timestamp.c:1544

◆ sanityChecks()

static void sanityChecks ( void  )
static

Definition at line 675 of file pg_rewind.c.

References CATALOG_VERSION_NO, ControlFileData::catalog_version_no, ControlFileData::data_checksum_version, datadir_source, DB_SHUTDOWNED, DB_SHUTDOWNED_IN_RECOVERY, PG_CONTROL_VERSION, ControlFileData::pg_control_version, PG_DATA_CHECKSUM_VERSION, pg_fatal, ControlFileData::state, ControlFileData::system_identifier, and ControlFileData::wal_log_hints.

Referenced by main().

676 {
677  /* TODO Check that there's no backup_label in either cluster */
678 
679  /* Check system_identifier match */
681  pg_fatal("source and target clusters are from different systems");
682 
683  /* check version */
688  {
689  pg_fatal("clusters are not compatible with this version of pg_rewind");
690  }
691 
692  /*
693  * Target cluster need to use checksums or hint bit wal-logging, this to
694  * prevent from data corruption that could occur because of hint bits.
695  */
698  {
699  pg_fatal("target server needs to use either data checksums or \"wal_log_hints = on\"");
700  }
701 
702  /*
703  * Target cluster better not be running. This doesn't guard against
704  * someone starting the cluster concurrently. Also, this is probably more
705  * strict than necessary; it's OK if the target node was not shut down
706  * cleanly, as long as it isn't running at the moment.
707  */
710  pg_fatal("target server must be shut down cleanly");
711 
712  /*
713  * When the source is a data directory, also require that the source
714  * server is shut down. There isn't any very strong reason for this
715  * limitation, but better safe than sorry.
716  */
717  if (datadir_source &&
720  pg_fatal("source data directory must be shut down cleanly");
721 }
#define pg_fatal(...)
Definition: pg_rewind.h:37
#define CATALOG_VERSION_NO
Definition: catversion.h:56
#define PG_CONTROL_VERSION
Definition: pg_control.h:25
uint32 pg_control_version
Definition: pg_control.h:121
uint64 system_identifier
Definition: pg_control.h:106
uint32 data_checksum_version
Definition: pg_control.h:220
char * datadir_source
Definition: pg_rewind.c:60
uint32 catalog_version_no
Definition: pg_control.h:122
#define PG_DATA_CHECKSUM_VERSION
Definition: bufpage.h:200
static ControlFileData ControlFile_target
Definition: pg_rewind.c:51
static ControlFileData ControlFile_source
Definition: pg_rewind.c:52

◆ usage()

static void usage ( const char *  progname)
static

Definition at line 82 of file pg_rewind.c.

References _, and printf.

Referenced by main().

83 {
84  printf(_("%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n\n"), progname);
85  printf(_("Usage:\n %s [OPTION]...\n\n"), progname);
86  printf(_("Options:\n"));
87  printf(_(" -c, --restore-target-wal use restore_command in target configuration to\n"
88  " retrieve WAL files from archives\n"));
89  printf(_(" -D, --target-pgdata=DIRECTORY existing data directory to modify\n"));
90  printf(_(" --source-pgdata=DIRECTORY source data directory to synchronize with\n"));
91  printf(_(" --source-server=CONNSTR source server to synchronize with\n"));
92  printf(_(" -n, --dry-run stop before modifying anything\n"));
93  printf(_(" -N, --no-sync do not wait for changes to be written\n"
94  " safely to disk\n"));
95  printf(_(" -P, --progress write progress messages\n"));
96  printf(_(" -R, --write-recovery-conf write configuration for replication\n"
97  " (requires --source-server)\n"));
98  printf(_(" --debug write a lot of debug messages\n"));
99  printf(_(" --no-ensure-shutdown do not automatically fix unclean shutdown\n"));
100  printf(_(" -V, --version output version information, then exit\n"));
101  printf(_(" -?, --help show this help, then exit\n"));
102  printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
103  printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
104 }
#define printf(...)
Definition: port.h:222
#define _(x)
Definition: elog.c:89
const char * progname
Definition: pg_rewind.c:55

Variable Documentation

◆ conn

PGconn* conn
static

Definition at line 78 of file pg_rewind.c.

◆ connstr_source

char* connstr_source = NULL

Definition at line 61 of file pg_rewind.c.

Referenced by main(), and perform_rewind().

◆ ControlFile_source

ControlFileData ControlFile_source
static

Definition at line 52 of file pg_rewind.c.

◆ ControlFile_source_after

ControlFileData ControlFile_source_after
static

Definition at line 53 of file pg_rewind.c.

◆ ControlFile_target

ControlFileData ControlFile_target
static

Definition at line 51 of file pg_rewind.c.

◆ datadir_source

char* datadir_source = NULL

Definition at line 60 of file pg_rewind.c.

Referenced by main(), perform_rewind(), and sanityChecks().

◆ datadir_target

◆ debug

bool debug = false
static

Definition at line 64 of file pg_rewind.c.

Referenced by getTimelineHistory(), and main().

◆ do_sync

bool do_sync = true

Definition at line 67 of file pg_rewind.c.

Referenced by main(), and perform_rewind().

◆ dry_run

◆ fetch_done

uint64 fetch_done

Definition at line 76 of file pg_rewind.c.

Referenced by main(), progress_report(), and write_target_range().

◆ fetch_size

uint64 fetch_size

Definition at line 75 of file pg_rewind.c.

Referenced by main(), and progress_report().

◆ progname

const char* progname

Definition at line 55 of file pg_rewind.c.

Referenced by ensureCleanShutdown(), getRestoreCommand(), and main().

◆ restore_command

char* restore_command = NULL

Definition at line 62 of file pg_rewind.c.

Referenced by getRestoreCommand(), and main().

◆ restore_wal

bool restore_wal = false

Definition at line 68 of file pg_rewind.c.

Referenced by getRestoreCommand(), and main().

◆ showprogress

bool showprogress = false

Definition at line 65 of file pg_rewind.c.

Referenced by main(), perform_rewind(), and progress_report().

◆ source

◆ targetHistory

TimeLineHistoryEntry* targetHistory

Definition at line 71 of file pg_rewind.c.

Referenced by SimpleXLogPageRead().

◆ targetNentries

int targetNentries

◆ WalSegSz

int WalSegSz

Definition at line 56 of file pg_rewind.c.

Referenced by createBackupLabel(), and digestControlFile().