multiple-socket-dirs.patch

text/x-patch

Filename: multiple-socket-dirs.patch
Type: text/x-patch
Part: 0
Message: Re: Ability to listen on two unix sockets

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
doc/src/sgml/client-auth.sgml 1 1
doc/src/sgml/config.sgml 12 6
doc/src/sgml/runtime.sgml 1 1
src/backend/bootstrap/bootstrap.c 1 1
src/backend/libpq/pqcomm.c 41 24
src/backend/postmaster/postmaster.c 126 12
src/backend/tcop/postgres.c 2 2
src/backend/utils/adt/varlena.c 101 0
src/backend/utils/init/miscinit.c 9 6
src/backend/utils/misc/guc.c 6 5
src/backend/utils/misc/postgresql.conf.sample 2 1
src/bin/pg_ctl/pg_ctl.c 15 2
src/include/miscadmin.h 3 2
src/include/postmaster/postmaster.h 1 1
src/include/utils/builtins.h 2 0
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index cfdb33a..679c40a 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -838,7 +838,7 @@ omicron         bryanh                  guest1
     <varname>unix_socket_permissions</varname> (and possibly
     <varname>unix_socket_group</varname>) configuration parameters as
     described in <xref linkend="runtime-config-connection">.  Or you
-    could set the <varname>unix_socket_directory</varname>
+    could set the <varname>unix_socket_directories</varname>
     configuration parameter to place the socket file in a suitably
     restricted directory.
    </para>
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 074afee..7634682 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -453,17 +453,23 @@ SET ENABLE_SEQSCAN TO OFF;
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-unix-socket-directory" xreflabel="unix_socket_directory">
-      <term><varname>unix_socket_directory</varname> (<type>string</type>)</term>
+     <varlistentry id="guc-unix-socket-directories" xreflabel="unix_socket_directories">
+      <term><varname>unix_socket_directories</varname> (<type>string</type>)</term>
       <indexterm>
-       <primary><varname>unix_socket_directory</> configuration parameter</primary>
+       <primary><varname>unix_socket_directories</> configuration parameter</primary>
       </indexterm>
       <listitem>
        <para>
-        Specifies the directory of the Unix-domain socket on which the
+        Specifies the directories of the Unix-domain sockets on which the
         server is to listen for
         connections from client applications.  The default is normally
         <filename>/tmp</filename>, but can be changed at build time.
+        Directories are separated by ',' and additional <replaceable>port</> 
+        number can be set, separated from directory by ':'. Port number will 
+        only be used as a part of the socket file name. For example,
+        <literal>'/var/run, /tmp:5431'</literal> would create socket files
+        <literal>/var/run/.s.PGSQL.5432</literal> and
+        <literal>/tmp/.s.PGSQL.5431</literal>.
         This parameter can only be set at server start.
        </para>
 
@@ -472,7 +478,7 @@ SET ENABLE_SEQSCAN TO OFF;
         <literal>.s.PGSQL.<replaceable>nnnn</></literal> where
         <replaceable>nnnn</> is the server's port number, an ordinary file
         named <literal>.s.PGSQL.<replaceable>nnnn</>.lock</literal> will be
-        created in the <varname>unix_socket_directory</> directory.  Neither
+        created in the <varname>unix_socket_directories</> directories.  Neither
         file should ever be removed manually.
        </para>
 
@@ -6593,7 +6599,7 @@ LOG:  CleanUpLock: deleting: lock(0xb7acd844) id(24688,24696,0,0,0,1)
        </row>
        <row>
         <entry><option>-k <replaceable>x</replaceable></option></entry>
