GNU bug report logs - #27426
[PATCH 0/2] 'guix-daemon --listen' can specify multiple interfaces

Previous Next

Package: guix-patches;

Reported by: Ludovic Courtès <ludo <at> gnu.org>

Date: Mon, 19 Jun 2017 16:04:02 UTC

Severity: normal

Tags: patch

Done: ludovic.courtes <at> inria.fr (Ludovic Courtès)

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 27426 in the body.
You can then email your comments to 27426 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to guix-patches <at> gnu.org:
bug#27426; Package guix-patches. (Mon, 19 Jun 2017 16:04:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Ludovic Courtès <ludo <at> gnu.org>:
New bug report received and forwarded. Copy sent to guix-patches <at> gnu.org. (Mon, 19 Jun 2017 16:04:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: guix-patches <at> gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH 0/2] 'guix-daemon --listen' can specify multiple interfaces
Date: Mon, 19 Jun 2017 18:03:02 +0200
Hello Guix!

Commit 3dff90ce34448551bc82a6a7262837c0561a4691 added support for
guix:// URIs on the client side.  This commit adds guix-daemon support
to specify TCP sockets to listen to, like this:

  # Listen on the loopback interface only, port 1234.
  guix-daemon --listen=localhost:1234

  # Listen on the Unix-domain socket and on the public interface,
  # port 44146.
  guix-daemon --listen=/var/guix/daemon-socket/socket \
              --listen=0.0.0.0

The primary use case is clusters running a single ‘guix-daemon’ instance
that can be accessed from other nodes on the local network.

Feedback welcome!

Ludo’.

Ludovic Courtès (2):
  store: Define a default port for TCP connections.
  daemon: '--listen' can be passed several times, can specify TCP
    endpoints.

 doc/guix.texi                 |  39 +++++-
 guix/store.scm                |  12 +-
 nix/nix-daemon/guix-daemon.cc | 152 +++++++++++++++++++++--
 nix/nix-daemon/nix-daemon.cc  | 283 +++++++++++++++++++-----------------------
 tests/guix-daemon.sh          |  12 ++
 5 files changed, 317 insertions(+), 181 deletions(-)

-- 
2.13.1





Information forwarded to guix-patches <at> gnu.org:
bug#27426; Package guix-patches. (Mon, 19 Jun 2017 16:06:01 GMT) Full text and rfc822 format available.

Message #8 received at 27426 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 27426 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludovic.courtes <at> inria.fr>
Subject: [PATCH 1/2] store: Define a default port for TCP connections.
Date: Mon, 19 Jun 2017 18:04:51 +0200
From: Ludovic Courtès <ludovic.courtes <at> inria.fr>

* guix/store.scm (%default-guix-port): New variable.
(connect-to-daemon)[connect]: Use it when (uri-port uri) is #f.
---
 guix/store.scm | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/guix/store.scm b/guix/store.scm
index 2acab6b1a..d8fa833ea 100644
--- a/guix/store.scm
+++ b/guix/store.scm
@@ -379,6 +379,10 @@
       (connect s a)
       s)))
 
