static void syncTargetDirectory(void);
static void sanityChecks(void);
static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex);
+static void ensureCleanShutdown(const char *argv0);
static ControlFileData ControlFile_target;
static ControlFileData ControlFile_source;
printf(_(" -N, --no-sync do not wait for changes to be written\n"
" safely to disk\n"));
printf(_(" -P, --progress write progress messages\n"));
+ printf(_(" --no-ensure-shutdown do not automatically fix unclean shutdown\n"));
printf(_(" --debug write a lot of debug messages\n"));
printf(_(" -V, --version output version information, then exit\n"));
printf(_(" -?, --help show this help, then exit\n"));
{"target-pgdata", required_argument, NULL, 'D'},
{"source-pgdata", required_argument, NULL, 1},
{"source-server", required_argument, NULL, 2},
+ {"no-ensure-shutdown", no_argument, NULL, 44},
{"version", no_argument, NULL, 'V'},
{"dry-run", no_argument, NULL, 'n'},
{"no-sync", no_argument, NULL, 'N'},
XLogRecPtr chkptredo;
size_t size;
char *buffer;
+ bool no_ensure_shutdown = false;
bool rewind_needed;
XLogRecPtr endrec;
TimeLineID endtli;
case 2: /* --source-server */
connstr_source = pg_strdup(optarg);
break;
+ case 4:
+ no_ensure_shutdown = true;
+ break;
}
}
digestControlFile(&ControlFile_target, buffer, size);
pg_free(buffer);
+ /*
+ * If the target instance was not cleanly shut down, run a single-user
+ * postgres session really quickly and reload the control file to get the
+ * new state. Note if no_ensure_shutdown is specified, pg_rewind won't do
+ * that automatically. That means users need to do themselves in advance,
+ * else pg_rewind will soon quit, see sanityChecks().
+ */
+ if (!no_ensure_shutdown &&
+ ControlFile_target.state != DB_SHUTDOWNED &&
+ ControlFile_target.state != DB_SHUTDOWNED_IN_RECOVERY)
+ {
+ ensureCleanShutdown(argv[0]);
+
+ buffer = slurpFile(datadir_target, "global/pg_control", &size);
+ digestControlFile(&ControlFile_target, buffer, size);
+ pg_free(buffer);
+ }
+
buffer = fetchFile("global/pg_control", &size);
digestControlFile(&ControlFile_source, buffer, size);
pg_free(buffer);
fsync_pgdata(datadir_target, PG_VERSION_NUM);
}
+
+/*
+ * Ensure clean shutdown of target instance by launching single-user mode
+ * postgres to do crash recovery.
+ */
+static void
+ensureCleanShutdown(const char *argv0)
+{
+ int ret;
+#define MAXCMDLEN (2 * MAXPGPATH)
+ char exec_path[MAXPGPATH];
+ char cmd[MAXCMDLEN];
+
+ /* locate postgres binary */
+ if ((ret = find_other_exec(argv0, "postgres",
+ PG_BACKEND_VERSIONSTR,
+ exec_path)) < 0)
+ {
+ char full_path[MAXPGPATH];
+
+ if (find_my_exec(argv0, full_path) < 0)
+ strlcpy(full_path, progname, sizeof(full_path));
+
+ if (ret == -1)
+ pg_fatal("The program \"%s\" is needed by %s but was\n"
+ "not found in the same directory as \"%s\".\n"
+ "Check your installation.",
+ "postgres", progname, full_path);
+ else
+ pg_fatal("The program \"%s\" was found by \"%s\" but was\n"
+ "not the same version as %s.\n"
+ "Check your installation.",
+ "postgres", full_path, progname);
+ }
+
+ pg_log_info("executing \"%s\" for target server to complete crash recovery",
+ exec_path);
+
+ /*
+ * Skip processing if requested, but only after ensuring presence of
+ * postgres.
+ */
+ if (dry_run)
+ return;
+
+ /* finally run postgres in single-user mode */
+ snprintf(cmd, MAXCMDLEN, "\"%s\" --single -D \"%s\" template1 < \"%s\"",
+ exec_path, datadir_target, DEVNULL);
+
+ if (system(cmd) != 0)
+ {
+ pg_log_error("postgres single-user mode of target instance failed");
+ pg_fatal("Command was: %s", cmd);
+ }
+}