-        <entry><literal>unix_socket_directory = <replaceable>x</replaceable></></entry>
+        <entry><literal>unix_socket_directories = <replaceable>x</replaceable></></entry>
        </row>
        <row>
         <entry><option>-l</option></entry>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 7ba18f0..6c74844 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1784,7 +1784,7 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   <para>
    The simplest way to prevent spoofing for <literal>local</>
    connections is to use a Unix domain socket directory (<xref
-   linkend="guc-unix-socket-directory">) that has write permission only
+   linkend="guc-unix-socket-directories">) that has write permission only
    for a trusted local user.  This prevents a malicious user from creating
    their own socket file in that directory.  If you are concerned that
    some applications might still reference <filename>/tmp</> for the
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e3ae92d..72505e3 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -349,7 +349,7 @@ AuxiliaryProcessMain(int argc, char *argv[])
 
 	/* If standalone, create lockfile for data directory */
 	if (!IsUnderPostmaster)
-		CreateDataDirLockFile(false);
+		CreateDataDirLockFile(false, NULL);
 
 	SetProcessingMode(BootstrapProcessing);
 	IgnoreSystemIndexes = true;
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 5272811..cf1e157 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -103,8 +103,8 @@ int			Unix_socket_permissions;
 char	   *Unix_socket_group;
 
 
-/* Where the Unix socket file is */
-static char sock_path[MAXPGPATH];
+/* Where the Unix socket files are */
+static List *sock_paths = NIL;
 
 
 /*
@@ -140,8 +140,8 @@ static int	internal_flush(void);
 static void pq_set_nonblocking(bool nonblocking);
 
 #ifdef HAVE_UNIX_SOCKETS
-static int	Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName);
-static int	Setup_AF_UNIX(void);
+static int	Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName, char **sock_path);
+static int	Setup_AF_UNIX(char *sock_path);
 #endif   /* HAVE_UNIX_SOCKETS */
 
 
@@ -234,14 +234,23 @@ pq_close(int code, Datum arg)
 
 /* StreamDoUnlink()
  * Shutdown routine for backend connection
- * If a Unix socket is used for communication, explicitly close it.
+ * If any Unix sockets are used for communication, explicitly close them.
  */
 #ifdef HAVE_UNIX_SOCKETS
 static void
 StreamDoUnlink(int code, Datum arg)
 {
-	Assert(sock_path[0]);
-	unlink(sock_path);
+	ListCell	*l;
+	char	*cursocket;
+	
+	/* Loop through all created sockets... */
+	foreach(l, sock_paths)
+	{
+		cursocket = (char *) lfirst(l);
+		unlink(cursocket);
+	}
+	list_free(sock_paths);
+	sock_paths = NIL;
 }
 #endif   /* HAVE_UNIX_SOCKETS */
 
@@ -286,10 +295,9 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
 #ifdef HAVE_UNIX_SOCKETS
 	if (family == AF_UNIX)
 	{
-		/* Lock_AF_UNIX will also fill in sock_path. */
-		if (Lock_AF_UNIX(portNumber, unixSocketName) != STATUS_OK)
+		/* Lock_AF_UNIX will also fill in service. */
+		if (Lock_AF_UNIX(portNumber, unixSocketName, &service) != STATUS_OK)
 			return STATUS_ERROR;
-		service = sock_path;
 	}
 	else
 #endif   /* HAVE_UNIX_SOCKETS */
@@ -432,7 +440,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
 					 (IS_AF_UNIX(addr->ai_family)) ?
 				  errhint("Is another postmaster already running on port %d?"
 						  " If not, remove socket file \"%s\" and retry.",
-						  (int) portNumber, sock_path) :
+						  (int) portNumber, service) :
 				  errhint("Is another postmaster already running on port %d?"
 						  " If not, wait a few seconds and retry.",
 						  (int) portNumber)));
