From 15ea2196d7d9457e0cefe626c410fe88a835bbba Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Tue, 6 Apr 2021 14:23:41 -0400
Subject: [PATCH] cfe-07-bin_over_cfe-06-backend squash commit

---
 doc/src/sgml/ref/initdb.sgml            |  47 +++++++++
 doc/src/sgml/ref/pg_ctl-ref.sgml        |  14 +++
 doc/src/sgml/ref/pgupgrade.sgml         |  20 +++-
 doc/src/sgml/ref/postgres-ref.sgml      |  13 +++
 src/bin/initdb/initdb.c                 | 126 ++++++++++++++++++++++--
 src/bin/pg_controldata/pg_controldata.c |   3 +
 src/bin/pg_ctl/pg_ctl.c                 |  59 +++++++++--
 src/bin/pg_resetwal/pg_resetwal.c       |   3 +
 src/bin/pg_rewind/filemap.c             |   8 ++
 src/bin/pg_upgrade/check.c              |  34 +++++++
 src/bin/pg_upgrade/controldata.c        |  41 +++++++-
 src/bin/pg_upgrade/file.c               |   2 +
 src/bin/pg_upgrade/option.c             |   7 +-
 src/bin/pg_upgrade/pg_upgrade.h         |   3 +
 src/bin/pg_upgrade/server.c             |   5 +-
 15 files changed, 362 insertions(+), 23 deletions(-)

diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index afd344b4c0..236d213f14 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -163,6 +163,18 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry id="app-initdb-cluster-key-command" xreflabel="cluster key command">
+      <term><option>-c <replaceable class="parameter">command</replaceable></option></term>
+      <term><option>--cluster-key-command=<replaceable class="parameter">command</replaceable></option></term>
+      <listitem>
+       <para>
+        This option specifies an external command to obtain the cluster-level
+        key for cluster file encryption during server initialization and
+        server start;  see <xref linkend="guc-cluster-key-command"/> for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-D <replaceable class="parameter">directory</replaceable></option></term>
       <term><option>--pgdata=<replaceable class="parameter">directory</replaceable></option></term>
@@ -224,6 +236,18 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry id="app-initdb-file-encryption-method"
+      xreflabel="file encryption">
+      <term><option>-K <replaceable class="parameter">encryption_method</replaceable></option></term>
+      <term><option>--file-encryption-method=<replaceable class="parameter">encryption_method</replaceable></option></term>
+      <listitem>
+       <para>
+        Specifies the cluster file encryption method.  The
+        value values are <literal>AES128</literal> (the default), <literal>AES192</literal>, and <literal>AES256</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--locale=<replaceable>locale</replaceable></option></term>
       <listitem>
@@ -299,6 +323,17 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-R</option></term>
+      <term><option>--authprompt</option></term>
+      <listitem>
+       <para>
+        Allows the <option>--cluster-key-command</option> command
+        to prompt for a passphrase or PIN.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-S</option></term>
       <term><option>--sync-only</option></term>
@@ -321,6 +356,18 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-u <replaceable>datadir</replaceable></option></term>
+      <term><option>--copy-encryption-keys=<replaceable>datadir</replaceable></option></term>
+      <listitem>
+       <para>
+        Copies cluster file encryption keys from another cluster; required
+        when using <application>pg_upgrade</application> on a cluster
+        with cluster file encryption enabled.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-U <replaceable class="parameter">username</replaceable></option></term>
       <term><option>--username=<replaceable class="parameter">username</replaceable></option></term>
diff --git a/doc/src/sgml/ref/pg_ctl-ref.sgml b/doc/src/sgml/ref/pg_ctl-ref.sgml
index 3946fa52ea..0662ae051a 100644
--- a/doc/src/sgml/ref/pg_ctl-ref.sgml
+++ b/doc/src/sgml/ref/pg_ctl-ref.sgml
@@ -38,6 +38,7 @@ PostgreSQL documentation
    <arg choice="opt"><option>-s</option></arg>
    <arg choice="opt"><option>-o</option> <replaceable>options</replaceable></arg>
    <arg choice="opt"><option>-p</option> <replaceable>path</replaceable></arg>
+   <arg choice="opt"><option>-R</option></arg>
    <arg choice="opt"><option>-c</option></arg>
   </cmdsynopsis>
 
@@ -72,6 +73,7 @@ PostgreSQL documentation
    <arg choice="opt"><option>-t</option> <replaceable>seconds</replaceable></arg>
    <arg choice="opt"><option>-s</option></arg>
    <arg choice="opt"><option>-o</option> <replaceable>options</replaceable></arg>
