summary refs log tree commit diff
path: root/nix/nix-daemon
diff options
context:
space:
mode:
Diffstat (limited to 'nix/nix-daemon')
-rw-r--r--nix/nix-daemon/guix-daemon.cc126
-rw-r--r--nix/nix-daemon/nix-daemon.cc159
2 files changed, 178 insertions, 107 deletions
diff --git a/nix/nix-daemon/guix-daemon.cc b/nix/nix-daemon/guix-daemon.cc
index f096ed5a97..1934487d24 100644
--- a/nix/nix-daemon/guix-daemon.cc
+++ b/nix/nix-daemon/guix-daemon.cc
@@ -33,6 +33,9 @@
 #include <strings.h>
 #include <exception>
 
+#include <libintl.h>
+#include <locale.h>
+
 /* Variables used by `nix-daemon.cc'.  */
 volatile ::sig_atomic_t blockInt;
 char **argvSaved;
@@ -45,16 +48,21 @@ extern void run (Strings args);
 
 /* Command-line options.  */
 
+#define n_(str)  str
+#define _(str)   gettext (str)
+static const char guix_textdomain[] = "guix";
+
+
 const char *argp_program_version =
   "guix-daemon (" PACKAGE_NAME ") " PACKAGE_VERSION;
 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
 
 static char doc[] =
-"guix-daemon -- perform derivation builds and store accesses\
-\v\
-This program is a daemon meant to run in the background.  It serves \
+  n_("guix-daemon -- perform derivation builds and store accesses")
+  "\v\n"
+  n_("This program is a daemon meant to run in the background.  It serves \
 requests sent over a Unix-domain socket.  It accesses the store, and \
-builds derivations on behalf of its clients.";
+builds derivations on behalf of its clients.");
 
 #define GUIX_OPT_SYSTEM 1
 #define GUIX_OPT_DISABLE_CHROOT 2
@@ -75,56 +83,59 @@ builds derivations on behalf of its clients.";
 
 static const struct argp_option options[] =
   {
-    { "system", GUIX_OPT_SYSTEM, "SYSTEM", 0,
-      "Assume SYSTEM as the current system type" },
-    { "cores", 'c', "N", 0,
-      "Use N CPU cores to build each derivation; 0 means as many as available" },
-    { "max-jobs", 'M', "N", 0,
-      "Allow at most N build jobs" },
+    { "system", GUIX_OPT_SYSTEM, n_("SYSTEM"), 0,
+      n_("assume SYSTEM as the current system type") },
+    { "cores", 'c', n_("N"), 0,
+      n_("use N CPU cores to build each derivation; 0 means as many as available")
+    },
+    { "max-jobs", 'M', n_("N"), 0,
+      n_("allow at most N build jobs") },
     { "disable-chroot", GUIX_OPT_DISABLE_CHROOT, 0, 0,
-      "Disable chroot builds" },
-    { "chroot-directory", GUIX_OPT_CHROOT_DIR, "DIR", 0,
-      "Add DIR to the build chroot" },
-    { "build-users-group", GUIX_OPT_BUILD_USERS_GROUP, "GROUP", 0,
-      "Perform builds as a user of GROUP" },
+      n_("disable chroot builds") },
+    { "chroot-directory", GUIX_OPT_CHROOT_DIR, n_("DIR"), 0,
+      n_("add DIR to the build chroot") },
+    { "build-users-group", GUIX_OPT_BUILD_USERS_GROUP, n_("GROUP"), 0,
+      n_("perform builds as a user of GROUP") },
     { "no-substitutes", GUIX_OPT_NO_SUBSTITUTES, 0, 0,
-      "Do not use substitutes" },
-    { "substitute-urls", GUIX_OPT_SUBSTITUTE_URLS, "URLS", 0,
-      "Use URLS as the default list of substitute providers" },
+      n_("do not use substitutes") },
+    { "substitute-urls", GUIX_OPT_SUBSTITUTE_URLS, n_("URLS"), 0,
+      n_("use URLS as the default list of substitute providers") },
     { "no-build-hook", GUIX_OPT_NO_BUILD_HOOK, 0, 0,
-      "Do not use the 'build hook'" },
+      n_("do not use the 'build hook'") },
     { "cache-failures", GUIX_OPT_CACHE_FAILURES, 0, 0,
-      "Cache build failures" },
+      n_("cache build failures") },
     { "lose-logs", GUIX_OPT_LOSE_LOGS, 0, 0,
-      "Do not keep build logs" },
+      n_("do not keep build logs") },
     { "disable-log-compression", GUIX_OPT_DISABLE_LOG_COMPRESSION, 0, 0,
-      "Disable compression of the build logs" },
+      n_("disable compression of the build logs") },
 
     /* '--disable-deduplication' was known as '--disable-store-optimization'
        up to Guix 0.7 included, so keep the alias around.  */
     { "disable-deduplication", GUIX_OPT_DISABLE_DEDUPLICATION, 0, 0,
-      "Disable automatic file \"deduplication\" in the store" },
+      n_("disable automatic file \"deduplication\" in the store") },
     { "disable-store-optimization", GUIX_OPT_DISABLE_DEDUPLICATION, 0,
       OPTION_ALIAS | OPTION_HIDDEN, NULL },
 