@@ -443,7 +451,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
 #ifdef HAVE_UNIX_SOCKETS
 		if (addr->ai_family == AF_UNIX)
 		{
-			if (Setup_AF_UNIX() != STATUS_OK)
+			if (Setup_AF_UNIX(service) != STATUS_OK)
 			{
 				closesocket(fd);
 				break;
@@ -490,9 +498,13 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
  * Lock_AF_UNIX -- configure unix socket file path
  */
 static int
-Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName)
+Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName, char **sock_path)
 {
-	UNIXSOCK_PATH(sock_path, portNumber, unixSocketName);
+	char new_sock[MAXPGPATH];
+
+	UNIXSOCK_PATH(new_sock, portNumber, unixSocketName);
+
+	*sock_path = pstrdup(new_sock);
 
 	/*
 	 * Grab an interlock file associated with the socket file.
@@ -502,13 +514,14 @@ Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName)
 	 * more portable, and second, it lets us remove any pre-existing socket
 	 * file without race conditions.
 	 */
-	CreateSocketLockFile(sock_path, true);
+	CreateSocketLockFile(*sock_path, true, unixSocketName);
 
 	/*
 	 * Once we have the interlock, we can safely delete any pre-existing
 	 * socket file to avoid failure at bind() time.
 	 */
-	unlink(sock_path);
+	unlink(*sock_path);
+	sock_paths = lappend(sock_paths, *sock_path);
 
 	return STATUS_OK;
 }
@@ -518,7 +531,7 @@ Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName)
  * Setup_AF_UNIX -- configure unix socket permissions
  */
 static int
-Setup_AF_UNIX(void)
+Setup_AF_UNIX(char *sock_path)
 {
 	/* Arrange to unlink the socket file at exit */
 	on_proc_exit(StreamDoUnlink, 0);
@@ -707,17 +720,21 @@ StreamClose(pgsocket sock)
  * TouchSocketFile -- mark socket file as recently accessed
  *
  * This routine should be called every so often to ensure that the socket
- * file has a recent mod date (ordinary operations on sockets usually won't
- * change the mod date).  That saves it from being removed by
+ * files have a recent mod date (ordinary operations on sockets usually won't
+ * change the mod date).  That saves them from being removed by
  * overenthusiastic /tmp-directory-cleaner daemons.  (Another reason we should
- * never have put the socket file in /tmp...)
+ * never have put the socket files in /tmp...)
  */
 void
 TouchSocketFile(void)
 {
-	/* Do nothing if we did not create a socket... */
-	if (sock_path[0] != '\0')
+	ListCell	*l;
+	char	*cursocket;
+	
+	/* Loop through all created sockets... */
+	foreach(l, sock_paths)
 	{
+		cursocket = (char *) lfirst(l);
 		/*
 		 * utime() is POSIX standard, utimes() is a common alternative. If we
 		 * have neither, there's no way to affect the mod or access time of
@@ -726,10 +743,10 @@ TouchSocketFile(void)
 		 * In either path, we ignore errors; there's no point in complaining.
 		 */
 #ifdef HAVE_UTIME
-		utime(sock_path, NULL);
+		utime(cursocket, NULL);
 #else							/* !HAVE_UTIME */
 #ifdef HAVE_UTIMES
-		utimes(sock_path, NULL);
+		utimes(cursocket, NULL);
 #endif   /* HAVE_UTIMES */
 #endif   /* HAVE_UTIME */
 	}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index eeea933..f91dcdd 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -156,7 +156,7 @@ static Backend *ShmemBackendArray;
 
 /* The socket number we are listening for connections on */
 int			PostPortNumber;
-char	   *UnixSocketDir;
+char	   *UnixSocketDirs;
 char	   *ListenAddresses;
 
 /*
@@ -499,6 +499,10 @@ PostmasterMain(int argc, char *argv[])
 	char	   *userDoption = NULL;
 	bool		listen_addr_saved = false;
 	int			i;
+#ifdef HAVE_UNIX_SOCKETS
+	List	   *socketsList;
+	char	   *mainSocket = NULL;
+#endif
 
 	MyProcPid = PostmasterPid = getpid();
 
@@ -607,7 +611,7 @@ PostmasterMain(int argc, char *argv[])
 				break;
 
 			case 'k':
-				SetConfigOption("unix_socket_directory", optarg, PGC_POSTMASTER, PGC_S_ARGV);
+				SetConfigOption("unix_socket_directories", optarg, PGC_POSTMASTER, PGC_S_ARGV);
 				break;
 
 			case 'l':
@@ -807,6 +811,40 @@ PostmasterMain(int argc, char *argv[])
 	}
 
 	/*
+	 * We need to parse UnixSocketDirs here, because we want only 
+	 * the first socket directory be used in postmaster.pid, which is done
+	 * in CreateDataDirLockFile(). 
+	 */
+#ifdef HAVE_UNIX_SOCKETS
+	if (UnixSocketDirs)
+	{
+		char	   *rawSocketsString;
+		ListCell   *l;
+
+		/* Need a modifiable copy of UnixSocketDirs */
+		rawSocketsString = pstrdup(UnixSocketDirs);
+
+		/* Parse string into list of directories */
+		if (!SplitDirectoriesString(rawSocketsString, ',', &socketsList))
+		{
+			/* syntax error in list */
+			ereport(FATAL,
+				   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							errmsg("invalid list syntax for \"unix_socket_directories\"")));
+		}
+
+		pfree(rawSocketsString);
+
+		if (l = list_head(socketsList))
+			mainSocket = (char *) lfirst(l);
+		else
+			mainSocket = UnixSocketDirs;
+	}
+	else
+		mainSocket = UnixSocketDirs;
+#endif
+
+	/*
 	 * Create lockfile for data directory.
 	 *
 	 * We want to do this before we try to grab the input sockets, because the
@@ -815,7 +853,7 @@ PostmasterMain(int argc, char *argv[])
 	 * For the same reason, it's best to grab the TCP socket(s) before the
 	 * Unix socket.
 	 */