+   <arg choice="opt"><option>-R</option></arg>
    <arg choice="opt"><option>-c</option></arg>
   </cmdsynopsis>
 
@@ -373,6 +375,18 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-R</option></term>
+      <term><option>--authprompt</option></term>
+      <listitem>
+       <para>
+        Allows <option>ssl_passphrase_command</option> or
+        <option>cluster_key_command</option> to prompt for a passphrase
+        or PIN.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-s</option></term>
       <term><option>--silent</option></term>
diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 92e1d09a55..9b49e8da59 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -167,6 +167,15 @@ PostgreSQL documentation
       </para></listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-R</option></term>
+      <term><option>--authprompt</option></term>
+      <listitem><para>allows <option>ssl_passphrase_command</option> or
+      <option>cluster_key_command</option> to prompt for a passphrase
+      or PIN.
+      </para></listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-s</option> <replaceable>dir</replaceable></term>
       <term><option>--socketdir=</option><replaceable>dir</replaceable></term>
@@ -309,7 +318,9 @@ make prefix=/usr/local/pgsql.new install
      Again, use compatible <command>initdb</command>
      flags that match the old cluster. Many
      prebuilt installers do this step automatically. There is no need to
-     start the new cluster.
+     start the new cluster.  If upgrading a cluster that uses
+     cluster file encryption, the <command>initdb</command> option
+     <option>--copy-encryption-keys</option> must be specified.
     </para>
    </step>
 
@@ -838,6 +849,13 @@ psql --username=postgres --file=script.sql postgres
    is down.
   </para>
 
+  <para>
+   If the old cluster uses file encryption, the new cluster must use
+   the same keys, so <command>pg_upgrade</command> copies them to the
+   new cluster.  It is necessary to initialize the new cluster with
+   the same <varname>cluster_key_command</varname> and the same
+   file encryption method.
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/doc/src/sgml/ref/postgres-ref.sgml b/doc/src/sgml/ref/postgres-ref.sgml
index 4aaa7abe1a..805da81e07 100644
--- a/doc/src/sgml/ref/postgres-ref.sgml
+++ b/doc/src/sgml/ref/postgres-ref.sgml
@@ -297,6 +297,19 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-R <replaceable class="parameter">file-descriptor</replaceable></option></term>
+      <listitem>
+       <para>
+        Makes <command>postgres</command> prompt for a passphrase or PIN
+        from the specified open numeric file descriptor.  The descriptor
+        is closed after the key is read.  The file descriptor number
+        <literal>-1</literal> duplicates standard error for the terminal;
+        this is useful for single-user mode.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-s</option></term>
       <listitem>
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 4500df6fc8..cb8a843f2b 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -65,6 +65,7 @@
 #include "catalog/pg_collation_d.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
+#include "common/kmgr_utils.h"
 #include "common/logging.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
@@ -142,11 +143,16 @@ static bool noclean = false;
 static bool noinstructions = false;
 static bool do_sync = true;
 static bool sync_only = false;
+static bool pass_terminal_fd = false;
+static char *term_fd_opt = NULL;
+static int file_encryption_method = DISABLED_ENCRYPTION_METHOD;
 static bool show_setting = false;
 static bool data_checksums = false;
 static char *xlog_dir = NULL;
 static char *str_wal_segment_size_mb = NULL;
 static int	wal_segment_size_mb;
+static char *cluster_key_cmd = NULL;
+static char *old_key_datadir = NULL;
 
 
 /* internal vars */
@@ -205,6 +211,7 @@ static const char *const subdirs[] = {
 	"global",
 	"pg_wal/archive_status",
 	"pg_commit_ts",
+	"pg_cryptokeys",
 	"pg_dynshmem",
 	"pg_notify",
 	"pg_serial",
@@ -961,12 +968,13 @@ test_config_settings(void)
 		test_buffs = MIN_BUFS_FOR_CONNS(test_conns);
 
 		snprintf(cmd, sizeof(cmd),
-				 "\"%s\" --boot -x0 %s "
+				 "\"%s\" --boot -x0 %s %s "
 				 "-c max_connections=%d "
 				 "-c shared_buffers=%d "
 				 "-c dynamic_shared_memory_type=%s "
 				 "< \"%s\" > \"%s\" 2>&1",
 				 backend_exec, boot_options,
+				 term_fd_opt ? term_fd_opt : "",
 				 test_conns, test_buffs,
 				 dynamic_shared_memory_type,
 				 DEVNULL, DEVNULL);
@@ -997,12 +1005,13 @@ test_config_settings(void)
 		}
 
 		snprintf(cmd, sizeof(cmd),
-				 "\"%s\" --boot -x0 %s "
+				 "\"%s\" --boot -x0 %s %s "
 				 "-c max_connections=%d "
 				 "-c shared_buffers=%d "
 				 "-c dynamic_shared_memory_type=%s "
 				 "< \"%s\" > \"%s\" 2>&1",
 				 backend_exec, boot_options,
+				 term_fd_opt ? term_fd_opt : "",
 				 n_connections, test_buffs,
 				 dynamic_shared_memory_type,
 				 DEVNULL, DEVNULL);
