>From b5bc405a85c6a84c7916f5cc2c3225438df70315 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Tue, 26 Jul 2016 11:23:43 -0400
Subject: [PATCH 3/5] pg_ctl: Detect current standby state from pg_control

pg_ctl used to determine whether a server was in standby mode by looking
for a recovery.conf file.  With this change, it instead looks into
pg_control, which is potentially more accurate.  There are also
occasional discussions about removing recovery.conf, so this removes one
dependency.

Add a wait_seconds argument to get_controlfile() so that pg_ctl can wait
a bit for a consistent pg_control file.  Otherwise, pg_ctl operations
might fail on rare occasions when the server is updating the file at the
same time.
---
 src/backend/utils/misc/pg_controldata.c |  8 ++--
 src/bin/pg_controldata/pg_controldata.c |  2 +-
 src/bin/pg_ctl/pg_ctl.c                 | 35 ++++++++------
 src/common/controldata_utils.c          | 83 ++++++++++++++++++++-------------
 src/include/common/controldata_utils.h  |  2 +-
 5 files changed, 76 insertions(+), 54 deletions(-)

diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index 34ee76a..13f0523 100644
--- a/src/backend/utils/misc/pg_controldata.c
+++ b/src/backend/utils/misc/pg_controldata.c
@@ -51,7 +51,7 @@ pg_control_system(PG_FUNCTION_ARGS)
 	tupdesc = BlessTupleDesc(tupdesc);
 
 	/* read the control file */
-	ControlFile = get_controlfile(DataDir, NULL);
+	ControlFile = get_controlfile(DataDir, NULL, NULL);
 
 	values[0] = Int32GetDatum(ControlFile->pg_control_version);
 	nulls[0] = false;
@@ -127,7 +127,7 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
 	tupdesc = BlessTupleDesc(tupdesc);
 
 	/* Read the control file. */
-	ControlFile = get_controlfile(DataDir, NULL);
+	ControlFile = get_controlfile(DataDir, NULL, NULL);
 
 	/*
 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
@@ -229,7 +229,7 @@ pg_control_recovery(PG_FUNCTION_ARGS)
 	tupdesc = BlessTupleDesc(tupdesc);
 
 	/* read the control file */
-	ControlFile = get_controlfile(DataDir, NULL);
+	ControlFile = get_controlfile(DataDir, NULL, NULL);
 
 	values[0] = LSNGetDatum(ControlFile->minRecoveryPoint);
 	nulls[0] = false;
@@ -294,7 +294,7 @@ pg_control_init(PG_FUNCTION_ARGS)
 	tupdesc = BlessTupleDesc(tupdesc);
 
 	/* read the control file */
-	ControlFile = get_controlfile(DataDir, NULL);
+	ControlFile = get_controlfile(DataDir, NULL, NULL);
 
 	values[0] = Int32GetDatum(ControlFile->maxAlign);
 	nulls[0] = false;
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index 96619a2..c107601 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -155,7 +155,7 @@ main(int argc, char *argv[])
 	}
 
 	/* get a copy of the control file */