-    { "impersonate-linux-2.6", GUIX_OPT_IMPERSONATE_LINUX_26, 0, 0,
-      "Impersonate Linux 2.6"
-#ifndef HAVE_SYS_PERSONALITY_H
-      " (this option has no effect in this configuration)"
+    { "impersonate-linux-2.6", GUIX_OPT_IMPERSONATE_LINUX_26, 0,
+#ifdef HAVE_SYS_PERSONALITY_H
+      0,
+#else
+      OPTION_HIDDEN,
 #endif
+      n_("impersonate Linux 2.6")
     },
     { "gc-keep-outputs", GUIX_OPT_GC_KEEP_OUTPUTS,
       "yes/no", OPTION_ARG_OPTIONAL,
-      "Tell whether the GC must keep outputs of live derivations" },
+      n_("tell whether the GC must keep outputs of live derivations") },
     { "gc-keep-derivations", GUIX_OPT_GC_KEEP_DERIVATIONS,
       "yes/no", OPTION_ARG_OPTIONAL,
-      "Tell whether the GC must keep derivations corresponding \
-to live outputs" },
+      n_("tell whether the GC must keep derivations corresponding \
+to live outputs") },
 
-    { "listen", GUIX_OPT_LISTEN, "SOCKET", 0,
-      "Listen for connections on SOCKET" },
+    { "listen", GUIX_OPT_LISTEN, n_("SOCKET"), 0,
+      n_("listen for connections on SOCKET") },
     { "debug", GUIX_OPT_DEBUG, 0, 0,
-      "Produce debugging output" },
+      n_("produce debugging output") },
     { 0, 0, 0, 0, 0 }
   };
 
@@ -154,8 +165,18 @@ parse_opt (int key, char *arg, struct argp_state *state)
       settings.useChroot = false;
       break;
     case GUIX_OPT_CHROOT_DIR:
-      settings.dirsInChroot.insert (arg);
-      break;
+      {
+	std::string chroot_dirs;
+
+	chroot_dirs = settings.get ("build-extra-chroot-dirs",
+				    (std::string) "");
+	if (chroot_dirs == "")
+	  chroot_dirs = arg;
+	else
+	  chroot_dirs = chroot_dirs + " " + arg;
+	settings.set("build-extra-chroot-dirs", chroot_dirs);
+	break;
+      }
     case GUIX_OPT_DISABLE_LOG_COMPRESSION:
       settings.compressLog = false;
       break;
@@ -181,7 +202,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
 	}
       catch (std::exception &e)
 	{
-	  fprintf (stderr, "error: %s\n", e.what ());
+	  fprintf (stderr, _("error: %s\n"), e.what ());
 	  exit (EXIT_FAILURE);
 	}
       break;
@@ -220,19 +241,29 @@ parse_opt (int key, char *arg, struct argp_state *state)
 }
 
 /* Argument parsing.  */