@@ -1192,6 +1201,13 @@ setup_config(void)
 								  "password_encryption = md5");
 	}
 
+	if (cluster_key_cmd)
+	{
+		snprintf(repltok, sizeof(repltok), "cluster_key_command = '%s'",
+				 escape_quotes(cluster_key_cmd));
+		conflines = replace_token(conflines, "#cluster_key_command = ''", repltok);
+	}
+
 	/*
 	 * If group access has been enabled for the cluster then it makes sense to
 	 * ensure that the log files also allow group access.  Otherwise a backup
@@ -1402,12 +1418,17 @@ bootstrap_template1(void)
 	unsetenv("PGCLIENTENCODING");
 
 	snprintf(cmd, sizeof(cmd),
-			 "\"%s\" --boot -x1 -X %u %s %s %s",
+			 "\"%s\" --boot -x1 -X %u %s %s %s %s %s %s %s %s",
 			 backend_exec,
 			 wal_segment_size_mb * (1024 * 1024),
 			 data_checksums ? "-k" : "",
+			 cluster_key_cmd ? "-K" : "",
+			 cluster_key_cmd ? encryption_methods[file_encryption_method].name : "",
+			 old_key_datadir ? "-u" : "",
+			 old_key_datadir ? old_key_datadir : "",
 			 boot_options,
-			 debug ? "-d 5" : "");
+			 debug ? "-d 5" : "",
+			 term_fd_opt ? term_fd_opt : "");
 
 
 	PG_CMD_OPEN;
@@ -2254,21 +2275,29 @@ usage(const char *progname)
 			 "                            set default locale in the respective category for\n"
 			 "                            new databases (default taken from environment)\n"));
 	printf(_("      --no-locale           equivalent to --locale=C\n"));
-	printf(_("      --pwfile=FILE         read password for the new superuser from file\n"));
+	printf(_("      --pwfile=FILE         read the new superuser password from file\n"));
 	printf(_("  -T, --text-search-config=CFG\n"
 			 "                            default text search configuration\n"));
 	printf(_("  -U, --username=NAME       database superuser name\n"));
-	printf(_("  -W, --pwprompt            prompt for a password for the new superuser\n"));
+	printf(_("  -W, --pwprompt            prompt for the new superuser password\n"));
 	printf(_("  -X, --waldir=WALDIR       location for the write-ahead log directory\n"));
 	printf(_("      --wal-segsize=SIZE    size of WAL segments, in megabytes\n"));
 	printf(_("\nLess commonly used options:\n"));
+	printf(_("  -c, --cluster-key-command=COMMAND\n"
+			 "                            enable cluster file encryption and set command\n"
+			 "                            to obtain the cluster key\n"));
 	printf(_("  -d, --debug               generate lots of debugging output\n"));
+	printf(_("  -K, --file-encryption-method=METHOD\n"
+			 "                            cluster file encryption method\n"));
 	printf(_("  -L DIRECTORY              where to find the input files\n"));
 	printf(_("  -n, --no-clean            do not clean up after errors\n"));
 	printf(_("  -N, --no-sync             do not wait for changes to be written safely to disk\n"));
+	printf(_("  -R, --authprompt          prompt for a passphrase or PIN\n"));
 	printf(_("      --no-instructions     do not print instructions for next steps\n"));
 	printf(_("  -s, --show                show internal settings\n"));
 	printf(_("  -S, --sync-only           only sync data directory\n"));
+	printf(_("  -u, --copy-encryption-keys=DATADIR\n"
+			 "                            copy the file encryption key from another cluster\n"));
 	printf(_("\nOther options:\n"));
 	printf(_("  -V, --version             output version information, then exit\n"));
 	printf(_("  -?, --help                show this help, then exit\n"));
@@ -2836,6 +2865,23 @@ initialize_data_directory(void)
 	/* Top level PG_VERSION is checked by bootstrapper, so make it first */
 	write_version_file(NULL);
 