+(define %default-guix-port
+  ;; Default port when connecting to a daemon over TCP/IP.
+  44146)
+
 (define (open-inet-socket host port)
   "Connect to the Unix-domain socket at HOST:PORT and return it.  Raise a
 '&nix-connection-error' upon error."
@@ -440,12 +444,8 @@ name."
             (open-unix-domain-socket (uri-path uri))))
          ('guix
           (lambda (_)
-            (unless (uri-port uri)
-              (raise (condition (&nix-connection-error
-                                 (file (uri->string uri))
-                                 (errno EBADR))))) ;bah!
-
-            (open-inet-socket (uri-host uri) (uri-port uri))))
+            (open-inet-socket (uri-host uri)
+                              (or (uri-port uri) %default-guix-port))))
          ((? symbol? scheme)
           ;; Try to dynamically load a module for SCHEME.
           ;; XXX: Errors are swallowed.
-- 
2.13.1





Information forwarded to guix-patches <at> gnu.org:
bug#27426; Package guix-patches. (Mon, 19 Jun 2017 16:06:02 GMT) Full text and rfc822 format available.

Message #11 received at 27426 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 27426 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludovic.courtes <at> inria.fr>
Subject: [PATCH 2/2] daemon: '--listen' can be passed several times,
 can specify TCP endpoints.
Date: Mon, 19 Jun 2017 18:04:52 +0200
From: Ludovic Courtès <ludovic.courtes <at> inria.fr>

* nix/nix-daemon/guix-daemon.cc (DEFAULT_GUIX_PORT): New macro.
(listen_options): New variable.
(parse_opt): Push back '--listen' options to LISTEN_OPTIONS.
(open_unix_domain_socket, open_inet_socket)
(listening_sockets): New functions.
(main): Use it.  Pass SOCKETS to 'run'.
* nix/nix-daemon/nix-daemon.cc (matchUser): Remove.
(SD_LISTEN_FDS_START): Remove.
(acceptConnection): New function.
(daemonLoop): Rewrite to take a vector of file descriptors, to select(2)
on them, and to call 'acceptConnection'.
(run): Change to take a vector of file descriptors.
* tests/guix-daemon.sh: Add test.
---
 doc/guix.texi                 |  39 +++++-
 nix/nix-daemon/guix-daemon.cc | 152 +++++++++++++++++++++--
 nix/nix-daemon/nix-daemon.cc  | 283 +++++++++++++++++++-----------------------
 tests/guix-daemon.sh          |  12 ++
 4 files changed, 311 insertions(+), 175 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 4933a98dd..ca265fc49 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -1258,12 +1258,35 @@ Assume @var{system} as the current system type.  By default it is the
 architecture/kernel pair found at configure time, such as
 @code{x86_64-linux}.
 
-@item --listen=@var{socket}
-Listen for connections on @var{socket}, the file name of a Unix-domain
-socket.  The default socket is
-@file{@var{localstatedir}/daemon-socket/socket}.  This option is only
-useful in exceptional circumstances, such as if you need to run several
-daemons on the same machine.
+@item --listen=@var{endpoint}
+Listen for connections on @var{endpoint}.  @var{endpoint} is interpreted
+as the file name of a Unix-domain socket if it starts with
+@code{/} (slash sign).  Otherwise, @var{endpoint} is interpreted as a
+host name or host name and port to listen to.  Here are a few example:
+
+@table @code
+@item --listen=/gnu/var/daemon
+Listen for connections on the @file{/gnu/var/daemon} Unix-domain socket,
+creating it if needed.
+
+@item --listen=localhost
+Listen for TCP connections on the network interface corresponding to
+@code{localhost}, on port 44146.
+
+@item --listen=128.0.0.42:1234
+Listen for TCP connections on the network interface corresponding to
+@code{128.0.0.42}, on port 1234.
+@end table
+
+This option can be repeated multiple times, in which case
+@command{guix-daemon} accepts connections on all the specified
+endpoints.  Users can tell client commands what endpoint to connect to
+by setting the @code{GUIX_DAEMON_SOCKET} environment variable
+(@pxref{The Store, @code{GUIX_DAEMON_SOCKET}}).
+
+When @code{--listen} is omitted, @command{guix-daemon} listens for
+connections on the Unix-domain socket located at
+@file{@var{localstatedir}/daemon-socket/socket}.
 @end table
 
 
@@ -3781,6 +3804,10 @@ This setup is suitable on local networks, such as clusters, where only
 trusted nodes may connect to the build daemon at
 @code{master.guix.example.org}.
 
+The @code{--listen} option of @command{guix-daemon} can be used to
+instruct it to listen for TCP connections (@pxref{Invoking guix-daemon,
+@code{--listen}}).
+
 @item ssh
 @cindex SSH access to build daemons
 These URIs allow you to connect to a remote daemon over
diff --git a/nix/nix-daemon/guix-daemon.cc b/nix/nix-daemon/guix-daemon.cc
index 0d9c33d1d..ba898f572 100644
--- a/nix/nix-daemon/guix-daemon.cc
+++ b/nix/nix-daemon/guix-daemon.cc
@@ -1,5 +1,6 @@
 /* GNU Guix --- Functional package management for GNU
    Copyright (C) 2012, 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo <at> gnu.org>
+   Copyright (C) 2006, 2010, 2012, 2014 Eelco Dolstra <e.dolstra <at> tudelft.nl>
 
    This file is part of GNU Guix.
 
@@ -30,8 +31,12 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netdb.h>
 #include <strings.h>
 #include <exception>
+#include <iostream>
 
 #include <libintl.h>
 #include <locale.h>
@@ -43,7 +48,7 @@ char **argvSaved;
 using namespace nix;
 
 /* Entry point in `nix-daemon.cc'.  */
-extern void run (Strings args);
+extern void run (const std::vector<int> &);
 
 
 /* Command-line options.  */
@@ -149,6 +154,12 @@ to live outputs") },
   };
 
 
+/* Default port for '--listen' on TCP/IP.  */
+#define DEFAULT_GUIX_PORT "44146"
+
+/* List of '--listen' options.  */
+static std::list<std::string> listen_options;
+
 /* Convert ARG to a Boolean value, or throw an error if it does not denote a
    Boolean.  */
 static bool
@@ -217,15 +228,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
       settings.keepLog = false;
       break;
     case GUIX_OPT_LISTEN:
-      try
-	{
-	  settings.nixDaemonSocketFile = canonPath (arg);
-	}
-      catch (std::exception &e)
-	{
-	  fprintf (stderr, _("error: %s\n"), e.what ());
-	  exit (EXIT_FAILURE);
-	}
+      listen_options.push_back (arg);
       break;
     case GUIX_OPT_SUBSTITUTE_URLS:
       settings.set ("substitute-urls", arg);
