about summary refs log tree commit diff
path: root/src/nix-daemon/nix-daemon.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix-daemon/nix-daemon.cc')
-rw-r--r--src/nix-daemon/nix-daemon.cc360
1 files changed, 196 insertions, 164 deletions
diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc
index ad8b0d133d82..9757086c650e 100644
--- a/src/nix-daemon/nix-daemon.cc
+++ b/src/nix-daemon/nix-daemon.cc
@@ -7,6 +7,7 @@
 #include "affinity.hh"
 #include "globals.hh"
 #include "monitor-fd.hh"
+#include "derivations.hh"
 
 #include <algorithm>
 
@@ -32,29 +33,43 @@ using namespace nix;
 static FdSource from(STDIN_FILENO);
 static FdSink to(STDOUT_FILENO);
 
-bool canSendStderr;
+static bool canSendStderr;
 
+static Logger * defaultLogger;
 
-/* This function is called anytime we want to write something to
-   stderr.  If we're in a state where the protocol allows it (i.e.,
-   when canSendStderr), send the message to the client over the
-   socket. */
-static void tunnelStderr(const unsigned char * buf, size_t count)
+
+/* Logger that forwards log messages to the client, *if* we're in a
+   state where the protocol allows it (i.e., when canSendStderr is
+   true). */
+class TunnelLogger : public Logger
 {
-    if (canSendStderr) {
-        try {
-            writeInt(STDERR_NEXT, to);
-            writeString(buf, count, to);
-            to.flush();
-        } catch (...) {
-            /* Write failed; that means that the other side is
-               gone. */
-            canSendStderr = false;
-            throw;
-        }
-    } else
-        writeFull(STDERR_FILENO, buf, count);
-}
+    void log(Verbosity lvl, const FormatOrString & fs) override
+    {
+        if (lvl > verbosity) return;
+
+        if (canSendStderr) {
+            try {
+                to << STDERR_NEXT << (fs.s + "\n");
+                to.flush();
+            } catch (...) {
+                /* Write failed; that means that the other side is
+                   gone. */
+                canSendStderr = false;
+                throw;
+            }
+        } else
+            defaultLogger->log(lvl, fs);
+    }
+
+    void startActivity(Activity & activity, Verbosity lvl, const FormatOrString & fs) override
+    {
+        log(lvl, fs);
+    }
+
+    void stopActivity(Activity & activity) override
+    {
+    }
+};
 
 
 /* startWork() means that we're starting an operation for which we
@@ -72,11 +87,10 @@ static void stopWork(bool success = true, const string & msg = "", unsigned int
     canSendStderr = false;
 
     if (success)
-        writeInt(STDERR_LAST, to);
+        to << STDERR_LAST;
     else {
-        writeInt(STDERR_ERROR, to);
-        writeString(msg, to);
-        if (status != 0) writeInt(status, to);
+        to << STDERR_ERROR << msg;
+        if (status != 0) to << status;
     }
 }
 
@@ -87,7 +101,7 @@ struct TunnelSink : Sink
     TunnelSink(Sink & to) : to(to) { }
     virtual void operator () (const unsigned char * data, size_t len)
     {
-        writeInt(STDERR_WRITE, to);
+        to << STDERR_WRITE;
         writeString(data, len, to);
     }
 };
@@ -99,8 +113,7 @@ struct TunnelSource : BufferedSource
     TunnelSource(Source & from) : from(from) { }
     size_t readUnbuffered(unsigned char * data, size_t len)
     {
-        writeInt(STDERR_READ, to);
-        writeInt(len, to);
+        to << STDERR_READ << len;
         to.flush();
         size_t n = readString(data, len, from);
         if (n == 0) throw EndOfFile("unexpected end-of-file");
@@ -150,7 +163,7 @@ struct SavingSourceAdapter : Source
 };
 
 
-static void performOp(bool trusted, unsigned int clientVersion,
+static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVersion,
     Source & from, Sink & to, unsigned int op)
 {
     switch (op) {
@@ -166,7 +179,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
         assertStorePath(path);
         bool result = store->isValidPath(path);
         stopWork();
-        writeInt(result, to);
+        to << result;
         break;
     }
 
@@ -175,16 +188,16 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         PathSet res = store->queryValidPaths(paths);
         stopWork();
-        writeStrings(res, to);
+        to << res;
         break;
     }
 
     case wopHasSubstitutes: {
         Path path = readStorePath(from);
         startWork();
-        PathSet res = store->querySubstitutablePaths(singleton<PathSet>(path));
+        PathSet res = store->querySubstitutablePaths({path});
         stopWork();
-        writeInt(res.find(path) != res.end(), to);
+        to << (res.find(path) != res.end());
         break;
     }
 
@@ -193,16 +206,16 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         PathSet res = store->querySubstitutablePaths(paths);
         stopWork();
-        writeStrings(res, to);
+        to << res;
         break;
     }
 
     case wopQueryPathHash: {
         Path path = readStorePath(from);
         startWork();
-        Hash hash = store->queryPathHash(path);
+        auto hash = store->queryPathInfo(path)->narHash;
         stopWork();
-        writeString(printHash(hash), to);
+        to << printHash(hash);
         break;
     }
 
@@ -214,14 +227,14 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         PathSet paths;
         if (op == wopQueryReferences)
-            store->queryReferences(path, paths);
+            paths = store->queryPathInfo(path)->references;
         else if (op == wopQueryReferrers)
             store->queryReferrers(path, paths);
         else if (op == wopQueryValidDerivers)
             paths = store->queryValidDerivers(path);
         else paths = store->queryDerivationOutputs(path);
         stopWork();
-        writeStrings(paths, to);
+        to << paths;
         break;
     }
 
@@ -231,16 +244,16 @@ static void performOp(bool trusted, unsigned int clientVersion,
         StringSet names;
         names = store->queryDerivationOutputNames(path);
         stopWork();
-        writeStrings(names, to);
+        to << names;
         break;
     }
 
     case wopQueryDeriver: {
         Path path = readStorePath(from);
         startWork();
-        Path deriver = store->queryDeriver(path);
+        auto deriver = store->queryPathInfo(path)->deriver;
         stopWork();
-        writeString(deriver, to);
+        to << deriver;
         break;
     }
 
@@ -249,7 +262,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         Path path = store->queryPathFromHashPart(hashPart);
         stopWork();
-        writeString(path, to);
+        to << path;
         break;
     }
 
@@ -279,11 +292,10 @@ static void performOp(bool trusted, unsigned int clientVersion,
 
         startWork();
         if (!savedRegular.regular) throw Error("regular file expected");
-        Path path = dynamic_cast<LocalStore *>(store.get())
-            ->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
+        Path path = store->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
         stopWork();
 
-        writeString(path, to);
+        to << path;
         break;
     }
 
@@ -294,36 +306,59 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         Path path = store->addTextToStore(suffix, s, refs);
         stopWork();
-        writeString(path, to);
+        to << path;
         break;
     }
 
     case wopExportPath: {
         Path path = readStorePath(from);
-        bool sign = readInt(from) == 1;
+        readInt(from); // obsolete
         startWork();
         TunnelSink sink(to);
-        store->exportPath(path, sign, sink);
+        store->exportPath(path, sink);
         stopWork();
-        writeInt(1, to);
+        to << 1;
         break;
     }
 
     case wopImportPaths: {
         startWork();
         TunnelSource source(from);
-        Paths paths = store->importPaths(!trusted, source);
+        Paths paths = store->importPaths(source, 0);
         stopWork();
-        writeStrings(paths, to);
+        to << paths;
         break;
     }
 
     case wopBuildPaths: {
         PathSet drvs = readStorePaths<PathSet>(from);
+        BuildMode mode = bmNormal;
+        if (GET_PROTOCOL_MINOR(clientVersion) >= 15) {
+            mode = (BuildMode)readInt(from);
+
+            /* Repairing is not atomic, so disallowed for "untrusted"
+               clients.  */
+            if (mode == bmRepair && !trusted)
+                throw Error("repairing is not supported when building through the Nix daemon");
+        }
         startWork();