-static struct argp argp = { options, parse_opt, 0, doc };
+static const struct argp argp =
+  {
+    options, parse_opt,
+    NULL, doc,
+    NULL, NULL,					  // children and help_filter
+    guix_textdomain
+  };
 
 
 
 int
 main (int argc, char *argv[])
 {
-  Strings nothing;
+  static const Strings nothing;
+
+  setlocale (LC_ALL, "");
+  bindtextdomain (guix_textdomain, LOCALEDIR);
+  textdomain (guix_textdomain);
 
   /* Initialize libgcrypt.  */
   if (!gcry_check_version (GCRYPT_VERSION))
     {
-      fprintf (stderr, "error: libgcrypt version mismatch\n");
+      fprintf (stderr, _("error: libgcrypt version mismatch\n"));
       exit (EXIT_FAILURE);
     }
 
@@ -323,16 +354,17 @@ main (int argc, char *argv[])
       settings.update ();
 
       if (geteuid () == 0 && settings.buildUsersGroup.empty ())
-	fprintf (stderr, "warning: daemon is running as root, so "
-		 "using `--build-users-group' is highly recommended\n");
+	fprintf (stderr, _("warning: daemon is running as root, so \
+using `--build-users-group' is highly recommended\n"));
 
       if (settings.useChroot)
 	{
-	  foreach (PathSet::iterator, i, settings.dirsInChroot)
-	    {
-	      printMsg (lvlDebug,
-			format ("directory `%1%' added to the chroot") % *i);
-	    }
+	  std::string chroot_dirs;
+
+	  chroot_dirs = settings.get ("build-extra-chroot-dirs",
+				      (std::string) "");
+	  printMsg (lvlDebug,
+		    format ("extra chroot directories: '%1%'") % chroot_dirs);
 	}
 
       printMsg (lvlDebug,
@@ -346,7 +378,7 @@ main (int argc, char *argv[])
     }
   catch (std::exception &e)
     {
-      fprintf (stderr, "error: %s\n", e.what ());
+      fprintf (stderr, _("error: %s\n"), e.what ());
       return EXIT_FAILURE;
     }
 
diff --git a/nix/nix-daemon/nix-daemon.cc b/nix/nix-daemon/nix-daemon.cc
index 8814fe3155..10159db62e 100644
--- a/nix/nix-daemon/nix-daemon.cc
+++ b/nix/nix-daemon/nix-daemon.cc
@@ -7,6 +7,8 @@
 #include "affinity.hh"
 #include "globals.hh"
 
+#include <algorithm>
+
 #include <cstring>
 #include <unistd.h>
 #include <signal.h>
@@ -17,6 +19,8 @@
 #include <sys/un.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <pwd.h>
+#include <grp.h>
 
 using namespace nix;
 
@@ -44,7 +48,6 @@ static FdSource from(STDIN_FILENO);
 static FdSink to(STDOUT_FILENO);
 
 bool canSendStderr;
-pid_t myPid;
 
 
 
@@ -54,11 +57,7 @@ pid_t myPid;
    socket. */
 static void tunnelStderr(const unsigned char * buf, size_t count)
 {
-    /* Don't send the message to the client if we're a child of the
-       process handling the connection.  Otherwise we could screw up
-       the protocol.  It's up to the parent to redirect stderr and
-       send it to the client somehow (e.g., as in build.cc). */
-    if (canSendStderr && myPid == getpid()) {
+    if (canSendStderr) {
         try {
             writeInt(STDERR_NEXT, to);
             writeString(buf, count, to);
@@ -284,15 +283,6 @@ static void performOp(bool trusted, unsigned int clientVersion,
 {
     switch (op) {
 
-#if 0
-    case wopQuit: {
-        /* Close the database. */
-        store.reset((StoreAPI *) 0);
-        writeInt(1, to);
-        break;
-    }
-#endif
-
     case wopIsValidPath: {
         /* 'readStorePath' could raise an error leading to the connection
            being closed.  To be able to recover from an invalid path error,
@@ -450,6 +440,9 @@ static void performOp(bool trusted, unsigned int clientVersion,
     case wopImportPaths: {
         startWork();
         TunnelSource source(from);
+
+	/* Unlike Nix, always require a signature, even for "trusted"
+	   users.  */
         Paths paths = store->importPaths(true, source);
         stopWork();
         writeStrings(paths, to);
@@ -650,6 +643,25 @@ static void performOp(bool trusted, unsigned int clientVersion,
         break;
     }
 
+    case wopOptimiseStore:
+        startWork();
+        store->optimiseStore();
+        stopWork();
+        writeInt(1, to);
+        break;
+
+    case wopVerifyStore: {
+        bool checkContents = readInt(from) != 0;
+        bool repair = readInt(from) != 0;
+        startWork();
+        if (repair && !trusted)
+            throw Error("you are not privileged to repair paths");
+        bool errors = store->verifyStore(checkContents, repair);
+        stopWork();
+        writeInt(errors, to);
+        break;
+    }
+
     default:
         throw Error(format("invalid operation %1%") % op);
     }
@@ -659,7 +671,6 @@ static void performOp(bool trusted, unsigned int clientVersion,
 static void processConnection(bool trusted)
 {
     canSendStderr = false;
-    myPid = getpid();
     _writeToStderr = tunnelStderr;
 
 #ifdef HAVE_HUP_NOTIFICATION
@@ -708,7 +719,7 @@ static void processConnection(bool trusted)
         to.flush();
 
     } catch (Error & e) {
-        stopWork(false, e.msg());
+        stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
         to.flush();
         return;
     }
@@ -735,12 +746,10 @@ static void processConnection(bool trusted)
                during addTextToStore() / importPath().  If that
                happens, just send the error message and exit. */
             bool errorAllowed = canSendStderr;
-            if (!errorAllowed) printMsg(lvlError, format("error processing client input: %1%") % e.msg());
             stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
-            if (!errorAllowed) break;
+            if (!errorAllowed) throw;
         } catch (std::bad_alloc & e) {
-            if (canSendStderr)
-                stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
+            stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
             throw;
         }
 
@@ -749,7 +758,9 @@ static void processConnection(bool trusted)
         assert(!canSendStderr);
     };
 
-    printMsg(lvlError, format("%1% operations") % opCount);
+    canSendStderr = false;
+    _isInterrupted = false;
+    printMsg(lvlDebug, format("%1% operations") % opCount);
 }
 
 
@@ -771,11 +782,35 @@ static void setSigChldAction(bool autoReap)
 }
 
 
+bool matchUser(const string & user, const string & group, const Strings & users)
+{
+    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);
@@ -804,7 +839,8 @@ static void daemonLoop()
         /* 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. */
-        chdir(dirOf(socketPath).c_str());
+        if (chdir(dirOf(socketPath).c_str()) == -1)
+            throw SysError("cannot change current directory");
         Path socketPathRel = "./" + baseNameOf(socketPath);
 
         struct sockaddr_un addr;
@@ -824,7 +860,8 @@ static void daemonLoop()
         if (res == -1)
             throw SysError(format("cannot bind to socket `%1%'") % socketPath);
 
-        chdir("/"); /* back to the root */
+        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);
@@ -856,58 +893,61 @@ static void daemonLoop()
 
             closeOnExec(remote);
 
-            /* Get the identity of the caller, if possible. */
-            uid_t clientUid = -1;
-            pid_t clientPid = -1;
             bool trusted = false;
+            pid_t clientPid = -1;
 
 #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) {
-                clientPid = cred.pid;
-                clientUid = cred.uid;
-                if (clientUid == 0) trusted = true;
-            }
-#endif
+            if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1)
+                throw SysError("getting peer credentials");
 
-            printMsg(lvlInfo, format("accepted connection from pid %1%, uid %2%") % clientPid % clientUid);
+            clientPid = cred.pid;
 
-            /* Fork a child to handle the connection. */
-            pid_t child;
-            child = fork();
+            struct passwd * pw = getpwuid(cred.uid);
+            string user = pw ? pw->pw_name : int2String(cred.uid);
 
-            switch (child) {
+            struct group * gr = getgrgid(cred.gid);
+            string group = gr ? gr->gr_name : int2String(cred.gid);
 
-            case -1:
-                throw SysError("unable to fork");
+            Strings trustedUsers = settings.get("trusted-users", Strings({"root"}));
+            Strings allowedUsers = settings.get("allowed-users", Strings({"*"}));
 
-            case 0:
-                try { /* child */
+            if (matchUser(user, group, trustedUsers))
+                trusted = true;
 
-                    /* Background the daemon. */
-                    if (setsid() == -1)
-                        throw SysError(format("creating a new session"));
+            if (!trusted && !matchUser(user, group, allowedUsers))
+                throw Error(format("user `%1%' is not allowed to connect to the Nix daemon") % user);
 
-                    /* Restore normal handling of SIGCHLD. */
-                    setSigChldAction(false);
+            printMsg(lvlInfo, format((string) "accepted connection from pid %1%, user %2%"
+                    + (trusted ? " (trusted)" : "")) % clientPid % user);
+#endif
 
-                    /* For debugging, stuff the pid into argv[1]. */
-                    if (clientPid != -1 && argvSaved[1]) {
-                        string processName = int2String(clientPid);
-                        strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
-                    }
+            /* Fork a child to handle the connection. */
+            startProcess([&]() {
+                fdSocket.close();
 
-                    /* Handle the connection. */
-                    from.fd = remote;
-                    to.fd = remote;
-                    processConnection(trusted);
+                /* Background the daemon. */
+                if (setsid() == -1)
+                    throw SysError(format("creating a new session"));
 
-                } catch (std::exception & e) {
-                    writeToStderr("unexpected Nix daemon error: " + string(e.what()) + "\n");
+                /* Restore normal handling of SIGCHLD. */
+                setSigChldAction(false);
+
+                /* For debugging, stuff the pid into argv[1]. */
+                if (clientPid != -1 && argvSaved[1]) {
+                    string processName = int2String(clientPid);
+                    strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
                 }
+
+                /* Handle the connection. */
+                from.fd = remote;
+                to.fd = remote;
+                processConnection(trusted);
+
                 exit(0);
-            }
+            }, false, "unexpected Nix daemon error: ", true);
 
         } catch (Interrupted & e) {
             throw;
@@ -925,7 +965,6 @@ void run(Strings args)
         if (arg == "--daemon") /* ignored for backwards compatibility */;
     }
 
-    chdir("/");
     daemonLoop();
 }