+	if (pass_terminal_fd)
+	{
+#ifndef WIN32
+		int terminal_fd = open("/dev/tty", O_RDWR, 0);
+#else
+		int terminal_fd = open("CONOUT$", O_RDWR, 0);
+#endif
+
+		if (terminal_fd < 0)
+		{
+			pg_log_error(_("%s: could not open terminal: %s\n"),
+						 progname, strerror(errno));
+			exit(1);
+		}
+		term_fd_opt = psprintf("-R %d", terminal_fd);
+	}
+
 	/* Select suitable configuration settings */
 	set_null_conf();
 	test_config_settings();
@@ -2859,8 +2905,9 @@ initialize_data_directory(void)
 	fflush(stdout);
 
 	snprintf(cmd, sizeof(cmd),
-			 "\"%s\" %s template1 >%s",
+			 "\"%s\" %s %s template1 >%s",
 			 backend_exec, backend_options,
+			 term_fd_opt ? term_fd_opt : "",
 			 DEVNULL);
 
 	PG_CMD_OPEN;
@@ -2936,7 +2983,11 @@ main(int argc, char *argv[])
 		{"waldir", required_argument, NULL, 'X'},
 		{"wal-segsize", required_argument, NULL, 12},
 		{"data-checksums", no_argument, NULL, 'k'},
+		{"authprompt", no_argument, NULL, 'R'},
+		{"file-encryption-method", required_argument, NULL, 'K'},
 		{"allow-group-access", no_argument, NULL, 'g'},
+		{"cluster-key-command", required_argument, NULL, 'c'},
+		{"copy-encryption-keys", required_argument, NULL, 'u'},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -2978,7 +3029,7 @@ main(int argc, char *argv[])
 
 	/* process command-line options */
 
-	while ((c = getopt_long(argc, argv, "A:dD:E:gkL:nNsST:U:WX:", long_options, &option_index)) != -1)
+	while ((c = getopt_long(argc, argv, "A:c:dD:E:gkK:L:nNRsST:u:U:WX:", long_options, &option_index)) != -1)
 	{
 		switch (c)
 		{
@@ -3024,6 +3075,28 @@ main(int argc, char *argv[])
 			case 'N':
 				do_sync = false;
 				break;
+			case 'R':
+				pass_terminal_fd = true;
+				break;
+			case 'K':
+				{
+					int i;
+
+					/* method 0/disabled cannot be specified */
+					for (i = DISABLED_ENCRYPTION_METHOD + 1;
+						 i < NUM_ENCRYPTION_METHODS; i++)
+						if (pg_strcasecmp(optarg, encryption_methods[i].name) == 0)
+						{
+							file_encryption_method = i;
+							break;
+						}
+					if (i == NUM_ENCRYPTION_METHODS)
+					{
+						fprintf(stderr, _("invalid cluster encryption method\n"));
+						exit(1);
+					}
+				}
+				break;
 			case 'S':
 				sync_only = true;
 				break;
@@ -3060,6 +3133,12 @@ main(int argc, char *argv[])
 			case 9:
 				pwfilename = pg_strdup(optarg);
 				break;
+			case 'c':
+				cluster_key_cmd = pg_strdup(optarg);
+				break;
+			case 'u':
+				old_key_datadir = pg_strdup(optarg);
+				break;
 			case 's':
 				show_setting = true;
 				break;
@@ -3133,6 +3212,32 @@ main(int argc, char *argv[])
 		exit(1);
 	}
 
+#ifndef USE_OPENSSL
+	if (cluster_key_cmd)
+	{
+		pg_log_error("cluster file encryption is not supported because OpenSSL is not supported by this build");
+		exit(1);
+	}
+#endif
+
+	if (old_key_datadir != NULL && cluster_key_cmd == NULL)
+	{
+		pg_log_error("copying encryption keys requires the cluster key command to be specified");
+		exit(1);
+	}
+
+	if (file_encryption_method != DISABLED_ENCRYPTION_METHOD &&
+		cluster_key_cmd == NULL)
+	{
+		pg_log_error("a file encryption method requires the cluster key command to be specified");
+		exit(1);
+	}
+
+	/* set the default */
+	if (file_encryption_method == DISABLED_ENCRYPTION_METHOD &&
+		cluster_key_cmd != NULL)
+		file_encryption_method = DEFAULT_ENABLED_ENCRYPTION_METHOD;
+
 	check_authmethod_unspecified(&authmethodlocal);
 	check_authmethod_unspecified(&authmethodhost);
 
@@ -3200,6 +3305,11 @@ main(int argc, char *argv[])
 	else
 		printf(_("Data page checksums are disabled.\n"));
 
+	if (cluster_key_cmd)
+		printf(_("Cluster file encryption is enabled.\n"));
+	else
+		printf(_("Cluster file encryption is disabled.\n"));
+
 	if (pwprompt || pwfilename)
 		get_su_pwd();
 
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index f911f98d94..e95eaae5e5 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -25,6 +25,7 @@
 #include "access/xlog_internal.h"
 #include "catalog/pg_control.h"
 #include "common/controldata_utils.h"
+#include "common/kmgr_utils.h"
 #include "common/logging.h"
 #include "getopt_long.h"
 #include "pg_getopt.h"
@@ -328,5 +329,7 @@ main(int argc, char *argv[])
 		   ControlFile->data_checksum_version);
 	printf(_("Mock authentication nonce:            %s\n"),
 		   mock_auth_nonce_str);
