From 616af29a17c0d18b3c9681855f54cead0d2f66ae Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Tue, 9 Oct 2018 15:01:30 +0200
Subject: [PATCH] Support custom socket directory during upgrades

The upgrade process will use CWD as the location for sockets dir, which
is limited by the sockaddr_un structure to around 100 bytes. In order to
avoid hitting the limit, support an optional command line parameter, with
an associated environment variable for overriding the socket directory.
---
 doc/src/sgml/ref/pgupgrade.sgml |  8 +++++++
 src/bin/pg_upgrade/option.c     | 49 ++++++++++++++++++++++++++---------------
 src/bin/pg_upgrade/pg_upgrade.h |  2 ++
 3 files changed, 41 insertions(+), 18 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index d51146d641..31a4144207 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -163,6 +163,14 @@
       </para></listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-s</option></term>
+      <term><option>--socketdir=</option><replaceable>dir</replaceable></term>
+      <listitem><para>directory to use for sockets during upgrade, default is
+      current working directory; environment variable <envar>PGSOCKETDIR</envar>
+      </para></listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-U</option> <replaceable>username</replaceable></term>
       <term><option>--username=</option><replaceable>username</replaceable></term>
diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c
index 9dbc9225a6..9c58125972 100644
--- a/src/bin/pg_upgrade/option.c
+++ b/src/bin/pg_upgrade/option.c
@@ -22,7 +22,8 @@
 
 static void usage(void);
 static void check_required_directory(char **dirpath, char **configpath,
-						 const char *envVarName, const char *cmdLineOption, const char *description);
+						 const char *envVarName, const char *cmdLineOption,
+						 const char *description, const char *defaultValue);
 #define FIX_DEFAULT_READ_ONLY "-c default_transaction_read_only=false"
 
 
@@ -52,6 +53,7 @@ parseCommandLine(int argc, char *argv[])
 		{"link", no_argument, NULL, 'k'},
 		{"retain", no_argument, NULL, 'r'},
 		{"jobs", required_argument, NULL, 'j'},
+		{"socketdir", required_argument, NULL, 's'},
 		{"verbose", no_argument, NULL, 'v'},
 		{NULL, 0, NULL, 0}
 	};
@@ -61,6 +63,7 @@ parseCommandLine(int argc, char *argv[])
 	FILE	   *fp;
 	char	  **filename;
 	time_t		run_time = time(NULL);
+	char		default_sockdir[MAXPGPATH];
 
 	user_opts.transfer_mode = TRANSFER_MODE_COPY;
 
@@ -100,7 +103,7 @@ parseCommandLine(int argc, char *argv[])
 	if ((log_opts.internal = fopen_priv(INTERNAL_LOG_FILE, "a")) == NULL)
 		pg_fatal("could not write to log file \"%s\"\n", INTERNAL_LOG_FILE);
 
-	while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rU:v",
+	while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rs:U:v",
 								 long_options, &optindex)) != -1)
 	{
 		switch (option)
@@ -186,6 +189,11 @@ parseCommandLine(int argc, char *argv[])
 				log_opts.retain = true;
 				break;
 
+			case 's':
+				user_opts.socketdir = pg_malloc0(MAXPGPATH);
+				strlcpy(user_opts.socketdir, optarg, MAXPGPATH);
+				break;
+
 			case 'U':
 				pg_free(os_info.user);
 				os_info.user = pg_strdup(optarg);
@@ -239,13 +247,18 @@ parseCommandLine(int argc, char *argv[])
 
 	/* Get values from env if not already set */
 	check_required_directory(&old_cluster.bindir, NULL, "PGBINOLD", "-b",
-							 _("old cluster binaries reside"));
+							 _("old cluster binaries reside"), NULL);
 	check_required_directory(&new_cluster.bindir, NULL, "PGBINNEW", "-B",
-							 _("new cluster binaries reside"));
+							 _("new cluster binaries reside"), NULL);
 	check_required_directory(&old_cluster.pgdata, &old_cluster.pgconfig,
-							 "PGDATAOLD", "-d", _("old cluster data resides"));
+							 "PGDATAOLD", "-d", _("old cluster data resides"),
+							 NULL);
 	check_required_directory(&new_cluster.pgdata, &new_cluster.pgconfig,
-							 "PGDATANEW", "-D", _("new cluster data resides"));
+							 "PGDATANEW", "-D", _("new cluster data resides"),
+							 NULL);
+	getcwd(default_sockdir, MAXPGPATH);
+	check_required_directory(&user_opts.socketdir, NULL, "PGSOCKETDIR", "-s",
+							 _("sockets will be created"), default_sockdir);
 
 #ifdef WIN32
 
@@ -291,6 +304,7 @@ usage(void)
 	printf(_("  -P, --new-port=PORT           new cluster port number (default %d)\n"), new_cluster.port);
 	printf(_("  -r, --retain                  retain SQL and log files after success\n"));
 	printf(_("  -U, --username=NAME           cluster superuser (default \"%s\")\n"), os_info.user);
+	printf(_("  -s, --socketdir=DIR           socket directory during upgrades (default CWD)\n"));
 	printf(_("  -v, --verbose                 enable verbose internal logging\n"));
 	printf(_("  -V, --version                 display version information, then exit\n"));
 	printf(_("  -?, --help                    show this help, then exit\n"));
@@ -335,29 +349,33 @@ usage(void)
  *	envVarName	  - the name of an environment variable to get if dirpath is NULL
  *	cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
  *	description   - a description of this directory option
+ *	defaultValue  - a default value to assign in case the environment variable
+ *                  isn't set
  *
  * We use the last two arguments to construct a meaningful error message if the
- * user hasn't provided the required directory name.
+ * user hasn't provided the required directory name, and a default isn't
+ * specified.
  */
 static void
 check_required_directory(char **dirpath, char **configpath,
 						 const char *envVarName, const char *cmdLineOption,
-						 const char *description)
+						 const char *description, const char *defaultValue)
 {
 	if (*dirpath == NULL || strlen(*dirpath) == 0)
 	{
 		const char *envVar;
 
 		if ((envVar = getenv(envVarName)) && strlen(envVar))
-		{
 			*dirpath = pg_strdup(envVar);
-			if (configpath)
-				*configpath = pg_strdup(envVar);
-		}
+		else if (defaultValue)
+			*dirpath = pg_strdup(defaultValue);
 		else
 			pg_fatal("You must identify the directory where the %s.\n"
 					 "Please use the %s command-line option or the %s environment variable.\n",
 					 description, cmdLineOption, envVarName);
+
+		if (configpath)
+			*configpath = pg_strdup(*dirpath);
 	}
 
 	/*
@@ -455,12 +473,7 @@ get_sock_dir(ClusterInfo *cluster, bool live_check)
 	if (GET_MAJOR_VERSION(cluster->major_version) >= 901)
 	{
 		if (!live_check)
-		{
-			/* Use the current directory for the socket */
-			cluster->sockdir = pg_malloc(MAXPGPATH);
-			if (!getcwd(cluster->sockdir, MAXPGPATH))
-				pg_fatal("could not determine current directory\n");
-		}
+			cluster->sockdir = user_opts.socketdir;
 		else
 		{
 			/*
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index f83a3eeb67..d745ee101c 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -298,6 +298,8 @@ typedef struct
 								 * changes */
 	transferMode transfer_mode; /* copy files or link them? */
 	int			jobs;
+	char	   *socketdir;		/* directory to use for sockets, NULL means
+								 * using the default of CWD */
 } UserOpts;
 
 typedef struct
-- 
2.14.1.145.gb3622a4ee

