Skip to content

Commit c9f0624

Browse files
committed
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. Reviewed-by: Michael Paquier <michael@paquier.xyz> Discussion: https://www.postgresql.org/message-id/flat/6dee8574-b0ad-fc49-9c8c-2edc796f0033@2ndquadrant.com
1 parent a7e65dc commit c9f0624

File tree

8 files changed

+76
-16
lines changed

8 files changed

+76
-16
lines changed

doc/src/sgml/config.sgml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,21 @@ include_dir 'conf.d'
749749
An empty value
750750
specifies not listening on any Unix-domain sockets, in which case
751751
only TCP/IP sockets can be used to connect to the server.
752+
</para>
753+
754+
<para>
755+
A value that starts with <literal>@</literal> specifies that a
756+
Unix-domain socket in the abstract namespace should be created
757+
(currently supported on Linux and Windows). In that case, this value
758+
does not specify a <quote>directory</quote> but a prefix from which
759+
the actual socket name is computed in the same manner as for the
760+
file-system namespace. While the abstract socket name prefix can be
761+
chosen freely, since it is not a file-system location, the convention
762+
is to nonetheless use file-system-like values such as
763+
<literal>@/tmp</literal>.
764+
</para>
765+
766+
<para>
752767
The default value is normally
753768
<filename>/tmp</filename>, but that can be changed at build time.
754769
On Windows, the default is empty, which means no Unix-domain socket is
@@ -763,6 +778,7 @@ include_dir 'conf.d'
763778
named <literal>.s.PGSQL.<replaceable>nnnn</replaceable>.lock</literal> will be
764779
created in each of the <varname>unix_socket_directories</varname> directories.
765780
Neither file should ever be removed manually.
781+
For sockets in the abstract namespace, no lock file is created.
766782
</para>
767783
</listitem>
768784
</varlistentry>
@@ -787,7 +803,8 @@ include_dir 'conf.d'
787803

788804
<para>
789805
This parameter is not supported on Windows. Any setting will be
790-
ignored.
806+
ignored. Also, sockets in the abstract namespace have no file owner,
807+
so this setting is also ignored in that case.
791808
</para>
792809
</listitem>
793810
</varlistentry>
@@ -834,6 +851,11 @@ include_dir 'conf.d'
834851
similar effect by pointing <varname>unix_socket_directories</varname> to a
835852
directory having search permission limited to the desired audience.
836853
</para>
854+
855+
<para>
856+
Sockets in the abstract namespace have no file permissions, so this
857+
setting is also ignored in that case.
858+
</para>
837859
</listitem>
838860
</varlistentry>
839861