+	printf(_("File encryption method:               %s\n"),
+		   encryption_methods[ControlFile->file_encryption_method].name);
 	return 0;
 }
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index 7985da0a94..8fd60cc78c 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -79,6 +79,7 @@ typedef enum
 static bool do_wait = true;
 static int	wait_seconds = DEFAULT_WAIT;
 static bool wait_seconds_arg = false;
+static bool pass_terminal_fd = false;
 static bool silent_mode = false;
 static ShutdownMode shutdown_mode = FAST_MODE;
 static int	sig = SIGINT;		/* default */
@@ -442,7 +443,7 @@ free_readfile(char **optlines)
 static pgpid_t
 start_postmaster(void)
 {
-	char		cmd[MAXPGPATH];
+	char		cmd[MAXPGPATH], *term_fd_opt = NULL;
 
 #ifndef WIN32
 	pgpid_t		pm_pid;
@@ -467,6 +468,19 @@ start_postmaster(void)
 
 	/* fork succeeded, in child */
 
+	if (pass_terminal_fd)
+	{
+		int terminal_fd = open("/dev/tty", O_RDWR, 0);
+
+		if (terminal_fd < 0)
+		{
+			write_stderr(_("%s: could not open terminal: %s\n"),
+						 progname, strerror(errno));
+			exit(1);
+		}
+		term_fd_opt = psprintf(" -R %d", terminal_fd);
+	}
+
 	/*
 	 * If possible, detach the postmaster process from the launching process
 	 * group and make it a group leader, so that it doesn't get signaled along
@@ -487,12 +501,14 @@ start_postmaster(void)
 	 * has the same PID as the current child process.
 	 */
 	if (log_file != NULL)
-		snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s < \"%s\" >> \"%s\" 2>&1",
+		snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s%s < \"%s\" >> \"%s\" 2>&1",
 				 exec_path, pgdata_opt, post_opts,
+				 term_fd_opt ? term_fd_opt : "",
 				 DEVNULL, log_file);
 	else
-		snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s < \"%s\" 2>&1",
-				 exec_path, pgdata_opt, post_opts, DEVNULL);
+		snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s%s < \"%s\" 2>&1",
+				 exec_path, pgdata_opt, post_opts,
+				 term_fd_opt ? term_fd_opt : "", DEVNULL);
 
 	(void) execl("/bin/sh", "/bin/sh", "-c", cmd, (char *) NULL);
 
@@ -513,6 +529,21 @@ start_postmaster(void)
 	PROCESS_INFORMATION pi;
 	const char *comspec;
 
+	if (pass_terminal_fd)
+	{
+		/*  Hopefully we can read and write CONOUT, see simple_prompt() XXX */
+		/*  Do CreateRestrictedProcess() children even inherit open file descriptors? XXX */
+		int terminal_fd = open("CONOUT$", O_RDWR, 0);
+
+		if (terminal_fd < 0)
+		{
+			write_stderr(_("%s: could not open terminal: %s\n"),
+						 progname, strerror(errno));
+			exit(1);
+		}
+		term_fd_opt = psprintf(" -R %d", terminal_fd);
+	}
+
 	/* Find CMD.EXE location using COMSPEC, if it's set */
 	comspec = getenv("COMSPEC");
 	if (comspec == NULL)
@@ -553,12 +584,14 @@ start_postmaster(void)
 		else
 			close(fd);
 