-	CreateDataDirLockFile(true);
+	CreateDataDirLockFile(true, mainSocket);
 
 	/*
 	 * Initialize SSL library, if specified.
@@ -862,12 +900,12 @@ PostmasterMain(int argc, char *argv[])
 			if (strcmp(curhost, "*") == 0)
 				status = StreamServerPort(AF_UNSPEC, NULL,
 										  (unsigned short) PostPortNumber,
-										  UnixSocketDir,
+										  UnixSocketDirs,
 										  ListenSocket, MAXLISTEN);
 			else
 				status = StreamServerPort(AF_UNSPEC, curhost,
 										  (unsigned short) PostPortNumber,
-										  UnixSocketDir,
+										  UnixSocketDirs,
 										  ListenSocket, MAXLISTEN);
 
 			if (status == STATUS_OK)
@@ -933,13 +971,89 @@ PostmasterMain(int argc, char *argv[])
 #endif
 
 #ifdef HAVE_UNIX_SOCKETS
-	status = StreamServerPort(AF_UNIX, NULL,
-							  (unsigned short) PostPortNumber,
-							  UnixSocketDir,
-							  ListenSocket, MAXLISTEN);
-	if (status != STATUS_OK)
-		ereport(WARNING,
-				(errmsg("could not create Unix-domain socket")));
+	/* 
+	 * We can specify several directories for Unix sockets to listen on,
+	 * separated with ','. Socket name itself is the same in all cases.
+	 */
+	if (!UnixSocketDirs)
+	{
+		status = StreamServerPort(AF_UNIX, NULL,
+								  (unsigned short) PostPortNumber,
+								  UnixSocketDirs,
+								  ListenSocket, MAXLISTEN);
+		if (status != STATUS_OK)
+			ereport(WARNING,
+					(errmsg("could not create Unix-domain socket")));
+	
+	}
+	else
+	{
+		ListCell   *l;
+
+		/* We have parse string into list of directories earlier */
+		foreach(l, socketsList)
+		{
+			char	*cursocket = (char *) lfirst(l);
+			char	*colon;
+			char	*port;
+			int64	portnum;
+			char	*endptr;
+			bool	bad_strtol;
+			
+			/* syntax is directory or directory:port */
+			colon = strrchr(cursocket, ':');
+			if (colon != NULL)
+			{
+				port = colon + 1;
+				*colon = '\0';
+				
+				/*
+				 * convert port number to integer, ignoring optional trailing
+				 * whitespace).  strtol should ignore leading whitespace, too.
+				 */
+				portnum = strtol(port, &endptr, 0);
+				bad_strtol = (errno == ERANGE);
+
+				while (isspace((unsigned char) *endptr))
+					endptr++;
+
+				if (*endptr != '\0')
+				ereport(FATAL,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("port number \"%s\" invalid in \"unix_socket_directories\"", port)));
+
+				if (bad_strtol || portnum < 1 || portnum > 65535)
+					ereport(FATAL,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("port number \"%s\" out of range in \"unix_socket_directories\"", port)));
+			}
+			else
+			{
+				portnum = PostPortNumber;
+			}
+
+			/* only absolute paths are allowed */
+			canonicalize_path(cursocket);
+			if (!is_absolute_path(cursocket))
+			{
+				ereport(FATAL,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("directory \"%s\" is not an absolute path", cursocket)));
+			}
+			else
+			{
+				status = StreamServerPort(AF_UNIX, NULL,
+					(unsigned short) portnum,
+					cursocket,
+					ListenSocket, MAXLISTEN);
+				if (status != STATUS_OK)
+				ereport(FATAL,
+					(errmsg("could not create secondary Unix-domain socket at \"%s\"", cursocket)));
+			}
+		}
+		
+		list_free(socketsList);
+	}
 #endif
 
 	/*
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 51b6df5..837d0e8 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3343,7 +3343,7 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx)
 				break;
 
 			case 'k':
-				SetConfigOption("unix_socket_directory", optarg, ctx, gucsource);
+				SetConfigOption("unix_socket_directories", optarg, ctx, gucsource);
 				break;
 
 			case 'l':
@@ -3660,7 +3660,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 		/*
 		 * Create lockfile for data directory.
 		 */