-        store->buildPaths(drvs);
+        store->buildPaths(drvs, mode);
         stopWork();
-        writeInt(1, to);
+        to << 1;
+        break;
+    }
+
+    case wopBuildDerivation: {
+        Path drvPath = readStorePath(from);
+        BasicDerivation drv;
+        from >> drv;
+        BuildMode buildMode = (BuildMode) readInt(from);
+        startWork();
+        if (!trusted)
+            throw Error("you are not privileged to build derivations");
+        auto res = store->buildDerivation(drvPath, drv, buildMode);
+        stopWork();
+        to << res.status << res.errorMsg;
         break;
     }
 
@@ -332,7 +367,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         store->ensurePath(path);
         stopWork();
-        writeInt(1, to);
+        to << 1;
         break;
     }
 
@@ -341,7 +376,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         store->addTempRoot(path);
         stopWork();
-        writeInt(1, to);
+        to << 1;
         break;
     }
 
@@ -350,7 +385,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         store->addIndirectRoot(path);
         stopWork();
-        writeInt(1, to);
+        to << 1;
         break;
     }
 
@@ -358,7 +393,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         store->syncWithGC();
         stopWork();
-        writeInt(1, to);
+        to << 1;
         break;
     }
 
@@ -366,11 +401,9 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         Roots roots = store->findRoots();
         stopWork();