-		snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"",
-				 comspec, exec_path, pgdata_opt, post_opts, DEVNULL, log_file);
+		snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s%s < \"%s\" >> \"%s\" 2>&1\"",
+				 comspec, exec_path, pgdata_opt, post_opts,
+				 term_fd_opt ? term_fd_opt : "", DEVNULL, log_file);
 	}
 	else
-		snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s < \"%s\" 2>&1\"",
-				 comspec, exec_path, pgdata_opt, post_opts, DEVNULL);
+		snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s%s < \"%s\" 2>&1\"",
+				 comspec, exec_path, pgdata_opt, post_opts,
+				 term_fd_opt ? term_fd_opt : "", DEVNULL);
 
 	if (!CreateRestrictedProcess(cmd, &pi, false))
 	{
@@ -689,7 +722,8 @@ wait_for_postmaster(pgpid_t pm_pid, bool do_checkpoint)
 			}
 			else
 #endif
-				print_msg(".");
+				if (!pass_terminal_fd)
+					print_msg(".");
 		}
 
 		pg_usleep(USEC_PER_SEC / WAITS_PER_SEC);
@@ -2065,6 +2099,7 @@ do_help(void)
 	printf(_("  -o, --options=OPTIONS  command line options to pass to postgres\n"
 			 "                         (PostgreSQL server executable) or initdb\n"));
 	printf(_("  -p PATH-TO-POSTGRES    normally not necessary\n"));
+	printf(_("  -R, --authprompt       prompt for a passphrase or PIN\n"));
 	printf(_("\nOptions for stop or restart:\n"));
 	printf(_("  -m, --mode=MODE        MODE can be \"smart\", \"fast\", or \"immediate\"\n"));
 
@@ -2259,6 +2294,7 @@ main(int argc, char **argv)
 		{"mode", required_argument, NULL, 'm'},
 		{"pgdata", required_argument, NULL, 'D'},
 		{"options", required_argument, NULL, 'o'},
+		{"authprompt", no_argument, NULL, 'R'},
 		{"silent", no_argument, NULL, 's'},
 		{"timeout", required_argument, NULL, 't'},
 		{"core-files", no_argument, NULL, 'c'},