-	ControlFile = get_controlfile(DataDir, progname);
+	ControlFile = get_controlfile(DataDir, progname, NULL);
 
 	/*
 	 * This slightly-chintzy coding will work as long as the control file
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index efc0729..fac3986 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -19,6 +19,8 @@
 
 #include "postgres_fe.h"
 
+#include "catalog/pg_control.h"
+#include "common/controldata_utils.h"
 #include "libpq-fe.h"
 #include "pqexpbuffer.h"
 
@@ -96,7 +98,6 @@ static char postopts_file[MAXPGPATH];
 static char version_file[MAXPGPATH];
 static char pid_file[MAXPGPATH];
 static char backup_file[MAXPGPATH];
-static char recovery_file[MAXPGPATH];
 static char promote_file[MAXPGPATH];
 
 #ifdef WIN32
@@ -988,15 +989,17 @@ do_stop(void)
 		/*
 		 * If backup_label exists, an online backup is running. Warn the user
 		 * that smart shutdown will wait for it to finish. However, if
-		 * recovery.conf is also present, we're recovering from an online
+		 * the server is in archive recovery, we're recovering from an online
 		 * backup instead of performing one.
 		 */
 		if (shutdown_mode == SMART_MODE &&
-			stat(backup_file, &statbuf) == 0 &&
-			stat(recovery_file, &statbuf) != 0)
+			stat(backup_file, &statbuf) == 0)
 		{
-			print_msg(_("WARNING: online backup mode is active\n"
-						"Shutdown will not complete until pg_stop_backup() is called.\n\n"));
+			ControlFileData *control_file_data = get_controlfile(pg_data, progname, &wait_seconds);
+			if (control_file_data->state != DB_IN_ARCHIVE_RECOVERY)
+				print_msg(_("WARNING: online backup mode is active\n"
+							"Shutdown will not complete until pg_stop_backup() is called.\n\n"));
+			pfree(control_file_data);
 		}
 
 		print_msg(_("waiting for server to shut down..."));
@@ -1076,15 +1079,17 @@ do_restart(void)
 		/*
 		 * If backup_label exists, an online backup is running. Warn the user
 		 * that smart shutdown will wait for it to finish. However, if
-		 * recovery.conf is also present, we're recovering from an online
+		 * the server is in archive recovery, we're recovering from an online
 		 * backup instead of performing one.
 		 */
 		if (shutdown_mode == SMART_MODE &&
-			stat(backup_file, &statbuf) == 0 &&
-			stat(recovery_file, &statbuf) != 0)
+			stat(backup_file, &statbuf) == 0)
 		{
-			print_msg(_("WARNING: online backup mode is active\n"
-						"Shutdown will not complete until pg_stop_backup() is called.\n\n"));
+			ControlFileData *control_file_data = get_controlfile(pg_data, progname, &wait_seconds);
+			if (control_file_data->state != DB_IN_ARCHIVE_RECOVERY)
+				print_msg(_("WARNING: online backup mode is active\n"
+							"Shutdown will not complete until pg_stop_backup() is called.\n\n"));
+			pfree(control_file_data);
 		}
 
 		print_msg(_("waiting for server to shut down..."));
@@ -1168,7 +1173,7 @@ do_promote(void)
 {
 	FILE	   *prmfile;
 	pgpid_t		pid;
-	struct stat statbuf;
+	ControlFileData *control_file_data;
 
 	pid = get_pgpid(false);
 
@@ -1187,14 +1192,15 @@ do_promote(void)
 		exit(1);
 	}
 
-	/* If recovery.conf doesn't exist, the server is not in standby mode */
-	if (stat(recovery_file, &statbuf) != 0)
+	control_file_data = get_controlfile(pg_data, progname, &wait_seconds);
+	if (control_file_data->state != DB_IN_ARCHIVE_RECOVERY)
 	{
 		write_stderr(_("%s: cannot promote server; "
 					   "server is not in standby mode\n"),
 					 progname);
 		exit(1);
 	}
+	pfree(control_file_data);
 
 	/*
 	 * For 9.3 onwards, "fast" promotion is performed. Promotion with a full
@@ -2401,7 +2407,6 @@ main(int argc, char **argv)
 		snprintf(version_file, MAXPGPATH, "%s/PG_VERSION", pg_data);
 		snprintf(pid_file, MAXPGPATH, "%s/postmaster.pid", pg_data);
 		snprintf(backup_file, MAXPGPATH, "%s/backup_label", pg_data);
-		snprintf(recovery_file, MAXPGPATH, "%s/recovery.conf", pg_data);
 	}
 
 	switch (ctl_command)
diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c
index 5592fe7..01002b0 100644
--- a/src/common/controldata_utils.c
+++ b/src/common/controldata_utils.c
@@ -35,60 +35,77 @@
  * for pfreeing the result.
  */
 ControlFileData *
