diff options
Diffstat (limited to 'src/nix')
-rw-r--r-- | src/nix/command.hh | 9 | ||||
-rw-r--r-- | src/nix/installables.cc | 22 | ||||
-rw-r--r-- | src/nix/run.cc | 101 |
3 files changed, 109 insertions, 23 deletions
diff --git a/src/nix/command.hh b/src/nix/command.hh index 26c308331..659b724c3 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -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()); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index eb3c27d6b..b6f05b314 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -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; diff --git a/src/nix/run.cc b/src/nix/run.cc index 35b763345..00a682832 100644 --- a/src/nix/run.cc +++ b/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,42 +185,59 @@ 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(); + runProgram(store, *command.begin(), args); + } +}; - restoreSignals(); +static RegisterCommand r1(make_ref<CmdRun>()); - restoreAffinity(); +struct CmdApp : InstallableCommand, RunCommon +{ + CmdApp() + { + } - /* 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>(); + std::string name() override + { + return "app"; + } - if (store2 && store->storeDir != store2->realStoreDir) { - Strings helperArgs = { chrootHelperName, store->storeDir, store2->realStoreDir, cmd }; - for (auto & arg : args) helperArgs.push_back(arg); + std::string description() override + { + return "run a Nix application"; + } - execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data()); + Examples examples() override + { + return { + Example{ + "To run Blender:", + "nix app blender-bin" + }, + }; + } - throw SysError("could not execute chroot helper"); - } + Strings getDefaultFlakeAttrPaths() override + { + return {"defaultApp"}; + } - execvp(cmd.c_str(), stringsToCharPtrs(args).data()); + void run(ref<Store> store) override + { + auto state = getEvalState(); + + auto app = installable->toApp(*state); - throw SysError("unable to exec '%s'", cmd); + state->realiseContext(app.context); + + runProgram(store, app.program, {app.program}); } }; -static RegisterCommand r1(make_ref<CmdRun>()); +static RegisterCommand r2(make_ref<CmdApp>()); void chrootHelper(int argc, char * * argv) { |