@@ -2331,7 +2367,7 @@ main(int argc, char **argv)
 	/* process command-line options */
 	while (optind < argc)
 	{
-		while ((c = getopt_long(argc, argv, "cD:e:l:m:N:o:p:P:sS:t:U:wW",
+		while ((c = getopt_long(argc, argv, "cD:e:l:m:N:o:p:P:RsS:t:U:wW",
 								long_options, &option_index)) != -1)
 		{
 			switch (c)
@@ -2383,6 +2419,9 @@ main(int argc, char **argv)
 				case 'P':
 					register_password = pg_strdup(optarg);
 					break;
+				case 'R':
+					pass_terminal_fd = true;
+					break;
 				case 's':
 					silent_mode = true;
 					break;
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 805dafef07..682c0e26c3 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -52,6 +52,7 @@
 #include "common/controldata_utils.h"
 #include "common/fe_memutils.h"
 #include "common/file_perm.h"
+#include "common/kmgr_utils.h"
 #include "common/logging.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
@@ -804,6 +805,8 @@ PrintControlValues(bool guessed)
 		   (ControlFile.float8ByVal ? _("by value") : _("by reference")));
 	printf(_("Data page checksum version:           %u\n"),
 		   ControlFile.data_checksum_version);
+	printf(_("File encryption method:               %s\n"),
+		   encryption_methods[ControlFile.file_encryption_method].name);
 }
 
 
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
index 2618b4c957..e52a914361 100644
--- a/src/bin/pg_rewind/filemap.c
+++ b/src/bin/pg_rewind/filemap.c
@@ -28,6 +28,7 @@
 
 #include "catalog/pg_tablespace_d.h"
 #include "common/hashfn.h"
+#include "common/kmgr_utils.h"
 #include "common/string.h"
 #include "datapagemap.h"
 #include "filemap.h"
@@ -107,6 +108,13 @@ static const char *excludeDirContents[] =
 	/* Contents removed on startup, see AsyncShmemInit(). */
 	"pg_notify",
 
+	/*
+	 * Skip cryptographic keys. It's generally not a good idea to copy the
+	 * cryptographic keys from source database because these might use
+	 * different cluster key.
+	 */
+	KMGR_DIR,
+
 	/*
 	 * Old contents are loaded for possible debugging but are not required for
 	 * normal operation, see SerialInit().
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index d77183b8d1..b816fb952f 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -10,6 +10,7 @@
 #include "postgres_fe.h"
 
 #include "catalog/pg_authid_d.h"
+#include "common/kmgr_utils.h"
 #include "fe_utils/string_utils.h"
 #include "mb/pg_wchar.h"
 #include "pg_upgrade.h"
@@ -27,6 +28,7 @@ static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_reg_data_type_usage(ClusterInfo *cluster);
 static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
 static void check_for_pg_role_prefix(ClusterInfo *cluster);
+static void check_for_cluster_key_failure(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(ClusterInfo *new_cluster);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static char *get_canonical_locale_name(int category, const char *locale);
@@ -149,6 +151,9 @@ check_and_dump_old_cluster(bool live_check)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
 		check_for_pg_role_prefix(&old_cluster);
 
+	if (GET_MAJOR_VERSION(old_cluster.major_version) >= 1400)
+		check_for_cluster_key_failure(&old_cluster);
+
 	if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 &&
 		old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
 		check_for_jsonb_9_4_usage(&old_cluster);
@@ -183,6 +188,9 @@ check_new_cluster(void)
 
 	check_loadable_libraries();
 
+	if (GET_MAJOR_VERSION(old_cluster.major_version) >= 1400)
+		check_for_cluster_key_failure(&new_cluster);
+
 	switch (user_opts.transfer_mode)
 	{
 		case TRANSFER_MODE_CLONE:
@@ -1364,6 +1372,32 @@ check_for_user_defined_encoding_conversions(ClusterInfo *cluster)
 }
 
 
+/*
+ * check_for_cluster_key_failure()
+ *
+ *	Make sure there was no unrepaired pg_alterckey failure
+ */
+static void
+check_for_cluster_key_failure(ClusterInfo *cluster)
+{
+	struct stat buffer;
+
+	if (stat (KMGR_DIR_PID, &buffer) == 0)
+	{
+		if (cluster == &old_cluster)
+			pg_fatal("The source cluster had a pg_alterckey failure that needs repair or\n"
+					 "pg_alterckey is running.  Run pg_alterckey --repair or wait for it\n"
+					 "to complete.\n");
+		else
+			pg_fatal("The target cluster had a pg_alterckey failure that needs repair or\n"
+					 "pg_alterckey is running.  Run pg_alterckey --repair or wait for it\n"
+					 "to complete.\n");
+	}
+
+	check_ok();
+}
+
+
 /*
  * get_canonical_locale_name
  *
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index 4f647cdf33..7deb040777 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -9,10 +9,16 @@
 
 #include "postgres_fe.h"
 
+#include <dirent.h>
 #include <ctype.h>
 
 #include "pg_upgrade.h"
 
+#include "access/xlog_internal.h"
+#include "common/controldata_utils.h"
+#include "common/file_utils.h"
+#include "common/kmgr_utils.h"
+
 /*
  * get_control_data()
  *
@@ -59,6 +65,7 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 	bool		got_date_is_int = false;
 	bool		got_data_checksum_version = false;
 	bool		got_cluster_state = false;
+	int			got_file_encryption_method = false;
 	char	   *lc_collate = NULL;
 	char	   *lc_ctype = NULL;
 	char	   *lc_monetary = NULL;
@@ -202,6 +209,13 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 		got_data_checksum_version = true;
 	}
 
+	/* Only in <= 14 */
+	if (GET_MAJOR_VERSION(cluster->major_version) <= 1400)
+	{
+		cluster->controldata.file_encryption_method = DISABLED_ENCRYPTION_METHOD;
+		got_file_encryption_method = true;
+	}
+
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -485,6 +499,18 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 			cluster->controldata.data_checksum_version = str2uint(p);
 			got_data_checksum_version = true;
 		}
+		else if ((p = strstr(bufin, "Cluster file encryption method:")) != NULL)
+		{
+			p = strchr(p, ':');
+
+			if (p == NULL || strlen(p) <= 1)
+				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+
+			p++;				/* remove ':' char */
+			/* used later for contrib check */
+			cluster->controldata.file_encryption_method = atoi(p);
+			got_file_encryption_method = true;
+		}
 	}
 
 	pclose(output);
@@ -553,7 +579,8 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 		!got_index || !got_toast ||
 		(!got_large_object &&
 		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
-		!got_date_is_int || !got_data_checksum_version)
+		!got_date_is_int || !got_data_checksum_version ||
+		!got_file_encryption_method)
 	{
 		if (cluster == &old_cluster)
 			pg_log(PG_REPORT,
@@ -619,6 +646,10 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 		if (!got_data_checksum_version)
 			pg_log(PG_REPORT, "  data checksum version\n");
 
+		/* value added in Postgres 14 */
+		if (!got_file_encryption_method)
+			pg_log(PG_REPORT, "  file encryption method\n");
+
 		pg_fatal("Cannot continue without required control information, terminating\n");
 	}
 }
