From 54098217b07bb49f724f57aa29f5da36e85ee0af Mon Sep 17 00:00:00 2001
From: Shlok Kyal <shlok.kyal.oss@gmail.com>
Date: Wed, 14 Feb 2024 16:27:15 +0530
Subject: [PATCH v20 10/12] Add version check for executables and standby
 server

Add version check for executables and standby server
---
 doc/src/sgml/ref/pg_createsubscriber.sgml   |  6 ++
 src/bin/pg_basebackup/pg_createsubscriber.c | 67 +++++++++++++++++++++
 2 files changed, 73 insertions(+)

diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index 63086a8e98..579e50a0a0 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -120,6 +120,12 @@ PostgreSQL documentation
      databases and walsenders.
     </para>
    </listitem>
+   <listitem>
+    <para>
+     Both the target and source instances must have same major versions with
+     <application>pg_createsubscriber</application>.
+    </para>
+   </listitem>
   </itemizedlist>
 
   <note>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 0a70f00252..db088024d3 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -23,6 +23,7 @@
 #include "common/file_perm.h"
 #include "common/logging.h"
 #include "common/restricted_token.h"
+#include "common/string.h"
 #include "fe_utils/recovery_gen.h"
 #include "fe_utils/simple_list.h"
 #include "getopt_long.h"
@@ -63,6 +64,7 @@ static char *get_pub_base_conninfo(char *conninfo, char **dbname);
 static char *construct_sub_conninfo(char *username, unsigned short subport,
 									char *socketdir);
 static char *get_bin_directory(const char *path);
+static void check_exec(const char *dir, const char *program);
 static bool check_data_directory(const char *datadir);
 static char *concat_conninfo_dbname(const char *conninfo, const char *dbname);
 static LogicalRepInfo *store_pub_sub_info(SimpleStringList dbnames,
@@ -277,6 +279,11 @@ get_bin_directory(const char *path)
 	pg_log_debug("pg_ctl path is:  %s/%s", dirname, "pg_ctl");
 	pg_log_debug("pg_resetwal path is:  %s/%s", dirname, "pg_resetwal");
 
+	/* Check version of binaries */
+	check_exec(dirname, "postgres");
+	check_exec(dirname, "pg_ctl");
+	check_exec(dirname, "pg_resetwal");
+
 	return dirname;
 }
 
@@ -290,6 +297,8 @@ check_data_directory(const char *datadir)
 {
 	struct stat statbuf;
 	char		versionfile[MAXPGPATH];
+	FILE	   *ver_fd;
+	char		rawline[64];
 
 	pg_log_info("checking if directory \"%s\" is a cluster data directory",
 				datadir);
@@ -313,6 +322,31 @@ check_data_directory(const char *datadir)
 		return false;
 	}
 
+	/* Check standby server version */
+	if ((ver_fd = fopen(versionfile, "r")) == NULL)
+		pg_fatal("could not open file \"%s\" for reading: %m", versionfile);
+
+	/* Version number has to be the first line read */
+	if (!fgets(rawline, sizeof(rawline), ver_fd))
+	{
+		if (!ferror(ver_fd))
+			pg_fatal("unexpected empty file \"%s\"", versionfile);
+		else
+			pg_fatal("could not read file \"%s\": %m", versionfile);
+	}
+
+	/* Strip trailing newline and carriage return */
+	(void) pg_strip_crlf(rawline);
+
+	if (strcmp(rawline, PG_MAJORVERSION) != 0)
+	{
+		pg_log_error("standby server is of wrong version");
+		pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
+							versionfile, rawline, PG_MAJORVERSION);
+		exit(1);
+	}
+
+	fclose(ver_fd);
 	return true;
 }
 
@@ -1581,6 +1615,39 @@ construct_sub_conninfo(char *username, unsigned short subport, char *sockdir)
 	return ret;
 }
 
+/*
+ * Make sure the given program has the same version with pg_createsubscriber.
+ */
+static void
+check_exec(const char *dir, const char *program)
+{
+	char		path[MAXPGPATH];
+	char	   *line;
+	char		cmd[MAXPGPATH];
+	char		versionstr[128];
+
+	snprintf(path, sizeof(path), "%s/%s", dir, program);
+
+	if (validate_exec(path) != 0)
+		pg_fatal("check for \"%s\" failed: %m", path);
+
+	snprintf(cmd, sizeof(cmd), "\"%s\" -V", path);
+
+	if ((line = pipe_read_line(cmd)) == NULL)
+		pg_fatal("check for \"%s\" failed: cannot execute", path);
+
+	pg_strip_crlf(line);
+
+	snprintf(versionstr, sizeof(versionstr), "%s (PostgreSQL) " PG_VERSION,
+			 program);
+
+	if (strcmp(line, versionstr) != 0)
+		pg_fatal("check for \"%s\" failed: incorrect version: found \"%s\", expected \"%s\"",
+				 path, line, versionstr);
+
+	pg_free(line);
+}
+
 int
 main(int argc, char **argv)
 {
-- 
2.43.0