-get_controlfile(char *DataDir, const char *progname)
+get_controlfile(const char *DataDir, const char *progname, int *wait_seconds)
 {
 	ControlFileData *ControlFile;
-	int			fd;
 	char		ControlFilePath[MAXPGPATH];
-	pg_crc32c	crc;
 
 	ControlFile = palloc(sizeof(ControlFileData));
 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
 
-	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
+	for (;;)
+	{
+		int			fd;
+		pg_crc32c	crc;
+
+		if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
 #ifndef FRONTEND
-		ereport(ERROR,
-				(errcode_for_file_access(),
-				 errmsg("could not open file \"%s\" for reading: %m",
-						ControlFilePath)));
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not open file \"%s\" for reading: %m",
+							ControlFilePath)));
 #else
-	{
-		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
-				progname, ControlFilePath, strerror(errno));
-		exit(EXIT_FAILURE);
-	}
+		{
+			fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
+					progname, ControlFilePath, strerror(errno));
+			exit(EXIT_FAILURE);
+		}
 #endif
 
-	if (read(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
+		if (read(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
 #ifndef FRONTEND
-		ereport(ERROR,
-				(errcode_for_file_access(),
-				 errmsg("could not read file \"%s\": %m", ControlFilePath)));
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not read file \"%s\": %m", ControlFilePath)));
 #else
-	{
-		fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
-				progname, ControlFilePath, strerror(errno));
-		exit(EXIT_FAILURE);
-	}
+		{
+			fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
+					progname, ControlFilePath, strerror(errno));
+			exit(EXIT_FAILURE);
+		}
 #endif
 
-	close(fd);
+		close(fd);
+
+		/* Check the CRC. */
+		INIT_CRC32C(crc);
+		COMP_CRC32C(crc,
+					(char *) ControlFile,
+					offsetof(ControlFileData, crc));
+		FIN_CRC32C(crc);
 
-	/* Check the CRC. */
-	INIT_CRC32C(crc);
-	COMP_CRC32C(crc,
-				(char *) ControlFile,
-				offsetof(ControlFileData, crc));
-	FIN_CRC32C(crc);
+		if (EQ_CRC32C(crc, ControlFile->crc))
+			break;
+
+		if (wait_seconds && *wait_seconds > 0)
+		{
+			sleep(1);
+			(*wait_seconds)--;
+			continue;
+		}
 
-	if (!EQ_CRC32C(crc, ControlFile->crc))
 #ifndef FRONTEND
 		elog(ERROR, _("calculated CRC checksum does not match value stored in file"));
 #else
-		printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
-				 "Either the file is corrupt, or it has a different layout than this program\n"
-				 "is expecting.  The results below are untrustworthy.\n\n"));
+		if (wait_seconds)
+			fprintf(stderr, _("%s: control file \"%s\" appears to be corrupt\n"),
+					progname, ControlFilePath);
+		else
+			printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
+					 "Either the file is corrupt, or it has a different layout than this program\n"
+					 "is expecting.  The results below are untrustworthy.\n\n"));
 #endif
+	}
 
 	/* Make sure the control file is valid byte order. */
 	if (ControlFile->pg_control_version % 65536 == 0 &&
diff --git a/src/include/common/controldata_utils.h b/src/include/common/controldata_utils.h
index a355d22..283ec55 100644
--- a/src/include/common/controldata_utils.h
+++ b/src/include/common/controldata_utils.h
@@ -12,6 +12,6 @@
 
 #include "catalog/pg_control.h"
 
-extern ControlFileData *get_controlfile(char *DataDir, const char *progname);
+extern ControlFileData *get_controlfile(const char *DataDir, const char *progname, int *wait_seconds);
 
 #endif   /* COMMON_CONTROLDATA_UTILS_H */
-- 
2.9.2