@@ -683,6 +714,14 @@ check_control_data(ControlData *oldctrl,
 		pg_fatal("old cluster uses data checksums but the new one does not\n");
 	else if (oldctrl->data_checksum_version != newctrl->data_checksum_version)
 		pg_fatal("old and new cluster pg_controldata checksum versions do not match\n");
+
+	/*
+	 * We cannot upgrade if the old cluster file encryption method
+	 * doesn't match the new one.
+	 */
+	if (oldctrl->file_encryption_method != newctrl->file_encryption_method)
+		pg_fatal("old and new clusters use different file encryption methods or\n"
+				 "one cluster uses encryption and the other does not");
 }
 
 
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 9b0cc16e45..ffa3017ad3 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -11,6 +11,7 @@
 
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <dirent.h>
 #ifdef HAVE_COPYFILE_H
 #include <copyfile.h>
 #endif
@@ -21,6 +22,7 @@
 
 #include "access/visibilitymap.h"
 #include "common/file_perm.h"
+#include "common/file_utils.h"
 #include "pg_upgrade.h"
 #include "storage/bufpage.h"
 #include "storage/checksum.h"
diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c
index 9c9b313e0c..db76534e68 100644
--- a/src/bin/pg_upgrade/option.c
+++ b/src/bin/pg_upgrade/option.c
@@ -52,6 +52,7 @@ parseCommandLine(int argc, char *argv[])
 		{"check", no_argument, NULL, 'c'},
 		{"link", no_argument, NULL, 'k'},
 		{"retain", no_argument, NULL, 'r'},
+		{"authprompt", no_argument, NULL, 'R'},
 		{"jobs", required_argument, NULL, 'j'},
 		{"socketdir", required_argument, NULL, 's'},
 		{"verbose", no_argument, NULL, 'v'},
@@ -102,7 +103,7 @@ parseCommandLine(int argc, char *argv[])
 	if (os_user_effective_id == 0)
 		pg_fatal("%s: cannot be run as root\n", os_info.progname);
 
-	while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rs:U:v",
+	while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rRs:U:v",
 								 long_options, &optindex)) != -1)
 	{
 		switch (option)
@@ -180,6 +181,10 @@ parseCommandLine(int argc, char *argv[])
 				log_opts.retain = true;
 				break;
 
+			case 'R':
+				user_opts.pass_terminal_fd = true;
+				break;
+
 			case 's':
 				user_opts.socketdir = pg_strdup(optarg);
 				break;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 919a7849fd..3fc8e299ad 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -11,6 +11,7 @@
 #include <sys/time.h>
 
 #include "libpq-fe.h"
+#include "common/kmgr_utils.h"
 
 /* Use port in the private/dynamic port number range */
 #define DEF_PGUPORT			50432
@@ -219,6 +220,7 @@ typedef struct
 	bool		date_is_int;
 	bool		float8_pass_by_value;
 	bool		data_checksum_version;
+	int			file_encryption_method;
 } ControlData;
 
 /*
@@ -293,6 +295,7 @@ typedef struct
 	int			jobs;			/* number of processes/threads to use */
 	char	   *socketdir;		/* directory to use for Unix sockets */
 	bool		ind_coll_unknown;	/* mark unknown index collation versions */
+	bool		pass_terminal_fd; /* pass -R to pg_ctl? */
 } UserOpts;
 
 typedef struct
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 7fed0ae108..f77ad24db2 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -245,8 +245,9 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 	 * vacuumdb --freeze actually freezes the tuples.
 	 */
 	snprintf(cmd, sizeof(cmd),
-			 "\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start",
-			 cluster->bindir, SERVER_LOG_FILE, cluster->pgconfig, cluster->port,
+			 "\"%s/pg_ctl\" -w%s -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start",
+			 cluster->bindir, user_opts.pass_terminal_fd ? " -R" : "",
+			 SERVER_LOG_FILE, cluster->pgconfig, cluster->port,
 			 (cluster->controldata.cat_ver >=
 			  BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? " -b" :
 			 " -c autovacuum=off -c autovacuum_freeze_max_age=2000000000",
-- 
2.20.1

