44 const char *content,
size_t size);
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);
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);
110 static struct option long_options[] = {
128 int lastcommontliIndex;
135 bool no_ensure_shutdown =
false;
147 if (strcmp(argv[1],
"--help") == 0 || strcmp(argv[1],
"-?") == 0)
152 if (strcmp(argv[1],
"--version") == 0 || strcmp(argv[1],
"-V") == 0)
154 puts(
"pg_rewind (PostgreSQL) " PG_VERSION);
159 while ((c =
getopt_long(argc, argv,
"cD:nNPR", long_options, &option_index)) != -1)
184 writerecoveryconf =
true;
205 no_ensure_shutdown =
true;
212 pg_log_error(
"no source specified (--source-pgdata or --source-server)");
219 pg_log_error(
"only one of --source-pgdata or --source-server can be specified");
226 pg_log_error(
"no target data directory specified (--target-pgdata)");
233 pg_log_error(
"no source server information (--source-server) specified for --write-recovery-conf");
240 pg_log_error(
"too many command-line arguments (first is \"%s\")",
256 fprintf(stderr,
_(
"You must run %s as the PostgreSQL superuser.\n"),
267 pg_log_error(
"could not read permissions of directory \"%s\": %m",
311 if (!no_ensure_shutdown &&
322 buffer = source->
fetch_file(source,
"global/pg_control", &size);
337 pg_log_info(
"source and target cluster are on the same timeline");
338 rewind_needed =
false;
339 target_wal_endrec = 0;
346 pg_log_info(
"servers diverged at WAL location %X/%X on timeline %u",
348 targetHistory[lastcommontliIndex].tli);
371 target_wal_endrec = chkptendrec;
379 if (target_wal_endrec > divergerec)
381 rewind_needed =
true;
386 Assert(target_wal_endrec == divergerec);
388 rewind_needed =
false;
395 if (writerecoveryconf && !
dry_run)
403 pg_log_info(
"rewinding from last common checkpoint at %X/%X on timeline %u",
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)));
469 if (writerecoveryconf && !
dry_run)
526 offset = blkno * BLCKSZ;
580 buffer = source->
fetch_file(source,
"global/pg_control", &size);
593 memcmp(&ControlFile_source, &ControlFile_source_after,
596 pg_fatal(
"source system was modified while pg_rewind was running");
600 pg_log_info(
"creating backup label and updating control file");
650 pg_fatal(
"source system was in unexpected state at end of rewind");
666 memcpy(&ControlFile_new, &ControlFile_source_after,
sizeof(
ControlFileData));
681 pg_fatal(
"source and target clusters are from different systems");
689 pg_fatal(
"clusters are not compatible with this version of pg_rewind");
699 pg_fatal(
"target server needs to use either data checksums or \"wal_log_hints = on\"");
710 pg_fatal(
"target server must be shut down cleanly");
720 pg_fatal(
"source data directory must be shut down cleanly");
737 char fetch_done_str[32];
738 char fetch_size_str[32];
745 if (now == last_progress_report && !finished)
748 last_progress_report =
now;
772 fprintf(stderr,
_(
"%*s/%s kB (%d%%) copied"),
773 (
int) strlen(fetch_size_str), fetch_done_str, fetch_size_str,
780 fputc((!finished && isatty(fileno(stderr))) ?
'\r' :
'\n', stderr);
830 if (controlFile == &ControlFile_source)
831 histfile = source->
fetch_file(source, path, NULL);
832 else if (controlFile == &ControlFile_target)
845 if (controlFile == &ControlFile_source)
847 else if (controlFile == &ControlFile_target)
901 for (i = 0; i < n; i++)
903 if (sourceHistory[i].tli != targetHistory[i].tli ||
904 sourceHistory[i].begin != targetHistory[i].begin)
911 *recptr =
MinXLogRecPtr(sourceHistory[i].end, targetHistory[i].end);
919 pg_fatal(
"could not find common ancestor of the source and target cluster's timelines");
945 stamp_time = time(NULL);
946 tmp = localtime(&stamp_time);
947 strftime(strfbuf,
sizeof(strfbuf),
"%Y-%m-%d %H:%M:%S %Z", tmp);
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" 959 if (len >=
sizeof(buf))
960 pg_fatal(
"backup label buffer too small");
983 pg_fatal(
"unexpected control file CRC");
995 pg_fatal(
"unexpected control file size %d, expected %d",
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",
1033 postgres_exec_path);
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.",
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.",
1059 snprintf(postgres_cmd,
sizeof(postgres_cmd),
1060 "\"%s\" -D \"%s\" -C restore_command",
1063 if (!
pipe_read_line(postgres_cmd, cmd_output,
sizeof(cmd_output)))
1068 if (strcmp(cmd_output,
"") == 0)
1069 pg_fatal(
"restore_command is not set in the target cluster");
1073 pg_log_debug(
"using for rewind restore_command = \'%s\'",
1086 #define MAXCMDLEN (2 * MAXPGPATH) 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.",
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.",
1112 pg_log_info(
"executing \"%s\" for target server to complete crash recovery",
1130 if (system(cmd) != 0)
1132 pg_log_error(
"postgres single-user mode in target cluster failed");
void extractPageMap(const char *datadir, XLogRecPtr startpoint, int tliIndex, XLogRecPtr endpoint, const char *restoreCommand)
#define IsValidWalSegSize(size)
int find_other_exec(const char *argv0, const char *target, const char *versionstr, char *retpath)
char * PQerrorMessage(const PGconn *conn)
#define InvalidXLogRecPtr
void open_target_file(const char *path, bool trunc)
void(* finish_fetch)(struct rewind_source *)
static void perform_rewind(filemap_t *filemap, rewind_source *source, XLogRecPtr chkptrec, TimeLineID chkpttli, XLogRecPtr chkptredo)
void write_target_range(char *buf, off_t begin, size_t size)
int pg_strip_crlf(char *str)
int main(int argc, char **argv)
void * pg_malloc(size_t size)
TimeLineID minRecoveryPointTLI
TimeLineHistoryEntry * targetHistory
const char * get_progname(const char *argv0)
#define pg_log_error(...)
rewind_source * init_local_source(const char *datadir)
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
void get_restricted_token(void)
void traverse_datadir(const char *datadir, process_file_callback_t callback)
void pg_logging_init(const char *argv0)
void WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
void(* traverse_files)(struct rewind_source *, process_file_callback_t callback)
static void ensureCleanShutdown(const char *argv0)
static XLogRecPtr MinXLogRecPtr(XLogRecPtr a, XLogRecPtr b)
char *(* fetch_file)(struct rewind_source *, const char *path, size_t *filesize)
static bool writerecoveryconf
void findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex, XLogRecPtr *lastchkptrec, TimeLineID *lastchkpttli, XLogRecPtr *lastchkptredo, const char *restoreCommand)
void PQfinish(PGconn *conn)
#define CATALOG_VERSION_NO
#define PG_CONTROL_VERSION
uint32 pg_control_version
CheckPoint checkPointCopy
void process_target_file(const char *path, file_type_t type, size_t size, const char *link_target)
static ControlFileData ControlFile_source_after
void truncate_target_file(const char *path, off_t newsize)
#define LSN_FORMAT_ARGS(lsn)
void update_controlfile(const char *DataDir, ControlFileData *ControlFile, bool do_sync)
#define required_argument
rewind_source * init_libpq_source(PGconn *conn)
bool datapagemap_next(datapagemap_iterator_t *iter, BlockNumber *blkno)
static void checkControlFile(ControlFileData *ControlFile)
#define pg_log_debug(...)
XLogRecPtr(* get_current_wal_insert_lsn)(struct rewind_source *)
int find_my_exec(const char *argv0, char *retpath)
uint32 data_checksum_version
static void usage(const char *progname)
char * pg_strdup(const char *in)
#define EQ_CRC32C(c1, c2)
void(* queue_fetch_range)(struct rewind_source *, const char *path, off_t offset, size_t len)
filemap_t * decide_file_actions(void)
static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex)
PQExpBuffer GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
#define XLogRecPtrIsInvalid(r)
#define ngettext(s, p, n)
#define PG_TEXTDOMAIN(domain)
void print_filemap(filemap_t *filemap)
static void disconnect_atexit(void)
datapagemap_t target_pages_to_overwrite
void sync_target_dir(void)
void(* destroy)(struct rewind_source *)
static ControlFileData * ControlFile
#define PG_BACKEND_VERSIONSTR
void calculate_totals(filemap_t *filemap)
size_t strlcpy(char *dst, const char *src, size_t siz)
static pg_time_t last_progress_report
void remove_target(file_entry_t *entry)
#define Assert(condition)
static rewind_source * source
#define XLogFileName(fname, tli, logSegNo, wal_segsz_bytes)
void pg_logging_increase_verbosity(void)
XLogRecPtr readOneRecord(const char *datadir, XLogRecPtr ptr, int tliIndex, const char *restoreCommand)
void progress_report(bool finished)
uint32 catalog_version_no
datapagemap_iterator_t * datapagemap_iterate(datapagemap_t *map)
#define PG_CONTROL_FILE_SIZE
static TimeLineHistoryEntry * getTimelineHistory(ControlFileData *controlFile, int *nentries)
void process_source_file(const char *path, file_type_t type, size_t size, const char *link_target)
static void getRestoreCommand(const char *argv0)
static void sanityChecks(void)
void close_target_file(void)
#define PG_DATA_CHECKSUM_VERSION
bool GetDataDirectoryCreatePerm(const char *dataDir)
TimeLineID ThisTimeLineID
void set_pglocale_pgservice(const char *argv0, const char *app)
TimeLineHistoryEntry * rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
static void digestControlFile(ControlFileData *ControlFile, const char *content, size_t size)
file_entry_t * entries[FLEXIBLE_ARRAY_MEMBER]
static ControlFileData ControlFile_target
char * pipe_read_line(char *cmd, char *line, int maxsize)
void create_target(file_entry_t *entry)
static ControlFileData ControlFile_source
static void createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli, XLogRecPtr checkpointloc)
ConnStatusType PQstatus(const PGconn *conn)
#define COMP_CRC32C(crc, data, len)
Datum now(PG_FUNCTION_ARGS)
#define offsetof(type, field)
PGconn * PQconnectdb(const char *conninfo)
XLogRecPtr minRecoveryPoint
char * slurpFile(const char *datadir, const char *path, size_t *filesize)
#define TLHistoryFilePath(path, tli)
#define XLByteToSeg(xlrp, logSegNo, wal_segsz_bytes)