libstore: Move some init to preStartBuilder

Change-Id: If36cd51f4b9352e38e7008b1295e7868727718f2
This commit is contained in:
Artemis Tosini 2024-05-03 02:22:16 +00:00
parent 2635a91cb4
commit e6275f8d2f
6 changed files with 170 additions and 110 deletions

View file

@ -1,14 +1,11 @@
#include "local-derivation-goal.hh"
#include "indirect-root-store.hh"
#include "hook-instance.hh"
#include "worker.hh"
#include "builtins.hh"
#include "builtins/buildenv.hh"
#include "path-references.hh"
#include "finally.hh"
#include "util.hh"
#include "archive.hh"
#include "compression.hh"
#include "daemon.hh"
#include "topo-sort.hh"
#include "callback.hh"
@ -85,14 +82,9 @@ LocalDerivationGoal::~LocalDerivationGoal() noexcept(false)
}
inline bool LocalDerivationGoal::needsHashRewrite()
bool LocalDerivationGoal::needsHashRewrite()
{
#if __linux__
return !useChroot;
#else
/* Darwin requires hash rewriting even when sandboxing is enabled. */
return true;
#endif
}
@ -127,19 +119,7 @@ void LocalDerivationGoal::killChild()
void LocalDerivationGoal::killSandbox(bool getStats)
{
if (cgroup) {
#if __linux__
auto stats = destroyCgroup(*cgroup);
if (getStats) {
buildResult.cpuUser = stats.cpuUser;
buildResult.cpuSystem = stats.cpuSystem;
}
#else
abort();
#endif
}
else if (buildUser) {
if (buildUser) {
auto uid = buildUser->getUID();
assert(uid != 0);
killUser(uid);
@ -166,11 +146,6 @@ void LocalDerivationGoal::tryLocalBuild()
if (noChroot)
throw Error("derivation '%s' has '__noChroot' set, "
"but that's not allowed when 'sandbox' is 'true'", worker.store.printStorePath(drvPath));
#if __APPLE__
if (additionalSandboxProfile != "")
throw Error("derivation '%s' specifies a sandbox profile, "
"but this is only allowed when 'sandbox' is 'relaxed'", worker.store.printStorePath(drvPath));
#endif
useChroot = true;
}
else if (settings.sandboxMode == smDisabled)
@ -181,23 +156,14 @@ void LocalDerivationGoal::tryLocalBuild()
auto & localStore = getLocalStore();
if (localStore.storeDir != localStore.realStoreDir.get()) {
#if __linux__
if (supportsDivertedStore()) {
// Diverted store is only possible with different mounts for the host system
// and builder. This necessarily means sandboxing.
useChroot = true;
#else
} else {
throw Error("building using a diverted store is not supported on this platform");
#endif
}
#if __linux__
if (useChroot) {
if (!mountAndPidNamespacesSupported()) {
if (!settings.sandboxFallback)
throw Error("this system does not support the kernel namespaces that are required for sandboxing; use '--no-sandbox' to disable sandboxing");
debug("auto-disabling sandboxing because the prerequisite namespaces are not available");
useChroot = false;
}
}
#endif
if (useBuildUsers()) {
if (!buildUser)
@ -216,6 +182,8 @@ void LocalDerivationGoal::tryLocalBuild()
try {
preStartBuilder();
/* Okay, we have to build. */
startBuilder();
@ -360,58 +328,6 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck()
void LocalDerivationGoal::startBuilder()
{
if ((buildUser && buildUser->getUIDCount() != 1)
#if __linux__
|| settings.useCgroups
#endif
)
{
#if __linux__
experimentalFeatureSettings.require(Xp::Cgroups);
auto cgroupFS = getCgroupFS();
if (!cgroupFS)
throw Error("cannot determine the cgroups file system");
auto ourCgroups = getCgroups("/proc/self/cgroup");
auto ourCgroup = ourCgroups[""];
if (ourCgroup == "")
throw Error("cannot determine cgroup name from /proc/self/cgroup");
auto ourCgroupPath = canonPath(*cgroupFS + "/" + ourCgroup);
if (!pathExists(ourCgroupPath))
throw Error("expected cgroup directory '%s'", ourCgroupPath);
static std::atomic<unsigned int> counter{0};
cgroup = buildUser
? fmt("%s/nix-build-uid-%d", ourCgroupPath, buildUser->getUID())
: fmt("%s/nix-build-pid-%d-%d", ourCgroupPath, getpid(), counter++);
debug("using cgroup '%s'", *cgroup);
/* When using a build user, record the cgroup we used for that
user so that if we got interrupted previously, we can kill
any left-over cgroup first. */
if (buildUser) {
auto cgroupsDir = settings.nixStateDir + "/cgroups";
createDirs(cgroupsDir);
auto cgroupFile = fmt("%s/%d", cgroupsDir, buildUser->getUID());
if (pathExists(cgroupFile)) {
auto prevCgroup = readFile(cgroupFile);
destroyCgroup(prevCgroup);
}
writeFile(cgroupFile, *cgroup);
}
#else
throw Error("cgroups are not supported on this platform");
#endif
}
/* Make sure that no other processes are executing under the
sandbox uids. This must be done before any chownToBuilder()
@ -427,10 +343,6 @@ void LocalDerivationGoal::startBuilder()
settings.thisSystem,
concatStringsSep<StringSet>(", ", worker.store.systemFeatures));
#if __APPLE__
additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or("");
#endif
/* Create a temporary directory where the build will take
place. */
tmpDir = createTempDir("", "nix-build-" + std::string(drvPath.name()), false, false, 0700);
@ -752,11 +664,7 @@ pid_t LocalDerivationGoal::startChild(std::function<void()> openSlave) {
void LocalDerivationGoal::initTmpDir() {
/* In a sandbox, for determinism, always use the same temporary
directory. */
#if __linux__
tmpDirInSandbox = useChroot ? settings.sandboxBuildDir : tmpDir;
#else
tmpDirInSandbox = tmpDir;
#endif
tmpDirInSandbox = findTmpDirInSandbox();
/* In non-structured mode, add all bindings specified in the
derivation via the environment, except those listed in the

View file

@ -96,11 +96,6 @@ struct LocalDerivationGoal : public DerivationGoal
typedef map<std::string, std::string> Environment;
Environment env;
#if __APPLE__
typedef std::string SandboxProfile;
SandboxProfile additionalSandboxProfile;
#endif
/**
* Hash rewriting.
*/
@ -217,7 +212,7 @@ struct LocalDerivationGoal : public DerivationGoal
/**
* Whether we need to perform hash rewriting if there are valid output paths.
*/
bool needsHashRewrite();
virtual bool needsHashRewrite();
/**
* The additional states.
@ -341,6 +336,22 @@ protected:
*/
virtual void materialisePath(const StorePath & path);
/**
* Early build preparation, right after build user has been
* allocated but before that user's processes have been killed.
* This should be used to check OS-specific flags
* and prepare to kill the build user's process
*/
virtual void preStartBuilder(){};
/**
* Calculate the value for tmpDirInSandbox. Called once, in `initTmpDir`.
*/
virtual Path findTmpDirInSandbox()
{
return tmpDir;
};
/**
* Setup dependencies outside the sandbox.
* Called in the parent nix process.
@ -371,12 +382,22 @@ protected:
virtual void execBuilder(std::string builder, Strings args, Strings envStrs);
/**
* Whether derivation feature `uid-range` is supported
* Whether derivation can be built on current platform with `uid-range` feature
*/
virtual bool supportsUidRange()
{
return false;
}
/**
* Whether derivation can be built on current platform with a diverted store
* (Store path appears at different path for derivation than on host,
* e.g. used in nixos-install)
*/
virtual bool supportsDivertedStore()
{
return false;
};
};
}

View file

@ -228,6 +228,19 @@ void DarwinLocalStore::findPlatformRoots(UncheckedRoots & unchecked)
}
}
void DarwinLocalDerivationGoal::preStartBuilder()
{
additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or("");
if (settings.sandboxMode == smEnabled && additionalSandboxProfile != "") {
throw Error(
"derivation '%s' specifies a sandbox profile, "
"but this is only allowed when 'sandbox' is 'relaxed'",
worker.store.printStorePath(drvPath)
);
}
}
void DarwinLocalDerivationGoal::enterSandbox()
{
/* This has to appear before import statements. */

View file

@ -42,10 +42,35 @@ public:
using LocalDerivationGoal::LocalDerivationGoal;
private:
// All work happens in enterSandbox and execBuilder
typedef std::string SandboxProfile;
SandboxProfile additionalSandboxProfile;
/**
* Validate and set additionalSandboxProfile
*/
void preStartBuilder() override;
/**
* Prepare the sandbox: This is empty on Darwin since sandbox setup happens in
* enterSandbox
*/
void prepareSandbox() override{};
/**
* Create a sandbox profile as a string, then enter it using `sandbox_init_with_parameters`
*/
void enterSandbox() override;
/**
* Set process flags to enter or leave rosetta, then execute the builder
*/
void execBuilder(std::string builder, Strings args, Strings envStrs) override;
bool needsHashRewrite() override
{
/* Darwin requires hash rewriting even when sandboxing is enabled. */
return true;
}
};
}

View file

@ -1,10 +1,11 @@
#include "build/worker.hh"
#include "cgroup.hh"
#include "finally.hh"
#include "gc-store.hh"
#include "namespaces.hh"
#include "signals.hh"
#include "platform/linux/linux.hh"
#include "regex.hh"
#include "signals.hh"
#include <regex>
@ -207,6 +208,74 @@ void LinuxLocalDerivationGoal::materialisePath(const StorePath & path)
}
}
void LinuxLocalDerivationGoal::preStartBuilder()
{
if (useChroot) {
if (!mountAndPidNamespacesSupported()) {
if (!settings.sandboxFallback) {
throw Error(
"this system does not support the kernel namespaces that are required for "
"sandboxing; use '--no-sandbox' to disable sandboxing"
);
}
debug("auto-disabling sandboxing because the prerequisite namespaces are not available"
);
useChroot = false;
}
}
// Prepare cgroups here since we might reuse one from a previous build
if ((buildUser && buildUser->getUIDCount() != 1) || settings.useCgroups) {
experimentalFeatureSettings.require(Xp::Cgroups);
auto cgroupFS = getCgroupFS();
if (!cgroupFS) {
throw Error("cannot determine the cgroups file system");
}
auto ourCgroups = getCgroups("/proc/self/cgroup");
auto ourCgroup = ourCgroups[""];
if (ourCgroup == "") {
throw Error("cannot determine cgroup name from /proc/self/cgroup");
}
auto ourCgroupPath = canonPath(*cgroupFS + "/" + ourCgroup);
if (!pathExists(ourCgroupPath)) {
throw Error("expected cgroup directory '%s'", ourCgroupPath);
}
static std::atomic<unsigned int> counter{0};
cgroup = buildUser ? fmt("%s/nix-build-uid-%d", ourCgroupPath, buildUser->getUID())
: fmt("%s/nix-build-pid-%d-%d", ourCgroupPath, getpid(), counter++);
debug("using cgroup '%s'", *cgroup);
/* When using a build user, record the cgroup we used for that
user so that if we got interrupted previously, we can kill
any left-over cgroup first. */
if (buildUser) {
auto cgroupsDir = settings.nixStateDir + "/cgroups";
createDirs(cgroupsDir);
auto cgroupFile = fmt("%s/%d", cgroupsDir, buildUser->getUID());
if (pathExists(cgroupFile)) {
auto prevCgroup = readFile(cgroupFile);
destroyCgroup(prevCgroup);
}
writeFile(cgroupFile, *cgroup);
}
}
}
Path LinuxLocalDerivationGoal::findTmpDirInSandbox()
{
return useChroot ? settings.sandboxBuildDir : tmpDir;
}
pid_t LinuxLocalDerivationGoal::startChild(std::function<void()> openSlave)
{
// If we're not sandboxing no need to faff about
@ -909,4 +978,19 @@ void LinuxLocalDerivationGoal::enterSandbox()
throw SysError("PR_SET_NO_NEW_PRIVS failed");
}
}
void LinuxLocalDerivationGoal::killSandbox(bool getStats)
{
if (cgroup) {
auto stats = destroyCgroup(*cgroup);
if (getStats) {
buildResult.cpuUser = stats.cpuUser;
buildResult.cpuSystem = stats.cpuSystem;
}
} else if (buildUser) {
auto uid = buildUser->getUID();
assert(uid != 0);
killUser(uid);
}
}
}

View file

@ -43,14 +43,23 @@ public:
private:
void materialisePath(const StorePath & path) override;
void preStartBuilder() override;
Path findTmpDirInSandbox() override;
void prepareSandbox() override;
pid_t startChild(std::function<void()> openSlave) override;
void enterSandbox() override;
void killSandbox(bool getStatus) override;
bool supportsUidRange() override
{
return true;
}
bool supportsDivertedStore() override
{
return true;
}
};
}