-        writeInt(roots.size(), to);
-        for (Roots::iterator i = roots.begin(); i != roots.end(); ++i) {
-            writeString(i->first, to);
-            writeString(i->second, to);
-        }
+        to << roots.size();
+        for (auto & i : roots)
+            to << i.first << i.second;
         break;
     }
 
@@ -395,9 +428,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
         store->collectGarbage(options, results);
         stopWork();
 
-        writeStrings(results.paths, to);
-        writeLongLong(results.bytesFreed, to);
-        writeLongLong(0, to); // obsolete
+        to << results.paths << results.bytesFreed << 0 /* obsolete */;
 
         break;
     }
@@ -407,17 +438,17 @@ static void performOp(bool trusted, unsigned int clientVersion,
         settings.keepGoing = readInt(from) != 0;
         settings.set("build-fallback", readInt(from) ? "true" : "false");
         verbosity = (Verbosity) readInt(from);
-        settings.set("build-max-jobs", int2String(readInt(from)));
-        settings.set("build-max-silent-time", int2String(readInt(from)));
+        settings.set("build-max-jobs", std::to_string(readInt(from)));
+        settings.set("build-max-silent-time", std::to_string(readInt(from)));
         if (GET_PROTOCOL_MINOR(clientVersion) >= 2)
             settings.useBuildHook = readInt(from) != 0;
         if (GET_PROTOCOL_MINOR(clientVersion) >= 4) {
-            settings.buildVerbosity = (Verbosity) readInt(from);
-            logType = (LogType) readInt(from);
-            settings.printBuildTrace = readInt(from) != 0;
+            settings.verboseBuild = lvlError == (Verbosity) readInt(from);
+            readInt(from); // obsolete logType
+            readInt(from); // obsolete printBuildTrace
         }
         if (GET_PROTOCOL_MINOR(clientVersion) >= 6)
-            settings.set("build-cores", int2String(readInt(from)));
+            settings.set("build-cores", std::to_string(readInt(from)));
         if (GET_PROTOCOL_MINOR(clientVersion) >= 10)
             settings.set("build-use-substitutes", readInt(from) ? "true" : "false");
         if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
@@ -441,18 +472,15 @@ static void performOp(bool trusted, unsigned int clientVersion,
         Path path = absPath(readString(from));
         startWork();
         SubstitutablePathInfos infos;
-        store->querySubstitutablePathInfos(singleton<PathSet>(path), infos);
+        store->querySubstitutablePathInfos({path}, infos);
         stopWork();
         SubstitutablePathInfos::iterator i = infos.find(path);
         if (i == infos.end())
-            writeInt(0, to);
+            to << 0;
         else {
-            writeInt(1, to);
-            writeString(i->second.deriver, to);
-            writeStrings(i->second.references, to);
-            writeLongLong(i->second.downloadSize, to);
+            to << 1 << i->second.deriver << i->second.references << i->second.downloadSize;
             if (GET_PROTOCOL_MINOR(clientVersion) >= 7)
-                writeLongLong(i->second.narSize, to);
+                to << i->second.narSize;
         }
         break;
     }
@@ -463,13 +491,10 @@ static void performOp(bool trusted, unsigned int clientVersion,
         SubstitutablePathInfos infos;
         store->querySubstitutablePathInfos(paths, infos);
         stopWork();
-        writeInt(infos.size(), to);
-        foreach (SubstitutablePathInfos::iterator, i, infos) {
-            writeString(i->first, to);
-            writeString(i->second.deriver, to);
-            writeStrings(i->second.references, to);
-            writeLongLong(i->second.downloadSize, to);
-            writeLongLong(i->second.narSize, to);
+        to << infos.size();
+        for (auto & i : infos) {
+            to << i.first << i.second.deriver << i.second.references
+               << i.second.downloadSize << i.second.narSize;
         }
         break;
     }
@@ -478,37 +503,33 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         PathSet paths = store->queryAllValidPaths();
         stopWork();
-        writeStrings(paths, to);
-        break;
-    }
-
-    case wopQueryFailedPaths: {
-        startWork();
-        PathSet paths = store->queryFailedPaths();
-        stopWork();
-        writeStrings(paths, to);
-        break;
-    }
-
-    case wopClearFailedPaths: {
-        PathSet paths = readStrings<PathSet>(from);
-        startWork();
-        store->clearFailedPaths(paths);
-        stopWork();
-        writeInt(1, to);
+        to << paths;
         break;
     }
 
     case wopQueryPathInfo: {
         Path path = readStorePath(from);
+        std::shared_ptr<const ValidPathInfo> info;
         startWork();
-        ValidPathInfo info = store->queryPathInfo(path);
+        try {
+            info = store->queryPathInfo(path);
+        } catch (InvalidPath &) {
+            if (GET_PROTOCOL_MINOR(clientVersion) < 17) throw;
+        }
         stopWork();
-        writeString(info.deriver, to);
-        writeString(printHash(info.hash), to);
-        writeStrings(info.references, to);
-        writeInt(info.registrationTime, to);
-        writeLongLong(info.narSize, to);
+        if (info) {
+            if (GET_PROTOCOL_MINOR(clientVersion) >= 17)
+                to << 1;
+            to << info->deriver << printHash(info->narHash) << info->references
+               << info->registrationTime << info->narSize;
+            if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
+                to << info->ultimate
+                   << info->sigs;
+            }
+        } else {
+            assert(GET_PROTOCOL_MINOR(clientVersion) >= 17);
+            to << 0;
+        }
         break;
     }
 
