forked from lix-project/lix
Compare commits
11 commits
f4dd67e436
...
7685a0f5e0
Author | SHA1 | Date | |
---|---|---|---|
alois31 | 7685a0f5e0 | ||
alois31 | 4e6306e130 | ||
eldritch horrors | ce6cb14995 | ||
eldritch horrors | 3d155fc509 | ||
eldritch horrors | b43a2e84c4 | ||
eldritch horrors | a9949f4760 | ||
eldritch horrors | 39a1e248c9 | ||
Artemis Tosini | f80d95e36d | ||
Artemis Tosini | 12f5d27363 | ||
Qyriad | fb8553f63c | ||
Qyriad | e09cc60df9 |
|
@ -4,6 +4,7 @@
|
||||||
#include "primops.hh"
|
#include "primops.hh"
|
||||||
#include "print-options.hh"
|
#include "print-options.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
|
#include "suggestions.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
|
@ -1426,11 +1427,13 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
|
||||||
|
|
||||||
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value vTmp;
|
Value vFirst;
|
||||||
PosIdx pos2;
|
|
||||||
Value * vAttrs = &vTmp;
|
|
||||||
|
|
||||||
e->eval(state, env, vTmp);
|
// Pointer to the current attrset Value in this select chain.
|
||||||
|
Value * vCurrent = &vFirst;
|
||||||
|
// Position for the current attrset Value in this select chain.
|
||||||
|
PosIdx posCurrent;
|
||||||
|
e->eval(state, env, vFirst);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto dts = state.debugRepl
|
auto dts = state.debugRepl
|
||||||
|
@ -1443,48 +1446,75 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
showAttrPath(state, env, attrPath))
|
showAttrPath(state, env, attrPath))
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
|
||||||
for (auto & i : attrPath) {
|
for (auto const & currentAttrName : attrPath) {
|
||||||
state.nrLookups++;
|
state.nrLookups++;
|
||||||
Bindings::iterator j;
|
|
||||||
auto name = getName(i, state, env);
|
Symbol const name = getName(currentAttrName, state, env);
|
||||||
if (def) {
|
|
||||||
state.forceValue(*vAttrs, pos);
|
state.forceValue(*vCurrent, pos);
|
||||||
if (vAttrs->type() != nAttrs ||
|
|
||||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
if (vCurrent->type() != nAttrs) {
|
||||||
{
|
|
||||||
def->eval(state, env, v);
|
// If we have an `or` provided default,
|
||||||
|
// then this is allowed to not be an attrset.
|
||||||
|
if (def != nullptr) {
|
||||||
|
this->def->eval(state, env, v);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
state.forceAttrs(*vAttrs, pos, "while selecting an attribute");
|
// Otherwise, we must type error.
|
||||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
state.error<TypeError>(
|
||||||
std::set<std::string> allAttrNames;
|
"expected a set but found %s: %s",
|
||||||
for (auto & attr : *vAttrs->attrs)
|
showType(*vCurrent),
|
||||||
allAttrNames.insert(state.symbols[attr.name]);
|
ValuePrinter(state, *vCurrent, errorPrintOptions)
|
||||||
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
|
).withTrace(pos, "while selecting an attribute").debugThrow();
|
||||||
state.error<EvalError>("attribute '%1%' missing", state.symbols[name])
|
|
||||||
.atPos(pos).withSuggestions(suggestions).withFrame(env, *this).debugThrow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
vAttrs = j->value;
|
|
||||||
pos2 = j->pos;
|
// Now that we know this is actually an attrset, try to find an attr
|
||||||
if (state.countCalls) state.attrSelects[pos2]++;
|
// with the selected name.
|
||||||
|
Bindings::iterator attrIt = vCurrent->attrs->find(name);
|
||||||
|
if (attrIt == vCurrent->attrs->end()) {
|
||||||
|
|
||||||
|
// If we have an `or` provided default, then we'll use that.
|
||||||
|
if (def != nullptr) {
|
||||||
|
this->def->eval(state, env, v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, missing attr error.
|
||||||
|
std::set<std::string> allAttrNames;
|
||||||
|
for (auto const & attr : *vCurrent->attrs) {
|
||||||
|
allAttrNames.insert(state.symbols[attr.name]);
|
||||||
|
}
|
||||||
|
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
|
||||||
|
state.error<EvalError>("attribute '%s' missing", state.symbols[name])
|
||||||
|
.atPos(pos)
|
||||||
|
.withSuggestions(suggestions)
|
||||||
|
.withFrame(env, *this)
|
||||||
|
.debugThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're here, then we successfully found the attribute.
|
||||||
|
// Set our currently operated-on attrset to this one, and keep going.
|
||||||
|
vCurrent = attrIt->value;
|
||||||
|
posCurrent = attrIt->pos;
|
||||||
|
if (state.countCalls) state.attrSelects[posCurrent]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) );
|
state.forceValue(*vCurrent, (posCurrent ? posCurrent : this->pos));
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
if (pos2) {
|
if (posCurrent) {
|
||||||
auto pos2r = state.positions[pos2];
|
auto pos2r = state.positions[posCurrent];
|
||||||
auto origin = std::get_if<SourcePath>(&pos2r.origin);
|
auto origin = std::get_if<SourcePath>(&pos2r.origin);
|
||||||
if (!(origin && *origin == state.derivationInternal))
|
if (!(origin && *origin == state.derivationInternal))
|
||||||
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
|
state.addErrorTrace(e, posCurrent, "while evaluating the attribute '%1%'",
|
||||||
showAttrPath(state, env, attrPath));
|
showAttrPath(state, env, attrPath));
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
v = *vAttrs;
|
v = *vCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -157,8 +157,18 @@ struct ExprInheritFrom : ExprVar
|
||||||
struct ExprSelect : Expr
|
struct ExprSelect : Expr
|
||||||
{
|
{
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
std::unique_ptr<Expr> e, def;
|
|
||||||
|
/** The expression attributes are being selected on. e.g. `foo` in `foo.bar.baz`. */
|
||||||
|
std::unique_ptr<Expr> e;
|
||||||
|
|
||||||
|
/** A default value specified with `or`, if the selected attr doesn't exist.
|
||||||
|
* e.g. `bix` in `foo.bar.baz or bix`
|
||||||
|
*/
|
||||||
|
std::unique_ptr<Expr> def;
|
||||||
|
|
||||||
|
/** The path of attributes being selected. e.g. `bar.baz` in `foo.bar.baz.` */
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
|
|
||||||
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, AttrPath attrPath, std::unique_ptr<Expr> def) : pos(pos), e(std::move(e)), def(std::move(def)), attrPath(std::move(attrPath)) { };
|
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, AttrPath attrPath, std::unique_ptr<Expr> def) : pos(pos), e(std::move(e)), def(std::move(def)), attrPath(std::move(attrPath)) { };
|
||||||
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, Symbol name) : pos(pos), e(std::move(e)) { attrPath.push_back(AttrName(name)); };
|
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, Symbol name) : pos(pos), e(std::move(e)) { attrPath.push_back(AttrName(name)); };
|
||||||
PosIdx getPos() const override { return pos; }
|
PosIdx getPos() const override { return pos; }
|
||||||
|
|
|
@ -378,7 +378,7 @@ RunPager::RunPager()
|
||||||
RunPager::~RunPager()
|
RunPager::~RunPager()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (pid != -1) {
|
if (pid) {
|
||||||
std::cout.flush();
|
std::cout.flush();
|
||||||
dup2(std_out, STDOUT_FILENO);
|
dup2(std_out, STDOUT_FILENO);
|
||||||
pid.wait();
|
pid.wait();
|
||||||
|
|
|
@ -79,7 +79,7 @@ HookInstance::~HookInstance()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
toHook.writeSide.reset();
|
toHook.writeSide.reset();
|
||||||
if (pid != -1) pid.kill();
|
if (pid) pid.kill();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,9 +50,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
#include <spawn.h>
|
|
||||||
#include <sys/sysctl.h>
|
|
||||||
|
|
||||||
/* This definition is undocumented but depended upon by all major browsers. */
|
/* This definition is undocumented but depended upon by all major browsers. */
|
||||||
extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
|
extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
|
||||||
#endif
|
#endif
|
||||||
|
@ -139,7 +136,7 @@ LocalStore & LocalDerivationGoal::getLocalStore()
|
||||||
|
|
||||||
void LocalDerivationGoal::killChild()
|
void LocalDerivationGoal::killChild()
|
||||||
{
|
{
|
||||||
if (pid != -1) {
|
if (pid) {
|
||||||
worker.childTerminated(this);
|
worker.childTerminated(this);
|
||||||
|
|
||||||
/* If we're using a build user, then there is a tricky race
|
/* If we're using a build user, then there is a tricky race
|
||||||
|
@ -147,7 +144,7 @@ void LocalDerivationGoal::killChild()
|
||||||
done its setuid() to the build user uid, then it won't be
|
done its setuid() to the build user uid, then it won't be
|
||||||
killed, and we'll potentially lock up in pid.wait(). So
|
killed, and we'll potentially lock up in pid.wait(). So
|
||||||
also send a conventional kill to the child. */
|
also send a conventional kill to the child. */
|
||||||
::kill(-pid, SIGKILL); /* ignore the result */
|
::kill(-pid.get(), SIGKILL); /* ignore the result */
|
||||||
|
|
||||||
killSandbox(true);
|
killSandbox(true);
|
||||||
|
|
||||||
|
@ -160,19 +157,7 @@ void LocalDerivationGoal::killChild()
|
||||||
|
|
||||||
void LocalDerivationGoal::killSandbox(bool getStats)
|
void LocalDerivationGoal::killSandbox(bool getStats)
|
||||||
{
|
{
|
||||||
if (cgroup) {
|
if (buildUser) {
|
||||||
#if __linux__
|
|
||||||
auto stats = destroyCgroup(*cgroup);
|
|
||||||
if (getStats) {
|
|
||||||
buildResult.cpuUser = stats.cpuUser;
|
|
||||||
buildResult.cpuSystem = stats.cpuSystem;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
abort();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (buildUser) {
|
|
||||||
auto uid = buildUser->getUID();
|
auto uid = buildUser->getUID();
|
||||||
assert(uid != 0);
|
assert(uid != 0);
|
||||||
killUser(uid);
|
killUser(uid);
|
||||||
|
@ -944,7 +929,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
if (usingUserNamespace)
|
if (usingUserNamespace)
|
||||||
options.cloneFlags |= CLONE_NEWUSER;
|
options.cloneFlags |= CLONE_NEWUSER;
|
||||||
|
|
||||||
pid_t child = startProcess([&]() { runChild(); }, options);
|
pid_t child = startProcess([&]() { runChild(); }, options).release();
|
||||||
|
|
||||||
writeFull(sendPid.writeSide.get(), fmt("%d\n", child));
|
writeFull(sendPid.writeSide.get(), fmt("%d\n", child));
|
||||||
_exit(0);
|
_exit(0);
|
||||||
|
@ -965,7 +950,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
|
|
||||||
auto ss = tokenizeString<std::vector<std::string>>(readLine(sendPid.readSide.get()));
|
auto ss = tokenizeString<std::vector<std::string>>(readLine(sendPid.readSide.get()));
|
||||||
assert(ss.size() == 1);
|
assert(ss.size() == 1);
|
||||||
pid = string2Int<pid_t>(ss[0]).value();
|
pid = Pid{string2Int<pid_t>(ss[0]).value()};
|
||||||
|
|
||||||
if (usingUserNamespace) {
|
if (usingUserNamespace) {
|
||||||
/* Set the UID/GID mapping of the builder's user namespace
|
/* Set the UID/GID mapping of the builder's user namespace
|
||||||
|
@ -975,13 +960,13 @@ void LocalDerivationGoal::startBuilder()
|
||||||
uid_t hostGid = buildUser ? buildUser->getGID() : getgid();
|
uid_t hostGid = buildUser ? buildUser->getGID() : getgid();
|
||||||
uid_t nrIds = buildUser ? buildUser->getUIDCount() : 1;
|
uid_t nrIds = buildUser ? buildUser->getUIDCount() : 1;
|
||||||
|
|
||||||
writeFile("/proc/" + std::to_string(pid) + "/uid_map",
|
writeFile("/proc/" + std::to_string(pid.get()) + "/uid_map",
|
||||||
fmt("%d %d %d", sandboxUid(), hostUid, nrIds));
|
fmt("%d %d %d", sandboxUid(), hostUid, nrIds));
|
||||||
|
|
||||||
if (!buildUser || buildUser->getUIDCount() == 1)
|
if (!buildUser || buildUser->getUIDCount() == 1)
|
||||||
writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny");
|
writeFile("/proc/" + std::to_string(pid.get()) + "/setgroups", "deny");
|
||||||
|
|
||||||
writeFile("/proc/" + std::to_string(pid) + "/gid_map",
|
writeFile("/proc/" + std::to_string(pid.get()) + "/gid_map",
|
||||||
fmt("%d %d %d", sandboxGid(), hostGid, nrIds));
|
fmt("%d %d %d", sandboxGid(), hostGid, nrIds));
|
||||||
} else {
|
} else {
|
||||||
debug("note: not using a user namespace");
|
debug("note: not using a user namespace");
|
||||||
|
@ -1004,19 +989,19 @@ void LocalDerivationGoal::startBuilder()
|
||||||
|
|
||||||
/* Save the mount- and user namespace of the child. We have to do this
|
/* Save the mount- and user namespace of the child. We have to do this
|
||||||
*before* the child does a chroot. */
|
*before* the child does a chroot. */
|
||||||
sandboxMountNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY)};
|
sandboxMountNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/mnt", pid.get()).c_str(), O_RDONLY)};
|
||||||
if (sandboxMountNamespace.get() == -1)
|
if (sandboxMountNamespace.get() == -1)
|
||||||
throw SysError("getting sandbox mount namespace");
|
throw SysError("getting sandbox mount namespace");
|
||||||
|
|
||||||
if (usingUserNamespace) {
|
if (usingUserNamespace) {
|
||||||
sandboxUserNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY)};
|
sandboxUserNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/user", pid.get()).c_str(), O_RDONLY)};
|
||||||
if (sandboxUserNamespace.get() == -1)
|
if (sandboxUserNamespace.get() == -1)
|
||||||
throw SysError("getting sandbox user namespace");
|
throw SysError("getting sandbox user namespace");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Move the child into its own cgroup. */
|
/* Move the child into its own cgroup. */
|
||||||
if (cgroup)
|
if (cgroup)
|
||||||
writeFile(*cgroup + "/cgroup.procs", fmt("%d", (pid_t) pid));
|
writeFile(*cgroup + "/cgroup.procs", fmt("%d", pid.get()));
|
||||||
|
|
||||||
/* Signal the builder that we've updated its user namespace. */
|
/* Signal the builder that we've updated its user namespace. */
|
||||||
writeFull(userNamespaceSync.writeSide.get(), "1");
|
writeFull(userNamespaceSync.writeSide.get(), "1");
|
||||||
|
@ -1584,7 +1569,7 @@ void LocalDerivationGoal::addDependency(const StorePath & path)
|
||||||
entering its mount namespace, which is not possible
|
entering its mount namespace, which is not possible
|
||||||
in multithreaded programs. So we do this in a
|
in multithreaded programs. So we do this in a
|
||||||
child process.*/
|
child process.*/
|
||||||
Pid child(startProcess([&]() {
|
Pid child = startProcess([&]() {
|
||||||
|
|
||||||
if (usingUserNamespace && (setns(sandboxUserNamespace.get(), 0) == -1))
|
if (usingUserNamespace && (setns(sandboxUserNamespace.get(), 0) == -1))
|
||||||
throw SysError("entering sandbox user namespace");
|
throw SysError("entering sandbox user namespace");
|
||||||
|
@ -1595,7 +1580,7 @@ void LocalDerivationGoal::addDependency(const StorePath & path)
|
||||||
doBind(source, target);
|
doBind(source, target);
|
||||||
|
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}));
|
});
|
||||||
|
|
||||||
int status = child.wait();
|
int status = child.wait();
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
|
@ -2672,31 +2657,8 @@ void LocalDerivationGoal::runChild()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __APPLE__
|
execBuilder(drv->builder, args, envStrs);
|
||||||
posix_spawnattr_t attrp;
|
// execBuilder should not return
|
||||||
|
|
||||||
if (posix_spawnattr_init(&attrp))
|
|
||||||
throw SysError("failed to initialize builder");
|
|
||||||
|
|
||||||
if (posix_spawnattr_setflags(&attrp, POSIX_SPAWN_SETEXEC))
|
|
||||||
throw SysError("failed to initialize builder");
|
|
||||||
|
|
||||||
if (drv->platform == "aarch64-darwin") {
|
|
||||||
// Unset kern.curproc_arch_affinity so we can escape Rosetta
|
|
||||||
int affinity = 0;
|
|
||||||
sysctlbyname("kern.curproc_arch_affinity", NULL, NULL, &affinity, sizeof(affinity));
|
|
||||||
|
|
||||||
cpu_type_t cpu = CPU_TYPE_ARM64;
|
|
||||||
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
|
||||||
} else if (drv->platform == "x86_64-darwin") {
|
|
||||||
cpu_type_t cpu = CPU_TYPE_X86_64;
|
|
||||||
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
|
||||||
#else
|
|
||||||
execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
throw SysError("executing '%1%'", drv->builder);
|
throw SysError("executing '%1%'", drv->builder);
|
||||||
|
|
||||||
|
@ -2712,6 +2674,11 @@ void LocalDerivationGoal::runChild()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LocalDerivationGoal::execBuilder(std::string builder, Strings args, Strings envStrs)
|
||||||
|
{
|
||||||
|
execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
||||||
{
|
{
|
||||||
|
|
|
@ -178,7 +178,28 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
|
|
||||||
friend struct RestrictedStore;
|
friend struct RestrictedStore;
|
||||||
|
|
||||||
using DerivationGoal::DerivationGoal;
|
/**
|
||||||
|
* Create a LocalDerivationGoal without an on-disk .drv file,
|
||||||
|
* possibly a platform-specific subclass
|
||||||
|
*/
|
||||||
|
static std::shared_ptr<LocalDerivationGoal> makeLocalDerivationGoal(
|
||||||
|
const StorePath & drvPath,
|
||||||
|
const OutputsSpec & wantedOutputs,
|
||||||
|
Worker & worker,
|
||||||
|
BuildMode buildMode
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a LocalDerivationGoal for an on-disk .drv file,
|
||||||
|
* possibly a platform-specific subclass
|
||||||
|
*/
|
||||||
|
static std::shared_ptr<LocalDerivationGoal> makeLocalDerivationGoal(
|
||||||
|
const StorePath & drvPath,
|
||||||
|
const BasicDerivation & drv,
|
||||||
|
const OutputsSpec & wantedOutputs,
|
||||||
|
Worker & worker,
|
||||||
|
BuildMode buildMode
|
||||||
|
);
|
||||||
|
|
||||||
virtual ~LocalDerivationGoal() noexcept(false) override;
|
virtual ~LocalDerivationGoal() noexcept(false) override;
|
||||||
|
|
||||||
|
@ -282,7 +303,7 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
* Kill any processes running under the build user UID or in the
|
* Kill any processes running under the build user UID or in the
|
||||||
* cgroup of the build.
|
* cgroup of the build.
|
||||||
*/
|
*/
|
||||||
void killSandbox(bool getStats);
|
virtual void killSandbox(bool getStats);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create alternative path calculated from but distinct from the
|
* Create alternative path calculated from but distinct from the
|
||||||
|
@ -299,6 +320,16 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
* rewrites caught everything
|
* rewrites caught everything
|
||||||
*/
|
*/
|
||||||
StorePath makeFallbackPath(OutputNameView outputName);
|
StorePath makeFallbackPath(OutputNameView outputName);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using DerivationGoal::DerivationGoal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the builder, replacing the current process.
|
||||||
|
* Generally this means an `execve` call.
|
||||||
|
*/
|
||||||
|
virtual void execBuilder(std::string builder, Strings args, Strings envStrs);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,6 +217,7 @@ void PathSubstitutionGoal::tryToRun()
|
||||||
promise = std::promise<void>();
|
promise = std::promise<void>();
|
||||||
|
|
||||||
thr = std::thread([this]() {
|
thr = std::thread([this]() {
|
||||||
|
auto & fetchPath = subPath ? *subPath : storePath;
|
||||||
try {
|
try {
|
||||||
ReceiveInterrupts receiveInterrupts;
|
ReceiveInterrupts receiveInterrupts;
|
||||||
|
|
||||||
|
@ -226,10 +227,17 @@ void PathSubstitutionGoal::tryToRun()
|
||||||
Activity act(*logger, actSubstitute, Logger::Fields{worker.store.printStorePath(storePath), sub->getUri()});
|
Activity act(*logger, actSubstitute, Logger::Fields{worker.store.printStorePath(storePath), sub->getUri()});
|
||||||
PushActivity pact(act.id);
|
PushActivity pact(act.id);
|
||||||
|
|
||||||
copyStorePath(*sub, worker.store,
|
copyStorePath(
|
||||||
subPath ? *subPath : storePath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs);
|
*sub, worker.store, fetchPath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs
|
||||||
|
);
|
||||||
|
|
||||||
promise.set_value();
|
promise.set_value();
|
||||||
|
} catch (const EndOfFile &) {
|
||||||
|
promise.set_exception(std::make_exception_ptr(EndOfFile(
|
||||||
|
"NAR for '%s' fetched from '%s' is incomplete",
|
||||||
|
sub->printStorePath(fetchPath),
|
||||||
|
sub->getUri()
|
||||||
|
)));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
promise.set_exception(std::current_exception());
|
promise.set_exception(std::current_exception());
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,8 +65,8 @@ std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drv
|
||||||
{
|
{
|
||||||
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
||||||
return !dynamic_cast<LocalStore *>(&store)
|
return !dynamic_cast<LocalStore *>(&store)
|
||||||
? std::make_shared</* */DerivationGoal>(drvPath, wantedOutputs, *this, buildMode)
|
? std::make_shared<DerivationGoal>(drvPath, wantedOutputs, *this, buildMode)
|
||||||
: std::make_shared<LocalDerivationGoal>(drvPath, wantedOutputs, *this, buildMode);
|
: LocalDerivationGoal::makeLocalDerivationGoal(drvPath, wantedOutputs, *this, buildMode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,8 +76,8 @@ std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath
|
||||||
{
|
{
|
||||||
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
||||||
return !dynamic_cast<LocalStore *>(&store)
|
return !dynamic_cast<LocalStore *>(&store)
|
||||||
? std::make_shared</* */DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode)
|
? std::make_shared<DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode)
|
||||||
: std::make_shared<LocalDerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode);
|
: LocalDerivationGoal::makeLocalDerivationGoal(drvPath, drv, wantedOutputs, *this, buildMode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
#include "build/local-derivation-goal.hh"
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
#include "platform/linux.hh"
|
#include "platform/linux.hh"
|
||||||
|
@ -19,4 +20,43 @@ std::shared_ptr<LocalStore> LocalStore::makeLocalStore(const Params & params)
|
||||||
return std::shared_ptr<LocalStore>(new FallbackLocalStore(params));
|
return std::shared_ptr<LocalStore>(new FallbackLocalStore(params));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<LocalDerivationGoal> LocalDerivationGoal::makeLocalDerivationGoal(
|
||||||
|
const StorePath & drvPath,
|
||||||
|
const OutputsSpec & wantedOutputs,
|
||||||
|
Worker & worker,
|
||||||
|
BuildMode buildMode
|
||||||
|
)
|
||||||
|
{
|
||||||
|
#if __linux__
|
||||||
|
return std::make_shared<LinuxLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
|
||||||
|
#elif __APPLE__
|
||||||
|
return std::make_shared<DarwinLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
|
||||||
|
#else
|
||||||
|
return std::make_shared<FallbackLocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<LocalDerivationGoal> LocalDerivationGoal::makeLocalDerivationGoal(
|
||||||
|
const StorePath & drvPath,
|
||||||
|
const BasicDerivation & drv,
|
||||||
|
const OutputsSpec & wantedOutputs,
|
||||||
|
Worker & worker,
|
||||||
|
BuildMode buildMode
|
||||||
|
)
|
||||||
|
{
|
||||||
|
#if __linux__
|
||||||
|
return std::make_shared<LinuxLocalDerivationGoal>(
|
||||||
|
drvPath, drv, wantedOutputs, worker, buildMode
|
||||||
|
);
|
||||||
|
#elif __APPLE__
|
||||||
|
return std::make_shared<DarwinLocalDerivationGoal>(
|
||||||
|
drvPath, drv, wantedOutputs, worker, buildMode
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
return std::make_shared<FallbackLocalDerivationGoal>(
|
||||||
|
drvPath, drv, wantedOutputs, worker, buildMode
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <sys/proc_info.h>
|
#include <sys/proc_info.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <libproc.h>
|
#include <libproc.h>
|
||||||
|
#include <spawn.h>
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
|
@ -220,4 +221,29 @@ void DarwinLocalStore::findPlatformRoots(UncheckedRoots & unchecked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DarwinLocalDerivationGoal::execBuilder(std::string builder, Strings args, Strings envStrs)
|
||||||
|
{
|
||||||
|
posix_spawnattr_t attrp;
|
||||||
|
|
||||||
|
if (posix_spawnattr_init(&attrp))
|
||||||
|
throw SysError("failed to initialize builder");
|
||||||
|
|
||||||
|
if (posix_spawnattr_setflags(&attrp, POSIX_SPAWN_SETEXEC))
|
||||||
|
throw SysError("failed to initialize builder");
|
||||||
|
|
||||||
|
if (drv->platform == "aarch64-darwin") {
|
||||||
|
// Unset kern.curproc_arch_affinity so we can escape Rosetta
|
||||||
|
int affinity = 0;
|
||||||
|
sysctlbyname("kern.curproc_arch_affinity", NULL, NULL, &affinity, sizeof(affinity));
|
||||||
|
|
||||||
|
cpu_type_t cpu = CPU_TYPE_ARM64;
|
||||||
|
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
||||||
|
} else if (drv->platform == "x86_64-darwin") {
|
||||||
|
cpu_type_t cpu = CPU_TYPE_X86_64;
|
||||||
|
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#include "build/local-derivation-goal.hh"
|
||||||
#include "gc-store.hh"
|
#include "gc-store.hh"
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
|
||||||
|
@ -32,4 +33,19 @@ private:
|
||||||
void findPlatformRoots(UncheckedRoots & unchecked) override;
|
void findPlatformRoots(UncheckedRoots & unchecked) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Darwin-specific implementation of LocalDerivationGoal
|
||||||
|
*/
|
||||||
|
class DarwinLocalDerivationGoal : public LocalDerivationGoal
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using LocalDerivationGoal::LocalDerivationGoal;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Set process flags to enter or leave rosetta, then execute the builder
|
||||||
|
*/
|
||||||
|
void execBuilder(std::string builder, Strings args, Strings envStrs) override;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#include "build/local-derivation-goal.hh"
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -28,4 +29,14 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fallback platform implementation of LocalDerivationGoal
|
||||||
|
* Exists so we can make LocalDerivationGoal constructor protected
|
||||||
|
*/
|
||||||
|
class FallbackLocalDerivationGoal : public LocalDerivationGoal
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using LocalDerivationGoal::LocalDerivationGoal;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "cgroup.hh"
|
||||||
#include "gc-store.hh"
|
#include "gc-store.hh"
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
#include "platform/linux.hh"
|
#include "platform/linux.hh"
|
||||||
|
@ -114,4 +115,17 @@ void LinuxLocalStore::findPlatformRoots(UncheckedRoots & unchecked)
|
||||||
readFileRoots("/proc/sys/kernel/fbsplash", unchecked);
|
readFileRoots("/proc/sys/kernel/fbsplash", unchecked);
|
||||||
readFileRoots("/proc/sys/kernel/poweroff_cmd", unchecked);
|
readFileRoots("/proc/sys/kernel/poweroff_cmd", unchecked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LinuxLocalDerivationGoal::killSandbox(bool getStats)
|
||||||
|
{
|
||||||
|
if (cgroup) {
|
||||||
|
auto stats = destroyCgroup(*cgroup);
|
||||||
|
if (getStats) {
|
||||||
|
buildResult.cpuUser = stats.cpuUser;
|
||||||
|
buildResult.cpuSystem = stats.cpuSystem;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LocalDerivationGoal::killSandbox(getStats);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#include "build/local-derivation-goal.hh"
|
||||||
#include "gc-store.hh"
|
#include "gc-store.hh"
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
|
||||||
|
@ -32,4 +33,16 @@ private:
|
||||||
void findPlatformRoots(UncheckedRoots & unchecked) override;
|
void findPlatformRoots(UncheckedRoots & unchecked) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linux-specific implementation of LocalDerivationGoal
|
||||||
|
*/
|
||||||
|
class LinuxLocalDerivationGoal : public LocalDerivationGoal
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using LocalDerivationGoal::LocalDerivationGoal;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void killSandbox(bool getStatus) override;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ Path SSHMaster::startMaster()
|
||||||
|
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
|
|
||||||
if (state->sshMaster != -1) return state->socketPath;
|
if (state->sshMaster) return state->socketPath;
|
||||||
|
|
||||||
state->socketPath = (Path) *state->tmpDir + "/ssh.sock";
|
state->socketPath = (Path) *state->tmpDir + "/ssh.sock";
|
||||||
|
|
||||||
|
|
|
@ -1072,8 +1072,6 @@ void copyStorePath(
|
||||||
});
|
});
|
||||||
TeeSink tee { sink, progressSink };
|
TeeSink tee { sink, progressSink };
|
||||||
srcStore.narFromPath(storePath, tee);
|
srcStore.narFromPath(storePath, tee);
|
||||||
}, [&]() {
|
|
||||||
throw EndOfFile("NAR for '%s' fetched from '%s' is incomplete", srcStore.printStorePath(storePath), srcStore.getUri());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
dstStore.addToStore(*info, *source, repair, checkSigs);
|
dstStore.addToStore(*info, *source, repair, checkSigs);
|
||||||
|
|
|
@ -94,12 +94,7 @@ bool userNamespacesSupported()
|
||||||
static auto res = [&]() -> bool
|
static auto res = [&]() -> bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Pid pid = startProcess([&]()
|
Pid pid = startProcess([&]() { _exit(0); }, {.cloneFlags = CLONE_NEWUSER});
|
||||||
{
|
|
||||||
_exit(0);
|
|
||||||
}, {
|
|
||||||
.cloneFlags = CLONE_NEWUSER
|
|
||||||
});
|
|
||||||
|
|
||||||
auto r = pid.wait();
|
auto r = pid.wait();
|
||||||
assert(!r);
|
assert(!r);
|
||||||
|
@ -120,8 +115,7 @@ bool mountAndPidNamespacesSupported()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Pid pid = startProcess([&]()
|
Pid pid = startProcess([&]() {
|
||||||
{
|
|
||||||
/* Make sure we don't remount the parent's /proc. */
|
/* Make sure we don't remount the parent's /proc. */
|
||||||
if (mount(0, "/", 0, MS_PRIVATE | MS_REC, 0) == -1)
|
if (mount(0, "/", 0, MS_PRIVATE | MS_REC, 0) == -1)
|
||||||
_exit(1);
|
_exit(1);
|
||||||
|
|
|
@ -35,9 +35,19 @@ Pid::Pid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Pid::Pid(pid_t pid)
|
Pid::Pid(Pid && other) : pid(other.pid), separatePG(other.separatePG), killSignal(other.killSignal)
|
||||||
: pid(pid)
|
|
||||||
{
|
{
|
||||||
|
other.pid = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pid & Pid::operator=(Pid && other)
|
||||||
|
{
|
||||||
|
Pid tmp(std::move(other));
|
||||||
|
std::swap(pid, tmp.pid);
|
||||||
|
std::swap(separatePG, tmp.separatePG);
|
||||||
|
std::swap(killSignal, tmp.killSignal);
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,20 +57,6 @@ Pid::~Pid() noexcept(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Pid::operator =(pid_t pid)
|
|
||||||
{
|
|
||||||
if (this->pid != -1 && this->pid != pid) kill();
|
|
||||||
this->pid = pid;
|
|
||||||
killSignal = SIGKILL; // reset signal to default
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Pid::operator pid_t()
|
|
||||||
{
|
|
||||||
return pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Pid::kill()
|
int Pid::kill()
|
||||||
{
|
{
|
||||||
assert(pid != -1);
|
assert(pid != -1);
|
||||||
|
@ -131,7 +127,7 @@ void killUser(uid_t uid)
|
||||||
users to which the current process can send signals. So we
|
users to which the current process can send signals. So we
|
||||||
fork a process, switch to uid, and send a mass kill. */
|
fork a process, switch to uid, and send a mass kill. */
|
||||||
|
|
||||||
Pid pid = startProcess([&]() {
|
Pid pid{startProcess([&]() {
|
||||||
|
|
||||||
if (setuid(uid) == -1)
|
if (setuid(uid) == -1)
|
||||||
throw SysError("setting uid");
|
throw SysError("setting uid");
|
||||||
|
@ -153,7 +149,7 @@ void killUser(uid_t uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
_exit(0);
|
_exit(0);
|
||||||
});
|
})};
|
||||||
|
|
||||||
int status = pid.wait();
|
int status = pid.wait();
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
|
@ -187,7 +183,7 @@ static int childEntry(void * arg)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
Pid startProcess(std::function<void()> fun, const ProcessOptions & options)
|
||||||
{
|
{
|
||||||
std::function<void()> wrapper = [&]() {
|
std::function<void()> wrapper = [&]() {
|
||||||
logger = makeSimpleLogger();
|
logger = makeSimpleLogger();
|
||||||
|
@ -231,7 +227,7 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
||||||
|
|
||||||
if (pid == -1) throw SysError("unable to fork");
|
if (pid == -1) throw SysError("unable to fork");
|
||||||
|
|
||||||
return pid;
|
return Pid{pid};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string runProgram(Path program, bool searchPath, const Strings & args,
|
std::string runProgram(Path program, bool searchPath, const Strings & args,
|
||||||
|
@ -294,7 +290,7 @@ void runProgram2(const RunOptions & options)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fork. */
|
/* Fork. */
|
||||||
Pid pid = startProcess([&]() {
|
Pid pid{startProcess([&]() {
|
||||||
if (options.environment)
|
if (options.environment)
|
||||||
replaceEnv(*options.environment);
|
replaceEnv(*options.environment);
|
||||||
if (options.standardOut && dup2(out.writeSide.get(), STDOUT_FILENO) == -1)
|
if (options.standardOut && dup2(out.writeSide.get(), STDOUT_FILENO) == -1)
|
||||||
|
@ -328,7 +324,7 @@ void runProgram2(const RunOptions & options)
|
||||||
execv(options.program.c_str(), stringsToCharPtrs(args_).data());
|
execv(options.program.c_str(), stringsToCharPtrs(args_).data());
|
||||||
|
|
||||||
throw SysError("executing '%1%'", options.program);
|
throw SysError("executing '%1%'", options.program);
|
||||||
}, processOptions);
|
}, processOptions)};
|
||||||
|
|
||||||
out.writeSide.close();
|
out.writeSide.close();
|
||||||
|
|
||||||
|
|
|
@ -26,16 +26,18 @@ class Pid
|
||||||
int killSignal = SIGKILL;
|
int killSignal = SIGKILL;
|
||||||
public:
|
public:
|
||||||
Pid();
|
Pid();
|
||||||
Pid(pid_t pid);
|
explicit Pid(pid_t pid): pid(pid) {}
|
||||||
|
Pid(Pid && other);
|
||||||
|
Pid & operator=(Pid && other);
|
||||||
~Pid() noexcept(false);
|
~Pid() noexcept(false);
|
||||||
void operator =(pid_t pid);
|
explicit operator bool() const { return pid != -1; }
|
||||||
operator pid_t();
|
|
||||||
int kill();
|
int kill();
|
||||||
int wait();
|
int wait();
|
||||||
|
|
||||||
void setSeparatePG(bool separatePG);
|
void setSeparatePG(bool separatePG);
|
||||||
void setKillSignal(int signal);
|
void setKillSignal(int signal);
|
||||||
pid_t release();
|
pid_t release();
|
||||||
|
pid_t get() const { return pid; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,7 +62,8 @@ struct ProcessOptions
|
||||||
int cloneFlags = 0;
|
int cloneFlags = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
[[nodiscard]]
|
||||||
|
Pid startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -266,20 +266,17 @@ std::unique_ptr<FinishSink> sourceToSink(std::function<void(Source &)> fun)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Source> sinkToSource(
|
std::unique_ptr<Source> sinkToSource(std::function<void(Sink &)> fun)
|
||||||
std::function<void(Sink &)> fun,
|
|
||||||
std::function<void()> eof)
|
|
||||||
{
|
{
|
||||||
struct SinkToSource : Source
|
struct SinkToSource : Source
|
||||||
{
|
{
|
||||||
typedef boost::coroutines2::coroutine<std::string> coro_t;
|
typedef boost::coroutines2::coroutine<std::string> coro_t;
|
||||||
|
|
||||||
std::function<void(Sink &)> fun;
|
std::function<void(Sink &)> fun;
|
||||||
std::function<void()> eof;
|
|
||||||
std::optional<coro_t::pull_type> coro;
|
std::optional<coro_t::pull_type> coro;
|
||||||
|
|
||||||
SinkToSource(std::function<void(Sink &)> fun, std::function<void()> eof)
|
SinkToSource(std::function<void(Sink &)> fun)
|
||||||
: fun(fun), eof(eof)
|
: fun(fun)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +295,9 @@ std::unique_ptr<Source> sinkToSource(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*coro) { eof(); abort(); }
|
if (!*coro) {
|
||||||
|
throw EndOfFile("coroutine has finished");
|
||||||
|
}
|
||||||
|
|
||||||
if (pos == cur.size()) {
|
if (pos == cur.size()) {
|
||||||
if (!cur.empty()) {
|
if (!cur.empty()) {
|
||||||
|
@ -317,7 +316,7 @@ std::unique_ptr<Source> sinkToSource(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return std::make_unique<SinkToSource>(fun, eof);
|
return std::make_unique<SinkToSource>(fun);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -338,11 +338,7 @@ std::unique_ptr<FinishSink> sourceToSink(std::function<void(Source &)> fun);
|
||||||
* Convert a function that feeds data into a Sink into a Source. The
|
* Convert a function that feeds data into a Sink into a Source. The
|
||||||
* Source executes the function as a coroutine.
|
* Source executes the function as a coroutine.
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<Source> sinkToSource(
|
std::unique_ptr<Source> sinkToSource(std::function<void(Sink &)> fun);
|
||||||
std::function<void(Sink &)> fun,
|
|
||||||
std::function<void()> eof = []() {
|
|
||||||
throw EndOfFile("coroutine has finished");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
void writePadding(size_t len, Sink & sink);
|
void writePadding(size_t len, Sink & sink);
|
||||||
|
|
|
@ -65,7 +65,7 @@ static void bindConnectProcHelper(
|
||||||
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
||||||
Pipe pipe;
|
Pipe pipe;
|
||||||
pipe.create();
|
pipe.create();
|
||||||
Pid pid = startProcess([&] {
|
Pid pid{startProcess([&] {
|
||||||
try {
|
try {
|
||||||
pipe.readSide.close();
|
pipe.readSide.close();
|
||||||
Path dir = dirOf(path);
|
Path dir = dirOf(path);
|
||||||
|
@ -83,7 +83,7 @@ static void bindConnectProcHelper(
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
writeFull(pipe.writeSide.get(), "-1\n");
|
writeFull(pipe.writeSide.get(), "-1\n");
|
||||||
}
|
}
|
||||||
});
|
})};
|
||||||
pipe.writeSide.close();
|
pipe.writeSide.close();
|
||||||
auto errNo = string2Int<int>(chomp(drainFD(pipe.readSide.get())));
|
auto errNo = string2Int<int>(chomp(drainFD(pipe.readSide.get())));
|
||||||
if (!errNo || *errNo == -1)
|
if (!errNo || *errNo == -1)
|
||||||
|
|
|
@ -369,7 +369,7 @@ static void daemonLoop(std::optional<TrustedFlag> forceTrustClientOpt)
|
||||||
processConnection(openUncachedStore(), from, to, trusted, NotRecursive);
|
processConnection(openUncachedStore(), from, to, trusted, NotRecursive);
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}, options);
|
}, options).release();
|
||||||
|
|
||||||
} catch (Interrupted & e) {
|
} catch (Interrupted & e) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -182,6 +182,7 @@ functional_tests_scripts = [
|
||||||
'debugger.sh',
|
'debugger.sh',
|
||||||
'test-libstoreconsumer.sh',
|
'test-libstoreconsumer.sh',
|
||||||
'extra-sandbox-profile.sh',
|
'extra-sandbox-profile.sh',
|
||||||
|
'substitute-truncated-nar.sh',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Plugin tests require shared libraries support.
|
# Plugin tests require shared libraries support.
|
||||||
|
|
|
@ -22,7 +22,7 @@ RunningProcess RunningProcess::start(std::string executable, Strings args)
|
||||||
procStdout.create();
|
procStdout.create();
|
||||||
|
|
||||||
// This is separate from runProgram2 because we have different IO requirements
|
// This is separate from runProgram2 because we have different IO requirements
|
||||||
pid_t pid = startProcess([&]() {
|
auto pid = startProcess([&]() {
|
||||||
if (dup2(procStdout.writeSide.get(), STDOUT_FILENO) == -1) {
|
if (dup2(procStdout.writeSide.get(), STDOUT_FILENO) == -1) {
|
||||||
throw SysError("dupping stdout");
|
throw SysError("dupping stdout");
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ RunningProcess RunningProcess::start(std::string executable, Strings args)
|
||||||
procStdin.readSide.close();
|
procStdin.readSide.close();
|
||||||
|
|
||||||
return RunningProcess{
|
return RunningProcess{
|
||||||
.pid = pid,
|
.pid = std::move(pid),
|
||||||
.procStdin = std::move(procStdin),
|
.procStdin = std::move(procStdin),
|
||||||
.procStdout = std::move(procStdout),
|
.procStdout = std::move(procStdout),
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,13 +7,14 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "file-descriptor.hh"
|
#include "file-descriptor.hh"
|
||||||
|
#include "processes.hh"
|
||||||
#include "tests/terminal-code-eater.hh"
|
#include "tests/terminal-code-eater.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct RunningProcess
|
struct RunningProcess
|
||||||
{
|
{
|
||||||
pid_t pid;
|
Pid pid;
|
||||||
Pipe procStdin;
|
Pipe procStdin;
|
||||||
Pipe procStdout;
|
Pipe procStdout;
|
||||||
|
|
||||||
|
|
29
tests/functional/substitute-truncated-nar.sh
Normal file
29
tests/functional/substitute-truncated-nar.sh
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
BINARY_CACHE=file://$cacheDir
|
||||||
|
|
||||||
|
build() {
|
||||||
|
nix-build --no-out-link "$@" --expr 'derivation {
|
||||||
|
name = "text";
|
||||||
|
system = builtins.currentSystem;
|
||||||
|
builder = "/bin/sh";
|
||||||
|
args = [ "-c" "echo some text to make the nar less empty > $out" ];
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
path=$(build)
|
||||||
|
nix copy --to "$BINARY_CACHE" "$path"
|
||||||
|
nix-collect-garbage >/dev/null 2>&1
|
||||||
|
|
||||||
|
nar=0bylmx35yjy2b1b4k7gjsl7i4vc03cpmryb41grfb1mp40n3hifl.nar.xz
|
||||||
|
|
||||||
|
[ -e $cacheDir/nar/$nar ] || fail "long nar missing?"
|
||||||
|
|
||||||
|
xzcat $cacheDir/nar/$nar > $TEST_HOME/tmp
|
||||||
|
truncate -s $(( $(stat -c %s $TEST_HOME/tmp) - 10 )) $TEST_HOME/tmp
|
||||||
|
xz < $TEST_HOME/tmp > $cacheDir/nar/$nar
|
||||||
|
|
||||||
|
# Copying back '$path' from the binary cache. This should fail as it is truncated
|
||||||
|
if build --option substituters "$BINARY_CACHE" --option require-sigs false -j0; then
|
||||||
|
fail "Importing a truncated nar should fail"
|
||||||
|
fi
|
166
tests/unit/libutil/serialise.cc
Normal file
166
tests/unit/libutil/serialise.cc
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
#include "serialise.hh"
|
||||||
|
#include "error.hh"
|
||||||
|
#include "fmt.hh"
|
||||||
|
#include "pos-table.hh"
|
||||||
|
#include "ref.hh"
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
TEST(Sink, uint64_t)
|
||||||
|
{
|
||||||
|
StringSink s;
|
||||||
|
s << 42;
|
||||||
|
ASSERT_EQ(s.s, std::string({42, 0, 0, 0, 0, 0, 0, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Sink, string_view)
|
||||||
|
{
|
||||||
|
StringSink s;
|
||||||
|
s << "";
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data (omitted)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
s = {};
|
||||||
|
s << "test";
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
4, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data
|
||||||
|
't', 'e', 's', 't',
|
||||||
|
// padding
|
||||||
|
0, 0, 0, 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
s = {};
|
||||||
|
s << "longer string";
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
13, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data
|
||||||
|
'l', 'o', 'n', 'g', 'e', 'r', ' ', 's', 't', 'r', 'i', 'n', 'g',
|
||||||
|
// padding
|
||||||
|
0, 0, 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Sink, StringSet)
|
||||||
|
{
|
||||||
|
StringSink s;
|
||||||
|
s << StringSet{};
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data (omitted)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
s = {};
|
||||||
|
s << StringSet{"a", ""};
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
2, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data ""
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data "a"
|
||||||
|
1, 0, 0, 0, 0, 0, 0, 0, 'a', 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Sink, Strings)
|
||||||
|
{
|
||||||
|
StringSink s;
|
||||||
|
s << Strings{};
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data (omitted)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
s = {};
|
||||||
|
s << Strings{"a", ""};
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
// length
|
||||||
|
2, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data "a"
|
||||||
|
1, 0, 0, 0, 0, 0, 0, 0, 'a', 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// data ""
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Sink, Error)
|
||||||
|
{
|
||||||
|
PosTable pt;
|
||||||
|
auto o = pt.addOrigin(Pos::String{make_ref<std::string>("test")}, 4);
|
||||||
|
|
||||||
|
StringSink s;
|
||||||
|
s << Error{ErrorInfo{
|
||||||
|
.level = lvlInfo,
|
||||||
|
.msg = HintFmt("foo"),
|
||||||
|
.pos = pt[pt.add(o, 1)],
|
||||||
|
.traces = {{.pos = pt[pt.add(o, 2)], .hint = HintFmt("b %1%", "foo")}},
|
||||||
|
}};
|
||||||
|
// NOTE position of the error and all traces are ignored
|
||||||
|
// by the wire format
|
||||||
|
// clang-format off
|
||||||
|
ASSERT_EQ(
|
||||||
|
s.s,
|
||||||
|
std::string({
|
||||||
|
5, 0, 0, 0, 0, 0, 0, 0, 'E', 'r', 'r', 'o', 'r', 0, 0, 0,
|
||||||
|
3, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
5, 0, 0, 0, 0, 0, 0, 0, 'E', 'r', 'r', 'o', 'r', 0, 0, 0,
|
||||||
|
3, 0, 0, 0, 0, 0, 0, 0, 'f', 'o', 'o', 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
1, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
16, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
'b', ' ', '\x1b', '[', '3', '5', ';', '1', 'm', 'f', 'o', 'o', '\x1b', '[', '0', 'm',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ libutil_tests_sources = files(
|
||||||
'libutil/paths-setting.cc',
|
'libutil/paths-setting.cc',
|
||||||
'libutil/pool.cc',
|
'libutil/pool.cc',
|
||||||
'libutil/references.cc',
|
'libutil/references.cc',
|
||||||
|
'libutil/serialise.cc',
|
||||||
'libutil/suggestions.cc',
|
'libutil/suggestions.cc',
|
||||||
'libutil/tests.cc',
|
'libutil/tests.cc',
|
||||||
'libutil/url.cc',
|
'libutil/url.cc',
|
||||||
|
|
Loading…
Reference in a new issue