@@ -276,13 +279,134 @@ static const struct argp argp =
     guix_textdomain
   };
 
+
+static int
+open_unix_domain_socket (const char *file)
+{
+  /* Create and bind to a Unix domain socket. */
+  AutoCloseFD fdSocket = socket (PF_UNIX, SOCK_STREAM, 0);
+  if (fdSocket == -1)
+    throw SysError ("cannot create Unix domain socket");
+
+  createDirs (dirOf (file));
+
+  /* Urgh, sockaddr_un allows path names of only 108 characters.
+     So chdir to the socket directory so that we can pass a
+     relative path name. */
+  if (chdir (dirOf (file).c_str ()) == -1)
+    throw SysError ("cannot change current directory");
+  Path fileRel = "./" + baseNameOf (file);
+
+  struct sockaddr_un addr;
+  addr.sun_family = AF_UNIX;
+  if (fileRel.size () >= sizeof (addr.sun_path))
+    throw Error (format ("socket path `%1%' is too long") % fileRel);
+  strcpy (addr.sun_path, fileRel.c_str ());
+
+  unlink (file);
+
+  /* Make sure that the socket is created with 0666 permission
+     (everybody can connect --- provided they have access to the
+     directory containing the socket). */
+  mode_t oldMode = umask (0111);
+  int res = bind (fdSocket, (struct sockaddr *) &addr, sizeof addr);
+  umask (oldMode);
+  if (res == -1)
+    throw SysError (format ("cannot bind to socket `%1%'") % file);
+
+  if (chdir ("/") == -1) /* back to the root */
+    throw SysError ("cannot change current directory");
+
+  if (listen (fdSocket, 5) == -1)
+    throw SysError (format ("cannot listen on socket `%1%'") % file);
+
+  return fdSocket.borrow ();
+}
+
+/* Return a listening socket for ADDRESS, which has the given LENGTH.  */
+static int
+open_inet_socket (const struct sockaddr *address, socklen_t length)
+{
+  AutoCloseFD fd = socket (address->sa_family, SOCK_STREAM, 0);
+  if (fd == -1)
+    throw SysError("cannot open inet socket");
+
+  int res = bind (fd, address, length);
+  if (res == -1)
+    throw SysError("cannot bind inet socket");
+
+  if (listen (fd, 5) == -1)
+    throw SysError (format ("cannot listen on inet socket"));
+
+  return fd.borrow ();
+}
+
+/* Return a list of file descriptors of listening sockets.  */
+static std::vector<int>
+listening_sockets (const std::list<std::string> &options)
+{
+  std::vector<int> result;
+
+  if (options.empty ())
+    {
+      /* Open the default Unix-domain socket.  */
+      auto fd = open_unix_domain_socket (settings.nixDaemonSocketFile.c_str ());
+      result.push_back (fd);
+      return result;
+    }
+
+  /* Open the user-specified sockets.  */
+  for (const std::string& option: options)
+    {
+      if (option[0] == '/')
+	{
+	  /* Assume OPTION is the file name of a Unix-domain socket.  */
+	  settings.nixDaemonSocketFile = canonPath (option);
+	  int fd =
+	    open_unix_domain_socket (settings.nixDaemonSocketFile.c_str ());
+	  result.push_back (fd);
+	}
+      else
+	{
+	  /* Assume OPTIONS has the form "HOST" or "HOST:PORT".  */
+	  auto colon = option.find_last_of (":");
+	  auto host = colon == std::string::npos
+	    ? option : option.substr (0, colon);
+	  auto port = colon == std::string::npos
+	    ? DEFAULT_GUIX_PORT
+	    : option.substr (colon + 1, option.size () - colon - 1);
+
+	  struct addrinfo *res, hints;
+
+	  memset (&hints, '\0', sizeof hints);
+	  hints.ai_socktype = SOCK_STREAM;
+	  hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG;
+
+	  int err = getaddrinfo (host.c_str(), port.c_str (),
+				 &hints, &res);
+
+	  if (err != 0)
+	    throw Error(format ("failed to look up '%1%': %2%")
+			% option % gai_strerror (err));
+
+	  printMsg (lvlDebug, format ("listening on '%1%', port '%2%'")
+		    % host % port);
+
+	  /* XXX: Pick the first result, RES.  */
+	  result.push_back (open_inet_socket (res->ai_addr,
+					      res->ai_addrlen));
+
+	  freeaddrinfo (res);
+	}
+    }
+
+  return result;
+}
 
 
 int
 main (int argc, char *argv[])
 {
-  static const Strings nothing;
-
   setlocale (LC_ALL, "");
   bindtextdomain (guix_textdomain, LOCALEDIR);
   textdomain (guix_textdomain);
@@ -359,6 +483,8 @@ main (int argc, char *argv[])
 
       argp_parse (&argp, argc, argv, 0, 0, 0);
 
+      auto sockets = listening_sockets (listen_options);
+
       /* Effect all the changes made via 'settings.set'.  */
       settings.update ();
 
@@ -402,7 +528,7 @@ using `--build-users-group' is highly recommended\n"));
       printMsg (lvlDebug,
 		format ("listening on `%1%'") % settings.nixDaemonSocketFile);
 
-      run (nothing);
+      run (sockets);
     }
   catch (std::exception &e)
     {
diff --git a/nix/nix-daemon/nix-daemon.cc b/nix/nix-daemon/nix-daemon.cc
index 79580ffb4..3d8e90990 100644
--- a/nix/nix-daemon/nix-daemon.cc
+++ b/nix/nix-daemon/nix-daemon.cc
@@ -18,6 +18,7 @@
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <arpa/inet.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <pwd.h>
@@ -809,151 +810,87 @@ static void setSigChldAction(bool autoReap)
 }
 
 
