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
unix_socket_permissions (and possibly
unix_socket_group) configuration parameters as
described in . Or you
- could set the unix_socket_directory
+ could set the unix_socket_directories
configuration parameter to place the socket file in a suitably
restricted directory.
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;
-
- unix_socket_directory (string)
+
+ unix_socket_directories (string)
- unix_socket_directory> configuration parameter
+ unix_socket_directories> configuration parameter
- 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
/tmp, but can be changed at build time.
+ Directories are separated by ',' and additional 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,
+ '/var/run, /tmp:5431' would create socket files
+ /var/run/.s.PGSQL.5432 and
+ /tmp/.s.PGSQL.5431.
+ It is not possible to define port> number in the first
+ entry. If set, it will be ignored and default port>
+ will be used.
This parameter can only be set at server start.
@@ -472,7 +481,7 @@ SET ENABLE_SEQSCAN TO OFF;
.s.PGSQL.nnnn> where
nnnn> is the server's port number, an ordinary file
named .s.PGSQL.nnnn>.lock will be
- created in the unix_socket_directory> directory. Neither
+ created in the unix_socket_directories> directories. Neither
file should ever be removed manually.
@@ -6593,7 +6602,7 @@ LOG: CleanUpLock: deleting: lock(0xb7acd844) id(24688,24696,0,0,0,1)
- unix_socket_directory = x>
+ unix_socket_directories = x>
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
The simplest way to prevent spoofing for local>
connections is to use a Unix domain 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 /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);