summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Jackson <ajax@redhat.com>2008-03-05 20:48:59 -0500
committerAdam Jackson <ajax@redhat.com>2008-03-05 20:53:35 -0500
commit2afe206ec9569e0d62caa6d91c3fb057b0efa23d (patch)
treedfb3f138fe69a2b4588d77b9f4dec60262609a2d
parentc8ed67f16f71042ef134a4d2189c20dd200a0648 (diff)
Add support for the abstract socket namespace under Linux.
Unlike normal unix sockets, the abstract namespace is not bound to the filesystem. This has some notable advantages; /tmp need not exist, the socket directory need not have magic permissions, etc. xtrans servers will listen on both the normal and abstract socket endpoints; clients will attempt to connect to the abstract socket before connecting to the corresponding filesystem socket. Based on a patch by Bill Crawford.
-rw-r--r--Xtransint.h3
-rw-r--r--Xtranssock.c128
2 files changed, 97 insertions, 34 deletions
diff --git a/Xtransint.h b/Xtransint.h
index 97e8a89..317dbf8 100644
--- a/Xtransint.h
+++ b/Xtransint.h
@@ -369,9 +369,10 @@ typedef struct _Xtransport_table {
#define TRANS_DISABLED (1<<2) /* Don't open this one */
#define TRANS_NOLISTEN (1<<3) /* Don't listen on this one */
#define TRANS_NOUNLINK (1<<4) /* Dont unlink transport endpoints */
+#define TRANS_ABSTRACT (1<<5) /* Use abstract sockets if available */
/* Flags to preserve when setting others */
-#define TRANS_KEEPFLAGS (TRANS_NOUNLINK)
+#define TRANS_KEEPFLAGS (TRANS_NOUNLINK|TRANS_ABSTRACT)
/*
* readv() and writev() don't exist or don't work correctly on some
diff --git a/Xtranssock.c b/Xtranssock.c
index 646d634..94b73e2 100644
--- a/Xtranssock.c
+++ b/Xtranssock.c
@@ -158,6 +158,10 @@ static int IBMsockInit = 0;
#define SocketInitOnce() /**/
#endif
+#ifdef linux
+#define HAVE_ABSTRACT_SOCKETS
+#endif
+
#define MIN_BACKLOG 128
#ifdef SOMAXCONN
#if SOMAXCONN > MIN_BACKLOG
@@ -880,23 +884,28 @@ TRANS(SocketSetOption) (XtransConnInfo ciptr, int option, int arg)
#ifdef UNIXCONN
static int
-set_sun_path(const char *port, const char *upath, char *path)
+set_sun_path(const char *port, const char *upath, char *path, int abstract)
{
struct sockaddr_un s;
int maxlen = sizeof(s.sun_path) - 1;
+ const char *at = "";
if (!port || !*port || !path)
return -1;
- if (*port == '/') { /* a full pathname */
- if (strlen(port) > maxlen)
- return -1;
- sprintf(path, "%s", port);
- } else {
- if (strlen(port) + strlen(upath) > maxlen)
- return -1;
- sprintf(path, "%s%s", upath, port);
- }
+#ifdef HAVE_ABSTRACT_SOCKETS
+ if (port[0] == '@')
+ upath = "";
+ else if (abstract)
+ at = "@";
+#endif
+
+ if (*port == '/') /* a full pathname */
+ upath = "";
+
+ if (strlen(port) + strlen(upath) > maxlen)
+ return -1;
+ sprintf(path, "%s%s%s", at, upath, port);
return 0;
}
#endif
@@ -1119,6 +1128,12 @@ TRANS(SocketUNIXCreateListener) (XtransConnInfo ciptr, char *port,
int oldUmask;
int status;
unsigned int mode;
+ char tmpport[108];
+
+ int abstract = 0;
+#ifdef HAVE_ABSTRACT_SOCKETS
+ abstract = ciptr->transptr->flags & TRANS_ABSTRACT;
+#endif
PRMSG (2, "SocketUNIXCreateListener(%s)\n",
port ? port : "NULL", 0, 0);
@@ -1141,16 +1156,16 @@ TRANS(SocketUNIXCreateListener) (XtransConnInfo ciptr, char *port,
}
#endif
+ memset(&sockname, 0, sizeof(sockname));
sockname.sun_family = AF_UNIX;
- if (port && *port) {
- if (set_sun_path(port, UNIX_PATH, sockname.sun_path) != 0) {
- PRMSG (1, "SocketUNIXCreateListener: path too long\n", 0, 0, 0);
- return TRANS_CREATE_LISTENER_FAILED;
- }
- } else {
- snprintf (sockname.sun_path, sizeof(sockname.sun_path),
- "%s%ld", UNIX_PATH, (long)getpid());
+ if (!(port && *port)) {
+ snprintf (tmpport, sizeof(tmpport), "%s%ld", UNIX_PATH, (long)getpid());
+ port = tmpport;
+ }
+ if (set_sun_path(port, UNIX_PATH, sockname.sun_path, abstract) != 0) {
+ PRMSG (1, "SocketUNIXCreateListener: path too long\n", 0, 0, 0);
+ return TRANS_CREATE_LISTENER_FAILED;
}
#if (defined(BSD44SOCKETS) || defined(__UNIXWARE__)) && !defined(Lynx)
@@ -1163,7 +1178,12 @@ TRANS(SocketUNIXCreateListener) (XtransConnInfo ciptr, char *port,
namelen = strlen(sockname.sun_path) + offsetof(struct sockaddr_un, sun_path);
#endif
- unlink (sockname.sun_path);
+ if (abstract) {
+ sockname.sun_path[0] = '\0';
+ namelen = sizeof(sockname);
+ }
+ else
+ unlink (sockname.sun_path);
if ((status = TRANS(SocketCreateListener) (ciptr,
(struct sockaddr *) &sockname, namelen, flags)) < 0)
@@ -1193,6 +1213,9 @@ TRANS(SocketUNIXCreateListener) (XtransConnInfo ciptr, char *port,
return TRANS_CREATE_LISTENER_FAILED;
}
+ if (abstract)
+ sockname.sun_path[0] = '@';
+
ciptr->family = sockname.sun_family;
ciptr->addrlen = namelen;
memcpy (ciptr->addr, &sockname, ciptr->addrlen);
@@ -1215,16 +1238,22 @@ TRANS(SocketUNIXResetListener) (XtransConnInfo ciptr)
struct stat statb;
int status = TRANS_RESET_NOOP;
unsigned int mode;
+ int abstract = 0;
+#ifdef HAVE_ABSTRACT_SOCKETS
+ abstract = ciptr->transptr->flags & TRANS_ABSTRACT;
+#endif
PRMSG (3, "SocketUNIXResetListener(%p,%d)\n", ciptr, ciptr->fd, 0);
- if (stat (unsock->sun_path, &statb) == -1 ||
+ if (!abstract && (
+ stat (unsock->sun_path, &statb) == -1 ||
((statb.st_mode & S_IFMT) !=
#if (defined (sun) && defined(SVR4)) || defined(NCR) || defined(SCO325) || !defined(S_IFSOCK)
- S_IFIFO))
+ S_IFIFO
#else
- S_IFSOCK))
+ S_IFSOCK
#endif
+ )))
{
int oldUmask = umask (0);
@@ -1407,6 +1436,10 @@ TRANS(SocketUNIXAccept) (XtransConnInfo ciptr, int *status)
return NULL;
}
+ /*
+ * if the socket is abstract, we already modified the address to have a
+ * @ instead of the initial NUL, so no need to do that again here.
+ */
newciptr->addrlen = ciptr->addrlen;
memcpy (newciptr->addr, ciptr->addr, newciptr->addrlen);
@@ -2005,6 +2038,10 @@ TRANS(SocketUNIXConnect) (XtransConnInfo ciptr, char *host, char *port)
int old_namelen;
#endif
+ int abstract = 0;
+#ifdef HAVE_ABSTRACT_SOCKETS
+ abstract = ciptr->transptr->flags & TRANS_ABSTRACT;
+#endif
PRMSG (2,"SocketUNIXConnect(%d,%s,%s)\n", ciptr->fd, host, port);
@@ -2042,7 +2079,7 @@ TRANS(SocketUNIXConnect) (XtransConnInfo ciptr, char *host, char *port)
sockname.sun_family = AF_UNIX;
- if (set_sun_path(port, UNIX_PATH, sockname.sun_path) != 0) {
+ if (set_sun_path(port, UNIX_PATH, sockname.sun_path, abstract) != 0) {
PRMSG (1, "SocketUNIXConnect: path too long\n", 0, 0, 0);
return TRANS_CONNECT_FAILED;
}
@@ -2063,7 +2100,7 @@ TRANS(SocketUNIXConnect) (XtransConnInfo ciptr, char *host, char *port)
* This is gross, but it was in Xlib
*/
old_sockname.sun_family = AF_UNIX;
- if (set_sun_path(port, OLD_UNIX_PATH, old_sockname.sun_path) != 0) {
+ if (set_sun_path(port, OLD_UNIX_PATH, old_sockname.sun_path, abstract) != 0) {
PRMSG (1, "SocketUNIXConnect: path too long\n", 0, 0, 0);
return TRANS_CONNECT_FAILED;
}
@@ -2071,6 +2108,17 @@ TRANS(SocketUNIXConnect) (XtransConnInfo ciptr, char *host, char *port)
offsetof(struct sockaddr_un, sun_path);
#endif
+ /*
+ * Adjust the socket path if using abstract sockets.
+ * Done here because otherwise all the strlen() calls above would fail.
+ */
+
+ if (abstract) {
+ sockname.sun_path[0] = '\0';
+#if defined(hpux) && defined(X11_t)
+ old_sockname.sun_path[0] = '\0';
+#endif
+ }
/*
* Do the connect()
@@ -2108,12 +2156,19 @@ TRANS(SocketUNIXConnect) (XtransConnInfo ciptr, char *host, char *port)
* should try again.
*/
- if (olderrno == ENOENT || olderrno == EINTR)
- return TRANS_TRY_CONNECT_AGAIN;
- else if (olderrno == EWOULDBLOCK || olderrno == EINPROGRESS)
+ if (olderrno == EWOULDBLOCK || olderrno == EINPROGRESS)
return TRANS_IN_PROGRESS;
- else
- {
+ else if (olderrno == EINTR)
+ return TRANS_TRY_CONNECT_AGAIN;
+ else if (olderrno == ENOENT) {
+ /*
+ * If opening as abstract socket failed, try again "normally"
+ */
+ if (abstract)
+ ciptr->transptr->flags &= ~(TRANS_ABSTRACT);
+ return TRANS_TRY_CONNECT_AGAIN;
+ }
+ else {
PRMSG (2,"SocketUNIXConnect: Can't connect: errno = %d\n",
EGET(),0, 0);
@@ -2136,12 +2191,15 @@ TRANS(SocketUNIXConnect) (XtransConnInfo ciptr, char *host, char *port)
return TRANS_CONNECT_FAILED;
}
+ if (abstract)
+ sockname.sun_path[0] = '@';
+
ciptr->family = AF_UNIX;
ciptr->addrlen = namelen;
ciptr->peeraddrlen = namelen;
memcpy (ciptr->addr, &sockname, ciptr->addrlen);
memcpy (ciptr->peeraddr, &sockname, ciptr->peeraddrlen);
-
+
return 0;
}
@@ -2281,7 +2339,6 @@ TRANS(SocketINETClose) (XtransConnInfo ciptr)
#ifdef UNIXCONN
static int
TRANS(SocketUNIXClose) (XtransConnInfo ciptr)
-
{
/*
* If this is the server side, then once the socket is closed,
@@ -2300,7 +2357,8 @@ TRANS(SocketUNIXClose) (XtransConnInfo ciptr)
&& sockname->sun_family == AF_UNIX
&& sockname->sun_path[0])
{
- if (!(ciptr->flags & TRANS_NOUNLINK))
+ if (!(ciptr->flags & TRANS_NOUNLINK
+ || ciptr->transptr->flags & TRANS_ABSTRACT))
unlink (sockname->sun_path);
}
@@ -2467,7 +2525,11 @@ Xtransport TRANS(SocketINET6Funcs) = {
Xtransport TRANS(SocketLocalFuncs) = {
/* Socket Interface */
"local",
+#ifdef HAVE_ABSTRACT_SOCKETS
+ TRANS_ABSTRACT,
+#else
0,
+#endif
#ifdef TRANS_CLIENT
TRANS(SocketOpenCOTSClient),
#endif /* TRANS_CLIENT */
@@ -2513,7 +2575,7 @@ static char* unix_nolisten[] = { "local" , NULL };
Xtransport TRANS(SocketUNIXFuncs) = {
/* Socket Interface */
"unix",
-#if !defined(LOCALCONN)
+#if !defined(LOCALCONN) && !defined(HAVE_ABSTRACT_SOCKETS)
TRANS_ALIAS,
#else
0,