-bool matchUser(const string & user, const string & group, const Strings & users)
+/* Accept a connection on FDSOCKET and fork a server process to process the
+   new connection.  */
+static void acceptConnection(int fdSocket)
 {
-    if (find(users.begin(), users.end(), "*") != users.end())
-        return true;
-
-    if (find(users.begin(), users.end(), user) != users.end())
-        return true;
-
-    for (auto & i : users)
-        if (string(i, 0, 1) == "@") {
-            if (group == string(i, 1)) return true;
-            struct group * gr = getgrnam(i.c_str() + 1);
-            if (!gr) continue;
-            for (char * * mem = gr->gr_mem; *mem; mem++)
-                if (user == string(*mem)) return true;
-        }
-
-    return false;
-}
-
-
-#define SD_LISTEN_FDS_START 3
-
-
-static void daemonLoop()
-{
-    if (chdir("/") == -1)
-        throw SysError("cannot change current directory");
-
-    /* Get rid of children automatically; don't let them become
-       zombies. */
-    setSigChldAction(true);
-
-    AutoCloseFD fdSocket;
-
-    /* Handle socket-based activation by systemd. */
-    if (getEnv("LISTEN_FDS") != "") {
-        if (getEnv("LISTEN_PID") != std::to_string(getpid()) || getEnv("LISTEN_FDS") != "1")
-            throw Error("unexpected systemd environment variables");
-        fdSocket = SD_LISTEN_FDS_START;
-    }
-
-    /* Otherwise, create and bind to a Unix domain socket. */
-    else {
-
-        /* Create and bind to a Unix domain socket. */
-        fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
-        if (fdSocket == -1)
-            throw SysError("cannot create Unix domain socket");
-
-        string socketPath = settings.nixDaemonSocketFile;
-
-        createDirs(dirOf(socketPath));
-
-        /* Urgh, sockaddr_un allows path names of only 108 characters.
-           So chdir to the socket directory so that we can pass a
-           relative path name. */
-        if (chdir(dirOf(socketPath).c_str()) == -1)
-            throw SysError("cannot change current directory");
-        Path socketPathRel = "./" + baseNameOf(socketPath);
-
-        struct sockaddr_un addr;
-        addr.sun_family = AF_UNIX;
-        if (socketPathRel.size() >= sizeof(addr.sun_path))
-            throw Error(format("socket path `%1%' is too long") % socketPathRel);
-        strcpy(addr.sun_path, socketPathRel.c_str());
-
-        unlink(socketPath.c_str());
-
-        /* Make sure that the socket is created with 0666 permission
-           (everybody can connect --- provided they have access to the
-           directory containing the socket). */
-        mode_t oldMode = umask(0111);
-        int res = bind(fdSocket, (struct sockaddr *) &addr, sizeof(addr));
-        umask(oldMode);
-        if (res == -1)
-            throw SysError(format("cannot bind to socket `%1%'") % socketPath);
-
-        if (chdir("/") == -1) /* back to the root */
-            throw SysError("cannot change current directory");
-
-        if (listen(fdSocket, 5) == -1)
-            throw SysError(format("cannot listen on socket `%1%'") % socketPath);
-    }
-
-    closeOnExec(fdSocket);
-
-    /* Loop accepting connections. */
-    while (1) {
-
-        try {
-            /* Important: the server process *cannot* open the SQLite
-               database, because it doesn't like forks very much. */
-            assert(!store);
-
-            /* Accept a connection. */
-            struct sockaddr_un remoteAddr;
-            socklen_t remoteAddrLen = sizeof(remoteAddr);
-
-            AutoCloseFD remote = accept(fdSocket,
-                (struct sockaddr *) &remoteAddr, &remoteAddrLen);
-            checkInterrupt();
-            if (remote == -1) {
-                if (errno == EINTR)
-                    continue;
-                else
-                    throw SysError("accepting connection");
-            }
-
-            closeOnExec(remote);
-
-            bool trusted = false;
-            pid_t clientPid = -1;
-
+    uid_t clientUid = (uid_t) -1;
+    gid_t clientGid = (gid_t) -1;
+
+    try {
+	/* Important: the server process *cannot* open the SQLite
+	   database, because it doesn't like forks very much. */
+	assert(!store);
+
+	/* Accept a connection. */
+	struct sockaddr_storage remoteAddr;
+	socklen_t remoteAddrLen = sizeof(remoteAddr);
+
+    try_again:
+	AutoCloseFD remote = accept(fdSocket,
+				    (struct sockaddr *) &remoteAddr, &remoteAddrLen);
+	checkInterrupt();
+	if (remote == -1) {
+	    if (errno == EINTR)
+		goto try_again;
+	    else
+		throw SysError("accepting connection");
+	}
+
+	closeOnExec(remote);
+
+	pid_t clientPid = -1;
+	bool trusted = false;
+
+	/* Get the identity of the caller, if possible. */
+	if (remoteAddr.ss_family == AF_UNIX) {
 #if defined(SO_PEERCRED)
-            /* Get the identity of the caller, if possible. */
-            ucred cred;
-            socklen_t credLen = sizeof(cred);
-            if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1)
-                throw SysError("getting peer credentials");
+	    ucred cred;
+	    socklen_t credLen = sizeof(cred);
+	    if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED,
+			   &cred, &credLen) == -1)
+		throw SysError("getting peer credentials");
 
-            clientPid = cred.pid;
+	    clientPid = cred.pid;
+	    clientUid = cred.uid;
+	    clientGid = cred.gid;
+	    trusted = clientUid == 0;
 
             struct passwd * pw = getpwuid(cred.uid);
             string user = pw ? pw->pw_name : std::to_string(cred.uid);
 
-            struct group * gr = getgrgid(cred.gid);
-            string group = gr ? gr->gr_name : std::to_string(cred.gid);
-
-            Strings trustedUsers = settings.get("trusted-users", Strings({"root"}));
-            Strings allowedUsers = settings.get("allowed-users", Strings({"*"}));
-
-            if (matchUser(user, group, trustedUsers))
-                trusted = true;
-
-            if (!trusted && !matchUser(user, group, allowedUsers))
-                throw Error(format("user `%1%' is not allowed to connect to the Nix daemon") % user);
-
-            printMsg(lvlInfo, format((string) "accepted connection from pid %1%, user %2%"
-                    + (trusted ? " (trusted)" : "")) % clientPid % user);
+	    printMsg(lvlInfo,
+		     format((string) "accepted connection from pid %1%, user %2%")
+		     % clientPid % user);
 #endif
+	} else {
+	    char address_str[128];
+	    const char *result;
 
-            /* Fork a child to handle the connection. */
-            startProcess([&]() {
-                fdSocket.close();
+	    if (remoteAddr.ss_family == AF_INET) {
+		struct sockaddr_in *addr = (struct sockaddr_in *) &remoteAddr;
+		struct in_addr inaddr = { addr->sin_addr };
+		result = inet_ntop(AF_INET, &inaddr,
+				   address_str, sizeof address_str);
+	    } else if (remoteAddr.ss_family == AF_INET6) {
+		struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &remoteAddr;
+		struct in6_addr inaddr = { addr->sin6_addr };
+		result = inet_ntop(AF_INET6, &inaddr,
+				   address_str, sizeof address_str);
+	    } else {
+		result = NULL;
+	    }
+
+	    if (result != NULL) {
+		printMsg(lvlInfo,
+			 format("accepted connection from %1%")
+			 % address_str);
+	    }
+	}
+
+	/* Fork a child to handle the connection. */
+	startProcess([&]() {
+                close(fdSocket);
 
                 /* Background the daemon. */
                 if (setsid() == -1)
@@ -968,17 +905,11 @@ static void daemonLoop()
                     strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
                 }
 
-#if defined(SO_PEERCRED)
                 /* Store the client's user and group for this connection. This
                    has to be done in the forked process since it is per
-                   connection. */
-                settings.clientUid = cred.uid;
-                settings.clientGid = cred.gid;
-#else
-                /* Setting these to -1 means: do not change */
-                settings.clientUid = (uid_t) -1;
-                settings.clientGid = (gid_t) -1;
-#endif
+                   connection.  Setting these to -1 means: do not change.  */
+                settings.clientUid = clientUid;
+		settings.clientGid = clientGid;
 
                 /* Handle the connection. */
                 from.fd = remote;
@@ -988,23 +919,63 @@ static void daemonLoop()
                 exit(0);
             }, false, "unexpected Nix daemon error: ", true);
 
-        } catch (Interrupted & e) {
-            throw;
-        } catch (Error & e) {
-            printMsg(lvlError, format("error processing connection: %1%") % e.msg());
-        }
+    } catch (Interrupted & e) {
+	throw;
+    } catch (Error & e) {
+	printMsg(lvlError, format("error processing connection: %1%") % e.msg());
     }
 }
 
