nix-shell: Fix $PATH handling in the impure case

We were passing "p=$PATH" rather than "p=$PATH;", resulting in some
invalid shell code.

Also, construct a separate environment for the child rather than
overwriting the parent's.
This commit is contained in:
Eelco Dolstra 2016-09-20 15:39:08 +02:00
parent 9fc4cb2ae9
commit 4de0639105
3 changed files with 55 additions and 29 deletions

View file

@ -59,6 +59,21 @@ string getEnv(const string & key, const string & def)
} }
std::map<std::string, std::string> getEnv()
{
std::map<std::string, std::string> env;
for (size_t i = 0; environ[i]; ++i) {
auto s = environ[i];
auto eq = strchr(s, '=');
if (!eq)
// invalid env, just keep going
continue;
env.emplace(std::string(s, eq), std::string(eq + 1));
}
return env;
}
Path absPath(Path path, Path dir) Path absPath(Path path, Path dir)
{ {
if (path[0] != '/') { if (path[0] != '/') {

View file

@ -8,9 +8,11 @@
#include <dirent.h> #include <dirent.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <cstdio> #include <cstdio>
#include <map>
#ifndef HAVE_STRUCT_DIRENT_D_TYPE #ifndef HAVE_STRUCT_DIRENT_D_TYPE
#define DT_UNKNOWN 0 #define DT_UNKNOWN 0
@ -25,6 +27,9 @@ namespace nix {
/* Return an environment variable. */ /* Return an environment variable. */
string getEnv(const string & key, const string & def = ""); string getEnv(const string & key, const string & def = "");
/* Get the entire environment. */
std::map<std::string, std::string> getEnv();
/* Return an absolutized path, resolving paths relative to the /* Return an absolutized path, resolving paths relative to the
specified directory, or the current directory otherwise. The path specified directory, or the current directory otherwise. The path
is also canonicalised. */ is also canonicalised. */

View file

@ -16,8 +16,6 @@
using namespace nix; using namespace nix;
extern char ** environ;
/* Recreate the effect of the perl shellwords function, breaking up a /* Recreate the effect of the perl shellwords function, breaking up a
* string into arguments like a shell word, including escapes * string into arguments like a shell word, including escapes
*/ */
@ -375,32 +373,26 @@ int main(int argc, char ** argv)
runProgram(settings.nixBinDir + "/nix-store", false, nixStoreArgs); runProgram(settings.nixBinDir + "/nix-store", false, nixStoreArgs);
// Set the environment. // Set the environment.
auto env = getEnv();
auto tmp = getEnv("TMPDIR", getEnv("XDG_RUNTIME_DIR", "/tmp")); auto tmp = getEnv("TMPDIR", getEnv("XDG_RUNTIME_DIR", "/tmp"));
if (pure) { if (pure) {
std::vector<string> skippedEnv{"HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER", "NIX_BUILD_SHELL"}; std::set<string> keepVars{"HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER", "NIX_BUILD_SHELL"};
std::vector<string> removed; decltype(env) newEnv;
for (auto i = size_t{0}; environ[i]; ++i) { for (auto & i : env)
auto eq = strchr(environ[i], '='); if (keepVars.count(i.first))
if (!eq) newEnv.emplace(i);
// invalid env, just keep going env = newEnv;
continue;
std::string name(environ[i], eq);
if (find(skippedEnv.begin(), skippedEnv.end(), name) == skippedEnv.end())
removed.emplace_back(std::move(name));
}
for (const auto & name : removed)
unsetenv(name.c_str());
// NixOS hack: prevent /etc/bashrc from sourcing /etc/profile. // NixOS hack: prevent /etc/bashrc from sourcing /etc/profile.
setenv("__ETC_PROFILE_SOURCED", "1", 1); env["__ETC_PROFILE_SOURCED"] = "1";
} }
setenv("NIX_BUILD_TOP", tmp.c_str(), 1);
setenv("TMPDIR", tmp.c_str(), 1); env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmp;
setenv("TEMPDIR", tmp.c_str(), 1); env["NIX_STORE"] = store->storeDir;
setenv("TMP", tmp.c_str(), 1);
setenv("TEMP", tmp.c_str(), 1); for (auto & var : drv.env)
setenv("NIX_STORE", store->storeDir.c_str(), 1); env.emplace(var);
for (const auto & env : drv.env)
setenv(env.first.c_str(), env.second.c_str(), 1);
restoreAffinity(); restoreAffinity();
@ -424,11 +416,25 @@ int main(int argc, char ** argv)
"shopt -u nullglob; " "shopt -u nullglob; "
"unset TZ; %4%" "unset TZ; %4%"
"%5%" "%5%"
) % (Path) tmpDir % (pure ? "" : "p=$PATH") % (pure ? "" : "PATH=$PATH:$p; unset p; ") % (getenv("TZ") ? (string("export TZ='") + getenv("TZ") + "'; ") : "") % envCommand).str()); )
if (interactive) % (Path) tmpDir
execlp(getEnv("NIX_BUILD_SHELL", "bash").c_str(), "bash", "--rcfile", rcfile.c_str(), NULL); % (pure ? "" : "p=$PATH; ")
else % (pure ? "" : "PATH=$PATH:$p; unset p; ")
execlp(getEnv("NIX_BUILD_SHELL", "bash").c_str(), "bash", rcfile.c_str(), NULL); % (getenv("TZ") ? (string("export TZ='") + getenv("TZ") + "'; ") : "")
% envCommand).str());
Strings envStrs;
for (auto & i : env)
envStrs.push_back(i.first + "=" + i.second);
auto args = interactive
? Strings{"bash", "--rcfile", rcfile}
: Strings{"bash", rcfile};
execvpe(getEnv("NIX_BUILD_SHELL", "bash").c_str(),
stringsToCharPtrs(args).data(),
stringsToCharPtrs(envStrs).data());
throw SysError("executing shell"); throw SysError("executing shell");
} }