-		CreateDataDirLockFile(false);
+		CreateDataDirLockFile(false, NULL);
 	}
 
 	/* Early initialization */
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index e1b57ba..ffc7e2b 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -2445,6 +2445,107 @@ SplitIdentifierString(char *rawstring, char separator,
 	return true;
 }
 
+/*
+ * SplitDirectoriesString --- parse a string containing directories
+ *
+ * Inputs:
+ *	rawstring: the input string; must be overwritable!	On return, it's
+ *			   been modified to contain the separated directories.
+ *	separator: the separator punctuation expected between directories
+ *			   (typically ',' or ';').	Whitespace may also appear around
+ *			   directories.
+ * Outputs:
+ *	namelist: filled with a palloc'd list of pointers to directories within
+ *			  rawstring.  Caller should list_free() this even on error return.
+ *
+ * Returns TRUE if okay, FALSE if there is a syntax error in the string.
+ *
+ * Note that an empty string is considered okay here.
+ */
+bool
+SplitDirectoriesString(char *rawstring, char separator,
+					  List **namelist)
+{
+	char	   *nextp = rawstring;
+	bool		done = false;
+
+	*namelist = NIL;
+
+	while (isspace((unsigned char) *nextp))
+		nextp++;				/* skip leading whitespace */
+
+	if (*nextp == '\0')
+		return true;			/* allow empty string */
+
+	/* At the top of the loop, we are at start of a new directories. */
+	do
+	{
+		char	   *curname;
+		char	   *endp;
+
+		if (*nextp == '\"')
+		{
+			/* Quoted name --- collapse quote-quote pairs */
+			curname = nextp + 1;
+			for (;;)
+			{
+				endp = strchr(nextp + 1, '\"');
+				if (endp == NULL)
+					return false;		/* mismatched quotes */
+				if (endp[1] != '\"')
+					break;		/* found end of quoted name */
+				/* Collapse adjacent quotes into one quote, and look again */
+				memmove(endp, endp + 1, strlen(endp));
+				nextp = endp;
+			}
+			/* endp now points at the terminating quote */
+			nextp = endp + 1;
+		}
+		else
+		{
+			/* Unquoted name --- extends to separator or whitespace */
+			curname = nextp;
+			while (*nextp && *nextp != separator &&
+				   !isspace((unsigned char) *nextp))
+				nextp++;
+			endp = nextp;
+			if (curname == nextp)
+				return false;	/* empty unquoted name not allowed */
+		}
+
+		while (isspace((unsigned char) *nextp))
+			nextp++;			/* skip trailing whitespace */
+
+		if (*nextp == separator)
+		{
+			nextp++;
+			while (isspace((unsigned char) *nextp))
+				nextp++;		/* skip leading whitespace for next */
+			/* we expect another name, so done remains false */
+		}
+		else if (*nextp == '\0')
+			done = true;
+		else
+			return false;		/* invalid syntax */
+
+		/* Now safe to overwrite separator with a null */
+		*endp = '\0';
+
+		/* Truncate path if it's overlength */
+		if (strlen(curname) >= MAXPGPATH)
+			curname[MAXPGPATH-1] = '\0';
+
+		/*
+		 * Finished isolating current name --- add it to list
+		 */
+		*namelist = lappend(*namelist, pstrdup(curname));
+
+		/* Loop back if we didn't reach end of string */
+	} while (!done);
+
+	return true;
+}
+
 
 /*****************************************************************************
  *	Comparison Functions used for bytea
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index fb376a0..704c33c 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -663,10 +663,11 @@ UnlinkLockFile(int status, Datum filename)
  * filename is the name of the lockfile to create.
  * amPostmaster is used to determine how to encode the output PID.
  * isDDLock and refName are used to determine what error message to produce.
+ * socketPath is the path to the Unix socket we want to lock.
  */
 static void
 CreateLockFile(const char *filename, bool amPostmaster,
-			   bool isDDLock, const char *refName)
+			   bool isDDLock, const char *refName, const char *socketPath)
 {
 	int			fd;
 	char		buffer[MAXPGPATH * 2 + 256];
@@ -892,7 +893,8 @@ CreateLockFile(const char *filename, bool amPostmaster,
 			 (long) MyStartTime,
 			 PostPortNumber,
 #ifdef HAVE_UNIX_SOCKETS
-			 (*UnixSocketDir != '\0') ? UnixSocketDir : DEFAULT_PGSOCKET_DIR
+			 (socketPath && *socketPath != '\0') ? socketPath
+												 : DEFAULT_PGSOCKET_DIR
 #else
 			 ""
 #endif
@@ -947,21 +949,22 @@ CreateLockFile(const char *filename, bool amPostmaster,
  * helps ensure that we are locking the directory we should be.
  */
 void
-CreateDataDirLockFile(bool amPostmaster)
+CreateDataDirLockFile(bool amPostmaster, const char *socketDir)
 {
-	CreateLockFile(DIRECTORY_LOCK_FILE, amPostmaster, true, DataDir);
+	CreateLockFile(DIRECTORY_LOCK_FILE, amPostmaster, true, DataDir, socketDir);
 }
 
 /*
  * Create a lockfile for the specified Unix socket file.
  */
 void
-CreateSocketLockFile(const char *socketfile, bool amPostmaster)
+CreateSocketLockFile(const char *socketfile, bool amPostmaster, 
+					 const char *socketDir)
 {
 	char		lockfile[MAXPGPATH];
 
 	snprintf(lockfile, sizeof(lockfile), "%s.lock", socketfile);
-	CreateLockFile(lockfile, amPostmaster, false, socketfile);
+	CreateLockFile(lockfile, amPostmaster, false, socketfile, socketDir);
 	/* Save name of lockfile for TouchSocketLockFile */
 	strcpy(socketLockFile, lockfile);
 }
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b756e58..bf90aff 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2894,14 +2894,15 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
-		{"unix_socket_directory", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
-			gettext_noop("Sets the directory where the Unix-domain socket will be created."),
-			NULL,
+		{"unix_socket_directories", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
+			gettext_noop("Sets the directories where the Unix-domain socket will be created."),
+			gettext_noop("Directories are separated by \",\" and an additional "
+						"port number can be set, separated from directory by \":\"."),
 			GUC_SUPERUSER_ONLY
 		},
-		&UnixSocketDir,
+		&UnixSocketDirs,
 		"",
-		check_canonical_path, NULL, NULL
+		NULL, NULL, NULL
 	},
 
 	{
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index fa75d00..b7f9ac9 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -65,7 +65,8 @@
 # Note:  Increasing max_connections costs ~400 bytes of shared memory per
 # connection slot, plus lock space (see max_locks_per_transaction).
 #superuser_reserved_connections = 3	# (change requires restart)
-#unix_socket_directory = ''		# (change requires restart)
+#unix_socket_directories = ''		# comma-separated list of directories
+					# (change requires restart)
 #unix_socket_group = ''			# (change requires restart)
 #unix_socket_permissions = 0777		# begin with 0 to use octal notation
 					# (change requires restart)
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index d7b8367..a407684 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -521,14 +521,27 @@ test_postmaster_connection(bool do_checkpoint)
 						hostaddr = optlines[LOCK_FILE_LINE_LISTEN_ADDR - 1];
 
 						/*
-						 * While unix_socket_directory can accept relative
+						 * While unix_socket_directories can accept relative
 						 * directories, libpq's host parameter must have a
 						 * leading slash to indicate a socket directory.  So,
 						 * ignore sockdir if it's relative, and try to use TCP
 						 * instead.
 						 */
 						if (sockdir[0] == '/')
-							strlcpy(host_str, sockdir, sizeof(host_str));
+						{
+							char *rawstring;
+							char *comma;
+							
+							/* Need a modifiable copy of UnixSocketDirs */
+							rawstring = xstrdup(sockdir);
+							
+							/* We want only the first socket, in case more are defined */
+							if ((comma = strchr(rawstring, ',')) != NULL)
+								*comma = '\0';
+							
+							strlcpy(host_str, rawstring, sizeof(host_str));
+							free(rawstring);
+						}
 						else
 							strlcpy(host_str, hostaddr, sizeof(host_str));
 
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index b186eed..40802cf 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -398,8 +398,9 @@ extern char *local_preload_libraries_string;
 #define LOCK_FILE_LINE_LISTEN_ADDR	6
 #define LOCK_FILE_LINE_SHMEM_KEY	7
 
-extern void CreateDataDirLockFile(bool amPostmaster);
-extern void CreateSocketLockFile(const char *socketfile, bool amPostmaster);
+extern void CreateDataDirLockFile(bool amPostmaster, const char *socketDir);
+extern void CreateSocketLockFile(const char *socketfile, bool amPostmaster,
+								 const char *socketDir);
 extern void TouchSocketLockFile(void);
 extern void AddToDataDirLockFile(int target_line, const char *str);
 extern void ValidatePgVersion(const char *path);
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 683ce3c..f19a3f8 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -19,7 +19,7 @@ extern int	ReservedBackends;
 extern int	PostPortNumber;
 extern int	Unix_socket_permissions;
 extern char *Unix_socket_group;
-extern char *UnixSocketDir;
+extern char *UnixSocketDirs;
 extern char *ListenAddresses;
 extern bool ClientAuthInProgress;
 extern int	PreAuthDelay;
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index d1e8370..65201fc 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -752,6 +752,8 @@ extern int	varstr_cmp(char *arg1, int len1, char *arg2, int len2, Oid collid);
 extern List *textToQualifiedNameList(text *textval);
 extern bool SplitIdentifierString(char *rawstring, char separator,
 					  List **namelist);
+extern bool SplitDirectoriesString(char *rawstring, char separator,
+					  List **namelist);
 extern Datum replace_text(PG_FUNCTION_ARGS);
 extern text *replace_text_regexp(text *src_text, void *regexp,
 					text *replace_text, bool glob);