diff options
author | Vincent Ambo <mail@tazj.in> | 2020-08-03T18·04+0100 |
---|---|---|
committer | tazjin <mail@tazj.in> | 2020-08-03T18·18+0000 |
commit | 229c1ed820fe20515a69f749c38d5f9f89d20c8d (patch) | |
tree | c43e1dd03d014aaa2050ed470e57e3229883e84f /third_party/nix/src/nix-daemon/nix-daemon.cc | |
parent | 209489e348904aa2e2cddc64340ea44ab3074dfd (diff) |
chore(3p/nix): Move legacy Nix daemon implementation r/1568
The filename this is squatting is too good. Change-Id: I694132b94d24786df84b6b5a698679bdce818b2e Reviewed-on: https://cl.tvl.fyi/c/depot/+/1621 Tested-by: BuildkiteCI Reviewed-by: glittershark <grfn@gws.fyi>
Diffstat (limited to 'third_party/nix/src/nix-daemon/nix-daemon.cc')
-rw-r--r-- | third_party/nix/src/nix-daemon/nix-daemon.cc | 1178 |
1 files changed, 0 insertions, 1178 deletions
diff --git a/third_party/nix/src/nix-daemon/nix-daemon.cc b/third_party/nix/src/nix-daemon/nix-daemon.cc deleted file mode 100644 index b36f9733c37a..000000000000 --- a/third_party/nix/src/nix-daemon/nix-daemon.cc +++ /dev/null @@ -1,1178 +0,0 @@ -#include <algorithm> -#include <cerrno> -#include <climits> -#include <csignal> -#include <cstring> - -#include <fcntl.h> -#include <glog/logging.h> -#include <grp.h> -#include <pwd.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/un.h> -#include <sys/wait.h> -#include <unistd.h> - -#include "libmain/shared.hh" -#include "libproto/worker.pb.h" -#include "libstore/derivations.hh" -#include "libstore/globals.hh" -#include "libstore/local-store.hh" -#include "libstore/worker-protocol.hh" -#include "libutil/affinity.hh" -#include "libutil/archive.hh" -#include "libutil/finally.hh" -#include "libutil/monitor-fd.hh" -#include "libutil/serialise.hh" -#include "libutil/util.hh" -#include "nix/legacy.hh" - -using namespace nix; - -#ifndef __linux__ -#define SPLICE_F_MOVE 0 -static ssize_t splice(int fd_in, void* off_in, int fd_out, void* off_out, - size_t len, unsigned int flags) { - /* We ignore most parameters, we just have them for conformance with the linux - * syscall */ - std::vector<char> buf(8192); - auto read_count = read(fd_in, buf.data(), buf.size()); - if (read_count == -1) { - return read_count; - } - auto write_count = decltype(read_count)(0); - while (write_count < read_count) { - auto res = - write(fd_out, buf.data() + write_count, read_count - write_count); - if (res == -1) { - return res; - } - write_count += res; - } - return read_count; -} -#endif - -static FdSource from(STDIN_FILENO); -static FdSink to(STDOUT_FILENO); - -/* 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). */ -struct TunnelLogger { - struct State { - bool canSendStderr = false; - std::vector<std::string> pendingMsgs; - }; - - Sync<State> state_; - - unsigned int clientVersion; - - explicit TunnelLogger(unsigned int clientVersion) - : clientVersion(clientVersion) {} - - void enqueueMsg(const std::string& s) { - auto state(state_.lock()); - - if (state->canSendStderr) { - assert(state->pendingMsgs.empty()); - try { - to(s); - to.flush(); - } catch (...) { - /* Write failed; that means that the other side is - gone. */ - state->canSendStderr = false; - throw; - } - } else { - state->pendingMsgs.push_back(s); - } - } - - void log(const FormatOrString& fs) { - StringSink buf; - buf << STDERR_NEXT << (fs.s + "\n"); - enqueueMsg(*buf.s); - } - - /* startWork() means that we're starting an operation for which we - want to send out stderr to the client. */ - void startWork() { - auto state(state_.lock()); - state->canSendStderr = true; - - for (auto& msg : state->pendingMsgs) { - to(msg); - } - - state->pendingMsgs.clear(); - - to.flush(); - } - - /* stopWork() means that we're done; stop sending stderr to the - client. */ - void stopWork(bool success = true, const std::string& msg = "", - unsigned int status = 0) { - auto state(state_.lock()); - - state->canSendStderr = false; - - if (success) { - to << STDERR_LAST; - } else { - to << STDERR_ERROR << msg; - if (status != 0) { - to << status; - } - } - } - - void startActivity(const std::string& s) { - DLOG(INFO) << "startActivity(" << s << ")"; - if (GET_PROTOCOL_MINOR(clientVersion) < 20) { - if (!s.empty()) { - LOG(INFO) << s; - } - return; - } - - StringSink buf; - buf << STDERR_START_ACTIVITY << s; - enqueueMsg(*buf.s); - } -}; - -struct TunnelSink : Sink { - Sink& to; - explicit TunnelSink(Sink& to) : to(to) {} - void operator()(const unsigned char* data, size_t len) override { - to << STDERR_WRITE; - writeString(data, len, to); - } -}; - -struct TunnelSource : BufferedSource { - Source& from; - explicit TunnelSource(Source& from) : from(from) {} - - protected: - size_t readUnbuffered(unsigned char* data, size_t len) override { - to << STDERR_READ << len; - to.flush(); - size_t n = readString(data, len, from); - if (n == 0) { - throw EndOfFile("unexpected end-of-file"); - } - return n; - } -}; - -/* If the NAR archive contains a single file at top-level, then save - the contents of the file to `s'. Otherwise barf. */ -struct RetrieveRegularNARSink : ParseSink { - bool regular{true}; - std::string s; - - RetrieveRegularNARSink() {} - - void createDirectory(const Path& path) override { regular = false; } - - void receiveContents(unsigned char* data, unsigned int len) override { - s.append((const char*)data, len); - } - - void createSymlink(const Path& path, const std::string& target) override { - regular = false; - } -}; - -static void performOp(TunnelLogger* logger, const ref<Store>& store, - bool trusted, unsigned int clientVersion, Source& from, - Sink& to, unsigned int op) { - switch (op) { - case wopIsValidPath: { - /* 'readStorePath' could raise an error leading to the connection - being closed. To be able to recover from an invalid path error, - call 'startWork' early, and do 'assertStorePath' afterwards so - that the 'Error' exception handler doesn't close the - connection. */ - Path path = readString(from); - logger->startWork(); - store->assertStorePath(path); - bool result = store->isValidPath(path); - logger->stopWork(); - to << static_cast<uint64_t>(result); - break; - } - - case wopQueryValidPaths: { - auto paths = readStorePaths<PathSet>(*store, from); - logger->startWork(); - PathSet res = store->queryValidPaths(paths); - logger->stopWork(); - to << res; - break; - } - - case wopHasSubstitutes: { - Path path = readStorePath(*store, from); - logger->startWork(); - PathSet res = store->querySubstitutablePaths({path}); - logger->stopWork(); - to << static_cast<uint64_t>(res.find(path) != res.end()); - break; - } - - case wopQuerySubstitutablePaths: { - auto paths = readStorePaths<PathSet>(*store, from); - logger->startWork(); - PathSet res = store->querySubstitutablePaths(paths); - logger->stopWork(); - to << res; - break; - } - - case wopQueryPathHash: { - Path path = readStorePath(*store, from); - logger->startWork(); - auto hash = store->queryPathInfo(path)->narHash; - logger->stopWork(); - to << hash.to_string(Base16, false); - break; - } - - case wopQueryReferences: - case wopQueryReferrers: - case wopQueryValidDerivers: - case wopQueryDerivationOutputs: { - Path path = readStorePath(*store, from); - logger->startWork(); - PathSet paths; - if (op == wopQueryReferences) { - 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); - } - logger->stopWork(); - to << paths; - break; - } - - case wopQueryDerivationOutputNames: { - Path path = readStorePath(*store, from); - logger->startWork(); - StringSet names; - names = store->queryDerivationOutputNames(path); - logger->stopWork(); - to << names; - break; - } - - case wopQueryDeriver: { - Path path = readStorePath(*store, from); - logger->startWork(); - auto deriver = store->queryPathInfo(path)->deriver; - logger->stopWork(); - to << deriver; - break; - } - - case wopQueryPathFromHashPart: { - std::string hashPart = readString(from); - logger->startWork(); - Path path = store->queryPathFromHashPart(hashPart); - logger->stopWork(); - to << path; - break; - } - - case wopAddToStore: { - bool fixed = false; - bool recursive = false; - std::string hashType; - std::string baseName; - from >> baseName >> fixed /* obsolete */ >> recursive >> hashType; - /* Compatibility hack. */ - if (!fixed) { - hashType = "sha256"; - recursive = true; - } - HashType hashAlgo = parseHashType(hashType); - - TeeSource savedNAR(from); - RetrieveRegularNARSink savedRegular; - - if (recursive) { - /* Get the entire NAR dump from the client and save it to - a string so that we can pass it to - addToStoreFromDump(). */ - ParseSink sink; /* null sink; just parse the NAR */ - parseDump(sink, savedNAR); - } else { - parseDump(savedRegular, from); - } - - logger->startWork(); - if (!savedRegular.regular) { - throw Error("regular file expected"); - } - - auto store2 = store.dynamic_pointer_cast<LocalStore>(); - if (!store2) { - throw Error("operation is only supported by LocalStore"); - } - - Path path = store2->addToStoreFromDump( - recursive ? *savedNAR.data : savedRegular.s, baseName, recursive, - hashAlgo); - logger->stopWork(); - - to << path; - break; - } - - case wopAddTextToStore: { - std::string suffix = readString(from); - std::string s = readString(from); - auto refs = readStorePaths<PathSet>(*store, from); - logger->startWork(); - Path path = store->addTextToStore(suffix, s, refs, NoRepair); - logger->stopWork(); - to << path; - break; - } - - case wopExportPath: { - Path path = readStorePath(*store, from); - readInt(from); // obsolete - logger->startWork(); - TunnelSink sink(to); - store->exportPath(path, sink); - logger->stopWork(); - to << 1; - break; - } - - case wopImportPaths: { - logger->startWork(); - TunnelSource source(from); - Paths paths = store->importPaths(source, nullptr, - trusted ? NoCheckSigs : CheckSigs); - logger->stopWork(); - to << paths; - break; - } - - case wopBuildPaths: { - auto drvs = readStorePaths<PathSet>(*store, 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 allowed because you are not in " - "'trusted-users'"); - } - } - logger->startWork(); - store->buildPaths(drvs, mode); - logger->stopWork(); - to << 1; - break; - } - - case wopBuildDerivation: { - Path drvPath = readStorePath(*store, from); - BasicDerivation drv; - readDerivation(from, *store, drv); - auto buildMode = (BuildMode)readInt(from); - logger->startWork(); - if (!trusted) { - throw Error("you are not privileged to build derivations"); - } - auto res = store->buildDerivation(drvPath, drv, buildMode); - logger->stopWork(); - to << res.status << res.errorMsg; - break; - } - - case wopEnsurePath: { - Path path = readStorePath(*store, from); - logger->startWork(); - store->ensurePath(path); - logger->stopWork(); - to << 1; - break; - } - - case wopAddTempRoot: { - Path path = readStorePath(*store, from); - logger->startWork(); - store->addTempRoot(path); - logger->stopWork(); - to << 1; - break; - } - - case wopAddIndirectRoot: { - Path path = absPath(readString(from)); - logger->startWork(); - store->addIndirectRoot(path); - logger->stopWork(); - to << 1; - break; - } - - case wopSyncWithGC: { - logger->startWork(); - store->syncWithGC(); - logger->stopWork(); - to << 1; - break; - } - - case wopFindRoots: { - logger->startWork(); - Roots roots = store->findRoots(!trusted); - logger->stopWork(); - - size_t size = 0; - for (auto& i : roots) { - size += i.second.size(); - } - - to << size; - - for (auto& [target, links] : roots) { - for (auto& link : links) { - to << link << target; - } - } - - break; - } - - case wopCollectGarbage: { - GCOptions options; - options.action = (GCOptions::GCAction)readInt(from); - options.pathsToDelete = readStorePaths<PathSet>(*store, from); - from >> options.ignoreLiveness >> options.maxFreed; - // obsolete fields - readInt(from); - readInt(from); - readInt(from); - - GCResults results; - - logger->startWork(); - if (options.ignoreLiveness) { - throw Error("you are not allowed to ignore liveness"); - } - store->collectGarbage(options, results); - logger->stopWork(); - - to << results.paths << results.bytesFreed << 0 /* obsolete */; - - break; - } - - case wopSetOptions: { - settings.keepFailed = readInt(from) != 0u; - settings.keepGoing = readInt(from) != 0u; - settings.tryFallback = readInt(from) != 0u; - readInt(from); // obsolete verbosity - settings.maxBuildJobs.assign(readInt(from)); - settings.maxSilentTime = readInt(from); - readInt(from); // obsolete useBuildHook - settings.verboseBuild = 0 == readInt(from); - readInt(from); // obsolete logType - readInt(from); // obsolete printBuildTrace - settings.buildCores = readInt(from); - settings.useSubstitutes = readInt(from) != 0u; - - StringMap overrides; - if (GET_PROTOCOL_MINOR(clientVersion) >= 12) { - unsigned int n = readInt(from); - for (unsigned int i = 0; i < n; i++) { - std::string name = readString(from); - std::string value = readString(from); - overrides.emplace(name, value); - } - } - - logger->startWork(); - - for (auto& i : overrides) { - auto& name(i.first); - auto& value(i.second); - - auto setSubstituters = [&](Setting<Strings>& res) { - if (name != res.name && res.aliases.count(name) == 0) { - return false; - } - StringSet trusted = settings.trustedSubstituters; - for (auto& s : settings.substituters.get()) { - trusted.insert(s); - } - Strings subs; - Strings ss = absl::StrSplit(value, absl::ByAnyChar(" \t\n\r")); - for (auto& s : ss) { - if (trusted.count(s) != 0u) { - subs.push_back(s); - } else { - LOG(WARNING) << "ignoring untrusted substituter '" << s << "'"; - } - } - res = subs; - return true; - }; - - try { - if (name == "ssh-auth-sock") { // obsolete - ; - } else if (trusted || name == settings.buildTimeout.name || - name == "connect-timeout" || - (name == "builders" && value.empty())) { - settings.set(name, value); - } else if (setSubstituters(settings.substituters)) { - ; - } else if (setSubstituters(settings.extraSubstituters)) { - ; - } else { - LOG(WARNING) << "ignoring the user-specified setting '" << name - << "', because it is a " - << "restricted setting and you are not a trusted user"; - } - } catch (UsageError& e) { - LOG(WARNING) << e.what(); - } - } - - logger->stopWork(); - break; - } - - case wopQuerySubstitutablePathInfo: { - Path path = absPath(readString(from)); - logger->startWork(); - SubstitutablePathInfos infos; - store->querySubstitutablePathInfos({path}, infos); - logger->stopWork(); - auto i = infos.find(path); - if (i == infos.end()) { - to << 0; - } else { - to << 1 << i->second.deriver << i->second.references - << i->second.downloadSize << i->second.narSize; - } - break; - } - - case wopQuerySubstitutablePathInfos: { - auto paths = readStorePaths<PathSet>(*store, from); - logger->startWork(); - SubstitutablePathInfos infos; - store->querySubstitutablePathInfos(paths, infos); - logger->stopWork(); - to << infos.size(); - for (auto& i : infos) { - to << i.first << i.second.deriver << i.second.references - << i.second.downloadSize << i.second.narSize; - } - break; - } - - case wopQueryAllValidPaths: { - logger->startWork(); - PathSet paths = store->queryAllValidPaths(); - logger->stopWork(); - to << paths; - break; - } - - case wopQueryPathInfo: { - Path path = readStorePath(*store, from); - std::shared_ptr<const ValidPathInfo> info; - logger->startWork(); - try { - info = store->queryPathInfo(path); - } catch (InvalidPath&) { - if (GET_PROTOCOL_MINOR(clientVersion) < 17) { - throw; - } - } - logger->stopWork(); - if (info) { - if (GET_PROTOCOL_MINOR(clientVersion) >= 17) { - to << 1; - } - to << info->deriver << info->narHash.to_string(Base16, false) - << info->references << info->registrationTime << info->narSize; - if (GET_PROTOCOL_MINOR(clientVersion) >= 16) { - to << static_cast<uint64_t>(info->ultimate) << info->sigs << info->ca; - } - } else { - assert(GET_PROTOCOL_MINOR(clientVersion) >= 17); - to << 0; - } - break; - } - - case wopOptimiseStore: { - logger->startWork(); - store->optimiseStore(); - logger->stopWork(); - to << 1; - break; - } - - case wopVerifyStore: { - bool checkContents; - bool repair; - from >> checkContents >> repair; - logger->startWork(); - if (repair && !trusted) { - throw Error("you are not privileged to repair paths"); - } - bool errors = store->verifyStore(checkContents, (RepairFlag)repair); - logger->stopWork(); - to << static_cast<uint64_t>(errors); - break; - } - - case wopAddSignatures: { - Path path = readStorePath(*store, from); - auto sigs = readStrings<StringSet>(from); - logger->startWork(); - if (!trusted) { - throw Error("you are not privileged to add signatures"); - } - store->addSignatures(path, sigs); - logger->stopWork(); - to << 1; - break; - } - - case wopNarFromPath: { - auto path = readStorePath(*store, from); - logger->startWork(); - logger->stopWork(); - dumpPath(path, to); - break; - } - - case wopAddToStoreNar: { - bool repair; - bool dontCheckSigs; - ValidPathInfo info; - info.path = readStorePath(*store, from); - from >> info.deriver; - if (!info.deriver.empty()) { - store->assertStorePath(info.deriver); - } - info.narHash = Hash(readString(from), htSHA256); - info.references = readStorePaths<PathSet>(*store, from); - from >> info.registrationTime >> info.narSize >> info.ultimate; - info.sigs = readStrings<StringSet>(from); - from >> info.ca >> repair >> dontCheckSigs; - if (!trusted && dontCheckSigs) { - dontCheckSigs = false; - } - if (!trusted) { - info.ultimate = false; - } - - std::string saved; - std::unique_ptr<Source> source; - if (GET_PROTOCOL_MINOR(clientVersion) >= 21) { - source = std::make_unique<TunnelSource>(from); - } else { - TeeSink tee(from); - parseDump(tee, tee.source); - saved = std::move(*tee.source.data); - source = std::make_unique<StringSource>(saved); - } - - logger->startWork(); - - // FIXME: race if addToStore doesn't read source? - store->addToStore(info, *source, (RepairFlag)repair, - dontCheckSigs ? NoCheckSigs : CheckSigs, nullptr); - - logger->stopWork(); - break; - } - - case wopQueryMissing: { - auto targets = readStorePaths<PathSet>(*store, from); - logger->startWork(); - PathSet willBuild; - PathSet willSubstitute; - PathSet unknown; - unsigned long long downloadSize; - unsigned long long narSize; - store->queryMissing(targets, willBuild, willSubstitute, unknown, - downloadSize, narSize); - logger->stopWork(); - to << willBuild << willSubstitute << unknown << downloadSize << narSize; - break; - } - - default: - throw Error(format("invalid operation %1%") % op); - } -} - -static void processConnection(bool trusted, const std::string& userName, - uid_t userId) { - MonitorFdHup monitor(from.fd); - - /* Exchange the greeting. */ - unsigned int magic = readInt(from); - if (magic != WORKER_MAGIC_1) { - throw Error("protocol mismatch"); - } - to << WORKER_MAGIC_2 << PROTOCOL_VERSION; - to.flush(); - unsigned int clientVersion = readInt(from); - - if (clientVersion < 0x10a) { - throw Error("the Nix client version is too old"); - } - - auto tunnelLogger = new TunnelLogger(clientVersion); - // logger = tunnelLogger; - - unsigned int opCount = 0; - - Finally finally([&]() { - _isInterrupted = false; - DLOG(INFO) << opCount << " operations"; - }); - - if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && (readInt(from) != 0u)) { - setAffinityTo(readInt(from)); - } - - readInt(from); // obsolete reserveSpace - - /* Send startup error messages to the client. */ - tunnelLogger->startWork(); - - try { - /* If we can't accept clientVersion, then throw an error - *here* (not above). */ - -#if 0 - /* Prevent users from doing something very dangerous. */ - if (geteuid() == 0 && - querySetting("build-users-group", "") == "") - throw Error("if you run 'nix-daemon' as root, then you MUST set 'build-users-group'!"); -#endif - - /* Open the store. */ - Store::Params params; // FIXME: get params from somewhere - // Disable caching since the client already does that. - params["path-info-cache-size"] = "0"; - auto store = openStore(settings.storeUri, params); - - store->createUser(userName, userId); - - tunnelLogger->stopWork(); - to.flush(); - - /* Process client requests. */ - while (true) { - WorkerOp op; - try { - op = (WorkerOp)readInt(from); - } catch (Interrupted& e) { - break; - } catch (EndOfFile& e) { - break; - } - - opCount++; - - try { - performOp(tunnelLogger, 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 = tunnelLogger->state_.lock()->canSendStderr; - tunnelLogger->stopWork(false, e.msg(), e.status); - if (!errorAllowed) { - throw; - } - } catch (std::bad_alloc& e) { - tunnelLogger->stopWork(false, "Nix daemon out of memory", 1); - throw; - } - - to.flush(); - - assert(!tunnelLogger->state_.lock()->canSendStderr); - }; - - } catch (std::exception& e) { - tunnelLogger->stopWork(false, e.what(), 1); - to.flush(); - return; - } -} - -static void sigChldHandler(int sigNo) { - // Ensure we don't modify errno of whatever we've interrupted - auto saved_errno = errno; - /* Reap all dead children. */ - while (waitpid(-1, nullptr, WNOHANG) > 0) { - ; - } - errno = saved_errno; -} - -static void setSigChldAction(bool autoReap) { - struct sigaction act; - struct sigaction oact; - act.sa_handler = autoReap ? sigChldHandler : SIG_DFL; - sigfillset(&act.sa_mask); - act.sa_flags = 0; - if (sigaction(SIGCHLD, &act, &oact) != 0) { - throw SysError("setting SIGCHLD handler"); - } -} - -bool matchUser(const std::string& user, const std::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 (std::string(i, 0, 1) == "@") { - if (group == std::string(i, 1)) { - return true; - } - struct group* gr = getgrnam(i.c_str() + 1); - if (gr == nullptr) { - continue; - } - for (char** mem = gr->gr_mem; *mem != nullptr; mem++) { - if (user == std::string(*mem)) { - return true; - } - } - } - } - - return false; -} - -struct PeerInfo { - bool pidKnown; - pid_t pid; - bool uidKnown; - uid_t uid; - bool gidKnown; - gid_t gid; -}; - -/* Get the identity of the caller, if possible. */ -static PeerInfo getPeerInfo(int remote) { - PeerInfo peer = {false, 0, false, 0, false, 0}; - -#if defined(SO_PEERCRED) - - ucred cred; - socklen_t credLen = sizeof(cred); - if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1) { - throw SysError("getting peer credentials"); - } - peer = {true, cred.pid, true, cred.uid, true, cred.gid}; - -#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) - throw SysError("getting peer credentials"); - peer = {false, 0, true, cred.cr_uid, false, 0}; - -#endif - - return peer; -} - -#define SD_LISTEN_FDS_START 3 - -static void daemonLoop(char** argv) { - 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").empty()) { - 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) { - throw SysError("cannot create Unix domain socket"); - } - - std::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; - strncpy(addr.sun_path, socketPathRel.c_str(), sizeof(addr.sun_path)); - if (addr.sun_path[sizeof(addr.sun_path) - 1] != '\0') { - throw Error(format("socket path '%1%' is too long") % socketPathRel); - } - - 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.get(), (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.get(), 5) == -1) { - throw SysError(format("cannot listen on socket '%1%'") % socketPath); - } - } - - closeOnExec(fdSocket.get()); - - /* Loop accepting connections. */ - while (true) { - try { - /* Accept a connection. */ - struct sockaddr_un remoteAddr; - socklen_t remoteAddrLen = sizeof(remoteAddr); - - AutoCloseFD remote = - accept(fdSocket.get(), (struct sockaddr*)&remoteAddr, &remoteAddrLen); - checkInterrupt(); - if (!remote) { - if (errno == EINTR) { - continue; - } - throw SysError("accepting connection"); - } - - closeOnExec(remote.get()); - - bool trusted = false; - PeerInfo peer = getPeerInfo(remote.get()); - - struct passwd* pw = peer.uidKnown ? getpwuid(peer.uid) : nullptr; - std::string user = pw != nullptr ? pw->pw_name : std::to_string(peer.uid); - - struct group* gr = peer.gidKnown ? getgrgid(peer.gid) : nullptr; - std::string group = - gr != nullptr ? gr->gr_name : std::to_string(peer.gid); - - Strings trustedUsers = settings.trustedUsers; - Strings allowedUsers = settings.allowedUsers; - - if (matchUser(user, group, trustedUsers)) { - trusted = true; - } - - if ((!trusted && !matchUser(user, group, allowedUsers)) || - group == settings.buildUsersGroup) { - throw Error( - format("user '%1%' is not allowed to connect to the Nix daemon") % - user); - } - - LOG(INFO) << "accepted connection from pid " - << (peer.pidKnown ? std::to_string(peer.pid) : "<unknown>") - << ", user " << (peer.uidKnown ? user : "<unknown>") - << (trusted ? " (trusted)" : ""); - - /* Fork a child to handle the connection. */ - ProcessOptions options; - options.errorPrefix = "unexpected Nix daemon error: "; - options.dieWithParent = false; - options.runExitHandlers = true; - startProcess( - [&]() { - fdSocket = -1; - - /* Background the daemon. */ - if (setsid() == -1) { - throw SysError(format("creating a new session")); - } - - /* Restore normal handling of SIGCHLD. */ - setSigChldAction(false); - - /* For debugging, stuff the pid into argv[1]. */ - if (peer.pidKnown && (argv[1] != nullptr)) { - std::string processName = std::to_string(peer.pid); - strncpy(argv[1], processName.c_str(), strlen(argv[1])); - } - - /* Handle the connection. */ - from.fd = remote.get(); - to.fd = remote.get(); - processConnection(trusted, user, peer.uid); - - exit(0); - }, - options); - - } catch (Interrupted& e) { - return; - } catch (Error& e) { - LOG(ERROR) << "error processing connection: " << e.msg(); - } - } -} - -static int _main(int argc, char** argv) { - { - auto stdio = false; - - parseCmdLine(argc, argv, - [&](Strings::iterator& arg, const Strings::iterator& end) { - if (*arg == "--daemon") { - ; /* ignored for backwards compatibility */ - } else if (*arg == "--help") { - showManPage("nix-daemon"); - } else if (*arg == "--version") { - printVersion("nix-daemon"); - } else if (*arg == "--stdio") { - stdio = true; - } else { - return false; - } - return true; - }); - - if (stdio) { - if (getStoreType() == tDaemon) { - /* Forward on this connection to the real daemon */ - auto socketPath = settings.nixDaemonSocketFile; - auto s = socket(PF_UNIX, SOCK_STREAM, 0); - if (s == -1) { - throw SysError("creating Unix domain socket"); - } - - auto socketDir = dirOf(socketPath); - if (chdir(socketDir.c_str()) == -1) { - throw SysError(format("changing to socket directory '%1%'") % - socketDir); - } - - auto socketName = baseNameOf(socketPath); - auto addr = sockaddr_un{}; - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, socketName.c_str(), sizeof(addr.sun_path)); - if (addr.sun_path[sizeof(addr.sun_path) - 1] != '\0') { - throw Error(format("socket name %1% is too long") % socketName); - } - - if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) == -1) { - throw SysError(format("cannot connect to daemon at %1%") % - socketPath); - } - - auto nfds = (s > STDIN_FILENO ? s : STDIN_FILENO) + 1; - while (true) { - fd_set fds; - FD_ZERO(&fds); - FD_SET(s, &fds); - FD_SET(STDIN_FILENO, &fds); - if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1) { - throw SysError("waiting for data from client or server"); - } - if (FD_ISSET(s, &fds)) { - auto res = splice(s, nullptr, STDOUT_FILENO, nullptr, SSIZE_MAX, - SPLICE_F_MOVE); - if (res == -1) { - throw SysError("splicing data from daemon socket to stdout"); - } - if (res == 0) { - throw EndOfFile("unexpected EOF from daemon socket"); - } - } - if (FD_ISSET(STDIN_FILENO, &fds)) { - auto res = splice(STDIN_FILENO, nullptr, s, nullptr, SSIZE_MAX, - SPLICE_F_MOVE); - if (res == -1) { - throw SysError("splicing data from stdin to daemon socket"); - } - if (res == 0) { - return 0; - } - } - } - } else { - processConnection(true, "root", 0); - } - } else { - daemonLoop(argv); - } - - return 0; - } -} - -static RegisterLegacyCommand s1("nix-daemon", _main); |