@@ -40,6 +40,7 @@ static void digestControlFile(ControlFileData *ControlFile, char *source,
4040static void syncTargetDirectory (void );
4141static void sanityChecks (void );
4242static void findCommonAncestorTimeline (XLogRecPtr * recptr , int * tliIndex );
43+ static void ensureCleanShutdown (const char * argv0 );
4344
4445static ControlFileData ControlFile_target ;
4546static ControlFileData ControlFile_source ;
@@ -79,6 +80,7 @@ usage(const char *progname)
7980 printf (_ (" -N, --no-sync do not wait for changes to be written\n"
8081 " safely to disk\n" ));
8182 printf (_ (" -P, --progress write progress messages\n" ));
83+ printf (_ (" --no-ensure-shutdown do not automatically fix unclean shutdown\n" ));
8284 printf (_ (" --debug write a lot of debug messages\n" ));
8385 printf (_ (" -V, --version output version information, then exit\n" ));
8486 printf (_ (" -?, --help show this help, then exit\n" ));
@@ -94,6 +96,7 @@ main(int argc, char **argv)
9496 {"target-pgdata" , required_argument , NULL , 'D' },
9597 {"source-pgdata" , required_argument , NULL , 1 },
9698 {"source-server" , required_argument , NULL , 2 },
99+ {"no-ensure-shutdown" , no_argument , NULL , 44 },
97100 {"version" , no_argument , NULL , 'V' },
98101 {"dry-run" , no_argument , NULL , 'n' },
99102 {"no-sync" , no_argument , NULL , 'N' },
@@ -110,6 +113,7 @@ main(int argc, char **argv)
110113 XLogRecPtr chkptredo ;
111114 size_t size ;
112115 char * buffer ;
116+ bool no_ensure_shutdown = false;
113117 bool rewind_needed ;
114118 XLogRecPtr endrec ;
115119 TimeLineID endtli ;
@@ -169,6 +173,9 @@ main(int argc, char **argv)
169173 case 2 : /* --source-server */
170174 connstr_source = pg_strdup (optarg );
171175 break ;
176+ case 4 :
177+ no_ensure_shutdown = true;
178+ break ;
172179 }
173180 }
174181
@@ -241,6 +248,24 @@ main(int argc, char **argv)
241248 digestControlFile (& ControlFile_target , buffer , size );
242249 pg_free (buffer );
243250
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+
244269 buffer = fetchFile ("global/pg_control" , & size );
245270 digestControlFile (& ControlFile_source , buffer , size );
246271 pg_free (buffer );
@@ -748,3 +773,58 @@ syncTargetDirectory(void)
748773
749774 fsync_pgdata (datadir_target , PG_VERSION_NUM );
750775}
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