diff options
Diffstat (limited to 'src/nix-daemon/nix-daemon.cc')
-rw-r--r-- | src/nix-daemon/nix-daemon.cc | 360 |
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])); } |