-
-void run(Strings args)
+static void daemonLoop(const std::vector<int>& sockets)
 {
-    for (Strings::iterator i = args.begin(); i != args.end(); ) {
-        string arg = *i++;
-        if (arg == "--daemon") /* ignored for backwards compatibility */;
+    if (chdir("/") == -1)
+        throw SysError("cannot change current directory");
+
+    /* Get rid of children automatically; don't let them become
+       zombies. */
+    setSigChldAction(true);
+
+    /* Mark sockets as close-on-exec.  */
+    for(int fd: sockets) {
+	closeOnExec(fd);
     }
 
-    daemonLoop();
+    /* Prepare the FD set corresponding to SOCKETS.  */
+    auto initializeFDSet = [&](fd_set *set) {
+	FD_ZERO(set);
+	for (int fd: sockets) {
+	    FD_SET(fd, set);
+	}
+    };
+
+    /* Loop accepting connections. */
+    while (1) {
+	fd_set readfds;
+
+	initializeFDSet(&readfds);
+	int count =
+	    select(*std::max_element(sockets.begin(), sockets.end()) + 1,
+		   &readfds, NULL, NULL,
+		   NULL);
+	if (count < 0) {
+	    int err = errno;
+	    if (err == EINTR)
+		continue;
+	    throw SysError(format("select error: %1%") % strerror(err));
+	}
+
+	for (unsigned int i = 0; i < sockets.size(); i++) {
+	    if (FD_ISSET(sockets[i], &readfds)) {
+		acceptConnection(sockets[i]);
+	    }
+	}
+    }
+}
+
+
+void run(const std::vector<int>& sockets)
+{
+    daemonLoop(sockets);
 }
 
 
diff --git a/tests/guix-daemon.sh b/tests/guix-daemon.sh
index 9186ffd58..7212e3eb6 100644
--- a/tests/guix-daemon.sh
+++ b/tests/guix-daemon.sh
@@ -81,6 +81,18 @@ guile -c "
 
 kill "$daemon_pid"
 
+# Pass several '--listen' options, and make sure they are all honored.
+guix-daemon --disable-chroot --listen="$socket" --listen="$socket-second" \
+	    --listen="localhost" --listen="localhost:9876" &
+daemon_pid=$!
+
+for uri in "$socket" "$socket-second" \
+		     "guix://localhost" "guix://localhost:9876"
+do
+    GUIX_DAEMON_SOCKET="$uri" guix build guile-bootstrap
+done
+
+kill "$daemon_pid"
 
 # Check the failed build cache.
 
-- 
2.13.1





Information forwarded to guix-patches <at> gnu.org:
bug#27426; Package guix-patches. (Tue, 20 Jun 2017 12:31:01 GMT) Full text and rfc822 format available.

Message #14 received at 27426 <at> debbugs.gnu.org (full text, mbox):

From: Roel Janssen <roel <at> gnu.org>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: 27426 <at> debbugs.gnu.org
Subject: Re: [bug#27426] [PATCH 0/2] 'guix-daemon --listen' can specify
 multiple interfaces
Date: Tue, 20 Jun 2017 14:29:46 +0200
Hi Ludo’,

Ludovic Courtès writes:

> Hello Guix!
>
> Commit 3dff90ce34448551bc82a6a7262837c0561a4691 added support for
> guix:// URIs on the client side.  This commit adds guix-daemon support
> to specify TCP sockets to listen to, like this:
>
>   # Listen on the loopback interface only, port 1234.
>   guix-daemon --listen=localhost:1234
>
>   # Listen on the Unix-domain socket and on the public interface,
>   # port 44146.
>   guix-daemon --listen=/var/guix/daemon-socket/socket \
>               --listen=0.0.0.0
>
> The primary use case is clusters running a single ‘guix-daemon’ instance
> that can be accessed from other nodes on the local network.
>
> Feedback welcome!

Thanks a lot for these patches!  Today I tried to run the guix-daemon
with it on our cluster.  It works fine, except for the following (which
might be unrelated):

[root <at> hpcguix ~]$ /gnu/repositories/guix/guix-daemon --listen=/gnu/daemon-socket/socket --listen=<ip-address>:<port> ...

[roel <at> submit-node1 ~]$ guixr package -i samtools
The following package will be installed:
   samtools    1.3.1    /gnu/store/syl74az7a5mw5f8r5jfldiddlyc3ry28-samtools-1.3.1

substitute: error: executing `/usr/local/libexec/guix/substitute': No such file or directory
guix package: error: build failed: substituter `substitute' died unexpectedly


When passing --no-substitutes, the command works, which means the
guix-daemon with these patches applied does what we expect.

Note that, I could've used 'guix' instead of 'guixr', but all 'guixr'
essentially does is set the GUIX_DAEMON_SOCKET and GUIX_PACKAGE_PATH
variables.

I wonder where this /usr/local/libexec comes from, and how/where I can
configure it so that it works the same as before.

Thanks again for these patches.

Kind regards,
Roel Janssen




Information forwarded to guix-patches <at> gnu.org:
bug#27426; Package guix-patches. (Tue, 20 Jun 2017 13:30:02 GMT) Full text and rfc822 format available.

Message #17 received at 27426 <at> debbugs.gnu.org (full text, mbox):

From: Roel Janssen <roel <at> gnu.org>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: 27426 <at> debbugs.gnu.org
Subject: Re: [bug#27426] [PATCH 0/2] 'guix-daemon --listen' can specify
 multiple interfaces
Date: Tue, 20 Jun 2017 15:28:37 +0200
Roel Janssen writes:

> Hi Ludo’,
>
> Ludovic Courtès writes:
>
>> Hello Guix!
>>
>> Commit 3dff90ce34448551bc82a6a7262837c0561a4691 added support for
>> guix:// URIs on the client side.  This commit adds guix-daemon support
>> to specify TCP sockets to listen to, like this:
>>
>>   # Listen on the loopback interface only, port 1234.
>>   guix-daemon --listen=localhost:1234
>>
>>   # Listen on the Unix-domain socket and on the public interface,
>>   # port 44146.
>>   guix-daemon --listen=/var/guix/daemon-socket/socket \
>>               --listen=0.0.0.0
>>
>> The primary use case is clusters running a single ‘guix-daemon’ instance
>> that can be accessed from other nodes on the local network.
>>
>> Feedback welcome!
>
> Thanks a lot for these patches!  Today I tried to run the guix-daemon
> with it on our cluster.  It works fine, except for the following (which
> might be unrelated):
>
> [root <at> hpcguix ~]$ /gnu/repositories/guix/guix-daemon --listen=/gnu/daemon-socket/socket --listen=<ip-address>:<port> ...
>
> [roel <at> submit-node1 ~]$ guixr package -i samtools
> The following package will be installed:
>    samtools    1.3.1    /gnu/store/syl74az7a5mw5f8r5jfldiddlyc3ry28-samtools-1.3.1
>
> substitute: error: executing `/usr/local/libexec/guix/substitute': No such file or directory
> guix package: error: build failed: substituter `substitute' died unexpectedly
>

Ooh, nevermind..  This has to do with the 'pre-inst-env' script.

>
> When passing --no-substitutes, the command works, which means the
> guix-daemon with these patches applied does what we expect.
>
> Note that, I could've used 'guix' instead of 'guixr', but all 'guixr'
> essentially does is set the GUIX_DAEMON_SOCKET and GUIX_PACKAGE_PATH
> variables.
>
> I wonder where this /usr/local/libexec comes from, and how/where I can
> configure it so that it works the same as before.
>
> Thanks again for these patches.
>
> Kind regards,
> Roel Janssen





Information forwarded to guix-patches <at> gnu.org:
bug#27426; Package guix-patches. (Tue, 20 Jun 2017 14:10:01 GMT) Full text and rfc822 format available.

Message #20 received at 27426 <at> debbugs.gnu.org (full text, mbox):

From: ludovic.courtes <at> inria.fr (Ludovic Courtès)
To: Roel Janssen <roel <at> gnu.org>
Cc: 27426 <at> debbugs.gnu.org
Subject: Re: [bug#27426] [PATCH 0/2] 'guix-daemon --listen' can specify
 multiple interfaces
Date: Tue, 20 Jun 2017 16:08:57 +0200
Hi Roel,

Roel Janssen <roel <at> gnu.org> skribis:

> Roel Janssen writes:
>
>> Hi Ludo’,
>>
>> Ludovic Courtès writes:
>>
>>> Hello Guix!
>>>
>>> Commit 3dff90ce34448551bc82a6a7262837c0561a4691 added support for
>>> guix:// URIs on the client side.  This commit adds guix-daemon support
>>> to specify TCP sockets to listen to, like this:
>>>
>>>   # Listen on the loopback interface only, port 1234.
>>>   guix-daemon --listen=localhost:1234
>>>
>>>   # Listen on the Unix-domain socket and on the public interface,
>>>   # port 44146.
>>>   guix-daemon --listen=/var/guix/daemon-socket/socket \
>>>               --listen=0.0.0.0
>>>
>>> The primary use case is clusters running a single ‘guix-daemon’ instance
>>> that can be accessed from other nodes on the local network.
>>>
>>> Feedback welcome!
>>
>> Thanks a lot for these patches!  Today I tried to run the guix-daemon
>> with it on our cluster.  It works fine, except for the following (which
>> might be unrelated):
>>
>> [root <at> hpcguix ~]$ /gnu/repositories/guix/guix-daemon --listen=/gnu/daemon-socket/socket --listen=<ip-address>:<port> ...
>>
>> [roel <at> submit-node1 ~]$ guixr package -i samtools
>> The following package will be installed:
>>    samtools    1.3.1    /gnu/store/syl74az7a5mw5f8r5jfldiddlyc3ry28-samtools-1.3.1
>>
>> substitute: error: executing `/usr/local/libexec/guix/substitute': No such file or directory
>> guix package: error: build failed: substituter `substitute' died unexpectedly
>>
>
> Ooh, nevermind..  This has to do with the 'pre-inst-env' script.

OK.

Thanks for testing!  I’ll merge it soon if there aren’t more comments
on the interface or code.

Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#27426; Package guix-patches. (Tue, 20 Jun 2017 15:16:02 GMT) Full text and rfc822 format available.

Message #23 received at 27426 <at> debbugs.gnu.org (full text, mbox):

From: Roel Janssen <roel <at> gnu.org>
To: Ludovic Courtès <ludovic.courtes <at> inria.fr>
Cc: 27426 <at> debbugs.gnu.org
Subject: Re: [bug#27426] [PATCH 0/2] 'guix-daemon --listen' can specify
 multiple interfaces
Date: Tue, 20 Jun 2017 17:15:08 +0200
Ludovic Courtès writes:

> Hi Roel,
>
> Roel Janssen <roel <at> gnu.org> skribis:
>
>> Roel Janssen writes:
>>
>>> Hi Ludo’,
>>>
>>> Ludovic Courtès writes:
>>>
>>>> Hello Guix!
>>>>
>>>> Commit 3dff90ce34448551bc82a6a7262837c0561a4691 added support for
>>>> guix:// URIs on the client side.  This commit adds guix-daemon support
>>>> to specify TCP sockets to listen to, like this:
>>>>
>>>>   # Listen on the loopback interface only, port 1234.
>>>>   guix-daemon --listen=localhost:1234
>>>>
>>>>   # Listen on the Unix-domain socket and on the public interface,
>>>>   # port 44146.
>>>>   guix-daemon --listen=/var/guix/daemon-socket/socket \
>>>>               --listen=0.0.0.0
>>>>
>>>> The primary use case is clusters running a single ‘guix-daemon’ instance
>>>> that can be accessed from other nodes on the local network.
>>>>
>>>> Feedback welcome!
>>>
>>> Thanks a lot for these patches!  Today I tried to run the guix-daemon
>>> with it on our cluster.  It works fine, except for the following (which
>>> might be unrelated):
>>>
>>> [root <at> hpcguix ~]$ /gnu/repositories/guix/guix-daemon --listen=/gnu/daemon-socket/socket --listen=<ip-address>:<port> ...
>>>
>>> [roel <at> submit-node1 ~]$ guixr package -i samtools
>>> The following package will be installed:
>>>    samtools    1.3.1    /gnu/store/syl74az7a5mw5f8r5jfldiddlyc3ry28-samtools-1.3.1
>>>
>>> substitute: error: executing `/usr/local/libexec/guix/substitute': No such file or directory
>>> guix package: error: build failed: substituter `substitute' died unexpectedly
>>>
>>
>> Ooh, nevermind..  This has to do with the 'pre-inst-env' script.
>
> OK.
>
> Thanks for testing!  I’ll merge it soon if there aren’t more comments
> on the interface or code.

I tested it with the pre-inst-env and it works fine.
Looking forward to see this in upstream!

Thanks a lot!

Kind regards,
Roel Janssen




Reply sent to ludovic.courtes <at> inria.fr (Ludovic Courtès):
You have taken responsibility. (Thu, 22 Jun 2017 09:03:02 GMT) Full text and rfc822 format available.

Notification sent to Ludovic Courtès <ludo <at> gnu.org>:
bug acknowledged by developer. (Thu, 22 Jun 2017 09:03:02 GMT) Full text and rfc822 format available.

Message #28 received at 27426-done <at> debbugs.gnu.org (full text, mbox):

From: ludovic.courtes <at> inria.fr (Ludovic Courtès)
To: Roel Janssen <roel <at> gnu.org>
Cc: 27426-done <at> debbugs.gnu.org
Subject: Re: [bug#27426] [PATCH 0/2] 'guix-daemon --listen' can specify
 multiple interfaces
Date: Thu, 22 Jun 2017 11:02:29 +0200
Hi!

Roel Janssen <roel <at> gnu.org> skribis:

> I tested it with the pre-inst-env and it works fine.
> Looking forward to see this in upstream!

Pushed, thanks!

Ludo’.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Thu, 20 Jul 2017 11:24:04 GMT) Full text and rfc822 format available.

This bug report was last modified 6 years and 275 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.