forked from lix-project/lix
commit
507c150034
4 changed files with 124 additions and 30 deletions
|
@ -72,6 +72,14 @@ public:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Attr & need(const Symbol & name, const Pos & pos = noPos)
|
||||||
|
{
|
||||||
|
auto a = get(name);
|
||||||
|
if (!a)
|
||||||
|
throw Error("attribute '%s' missing, at %s", name, pos);
|
||||||
|
return **a;
|
||||||
|
}
|
||||||
|
|
||||||
iterator begin() { return &attrs[0]; }
|
iterator begin() { return &attrs[0]; }
|
||||||
iterator end() { return &attrs[size_]; }
|
iterator end() { return &attrs[size_]; }
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,13 @@ struct Buildable
|
||||||
|
|
||||||
typedef std::vector<Buildable> Buildables;
|
typedef std::vector<Buildable> Buildables;
|
||||||
|
|
||||||
|
struct App
|
||||||
|
{
|
||||||
|
PathSet context;
|
||||||
|
Path program;
|
||||||
|
// FIXME: add args, sandbox settings, metadata, ...
|
||||||
|
};
|
||||||
|
|
||||||
struct Installable
|
struct Installable
|
||||||
{
|
{
|
||||||
virtual std::string what() = 0;
|
virtual std::string what() = 0;
|
||||||
|
@ -49,6 +56,8 @@ struct Installable
|
||||||
|
|
||||||
Buildable toBuildable();
|
Buildable toBuildable();
|
||||||
|
|
||||||
|
App toApp(EvalState & state);
|
||||||
|
|
||||||
virtual Value * toValue(EvalState & state)
|
virtual Value * toValue(EvalState & state)
|
||||||
{
|
{
|
||||||
throw Error("argument '%s' cannot be evaluated", what());
|
throw Error("argument '%s' cannot be evaluated", what());
|
||||||
|
|
|
@ -68,6 +68,28 @@ Buildable Installable::toBuildable()
|
||||||
return std::move(buildables[0]);
|
return std::move(buildables[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
App Installable::toApp(EvalState & state)
|
||||||
|
{
|
||||||
|
auto v = toValue(state);
|
||||||
|
|
||||||
|
state.forceAttrs(*v);
|
||||||
|
|
||||||
|
auto aType = v->attrs->need(state.sType);
|
||||||
|
if (state.forceStringNoCtx(*aType.value, *aType.pos) != "app")
|
||||||
|
throw Error("value does not have type 'app', at %s", *aType.pos);
|
||||||
|
|
||||||
|
App app;
|
||||||
|
|
||||||
|
auto aProgram = v->attrs->need(state.symbols.create("program"));
|
||||||
|
app.program = state.forceString(*aProgram.value, app.context, *aProgram.pos);
|
||||||
|
|
||||||
|
// FIXME: check that 'program' is in the closure of 'context'.
|
||||||
|
if (!state.store->isInStore(app.program))
|
||||||
|
throw Error("app program '%s' is not in the Nix store", app.program);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
struct InstallableStorePath : Installable
|
struct InstallableStorePath : Installable
|
||||||
{
|
{
|
||||||
Path storePath;
|
Path storePath;
|
||||||
|
|
115
src/nix/run.cc
115
src/nix/run.cc
|
@ -8,6 +8,7 @@
|
||||||
#include "fs-accessor.hh"
|
#include "fs-accessor.hh"
|
||||||
#include "progress-bar.hh"
|
#include "progress-bar.hh"
|
||||||
#include "affinity.hh"
|
#include "affinity.hh"
|
||||||
|
#include "eval.hh"
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
|
@ -19,7 +20,44 @@ using namespace nix;
|
||||||
|
|
||||||
std::string chrootHelperName = "__run_in_chroot";
|
std::string chrootHelperName = "__run_in_chroot";
|
||||||
|
|
||||||
struct CmdRun : InstallablesCommand
|
struct RunCommon : virtual Command
|
||||||
|
{
|
||||||
|
void runProgram(ref<Store> store,
|
||||||
|
const std::string & program,
|
||||||
|
const Strings & args)
|
||||||
|
{
|
||||||
|
stopProgressBar();
|
||||||
|
|
||||||
|
restoreSignals();
|
||||||
|
|
||||||
|
restoreAffinity();
|
||||||
|
|
||||||
|
/* If this is a diverted store (i.e. its "logical" location
|
||||||
|
(typically /nix/store) differs from its "physical" location
|
||||||
|
(e.g. /home/eelco/nix/store), then run the command in a
|
||||||
|
chroot. For non-root users, this requires running it in new
|
||||||
|
mount and user namespaces. Unfortunately,
|
||||||
|
unshare(CLONE_NEWUSER) doesn't work in a multithreaded
|
||||||
|
program (which "nix" is), so we exec() a single-threaded
|
||||||
|
helper program (chrootHelper() below) to do the work. */
|
||||||
|
auto store2 = store.dynamic_pointer_cast<LocalStore>();
|
||||||
|
|
||||||
|
if (store2 && store->storeDir != store2->realStoreDir) {
|
||||||
|
Strings helperArgs = { chrootHelperName, store->storeDir, store2->realStoreDir, program };
|
||||||
|
for (auto & arg : args) helperArgs.push_back(arg);
|
||||||
|
|
||||||
|
execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
|
||||||
|
|
||||||
|
throw SysError("could not execute chroot helper");
|
||||||
|
}
|
||||||
|
|
||||||
|
execvp(program.c_str(), stringsToCharPtrs(args).data());
|
||||||
|
|
||||||
|
throw SysError("unable to execute '%s'", program);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CmdRun : InstallablesCommand, RunCommon
|
||||||
{
|
{
|
||||||
std::vector<std::string> command = { "bash" };
|
std::vector<std::string> command = { "bash" };
|
||||||
StringSet keep, unset;
|
StringSet keep, unset;
|
||||||
|
@ -147,43 +185,60 @@ struct CmdRun : InstallablesCommand
|
||||||
|
|
||||||
setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1);
|
setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1);
|
||||||
|
|
||||||
std::string cmd = *command.begin();
|
|
||||||
Strings args;
|
Strings args;
|
||||||
for (auto & arg : command) args.push_back(arg);
|
for (auto & arg : command) args.push_back(arg);
|
||||||
|
|
||||||
stopProgressBar();
|
runProgram(store, *command.begin(), args);
|
||||||
|
|
||||||
restoreSignals();
|
|
||||||
|
|
||||||
restoreAffinity();
|
|
||||||
|
|
||||||
/* If this is a diverted store (i.e. its "logical" location
|
|
||||||
(typically /nix/store) differs from its "physical" location
|
|
||||||
(e.g. /home/eelco/nix/store), then run the command in a
|
|
||||||
chroot. For non-root users, this requires running it in new
|
|
||||||
mount and user namespaces. Unfortunately,
|
|
||||||
unshare(CLONE_NEWUSER) doesn't work in a multithreaded
|
|
||||||
program (which "nix" is), so we exec() a single-threaded
|
|
||||||
helper program (chrootHelper() below) to do the work. */
|
|
||||||
auto store2 = store.dynamic_pointer_cast<LocalStore>();
|
|
||||||
|
|
||||||
if (store2 && store->storeDir != store2->realStoreDir) {
|
|
||||||
Strings helperArgs = { chrootHelperName, store->storeDir, store2->realStoreDir, cmd };
|
|
||||||
for (auto & arg : args) helperArgs.push_back(arg);
|
|
||||||
|
|
||||||
execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
|
|
||||||
|
|
||||||
throw SysError("could not execute chroot helper");
|
|
||||||
}
|
|
||||||
|
|
||||||
execvp(cmd.c_str(), stringsToCharPtrs(args).data());
|
|
||||||
|
|
||||||
throw SysError("unable to exec '%s'", cmd);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static RegisterCommand r1(make_ref<CmdRun>());
|
static RegisterCommand r1(make_ref<CmdRun>());
|
||||||
|
|
||||||
|
struct CmdApp : InstallableCommand, RunCommon
|
||||||
|
{
|
||||||
|
CmdApp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name() override
|
||||||
|
{
|
||||||
|
return "app";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "run a Nix application";
|
||||||
|
}
|
||||||
|
|
||||||
|
Examples examples() override
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
Example{
|
||||||
|
"To run Blender:",
|
||||||
|
"nix app blender-bin"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Strings getDefaultFlakeAttrPaths() override
|
||||||
|
{
|
||||||
|
return {"defaultApp"};
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(ref<Store> store) override
|
||||||
|
{
|
||||||
|
auto state = getEvalState();
|
||||||
|
|
||||||
|
auto app = installable->toApp(*state);
|
||||||
|
|
||||||
|
state->realiseContext(app.context);
|
||||||
|
|
||||||
|
runProgram(store, app.program, {app.program});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static RegisterCommand r2(make_ref<CmdApp>());
|
||||||
|
|
||||||
void chrootHelper(int argc, char * * argv)
|
void chrootHelper(int argc, char * * argv)
|
||||||
{
|
{
|
||||||
int p = 1;
|
int p = 1;
|
||||||
|
|
Loading…
Reference in a new issue