commit
507c150034
4 changed files with 124 additions and 30 deletions
|
@ -72,6 +72,14 @@ public:
|
|||
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 end() { return &attrs[size_]; }
|
||||
|
||||
|
|
|
@ -38,6 +38,13 @@ struct Buildable
|
|||
|
||||
typedef std::vector<Buildable> Buildables;
|
||||
|
||||
struct App
|
||||
{
|
||||
PathSet context;
|
||||
Path program;
|
||||
// FIXME: add args, sandbox settings, metadata, ...
|
||||
};
|
||||
|
||||
struct Installable
|
||||
{
|
||||
virtual std::string what() = 0;
|
||||
|
@ -49,6 +56,8 @@ struct Installable
|
|||
|
||||
Buildable toBuildable();
|
||||
|
||||
App toApp(EvalState & state);
|
||||
|
||||
virtual Value * toValue(EvalState & state)
|
||||
{
|
||||
throw Error("argument '%s' cannot be evaluated", what());
|
||||
|
|
|
@ -68,6 +68,28 @@ Buildable Installable::toBuildable()
|
|||
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
|
||||
{
|
||||
Path storePath;
|
||||
|
|
115
src/nix/run.cc
115
src/nix/run.cc
|
@ -8,6 +8,7 @@
|
|||
#include "fs-accessor.hh"
|
||||
#include "progress-bar.hh"
|
||||
#include "affinity.hh"
|
||||
#include "eval.hh"
|
||||
|
||||
#if __linux__
|
||||
#include <sys/mount.h>
|
||||
|
@ -19,7 +20,44 @@ using namespace nix;
|
|||
|
||||
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" };
|
||||
StringSet keep, unset;
|
||||
|
@ -147,43 +185,60 @@ struct CmdRun : InstallablesCommand
|
|||
|
||||
setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1);
|
||||
|
||||
std::string cmd = *command.begin();
|
||||
Strings args;
|
||||
for (auto & arg : command) args.push_back(arg);
|
||||
|
||||
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, 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);
|
||||
runProgram(store, *command.begin(), args);
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
int p = 1;
|
||||
|
|
Loading…
Reference in a new issue