@@ -516,7 +537,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         store->optimiseStore();
         stopWork();
-        writeInt(1, to);
+        to << 1;
         break;
 
     case wopVerifyStore: {
@@ -527,7 +548,19 @@ static void performOp(bool trusted, unsigned int clientVersion,
             throw Error("you are not privileged to repair paths");
         bool errors = store->verifyStore(checkContents, repair);
         stopWork();
-        writeInt(errors, to);
+        to << errors;
+        break;
+    }
+
+    case wopAddSignatures: {
+        Path path = readStorePath(from);
+        StringSet sigs = readStrings<StringSet>(from);
+        startWork();
+        if (!trusted)
+            throw Error("you are not privileged to add signatures");
+        store->addSignatures(path, sigs);
+        stopWork();
+        to << 1;
         break;
     }
 
@@ -542,22 +575,21 @@ static void processConnection(bool trusted)
     MonitorFdHup monitor(from.fd);
 
     canSendStderr = false;
-    _writeToStderr = tunnelStderr;
+    defaultLogger = logger;
+    logger = new TunnelLogger();
 
     /* Exchange the greeting. */
     unsigned int magic = readInt(from);
     if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch");
-    writeInt(WORKER_MAGIC_2, to);
-    writeInt(PROTOCOL_VERSION, to);
+    to << WORKER_MAGIC_2 << PROTOCOL_VERSION;
     to.flush();
     unsigned int clientVersion = readInt(from);
 
     if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from))
         setAffinityTo(readInt(from));
 
-    bool reserveSpace = true;
     if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
-        reserveSpace = readInt(from) != 0;
+        readInt(from); // obsolete reserveSpace
 
     /* Send startup error messages to the client. */
     startWork();
@@ -575,56 +607,56 @@ static void processConnection(bool trusted)
 #endif
 
         /* Open the store. */
-        store = std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
+        auto store = make_ref<LocalStore>();
 
         stopWork();
         to.flush();
 
