diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index b3b4086916..75a0e185e3 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -8,6 +8,8 @@ libexpr_SOURCES := $(wildcard $(d)/*.cc) $(d)/lexer-tab.cc $(d)/parser-tab.cc libexpr_LIBS = libutil libstore libformat +libexpr_LDFLAGS = -ldl + # The dependency on libgc must be propagated (i.e. meaning that # programs/libraries that use libexpr must explicitly pass -lgc), # because inline functions in libexpr's header files call libgc. diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 366911b54d..ff82f36b52 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -15,6 +15,7 @@ #include #include +#include namespace nix { @@ -129,6 +130,46 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args } +/* Want reasonable symbol names, so extern C */ +/* !!! Should we pass the Pos or the file name too? */ +extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v); + +/* Load a ValueInitializer from a dso and return whatever it initializes */ +static void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v) +{ + PathSet context; + Path path = state.coerceToPath(pos, *args[0], context); + + try { + realiseContext(context); + } catch (InvalidPathError & e) { + throw EvalError(format("cannot import `%1%', since path `%2%' is not valid, at %3%") + % path % e.path % pos); + } + + string sym = state.forceStringNoCtx(*args[1], pos); + + void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); + if (!handle) + throw EvalError(format("could not open `%1%': %2%") % path % dlerror()); + + dlerror(); + ValueInitializer func = (ValueInitializer) dlsym(handle, sym.c_str()); + if(!func) { + char *message = dlerror(); + if (message) + throw EvalError(format("could not load symbol `%1%' from `%2%': %3%") % sym % path % message); + else + throw EvalError(format("symbol `%1%' from `%2%' resolved to NULL when a function pointer was expected") + % sym % path); + } + + (func)(state, v); + + /* We don't dlclose because v may be a primop referencing a function in the shared object file */ +} + + /* Return a string representing the type of the expression. */ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { @@ -1327,6 +1368,8 @@ void EvalState::createBaseEnv() mkApp(v, *baseEnv.values[baseEnvDispl - 1], *v2); forceValue(v); addConstant("import", v); + if (settings.enableImportNative) + addPrimOp("__importNative", 2, prim_importNative); addPrimOp("__typeOf", 1, prim_typeOf); addPrimOp("isNull", 1, prim_isNull); addPrimOp("__isFunction", 1, prim_isFunction); diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 180344e336..5d359e1281 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -61,6 +61,7 @@ Settings::Settings() envKeepDerivations = false; lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1"; showTrace = false; + enableImportNative = false; } @@ -148,6 +149,7 @@ void Settings::update() get(sshSubstituterHosts, "ssh-substituter-hosts"); get(useSshSubstituter, "use-ssh-substituter"); get(logServers, "log-servers"); + get(enableImportNative, "allow-arbitrary-code-during-evaluation"); string subs = getEnv("NIX_SUBSTITUTERS", "default"); if (subs == "default") { diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 65a6c388b8..8dd59a9c79 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -200,6 +200,9 @@ struct Settings { /* A list of URL prefixes that can return Nix build logs. */ Strings logServers; + /* Whether the importNative primop should be enabled */ + bool enableImportNative; + private: SettingsMap settings, overrides;