aboutsummaryrefslogtreecommitdiff
path: root/src/nix
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix')
-rw-r--r--src/nix/command.hh9
-rw-r--r--src/nix/installables.cc22
-rw-r--r--src/nix/run.cc101
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)
{