v2-0001-Add-support-for-abstract-Unix-domain-sockets.patch

text/plain

Filename: v2-0001-Add-support-for-abstract-Unix-domain-sockets.patch
Type: text/plain
Part: 0
Message: Re: abstract Unix-domain sockets

Patch

Format: format-patch
Series: patch v2-0001
Subject: Add support for abstract Unix-domain sockets
File+
doc/src/sgml/config.sgml 23 1
doc/src/sgml/libpq.sgml 4 1
src/backend/libpq/pqcomm.c 8 0
src/bin/psql/command.c 5 10
src/bin/psql/prompt.c 2 1
src/common/ip.c 23 1
src/include/libpq/pqcomm.h 9 0
src/interfaces/libpq/fe-connect.c 2 2
From 537b5bbfc231551a76f55de3ff0cba5722b20298 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 22 Oct 2020 08:47:52 +0200
Subject: [PATCH v2 1/2] Add support for abstract Unix-domain sockets

This is a variant of the normal Unix-domain sockets that don't use the
file system but a separate "abstract" namespace.  At the user
interface, such sockets are represented by names starting with "@".
Supported on Linux and Windows right now.

Discussion: https://www.postgresql.org/message-id/flat/6dee8574-b0ad-fc49-9c8c-2edc796f0033@2ndquadrant.com
---
 doc/src/sgml/config.sgml          | 24 +++++++++++++++++++++++-
 doc/src/sgml/libpq.sgml           |  5 ++++-
 src/backend/libpq/pqcomm.c        |  8 ++++++++
 src/bin/psql/command.c            | 15 +++++----------
 src/bin/psql/prompt.c             |  3 ++-
 src/common/ip.c                   | 24 +++++++++++++++++++++++-
 src/include/libpq/pqcomm.h        |  9 +++++++++
 src/interfaces/libpq/fe-connect.c |  4 ++--
 8 files changed, 76 insertions(+), 16 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f043433e31..e2d92dd523 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -749,6 +749,21 @@ <title>Connection Settings</title>
         An empty value
         specifies not listening on any Unix-domain sockets, in which case
         only TCP/IP sockets can be used to connect to the server.
+       </para>
+
+       <para>
+        A value that starts with <literal>@</literal> specifies that a
+        Unix-domain socket in the abstract namespace should be created
+        (currently supported on Linux and Windows).  In that case, this value
+        does not specify a <quote>directory</quote> but a prefix from which
+        the actual socket name is computed in the same manner as for the
+        file-system namespace.  While the abstract socket name prefix can be
+        chosen freely, since it is not a file-system location, the convention
+        is to nonetheless use file-system-like values such as
+        <literal>@/tmp</literal>.
+       </para>
+
+       <para>
         The default value is normally
         <filename>/tmp</filename>, but that can be changed at build time.
         On Windows, the default is empty, which means no Unix-domain socket is
@@ -763,6 +778,7 @@ <title>Connection Settings</title>
         named <literal>.s.PGSQL.<replaceable>nnnn</replaceable>.lock</literal> will be
         created in each of the <varname>unix_socket_directories</varname> directories.
         Neither file should ever be removed manually.
+        For sockets in the abstract namespace, no lock file is created.
        </para>
       </listitem>
      </varlistentry>
@@ -787,7 +803,8 @@ <title>Connection Settings</title>
 
        <para>
         This parameter is not supported on Windows.  Any setting will be
-        ignored.
+        ignored.  Also, sockets in the abstract namespace have no file owner,
+        so this setting is also ignored in that case.
        </para>
       </listitem>
      </varlistentry>
@@ -834,6 +851,11 @@ <title>Connection Settings</title>
         similar effect by pointing <varname>unix_socket_directories</varname> to a
         directory having search permission limited to the desired audience.
        </para>
