On 06/15/2012 05:40 PM, Honza Horak wrote:
I realized the patch has some difficulties -- namely the socket path in the 
data dir lock file, which currently uses one port for socket and the same for 
interface. So to allow users to use arbitrary port for all unix sockets, we'd 
need to add another line only for unix socket, which doesn't apply for other 
platforms. Or we could just say that the first socket will allways use the 
default port (PostPortNumber), which is a solution I prefer currently, but will 
be glad for any other opinion. This is also why there is still un-necesary 
string splitting in pg_ctl.c, which will be removed after the issue above is 
solved.


This is an enhanced patch, which forbids using a port number in the first socket directory entry.

Honza
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..d1727f8 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -453,17 +453,26 @@ 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>. 
+        It is not possible to define <replaceable>port</> number in the first
+        entry. If set, it will be ignored and default <varname>port</>
+        will be used. 
         This parameter can only be set at server start.
        </para>
 
@@ -472,7 +481,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 +6602,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..50d4167 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -31,6 +31,7 @@
 #include "postmaster/bgwriter.h"
 #include "postmaster/startup.h"
 #include "postmaster/walwriter.h"
+#include "postmaster/postmaster.h"
 #include "replication/walreceiver.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -349,7 +350,20 @@ AuxiliaryProcessMain(int argc, char *argv[])
 
 	/* If standalone, create lockfile for data directory */
 	if (!IsUnderPostmaster)
-		CreateDataDirLockFile(false);
+	{
+#ifdef HAVE_UNIX_SOCKETS
+		List	   *socketsList;
+		char	   *mainSocket = NULL;
+#endif
+		/*
+		 * We need to parse UnixSocketDirs here, because we want the first 
+		 * socket directory, which will be used in postmaster.pid. 
+		 */
+#ifdef HAVE_UNIX_SOCKETS
+		SplitUnixDirectories(UnixSocketDirs, &socketsList, &mainSocket);
+#endif
+		CreateDataDirLockFile(false, mainSocket);
+	}
 
 	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..0cc3521 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,15 @@ 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
+	SplitUnixDirectories(UnixSocketDirs, &socketsList, &mainSocket);
+#endif
+
+	/*
 	 * Create lockfile for data directory.
 	 *
 	 * We want to do this before we try to grab the input sockets, because the
@@ -815,7 +828,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 +875,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 +946,92 @@ 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;
+		bool		first = TRUE;
+
+		/* 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, except the first one */
+			if ((colon = strrchr(cursocket, ':')) != 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)));
+				if (first)
+					portnum = PostPortNumber;
+			}
+			else
+			{
+				portnum = PostPortNumber;
+			}
+			first = FALSE;
+
+			/* 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..22c8228 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':
@@ -3647,6 +3647,10 @@ PostgresMain(int argc, char *argv[], const char *username)
 
 	if (!IsUnderPostmaster)
 	{
+#ifdef HAVE_UNIX_SOCKETS
+		List	   *socketsList;
+		char	   *mainSocket = NULL;
+#endif
 		/*
 		 * Validate we have been given a reasonable-looking DataDir (if under
 		 * postmaster, assume postmaster did this already).
@@ -3658,9 +3662,16 @@ PostgresMain(int argc, char *argv[], const char *username)
 		ChangeToDataDir();
 
 		/*
+		 * We need to parse UnixSocketDirs here, because we want the first 
+		 * socket directory, which will be used in postmaster.pid. 
+		 */
+#ifdef HAVE_UNIX_SOCKETS
+		SplitUnixDirectories(UnixSocketDirs, &socketsList, &mainSocket);
+#endif
+		/*
 		 * Create lockfile for data directory.
 		 */
-		CreateDataDirLockFile(false);
+		CreateDataDirLockFile(false, mainSocket);
 	}
 
 	/* Early initialization */
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index e1b57ba..466d738 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -2445,6 +2445,155 @@ 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;
+}
+
+/*
+ * parse UnixSocketDirs as a list of directory[:port], return the list
+ * in socketsList and store the first item into mainSocket.
+ */
+void
+SplitUnixDirectories(char *unixSocketDirs, List **socketsList,
+					 char **mainSocket)
+{
+#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))
+		{
+			char *colon;
+			
+			*mainSocket = (char *) lfirst(l);
+			
+			/* 
+			 * the first unix socket directory cannot contain :port
+			 * and the default port is used every-time, so strip the port 
+			 */
+			if ((colon = strrchr(*mainSocket, ':')) != NULL)
+				*colon = '\0';
+		}
+		else
+			*mainSocket = unixSocketDirs;
+	}
+	else
+		*mainSocket = unixSocketDirs;
+#endif
+}
 
 /*****************************************************************************
  *	Comparison Functions used for bytea
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index fb376a0..670e1a7 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);
 }
@@ -1292,3 +1295,4 @@ pg_bindtextdomain(const char *domain)
 	}
 #endif
 }
+
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..1eea8aa 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -65,7 +65,9 @@
 # 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:port,
+					# while the first socket's port is the default one
+					# (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 3826312..6d811ff 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -521,7 +521,7 @@ 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
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..492a08e 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -752,6 +752,10 @@ 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 void SplitUnixDirectories(char *unixSocketDirs, List **socketsList,
+					  char **mainSocket);
 extern Datum replace_text(PG_FUNCTION_ARGS);
 extern text *replace_text_regexp(text *src_text, void *regexp,
 					text *replace_text, bool glob);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to