doc/src/sgml/libpq.sgml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,10 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
10311031
communication; the value is the name of the directory in which the
10321032
socket file is stored. (On Unix, an absolute path name begins with a
10331033
slash. On Windows, paths starting with drive letters are also
1034-
recognized.) The default behavior when <literal>host</literal> is not
1034+
recognized.) If the host name starts with <literal>@</literal>, it is
1035+
taken as a Unix-domain socket in the abstract namespace (currently
1036+
supported on Linux and Windows).
1037+
The default behavior when <literal>host</literal> is not
10351038
specified, or is empty, is to connect to a Unix-domain
10361039
socket<indexterm><primary>Unix domain socket</primary></indexterm> in
10371040
<filename>/tmp</filename> (or whatever socket directory was specified

src/backend/libpq/pqcomm.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,10 @@ StreamServerPort(int family, const char *hostName, unsigned short portNumber,
611611
static int
612612
Lock_AF_UNIX(const char *unixSocketDir, const char *unixSocketPath)
613613
{
614+
/* no lock file for abstract sockets */
615+
if (unixSocketPath[0] == '@')
616+
return STATUS_OK;
617+
614618
/*
615619
* Grab an interlock file associated with the socket file.
616620
*
@@ -642,6 +646,10 @@ Lock_AF_UNIX(const char *unixSocketDir, const char *unixSocketPath)
642646
static int
643647
Setup_AF_UNIX(const char *sock_path)
644648
{
649+
/* no file system permissions for abstract sockets */
650+
if (sock_path[0] == '@')
651+
return STATUS_OK;
652+
645653
/*
646654
* Fix socket ownership/permission if requested. Note we must do this
647655
* before we listen() to avoid a window where unwanted connections could

src/bin/psql/command.c

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "input.h"
3838
#include "large_obj.h"
3939
#include "libpq-fe.h"
40+
#include "libpq/pqcomm.h"
4041
#include "mainloop.h"
4142
#include "portability/instr_time.h"
4243
#include "pqexpbuffer.h"
@@ -604,12 +605,9 @@ exec_command_conninfo(PsqlScanState scan_state, bool active_branch)
604605
char *host = PQhost(pset.db);
605606
char *hostaddr = PQhostaddr(pset.db);
606607

607-
/*
608-
* If the host is an absolute path, the connection is via socket
609-
* unless overridden by hostaddr
610-
*/
611-
if (is_absolute_path(host))
608+
if (is_unixsock_path(host))
612609
{
610+
/* hostaddr overrides host */
613611
if (hostaddr && *hostaddr)
614612
printf(_("You are connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
615613
db, PQuser(pset.db), hostaddr, PQport(pset.db));
@@ -3407,12 +3405,9 @@ do_connect(enum trivalue reuse_previous_specification,
34073405
char *host = PQhost(pset.db);
34083406
char *hostaddr = PQhostaddr(pset.db);
34093407

3410-
/*
3411-
* If the host is an absolute path, the connection is via socket
3412-
* unless overridden by hostaddr
3413-
*/
3414-
if (is_absolute_path(host))
3408+
if (is_unixsock_path(host))
34153409
{
3410+
/* hostaddr overrides host */
34163411
if (hostaddr && *hostaddr)
34173412
printf(_("You are now connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
34183413
PQdb(pset.db), PQuser(pset.db), hostaddr, PQport(pset.db));

src/bin/psql/prompt.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "common.h"
1616
#include "common/string.h"
1717
#include "input.h"
18+
#include "libpq/pqcomm.h"
1819
#include "prompt.h"
1920
#include "settings.h"
2021

@@ -136,7 +137,7 @@ get_prompt(promptStatus_t status, ConditionalStack cstack)
136137
const char *host = PQhost(pset.db);
137138

138139
/* INET socket */
139-
if (host && host[0] && !is_absolute_path(host))
140+
if (host && host[0] && !is_unixsock_path(host))
140141
{
141142
strlcpy(buf, host, sizeof(buf));
142143
if (*p == 'm')

src/common/ip.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,21 @@ getaddrinfo_unix(const char *path, const struct addrinfo *hintsp,
217217

218218
strcpy(unp->sun_path, path);
219219

220+
/*
221+
* If the supplied path starts with @, replace that with a zero byte for
222+
* the internal representation. In that mode, the entire sun_path is the
223+
* address, including trailing zero bytes. But we set the address length
224+
* to only include the length of the original string. That way the
225+
* trailing zero bytes won't show up in any network or socket lists of the
226+
* operating system. This is just a convention, also followed by other
227+
* packages.
228+
*/
229+
if (path[0] == '@')
230+
{
231+
unp->sun_path[0] = '\0';
232+
aip->ai_addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(path);
233+
}
234+
220235
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
221236
unp->sun_len = sizeof(struct sockaddr_un);
222237
#endif
@@ -249,7 +264,14 @@ getnameinfo_unix(const struct sockaddr_un *sa, int salen,
249264

250265
if (service)
251266
{
252-
ret = snprintf(service, servicelen, "%s", sa->sun_path);
267+
/*
268+
* Check whether it looks like an abstract socket, but it could also
269+
* just be an empty string.
270+
*/
271+
if (sa->sun_path[0] == '\0' && sa->sun_path[1] != '\0')
272+
ret = snprintf(service, servicelen, "@%s", sa->sun_path + 1);
273+
else
274+
ret = snprintf(service, servicelen, "%s", sa->sun_path);
253275
if (ret < 0 || ret >= servicelen)
254276
return EAI_MEMORY;
255277
}

src/include/libpq/pqcomm.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ typedef struct
8585
*/
8686
#define UNIXSOCK_PATH_BUFLEN sizeof(((struct sockaddr_un *) NULL)->sun_path)
8787

88+
/*
89+
* A host that looks either like an absolute path or starts with @ is
90+
* interpreted as a Unix-domain socket address.
91+
*/
92+
static inline bool
93+
is_unixsock_path(const char *path)
94+
{
95+
return is_absolute_path(path) || path[0] == '@';
96+
}
8897

8998
/*
9099
* These manipulate the frontend/backend protocol version number.

src/interfaces/libpq/fe-connect.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,7 +1093,7 @@ connectOptions2(PGconn *conn)
10931093
{
10941094
ch->type = CHT_HOST_NAME;
10951095
#ifdef HAVE_UNIX_SOCKETS
1096-
if (is_absolute_path(ch->host))
1096+
if (is_unixsock_path(ch->host))
10971097
ch->type = CHT_UNIX_SOCKET;
10981098
#endif
10991099
}
@@ -6945,7 +6945,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
69456945
/* 'localhost' matches pghost of '' or the default socket directory */
69466946
if (hostname == NULL || hostname[0] == '\0')
69476947
hostname = DefaultHost;
6948-
else if (is_absolute_path(hostname))
6948+
else if (is_unixsock_path(hostname))
69496949

69506950
/*
69516951
* We should probably use canonicalize_path(), but then we have to

0 commit comments

Comments
 (0)