mirror of
https://github.com/NixOS/nix.git
synced 2025-11-20 01:09:37 +01:00
Merge remote-tracking branch 'upstream/master' into path-info
This commit is contained in:
commit
a0f369aa3f
127 changed files with 650 additions and 549 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "serialise.hh"
|
||||
#include "store-api.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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "derivations.hh"
|
||||
#include "globals.hh"
|
||||
#include "local-store.hh"
|
||||
#include "local-fs-store.hh"
|
||||
#include "finally.hh"
|
||||
|
||||
#include <functional>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace nix {
|
|||
|
||||
Settings settings;
|
||||
|
||||
static GlobalConfig::Register r1(&settings);
|
||||
static GlobalConfig::Register rSettings(&settings);
|
||||
|
||||
Settings::Settings()
|
||||
: nixPrefix(NIX_PREFIX)
|
||||
|
|
|
|||
|
|
@ -181,6 +181,6 @@ protected:
|
|||
|
||||
};
|
||||
|
||||
static RegisterStoreImplementation<HttpBinaryCacheStore, HttpBinaryCacheStoreConfig> regStore;
|
||||
static RegisterStoreImplementation<HttpBinaryCacheStore, HttpBinaryCacheStoreConfig> regHttpBinaryCacheStore;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -334,6 +334,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
static RegisterStoreImplementation<LegacySSHStore, LegacySSHStoreConfig> regStore;
|
||||
static RegisterStoreImplementation<LegacySSHStore, LegacySSHStoreConfig> regLegacySSHStore;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,6 +105,6 @@ std::set<std::string> LocalBinaryCacheStore::uriSchemes()
|
|||
return {"file"};
|
||||
}
|
||||
|
||||
static RegisterStoreImplementation<LocalBinaryCacheStore, LocalBinaryCacheStoreConfig> regStore;
|
||||
static RegisterStoreImplementation<LocalBinaryCacheStore, LocalBinaryCacheStoreConfig> regLocalBinaryCacheStore;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
48
src/libstore/local-fs-store.hh
Normal file
48
src/libstore/local-fs-store.hh
Normal file
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "pathlocks.hh"
|
||||
#include "store-api.hh"
|
||||
#include "local-fs-store.hh"
|
||||
#include "sync.hh"
|
||||
#include "util.hh"
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "store-api.hh"
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "profiles.hh"
|
||||
#include "store-api.hh"
|
||||
#include "local-fs-store.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore, virtual S3BinaryCache
|
|||
|
||||
};
|
||||
|
||||
static RegisterStoreImplementation<S3BinaryCacheStoreImpl, S3BinaryCacheStoreConfig> regStore;
|
||||
static RegisterStoreImplementation<S3BinaryCacheStoreImpl, S3BinaryCacheStoreConfig> regS3BinaryCacheStore;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,6 @@ ref<RemoteStore::Connection> SSHStore::openConnection()
|
|||
return conn;
|
||||
}
|
||||
|
||||
static RegisterStoreImplementation<SSHStore, SSHStoreConfig> regStore;
|
||||
static RegisterStoreImplementation<SSHStore, SSHStoreConfig> regSSHStore;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
81
src/libstore/uds-remote-store.cc
Normal file
81
src/libstore/uds-remote-store.cc
Normal file
|
|
@ -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;
|
||||
|
||||
}
|
||||
52
src/libstore/uds-remote-store.hh
Normal file
52
src/libstore/uds-remote-store.hh
Normal file
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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>);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue