diff options
Diffstat (limited to 'src/libstore')
26 files changed, 310 insertions, 256 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 12ce6f2ec..48e123f36 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -831,6 +831,10 @@ private: paths to the sandbox as a result of recursive Nix calls. */ AutoCloseFD sandboxMountNamespace; + /* On Linux, whether we're doing the build in its own user + namespace. */ + bool usingUserNamespace = true; + /* The build hook. */ std::unique_ptr<HookInstance> hook; @@ -920,8 +924,8 @@ private: result. */ std::map<Path, ValidPathInfo> prevInfos; - const uid_t sandboxUid = 1000; - const gid_t sandboxGid = 100; + uid_t sandboxUid() { return usingUserNamespace ? 1000 : buildUser->getUID(); } + gid_t sandboxGid() { return usingUserNamespace ? 100 : buildUser->getGID(); } const static Path homeDir; @@ -2355,7 +2359,8 @@ void DerivationGoal::startBuilder() worker.store.computeFSClosure(worker.store.toStorePath(i.second.source).first, closure); } catch (InvalidPath & e) { } catch (Error & e) { - throw Error("while processing 'sandbox-paths': %s", e.what()); + e.addTrace({}, "while processing 'sandbox-paths'"); + throw; } for (auto & i : closure) { auto p = worker.store.printStorePath(i); @@ -2423,15 +2428,14 @@ void DerivationGoal::startBuilder() "root:x:0:0:Nix build user:%3%:/noshell\n" "nixbld:x:%1%:%2%:Nix build user:%3%:/noshell\n" "nobody:x:65534:65534:Nobody:/:/noshell\n", - sandboxUid, sandboxGid, settings.sandboxBuildDir)); + sandboxUid(), sandboxGid(), settings.sandboxBuildDir)); /* Declare the build user's group so that programs get a consistent view of the system (e.g., "id -gn"). */ writeFile(chrootRootDir + "/etc/group", - (format( - "root:x:0:\n" + fmt("root:x:0:\n" "nixbld:!:%1%:\n" - "nogroup:x:65534:\n") % sandboxGid).str()); + "nogroup:x:65534:\n", sandboxGid())); /* Create /etc/hosts with localhost entry. */ if (!(derivationIsImpure(derivationType))) @@ -2628,6 +2632,13 @@ void DerivationGoal::startBuilder() options.allowVfork = false; + Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces"; + static bool userNamespacesEnabled = + pathExists(maxUserNamespaces) + && trim(readFile(maxUserNamespaces)) != "0"; + + usingUserNamespace = userNamespacesEnabled; + Pid helper = startProcess([&]() { /* Drop additional groups here because we can't do it @@ -2646,9 +2657,11 @@ void DerivationGoal::startBuilder() PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); if (stack == MAP_FAILED) throw SysError("allocating stack"); - int flags = CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD; + int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD; if (privateNetwork) flags |= CLONE_NEWNET; + if (usingUserNamespace) + flags |= CLONE_NEWUSER; pid_t child = clone(childEntry, stack + stackSize, flags, this); if (child == -1 && errno == EINVAL) { @@ -2657,11 +2670,12 @@ void DerivationGoal::startBuilder() flags &= ~CLONE_NEWPID; child = clone(childEntry, stack + stackSize, flags, this); } - if (child == -1 && (errno == EPERM || errno == EINVAL)) { + if (usingUserNamespace && child == -1 && (errno == EPERM || errno == EINVAL)) { /* Some distros patch Linux to not allow unprivileged * user namespaces. If we get EPERM or EINVAL, try * without CLONE_NEWUSER and see if that works. */ + usingUserNamespace = false; flags &= ~CLONE_NEWUSER; child = clone(childEntry, stack + stackSize, flags, this); } @@ -2672,7 +2686,8 @@ void DerivationGoal::startBuilder() _exit(1); if (child == -1) throw SysError("cloning builder process"); - writeFull(builderOut.writeSide.get(), std::to_string(child) + "\n"); + writeFull(builderOut.writeSide.get(), + fmt("%d %d\n", usingUserNamespace, child)); _exit(0); }, options); @@ -2686,23 +2701,38 @@ void DerivationGoal::startBuilder() userNamespaceSync.readSide = -1; + /* Close the write side to prevent runChild() from hanging + reading from this. */ + Finally cleanup([&]() { + userNamespaceSync.writeSide = -1; + }); + pid_t tmp; - if (!string2Int<pid_t>(readLine(builderOut.readSide.get()), tmp)) abort(); + auto ss = tokenizeString<std::vector<std::string>>(readLine(builderOut.readSide.get())); + assert(ss.size() == 2); + usingUserNamespace = ss[0] == "1"; + if (!string2Int<pid_t>(ss[1], tmp)) abort(); pid = tmp; - /* Set the UID/GID mapping of the builder's user namespace - such that the sandbox user maps to the build user, or to - the calling user (if build users are disabled). */ - uid_t hostUid = buildUser ? buildUser->getUID() : getuid(); - uid_t hostGid = buildUser ? buildUser->getGID() : getgid(); + if (usingUserNamespace) { + /* Set the UID/GID mapping of the builder's user namespace + such that the sandbox user maps to the build user, or to + the calling user (if build users are disabled). */ + uid_t hostUid = buildUser ? buildUser->getUID() : getuid(); + uid_t hostGid = buildUser ? buildUser->getGID() : getgid(); - writeFile("/proc/" + std::to_string(pid) + "/uid_map", - (format("%d %d 1") % sandboxUid % hostUid).str()); + writeFile("/proc/" + std::to_string(pid) + "/uid_map", + fmt("%d %d 1", sandboxUid(), hostUid)); - writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny"); + writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny"); - writeFile("/proc/" + std::to_string(pid) + "/gid_map", - (format("%d %d 1") % sandboxGid % hostGid).str()); + writeFile("/proc/" + std::to_string(pid) + "/gid_map", + fmt("%d %d 1", sandboxGid(), hostGid)); + } else { + debug("note: not using a user namespace"); + if (!buildUser) + throw Error("cannot perform a sandboxed build because user namespaces are not enabled; check /proc/sys/user/max_user_namespaces"); + } /* Save the mount namespace of the child. We have to do this *before* the child does a chroot. */ @@ -2712,7 +2742,6 @@ void DerivationGoal::startBuilder() /* Signal the builder that we've updated its user namespace. */ writeFull(userNamespaceSync.writeSide.get(), "1"); - userNamespaceSync.writeSide = -1; } else #endif @@ -2732,11 +2761,14 @@ void DerivationGoal::startBuilder() /* Check if setting up the build environment failed. */ while (true) { string msg = readLine(builderOut.readSide.get()); + if (string(msg, 0, 1) == "\2") break; if (string(msg, 0, 1) == "\1") { - if (msg.size() == 1) break; - throw Error(string(msg, 1)); + FdSource source(builderOut.readSide.get()); + auto ex = readError(source); + ex.addTrace({}, "while setting up the build environment"); + throw ex; } - debug(msg); + debug("sandbox setup: " + msg); } } @@ -3560,9 +3592,9 @@ void DerivationGoal::runChild() /* Switch to the sandbox uid/gid in the user namespace, which corresponds to the build user or calling user in the parent namespace. */ - if (setgid(sandboxGid) == -1) + if (setgid(sandboxGid()) == -1) throw SysError("setgid failed"); - if (setuid(sandboxUid) == -1) + if (setuid(sandboxUid()) == -1) throw SysError("setuid failed"); setUser = false; @@ -3780,7 +3812,7 @@ void DerivationGoal::runChild() args.push_back(rewriteStrings(i, inputRewrites)); /* Indicate that we managed to set up the build environment. */ - writeFull(STDERR_FILENO, string("\1\n")); + writeFull(STDERR_FILENO, string("\2\n")); /* Execute the program. This should not return. */ if (drv->isBuiltin()) { @@ -3801,7 +3833,7 @@ void DerivationGoal::runChild() throw Error("unsupported builtin function '%1%'", string(drv->builder, 8)); _exit(0); } catch (std::exception & e) { - writeFull(STDERR_FILENO, "error: " + string(e.what()) + "\n"); + writeFull(STDERR_FILENO, e.what() + std::string("\n")); _exit(1); } } @@ -3810,8 +3842,11 @@ void DerivationGoal::runChild() throw SysError("executing '%1%'", drv->builder); - } catch (std::exception & e) { - writeFull(STDERR_FILENO, "\1while setting up the build environment: " + string(e.what()) + "\n"); + } catch (Error & e) { + writeFull(STDERR_FILENO, "\1\n"); + FdSink sink(STDERR_FILENO); + sink << e; + sink.flush(); _exit(1); } } diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 36953975a..7ae88b49a 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -101,17 +101,20 @@ struct TunnelLogger : public Logger /* stopWork() means that we're done; stop sending stderr to the client. */ - void stopWork(bool success = true, const string & msg = "", unsigned int status = 0) + void stopWork(const Error * ex = nullptr) { auto state(state_.lock()); state->canSendStderr = false; - if (success) + if (!ex) to << STDERR_LAST; else { - to << STDERR_ERROR << msg; - if (status != 0) to << status; + if (GET_PROTOCOL_MINOR(clientVersion) >= 26) { + to << STDERR_ERROR << *ex; + } else { + to << STDERR_ERROR << ex->what() << ex->status; + } } } @@ -935,10 +938,11 @@ void processConnection( 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); + tunnelLogger->stopWork(&e); if (!errorAllowed) throw; } catch (std::bad_alloc & e) { - tunnelLogger->stopWork(false, "Nix daemon out of memory", 1); + auto ex = Error("Nix daemon out of memory"); + tunnelLogger->stopWork(&ex); throw; } @@ -947,8 +951,13 @@ void processConnection( assert(!tunnelLogger->state_.lock()->canSendStderr); }; + } catch (Error & e) { + tunnelLogger->stopWork(&e); + to.flush(); + return; } catch (std::exception & e) { - tunnelLogger->stopWork(false, e.what(), 1); + auto ex = Error(e.what()); + tunnelLogger->stopWork(&ex); to.flush(); return; } diff --git a/src/libstore/daemon.hh b/src/libstore/daemon.hh index 841ace316..67755d54e 100644 --- a/src/libstore/daemon.hh +++ b/src/libstore/daemon.hh @@ -1,3 +1,5 @@ +#pragma once + #include "serialise.hh" #include "store-api.hh" diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index be19aa300..e2e5578a8 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -61,8 +61,6 @@ typedef std::map<string, DerivationOutput> DerivationOutputs; also contains, for each output, the (optional) store path in which it would be written. To calculate values of these types, see the corresponding functions in BasicDerivation */ -typedef std::map<string, std::pair<DerivationOutput, StorePath>> - DerivationOutputsAndPaths; typedef std::map<string, std::pair<DerivationOutput, std::optional<StorePath>>> DerivationOutputsAndOptPaths; diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc index 49641c2ac..98b745c3a 100644 --- a/src/libstore/dummy-store.cc +++ b/src/libstore/dummy-store.cc @@ -18,8 +18,7 @@ struct DummyStore : public Store, public virtual DummyStoreConfig DummyStore(const Params & params) : StoreConfig(params) , Store(params) - { - } + { } string getUri() override { @@ -63,6 +62,6 @@ struct DummyStore : public Store, public virtual DummyStoreConfig { unsupported("buildDerivation"); } }; -static RegisterStoreImplementation<DummyStore, DummyStoreConfig> regStore; +static RegisterStoreImplementation<DummyStore, DummyStoreConfig> regDummyStore; } diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index cd619672f..c2c65af05 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -31,7 +31,7 @@ namespace nix { FileTransferSettings fileTransferSettings; -static GlobalConfig::Register r1(&fileTransferSettings); +static GlobalConfig::Register rFileTransferSettings(&fileTransferSettings); std::string resolveUri(const std::string & uri) { diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 518a357ef..7a04b2f89 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -1,6 +1,7 @@ #include "derivations.hh" #include "globals.hh" #include "local-store.hh" +#include "local-fs-store.hh" #include "finally.hh" #include <functional> diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index c5734852d..1238dc530 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -25,7 +25,7 @@ namespace nix { Settings settings; -static GlobalConfig::Register r1(&settings); +static GlobalConfig::Register rSettings(&settings); Settings::Settings() : nixPrefix(NIX_PREFIX) diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index 3d3d91e5e..9d2a89f96 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -181,6 +181,6 @@ protected: }; -static RegisterStoreImplementation<HttpBinaryCacheStore, HttpBinaryCacheStoreConfig> regStore; +static RegisterStoreImplementation<HttpBinaryCacheStore, HttpBinaryCacheStoreConfig> regHttpBinaryCacheStore; } diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 17d9dd1df..e440536df 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -334,6 +334,6 @@ public: } }; -static RegisterStoreImplementation<LegacySSHStore, LegacySSHStoreConfig> regStore; +static RegisterStoreImplementation<LegacySSHStore, LegacySSHStoreConfig> regLegacySSHStore; } diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index b5744448e..7d979c5c2 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -105,6 +105,6 @@ std::set<std::string> LocalBinaryCacheStore::uriSchemes() return {"file"}; } -static RegisterStoreImplementation<LocalBinaryCacheStore, LocalBinaryCacheStoreConfig> regStore; +static RegisterStoreImplementation<LocalBinaryCacheStore, LocalBinaryCacheStoreConfig> regLocalBinaryCacheStore; } diff --git a/src/libstore/local-fs-store.cc b/src/libstore/local-fs-store.cc index 2f1d9663a..e7c3dae92 100644 --- a/src/libstore/local-fs-store.cc +++ b/src/libstore/local-fs-store.cc @@ -1,6 +1,7 @@ #include "archive.hh" #include "fs-accessor.hh" #include "store-api.hh" +#include "local-fs-store.hh" #include "globals.hh" #include "compression.hh" #include "derivations.hh" diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh new file mode 100644 index 000000000..8eccd8236 --- /dev/null +++ b/src/libstore/local-fs-store.hh @@ -0,0 +1,48 @@ +#pragma once + +#include "store-api.hh" + +namespace nix { + +struct LocalFSStoreConfig : virtual StoreConfig +{ + using StoreConfig::StoreConfig; + // FIXME: the (StoreConfig*) cast works around a bug in gcc that causes + // it to omit the call to the Setting constructor. Clang works fine + // either way. + const PathSetting rootDir{(StoreConfig*) this, true, "", + "root", "directory prefixed to all other paths"}; + const PathSetting stateDir{(StoreConfig*) this, false, + rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir, + "state", "directory where Nix will store state"}; + const PathSetting logDir{(StoreConfig*) this, false, + rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir, + "log", "directory where Nix will store state"}; +}; + +class LocalFSStore : public virtual Store, public virtual LocalFSStoreConfig +{ +public: + + const static string drvsLogDir; + + LocalFSStore(const Params & params); + + void narFromPath(const StorePath & path, Sink & sink) override; + ref<FSAccessor> getFSAccessor() override; + + /* Register a permanent GC root. */ + Path addPermRoot(const StorePath & storePath, const Path & gcRoot); + + virtual Path getRealStoreDir() { return storeDir; } + + Path toRealPath(const Path & storePath) override + { + assert(isInStore(storePath)); + return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1); + } + + std::shared_ptr<std::string> getBuildLog(const StorePath & path) override; +}; + +} diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index e7c9d1605..f1e2ab7f9 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -4,6 +4,7 @@ #include "pathlocks.hh" #include "store-api.hh" +#include "local-fs-store.hh" #include "sync.hh" #include "util.hh" diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index c032a5e22..a0d482ddf 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -276,21 +276,15 @@ void LocalStore::optimiseStore(OptimiseStats & stats) } } -static string showBytes(uint64_t bytes) -{ - return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str(); -} - void LocalStore::optimiseStore() { OptimiseStats stats; optimiseStore(stats); - printInfo( - format("%1% freed by hard-linking %2% files") - % showBytes(stats.bytesFreed) - % stats.filesLinked); + printInfo("%s freed by hard-linking %d files", + showBytes(stats.bytesFreed), + stats.filesLinked); } void LocalStore::optimisePath(const Path & path) diff --git a/src/libstore/parsed-derivations.hh b/src/libstore/parsed-derivations.hh index 3fa09f34f..c9fbe68c4 100644 --- a/src/libstore/parsed-derivations.hh +++ b/src/libstore/parsed-derivations.hh @@ -1,3 +1,5 @@ +#pragma once + #include "store-api.hh" #include <nlohmann/json_fwd.hpp> diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index c3809bad7..ed10dd519 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -1,5 +1,6 @@ #include "profiles.hh" #include "store-api.hh" +#include "local-fs-store.hh" #include "util.hh" #include <sys/types.h> diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 04b80d750..5ff787ed2 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -12,16 +12,6 @@ #include "logging.hh" #include "callback.hh" -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> - -#include <cstring> - namespace nix { namespace worker_proto { @@ -125,69 +115,6 @@ ref<RemoteStore::Connection> RemoteStore::openConnectionWrapper() } -UDSRemoteStore::UDSRemoteStore(const Params & params) - : StoreConfig(params) - , Store(params) - , LocalFSStore(params) - , RemoteStore(params) -{ -} - - -UDSRemoteStore::UDSRemoteStore( - const std::string scheme, - std::string socket_path, - const Params & params) - : UDSRemoteStore(params) -{ - path.emplace(socket_path); -} - - -std::string UDSRemoteStore::getUri() -{ - if (path) { - return std::string("unix://") + *path; - } else { - return "daemon"; - } -} - - -ref<RemoteStore::Connection> UDSRemoteStore::openConnection() -{ - auto conn = make_ref<Connection>(); - - /* Connect to a daemon that does the privileged work for us. */ - conn->fd = socket(PF_UNIX, SOCK_STREAM - #ifdef SOCK_CLOEXEC - | SOCK_CLOEXEC - #endif - , 0); - if (!conn->fd) - throw SysError("cannot create Unix domain socket"); - closeOnExec(conn->fd.get()); - - string socketPath = path ? *path : settings.nixDaemonSocketFile; - - struct sockaddr_un addr; - addr.sun_family = AF_UNIX; - if (socketPath.size() + 1 >= sizeof(addr.sun_path)) - throw Error("socket path '%1%' is too long", socketPath); - strcpy(addr.sun_path, socketPath.c_str()); - - if (::connect(conn->fd.get(), (struct sockaddr *) &addr, sizeof(addr)) == -1) - throw SysError("cannot connect to daemon at '%1%'", socketPath); - - conn->from.fd = conn->fd.get(); - conn->to.fd = conn->fd.get(); - - conn->startTime = std::chrono::steady_clock::now(); - - return conn; -} - - void RemoteStore::initConnection(Connection & conn) { /* Send the magic greeting, check for the reply. */ @@ -926,9 +853,13 @@ std::exception_ptr RemoteStore::Connection::processStderr(Sink * sink, Source * } else if (msg == STDERR_ERROR) { - string error = readString(from); - unsigned int status = readInt(from); - return std::make_exception_ptr(Error(status, error)); + if (GET_PROTOCOL_MINOR(daemonVersion) >= 26) { + return std::make_exception_ptr(readError(from)); + } else { + string error = readString(from); + unsigned int status = readInt(from); + return std::make_exception_ptr(Error(status, error)); + } } else if (msg == STDERR_NEXT) @@ -1009,6 +940,4 @@ void ConnectionHandle::withFramedSink(std::function<void(Sink &sink)> fun) } -static RegisterStoreImplementation<UDSRemoteStore, UDSRemoteStoreConfig> regStore; - } diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index ec04be985..9f78fcb02 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -155,49 +155,5 @@ private: }; -struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreConfig -{ - UDSRemoteStoreConfig(const Store::Params & params) - : StoreConfig(params) - , LocalFSStoreConfig(params) - , RemoteStoreConfig(params) - { - } - - UDSRemoteStoreConfig() - : UDSRemoteStoreConfig(Store::Params({})) - { - } - - const std::string name() override { return "Local Daemon Store"; } -}; - -class UDSRemoteStore : public LocalFSStore, public RemoteStore, public virtual UDSRemoteStoreConfig -{ -public: - - UDSRemoteStore(const Params & params); - UDSRemoteStore(const std::string scheme, std::string path, const Params & params); - - std::string getUri() override; - - static std::set<std::string> uriSchemes() - { return {"unix"}; } - - bool sameMachine() override - { return true; } - - ref<FSAccessor> getFSAccessor() override - { return LocalFSStore::getFSAccessor(); } - - void narFromPath(const StorePath & path, Sink & sink) override - { LocalFSStore::narFromPath(path, sink); } - -private: - - ref<RemoteStore::Connection> openConnection() override; - std::optional<std::string> path; -}; - } diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index d43f267e0..552c4aac7 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -439,7 +439,7 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore, virtual S3BinaryCache }; -static RegisterStoreImplementation<S3BinaryCacheStoreImpl, S3BinaryCacheStoreConfig> regStore; +static RegisterStoreImplementation<S3BinaryCacheStoreImpl, S3BinaryCacheStoreConfig> regS3BinaryCacheStore; } diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 6d6eca98d..08d0bd565 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -83,6 +83,6 @@ ref<RemoteStore::Connection> SSHStore::openConnection() return conn; } -static RegisterStoreImplementation<SSHStore, SSHStoreConfig> regStore; +static RegisterStoreImplementation<SSHStore, SSHStoreConfig> regSSHStore; } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 5d63b8e3c..c2696f7a5 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1077,7 +1077,7 @@ Derivation Store::readDerivation(const StorePath & drvPath) #include "local-store.hh" -#include "remote-store.hh" +#include "uds-remote-store.hh" namespace nix { diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index e6a6053a3..dba6198eb 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -196,6 +196,8 @@ struct StoreConfig : public Config */ StoreConfig() { assert(false); } + virtual ~StoreConfig() { } + virtual const std::string name() = 0; const PathSetting storeDir_{this, false, settings.nixStore, @@ -709,47 +711,6 @@ protected: }; -struct LocalFSStoreConfig : virtual StoreConfig -{ - using StoreConfig::StoreConfig; - // FIXME: the (StoreConfig*) cast works around a bug in gcc that causes - // it to omit the call to the Setting constructor. Clang works fine - // either way. - const PathSetting rootDir{(StoreConfig*) this, true, "", - "root", "directory prefixed to all other paths"}; - const PathSetting stateDir{(StoreConfig*) this, false, - rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir, - "state", "directory where Nix will store state"}; - const PathSetting logDir{(StoreConfig*) this, false, - rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir, - "log", "directory where Nix will store state"}; -}; - -class LocalFSStore : public virtual Store, public virtual LocalFSStoreConfig -{ -public: - - const static string drvsLogDir; - - LocalFSStore(const Params & params); - - void narFromPath(const StorePath & path, Sink & sink) override; - ref<FSAccessor> getFSAccessor() override; - - /* Register a permanent GC root. */ - Path addPermRoot(const StorePath & storePath, const Path & gcRoot); - - virtual Path getRealStoreDir() { return storeDir; } - - Path toRealPath(const Path & storePath) override - { - assert(isInStore(storePath)); - return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1); - } - - std::shared_ptr<std::string> getBuildLog(const StorePath & path) override; -}; - /* Copy a path from one store to another. */ void copyStorePath(ref<Store> srcStore, ref<Store> dstStore, diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/uds-remote-store.cc new file mode 100644 index 000000000..24f3e9c6d --- /dev/null +++ b/src/libstore/uds-remote-store.cc @@ -0,0 +1,81 @@ +#include "uds-remote-store.hh" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <cstring> + + +namespace nix { + +UDSRemoteStore::UDSRemoteStore(const Params & params) + : StoreConfig(params) + , Store(params) + , LocalFSStore(params) + , RemoteStore(params) +{ +} + + +UDSRemoteStore::UDSRemoteStore( + const std::string scheme, + std::string socket_path, + const Params & params) + : UDSRemoteStore(params) +{ + path.emplace(socket_path); +} + + +std::string UDSRemoteStore::getUri() +{ + if (path) { + return std::string("unix://") + *path; + } else { + return "daemon"; + } +} + + +ref<RemoteStore::Connection> UDSRemoteStore::openConnection() +{ + auto conn = make_ref<Connection>(); + + /* Connect to a daemon that does the privileged work for us. */ + conn->fd = socket(PF_UNIX, SOCK_STREAM + #ifdef SOCK_CLOEXEC + | SOCK_CLOEXEC + #endif + , 0); + if (!conn->fd) + throw SysError("cannot create Unix domain socket"); + closeOnExec(conn->fd.get()); + + string socketPath = path ? *path : settings.nixDaemonSocketFile; + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + if (socketPath.size() + 1 >= sizeof(addr.sun_path)) + throw Error("socket path '%1%' is too long", socketPath); + strcpy(addr.sun_path, socketPath.c_str()); + + if (::connect(conn->fd.get(), (struct sockaddr *) &addr, sizeof(addr)) == -1) + throw SysError("cannot connect to daemon at '%1%'", socketPath); + + conn->from.fd = conn->fd.get(); + conn->to.fd = conn->fd.get(); + + conn->startTime = std::chrono::steady_clock::now(); + + return conn; +} + + +static RegisterStoreImplementation<UDSRemoteStore, UDSRemoteStoreConfig> regUDSRemoteStore; + +} diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh new file mode 100644 index 000000000..e5de104c9 --- /dev/null +++ b/src/libstore/uds-remote-store.hh @@ -0,0 +1,52 @@ +#pragma once + +#include "remote-store.hh" +#include "local-fs-store.hh" + +namespace nix { + +struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreConfig +{ + UDSRemoteStoreConfig(const Store::Params & params) + : StoreConfig(params) + , LocalFSStoreConfig(params) + , RemoteStoreConfig(params) + { + } + + UDSRemoteStoreConfig() + : UDSRemoteStoreConfig(Store::Params({})) + { + } + + const std::string name() override { return "Local Daemon Store"; } +}; + +class UDSRemoteStore : public LocalFSStore, public RemoteStore, public virtual UDSRemoteStoreConfig +{ +public: + + UDSRemoteStore(const Params & params); + UDSRemoteStore(const std::string scheme, std::string path, const Params & params); + + std::string getUri() override; + + static std::set<std::string> uriSchemes() + { return {"unix"}; } + + bool sameMachine() override + { return true; } + + ref<FSAccessor> getFSAccessor() override + { return LocalFSStore::getFSAccessor(); } + + void narFromPath(const StorePath & path, Sink & sink) override + { LocalFSStore::narFromPath(path, sink); } + +private: + + ref<RemoteStore::Connection> openConnection() override; + std::optional<std::string> path; +}; + +} diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 2934c1d67..b3705578e 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -6,7 +6,7 @@ namespace nix { #define WORKER_MAGIC_1 0x6e697863 #define WORKER_MAGIC_2 0x6478696f -#define PROTOCOL_VERSION 0x119 +#define PROTOCOL_VERSION 0x11a #define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00) #define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff) @@ -83,7 +83,6 @@ MAKE_WORKER_PROTO(, StorePath); MAKE_WORKER_PROTO(, ContentAddress); MAKE_WORKER_PROTO(template<typename T>, std::set<T>); -MAKE_WORKER_PROTO(template<typename T>, std::optional<T>); #define X_ template<typename K, typename V> #define Y_ std::map<K, V> @@ -91,6 +90,22 @@ MAKE_WORKER_PROTO(X_, Y_); #undef X_ #undef Y_ +/* These use the empty string for the null case, relying on the fact + that the underlying types never serialize to the empty string. + + We do this instead of a generic std::optional<T> instance because + ordinal tags (0 or 1, here) are a bit of a compatability hazard. For + the same reason, we don't have a std::variant<T..> instances (ordinal + tags 0...n). + + We could the generic instances and then these as specializations for + compatability, but that's proven a bit finnicky, and also makes the + worker protocol harder to implement in other languages where such + specializations may not be allowed. + */ +MAKE_WORKER_PROTO(, std::optional<StorePath>); +MAKE_WORKER_PROTO(, std::optional<ContentAddress>); + template<typename T> std::set<T> read(const Store & store, Source & from, Phantom<std::set<T>> _) { @@ -134,37 +149,6 @@ void write(const Store & store, Sink & out, const std::map<K, V> & resMap) } } -template<typename T> -std::optional<T> read(const Store & store, Source & from, Phantom<std::optional<T>> _) -{ - auto tag = readNum<uint8_t>(from); - switch (tag) { - case 0: - return std::nullopt; - case 1: - return read(store, from, Phantom<T> {}); - default: - throw Error("got an invalid tag bit for std::optional: %#04x", (size_t)tag); - } -} - -template<typename T> -void write(const Store & store, Sink & out, const std::optional<T> & optVal) -{ - out << (uint64_t) (optVal ? 1 : 0); - if (optVal) - worker_proto::write(store, out, *optVal); -} - -/* Specialization which uses and empty string for the empty case, taking - advantage of the fact these types always serialize to non-empty strings. - This is done primarily for backwards compatability, so that T <= - std::optional<T>, where <= is the compatability partial order, T is one of - the types below. - */ -MAKE_WORKER_PROTO(, std::optional<StorePath>); -MAKE_WORKER_PROTO(, std::optional<ContentAddress>); - } } |