-    } catch (Error & e) {
-        stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
-        to.flush();
-        return;
-    }
+        /* Process client requests. */
+        unsigned int opCount = 0;
+
+        while (true) {
+            WorkerOp op;
+            try {
+                op = (WorkerOp) readInt(from);
+            } catch (Interrupted & e) {
+                break;
+            } catch (EndOfFile & e) {
+                break;
+            }
 
-    /* Process client requests. */
-    unsigned int opCount = 0;
+            opCount++;
+
+            try {
+                performOp(store, trusted, clientVersion, from, to, op);
+            } catch (Error & e) {
+                /* If we're not in a state where we can send replies, then
+                   something went wrong processing the input of the
+                   client.  This can happen especially if I/O errors occur
+                   during addTextToStore() / importPath().  If that
+                   happens, just send the error message and exit. */
+                bool errorAllowed = canSendStderr;
+                stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
+                if (!errorAllowed) throw;
+            } catch (std::bad_alloc & e) {
+                stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
+                throw;
+            }
 
-    while (true) {
-        WorkerOp op;
-        try {
-            op = (WorkerOp) readInt(from);
-        } catch (Interrupted & e) {
-            break;
-        } catch (EndOfFile & e) {
-            break;
-        }
+            to.flush();
 
-        opCount++;
+            assert(!canSendStderr);
+        };
 
-        try {
-            performOp(trusted, clientVersion, from, to, op);
-        } catch (Error & e) {
-            /* If we're not in a state where we can send replies, then
-               something went wrong processing the input of the
-               client.  This can happen especially if I/O errors occur
-               during addTextToStore() / importPath().  If that
-               happens, just send the error message and exit. */
-            bool errorAllowed = canSendStderr;
-            stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
-            if (!errorAllowed) throw;
-        } catch (std::bad_alloc & e) {
-            stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
-            throw;
-        }
+        canSendStderr = false;
+        _isInterrupted = false;
+        printMsg(lvlDebug, format("%1% operations") % opCount);
 
+    } catch (Error & e) {
+        stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
         to.flush();
-
-        assert(!canSendStderr);
-    };
-
-    canSendStderr = false;
-    _isInterrupted = false;
-    printMsg(lvlDebug, format("%1% operations") % opCount);
+        return;
+    }
 }
 
 
@@ -693,6 +725,10 @@ static PeerInfo getPeerInfo(int remote)
 
 #elif defined(LOCAL_PEERCRED)
 
+#if !defined(SOL_LOCAL)
+#define SOL_LOCAL 0
+#endif
+
     xucred cred;
     socklen_t credLen = sizeof(cred);
     if (getsockopt(remote, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == -1)
@@ -721,7 +757,7 @@ static void daemonLoop(char * * argv)
 
     /* Handle socket-based activation by systemd. */
     if (getEnv("LISTEN_FDS") != "") {
-        if (getEnv("LISTEN_PID") != int2String(getpid()) || getEnv("LISTEN_FDS") != "1")
+        if (getEnv("LISTEN_PID") != std::to_string(getpid()) || getEnv("LISTEN_FDS") != "1")
             throw Error("unexpected systemd environment variables");
         fdSocket = SD_LISTEN_FDS_START;
     }
@@ -775,10 +811,6 @@ static void daemonLoop(char * * argv)
     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);
@@ -797,10 +829,10 @@ static void daemonLoop(char * * argv)
             PeerInfo peer = getPeerInfo(remote);
 
             struct passwd * pw = peer.uidKnown ? getpwuid(peer.uid) : 0;
-            string user = pw ? pw->pw_name : int2String(peer.uid);
+            string user = pw ? pw->pw_name : std::to_string(peer.uid);
 
             struct group * gr = peer.gidKnown ? getgrgid(peer.gid) : 0;
-            string group = gr ? gr->gr_name : int2String(peer.gid);
+            string group = gr ? gr->gr_name : std::to_string(peer.gid);
 
             Strings trustedUsers = settings.get("trusted-users", Strings({"root"}));
             Strings allowedUsers = settings.get("allowed-users", Strings({"*"}));
@@ -812,7 +844,7 @@ static void daemonLoop(char * * argv)
                 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)" : ""))
-                % (peer.pidKnown ? int2String(peer.pid) : "<unknown>")
+                % (peer.pidKnown ? std::to_string(peer.pid) : "<unknown>")
                 % (peer.uidKnown ? user : "<unknown>"));
 
             /* Fork a child to handle the connection. */
@@ -833,7 +865,7 @@ static void daemonLoop(char * * argv)
 
                 /* For debugging, stuff the pid into argv[1]. */
                 if (peer.pidKnown && argv[1]) {
-                    string processName = int2String(peer.pid);
+                    string processName = std::to_string(peer.pid);
                     strncpy(argv[1], processName.c_str(), strlen(argv[1]));
                 }