@@ -40,6 +40,7 @@ static void digestControlFile(ControlFileData *ControlFile, char *source,
40
40
static void syncTargetDirectory (void );
41
41
static void sanityChecks (void );
42
42
static void findCommonAncestorTimeline (XLogRecPtr * recptr , int * tliIndex );
43
+ static void ensureCleanShutdown (const char * argv0 );
43
44
44
45
static ControlFileData ControlFile_target ;
45
46
static ControlFileData ControlFile_source ;
@@ -79,6 +80,7 @@ usage(const char *progname)
79
80
printf (_ (" -N, --no-sync do not wait for changes to be written\n"
80
81
" safely to disk\n" ));
81
82
printf (_ (" -P, --progress write progress messages\n" ));
83
+ printf (_ (" --no-ensure-shutdown do not automatically fix unclean shutdown\n" ));
82
84
printf (_ (" --debug write a lot of debug messages\n" ));
83
85
printf (_ (" -V, --version output version information, then exit\n" ));
84
86
printf (_ (" -?, --help show this help, then exit\n" ));
@@ -94,6 +96,7 @@ main(int argc, char **argv)
94
96
{"target-pgdata" , required_argument , NULL , 'D' },
95
97
{"source-pgdata" , required_argument , NULL , 1 },
96
98
{"source-server" , required_argument , NULL , 2 },
99
+ {"no-ensure-shutdown" , no_argument , NULL , 44 },
97
100
{"version" , no_argument , NULL , 'V' },
98
101
{"dry-run" , no_argument , NULL , 'n' },
99
102
{"no-sync" , no_argument , NULL , 'N' },
@@ -110,6 +113,7 @@ main(int argc, char **argv)
110
113
XLogRecPtr chkptredo ;
111
114
size_t size ;
112
115
char * buffer ;
116
+ bool no_ensure_shutdown = false;
113
117
bool rewind_needed ;
114
118
XLogRecPtr endrec ;
115
119
TimeLineID endtli ;
@@ -169,6 +173,9 @@ main(int argc, char **argv)
169
173
case 2 : /* --source-server */
170
174
connstr_source = pg_strdup (optarg );
171
175
break ;
176
+ case 4 :
177
+ no_ensure_shutdown = true;
178
+ break ;
172
179
}
173
180
}
174
181
@@ -241,6 +248,24 @@ main(int argc, char **argv)
241
248
digestControlFile (& ControlFile_target , buffer , size );
242
249
pg_free (buffer );
243
250
251
+ /*
252
+ * If the target instance was not cleanly shut down, run a single-user
253
+ * postgres session really quickly and reload the control file to get the
254
+ * new state. Note if no_ensure_shutdown is specified, pg_rewind won't do
255
+ * that automatically. That means users need to do themselves in advance,
256
+ * else pg_rewind will soon quit, see sanityChecks().
257
+ */
258
+ if (!no_ensure_shutdown &&
259
+ ControlFile_target .state != DB_SHUTDOWNED &&
260
+ ControlFile_target .state != DB_SHUTDOWNED_IN_RECOVERY )
261
+ {
262
+ ensureCleanShutdown (argv [0 ]);
263
+
264
+ buffer = slurpFile (datadir_target , "global/pg_control" , & size );
265
+ digestControlFile (& ControlFile_target , buffer , size );
266
+ pg_free (buffer );
267
+ }
268
+
244
269
buffer = fetchFile ("global/pg_control" , & size );
245
270
digestControlFile (& ControlFile_source , buffer , size );
246
271
pg_free (buffer );
@@ -748,3 +773,58 @@ syncTargetDirectory(void)
748
773
749
774
fsync_pgdata (datadir_target , PG_VERSION_NUM );
750
775
}
776
+
777
+ /*
778
+ * Ensure clean shutdown of target instance by launching single-user mode
779
+ * postgres to do crash recovery.
780
+ */
781
+ static void
782
+ ensureCleanShutdown (const char * argv0 )
783
+ {
784
+ int ret ;
785
+ #define MAXCMDLEN (2 * MAXPGPATH)
786
+ char exec_path [MAXPGPATH ];
787
+ char cmd [MAXCMDLEN ];
788
+
789
+ /* locate postgres binary */
790
+ if ((ret = find_other_exec (argv0 , "postgres" ,
791
+ PG_BACKEND_VERSIONSTR ,
792
+ exec_path )) < 0 )
793
+ {
794
+ char full_path [MAXPGPATH ];
795
+
796
+ if (find_my_exec (argv0 , full_path ) < 0 )
797
+ strlcpy (full_path , progname , sizeof (full_path ));
798
+
799
+ if (ret == -1 )
800
+ pg_fatal ("The program \"%s\" is needed by %s but was\n"
801
+ "not found in the same directory as \"%s\".\n"
802
+ "Check your installation." ,
803
+ "postgres" , progname , full_path );
804
+ else
805
+ pg_fatal ("The program \"%s\" was found by \"%s\" but was\n"
806
+ "not the same version as %s.\n"
807
+ "Check your installation." ,
808
+ "postgres" , full_path , progname );
809
+ }
810
+
811
+ pg_log_info ("executing \"%s\" for target server to complete crash recovery" ,
812
+ exec_path );
813
+
814
+ /*
815
+ * Skip processing if requested, but only after ensuring presence of
816
+ * postgres.
817
+ */
818
+ if (dry_run )
819
+ return ;
820
+
821
+ /* finally run postgres in single-user mode */
822
+ snprintf (cmd , MAXCMDLEN , "\"%s\" --single -D \"%s\" template1 < \"%s\"" ,
823
+ exec_path , datadir_target , DEVNULL );
824
+
825
+ if (system (cmd ) != 0 )
826
+ {
827
+ pg_log_error ("postgres single-user mode of target instance failed" );
828
+ pg_fatal ("Command was: %s" , cmd );
829
+ }
830
+ }
0 commit comments