+
+       <para>
+        Sockets in the abstract namespace have no file permissions, so this
+        setting is also ignored in that case.
+       </para>
       </listitem>
      </varlistentry>
 
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index de60281fcb..612c1c94be 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1031,7 +1031,10 @@ <title>Parameter Key Words</title>
         communication; the value is the name of the directory in which the
         socket file is stored.  (On Unix, an absolute path name begins with a
         slash.  On Windows, paths starting with drive letters are also
-        recognized.)  The default behavior when <literal>host</literal> is not
+        recognized.)  If the host name starts with <literal>@</literal>, it is
+        taken as a Unix-domain socket in the abstract namespace (currently
+        supported on Linux and Windows).
+        The default behavior when <literal>host</literal> is not
         specified, or is empty, is to connect to a Unix-domain
         socket<indexterm><primary>Unix domain socket</primary></indexterm> in
         <filename>/tmp</filename> (or whatever socket directory was specified
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index ac986c0505..84568508c5 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -611,6 +611,10 @@ StreamServerPort(int family, const char *hostName, unsigned short portNumber,
 static int
 Lock_AF_UNIX(const char *unixSocketDir, const char *unixSocketPath)
 {
+	/* no lock file for abstract sockets */
+	if (unixSocketPath[0] == '@')
+		return STATUS_OK;
+
 	/*
 	 * Grab an interlock file associated with the socket file.
 	 *
@@ -642,6 +646,10 @@ Lock_AF_UNIX(const char *unixSocketDir, const char *unixSocketPath)
 static int
 Setup_AF_UNIX(const char *sock_path)
 {
+	/* no file system permissions for abstract sockets */
+	if (sock_path[0] == '@')
+		return STATUS_OK;
+
 	/*
 	 * Fix socket ownership/permission if requested.  Note we must do this
 	 * before we listen() to avoid a window where unwanted connections could
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index ba3ea39aaa..da9e21a2bb 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -37,6 +37,7 @@
 #include "input.h"
 #include "large_obj.h"
 #include "libpq-fe.h"
+#include "libpq/pqcomm.h"
 #include "mainloop.h"
 #include "portability/instr_time.h"
 #include "pqexpbuffer.h"
@@ -604,12 +605,9 @@ exec_command_conninfo(PsqlScanState scan_state, bool active_branch)
 			char	   *host = PQhost(pset.db);
 			char	   *hostaddr = PQhostaddr(pset.db);
 
-			/*
-			 * If the host is an absolute path, the connection is via socket
-			 * unless overridden by hostaddr
-			 */
-			if (is_absolute_path(host))
+			if (is_unixsock_path(host))
 			{
+				/* hostaddr overrides host */
 				if (hostaddr && *hostaddr)
 					printf(_("You are connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
 						   db, PQuser(pset.db), hostaddr, PQport(pset.db));
@@ -3380,12 +3378,9 @@ do_connect(enum trivalue reuse_previous_specification,
 			char	   *host = PQhost(pset.db);
 			char	   *hostaddr = PQhostaddr(pset.db);
 
-			/*
-			 * If the host is an absolute path, the connection is via socket
-			 * unless overridden by hostaddr
-			 */
-			if (is_absolute_path(host))
+			if (is_unixsock_path(host))
 			{
+				/* hostaddr overrides host */
 				if (hostaddr && *hostaddr)
 					printf(_("You are now connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
 						   PQdb(pset.db), PQuser(pset.db), hostaddr, PQport(pset.db));
diff --git a/src/bin/psql/prompt.c b/src/bin/psql/prompt.c
index ef503ec41b..f42c3dfc74 100644
--- a/src/bin/psql/prompt.c
+++ b/src/bin/psql/prompt.c
@@ -15,6 +15,7 @@
 #include "common.h"
 #include "common/string.h"
 #include "input.h"
+#include "libpq/pqcomm.h"
 #include "prompt.h"
 #include "settings.h"
 
@@ -136,7 +137,7 @@ get_prompt(promptStatus_t status, ConditionalStack cstack)
 						const char *host = PQhost(pset.db);
 
 						/* INET socket */
-						if (host && host[0] && !is_absolute_path(host))
+						if (host && host[0] && !is_unixsock_path(host))
 						{
 							strlcpy(buf, host, sizeof(buf));
 							if (*p == 'm')
diff --git a/src/common/ip.c b/src/common/ip.c
index 69fcca8479..bcc779e00c 100644
--- a/src/common/ip.c
+++ b/src/common/ip.c
@@ -217,6 +217,21 @@ getaddrinfo_unix(const char *path, const struct addrinfo *hintsp,
 
 	strcpy(unp->sun_path, path);
 
+	/*
+	 * If the supplied path starts with @, replace that with a zero byte for
+	 * the internal representation.  In that mode, the entire sun_path is the
+	 * address, including trailing zero bytes.  But we set the address length
+	 * to only include the length of the original string.  That way the
+	 * trailing zero bytes won't show up in any network or socket lists of the
+	 * operating system.  This is just a convention, also followed by other
+	 * packages.
+	 */
+	if (path[0] == '@')
+	{
+		unp->sun_path[0] = '\0';
+		aip->ai_addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(path);
+	}
+
 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
 	unp->sun_len = sizeof(struct sockaddr_un);
 #endif
@@ -249,7 +264,14 @@ getnameinfo_unix(const struct sockaddr_un *sa, int salen,
 
 	if (service)
 	{
-		ret = snprintf(service, servicelen, "%s", sa->sun_path);
+		/*
+		 * Check whether it looks like an abstract socket, but it could also
+		 * just be an empty string.
+		 */
+		if (sa->sun_path[0] == '\0' && sa->sun_path[1] != '\0')
+			ret = snprintf(service, servicelen, "@%s", sa->sun_path + 1);
+		else
+			ret = snprintf(service, servicelen, "%s", sa->sun_path);
 		if (ret < 0 || ret >= servicelen)
 			return EAI_MEMORY;
 	}
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 781d86c8ef..cf967c3987 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -85,6 +85,15 @@ typedef struct
  */
 #define UNIXSOCK_PATH_BUFLEN sizeof(((struct sockaddr_un *) NULL)->sun_path)
 
+/*
+ * A host that looks either like an absolute path or starts with @ is
+ * interpreted as a Unix-domain socket address.
+ */
+static inline bool
+is_unixsock_path(const char *path)
+{
+	return is_absolute_path(path) || path[0] == '@';
+}
 
 /*
  * These manipulate the frontend/backend protocol version number.
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index b0ca37c2ed..6aa7b41d2b 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1093,7 +1093,7 @@ connectOptions2(PGconn *conn)
 		{
 			ch->type = CHT_HOST_NAME;
 #ifdef HAVE_UNIX_SOCKETS
-			if (is_absolute_path(ch->host))
+			if (is_unixsock_path(ch->host))
 				ch->type = CHT_UNIX_SOCKET;
 #endif
 		}
@@ -6945,7 +6945,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
 	/* 'localhost' matches pghost of '' or the default socket directory */
 	if (hostname == NULL || hostname[0] == '\0')
 		hostname = DefaultHost;
-	else if (is_absolute_path(hostname))
+	else if (is_unixsock_path(hostname))
 
 		/*
 		 * We should probably use canonicalize_path(), but then we have to
-- 
2.28.0