From 465cb6824401541d82489e11b5223dbfd50bb132 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 5 May 2017 16:40:12 +0200 Subject: Figure out the user's home directory if $HOME is not set --- src/libexpr/parser.y | 2 +- src/libutil/lazy.hh | 48 ++++++++++++++++++++++++++++++++++++++++++ src/libutil/util.cc | 45 ++++++++++++++++++++++++--------------- src/libutil/util.hh | 3 +++ src/nix-channel/nix-channel.cc | 4 +--- src/nix-env/nix-env.cc | 14 +++--------- 6 files changed, 84 insertions(+), 32 deletions(-) create mode 100644 src/libutil/lazy.hh (limited to 'src') diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index d07eedddaf..62982650a2 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -376,7 +376,7 @@ expr_simple $$ = stripIndentation(CUR_POS, data->symbols, *$2); } | PATH { $$ = new ExprPath(absPath($1, data->basePath)); } - | HPATH { $$ = new ExprPath(getEnv("HOME", "") + string{$1 + 1}); } + | HPATH { $$ = new ExprPath(getHome() + string{$1 + 1}); } | SPATH { string path($1 + 1, strlen($1) - 2); $$ = new ExprApp(CUR_POS, diff --git a/src/libutil/lazy.hh b/src/libutil/lazy.hh new file mode 100644 index 0000000000..d073e486c2 --- /dev/null +++ b/src/libutil/lazy.hh @@ -0,0 +1,48 @@ +#include +#include +#include + +namespace nix { + +/* A helper class for lazily-initialized variables. + + Lazy var([]() { return value; }); + + declares a variable of type T that is initialized to 'value' (in a + thread-safe way) on first use, that is, when var() is first + called. If the initialiser code throws an exception, then all + subsequent calls to var() will rethrow that exception. */ +template +class Lazy +{ + + typedef std::function Init; + + Init init; + + std::once_flag done; + + T value; + + std::exception_ptr ex; + +public: + + Lazy(Init init) : init(init) + { } + + const T & operator () () + { + std::call_once(done, [&]() { + try { + value = init(); + } catch (...) { + ex = std::current_exception(); + } + }); + if (ex) std::rethrow_exception(ex); + return value; + } +}; + +} diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 98c0aff1e7..1d1f68fc84 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1,3 +1,4 @@ +#include "lazy.hh" #include "util.hh" #include "affinity.hh" #include "sync.hh" @@ -13,10 +14,12 @@ #include #include -#include -#include #include #include +#include +#include +#include +#include #ifdef __APPLE__ #include @@ -417,14 +420,28 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix, } +static Lazy getHome2([]() { + Path homeDir = getEnv("HOME"); + if (homeDir.empty()) { + char buf[16384]; + struct passwd pwbuf; + struct passwd * pw; + if (getpwuid_r(getuid(), &pwbuf, buf, sizeof(buf), &pw) != 0 + || !pw || !pw->pw_dir || !pw->pw_dir[0]) + throw Error("cannot determine user's home directory"); + homeDir = pw->pw_dir; + } + return homeDir; +}); + +Path getHome() { return getHome2(); } + + Path getCacheDir() { Path cacheDir = getEnv("XDG_CACHE_HOME"); - if (cacheDir.empty()) { - Path homeDir = getEnv("HOME"); - if (homeDir.empty()) throw Error("$XDG_CACHE_HOME and $HOME are not set"); - cacheDir = homeDir + "/.cache"; - } + if (cacheDir.empty()) + cacheDir = getHome() + "/.cache"; return cacheDir; } @@ -432,11 +449,8 @@ Path getCacheDir() Path getConfigDir() { Path configDir = getEnv("XDG_CONFIG_HOME"); - if (configDir.empty()) { - Path homeDir = getEnv("HOME"); - if (homeDir.empty()) throw Error("$XDG_CONFIG_HOME and $HOME are not set"); - configDir = homeDir + "/.config"; - } + if (configDir.empty()) + configDir = getHome() + "/.config"; return configDir; } @@ -444,11 +458,8 @@ Path getConfigDir() Path getDataDir() { Path dataDir = getEnv("XDG_DATA_HOME"); - if (dataDir.empty()) { - Path homeDir = getEnv("HOME"); - if (homeDir.empty()) throw Error("$XDG_DATA_HOME and $HOME are not set"); - dataDir = homeDir + "/.local/share"; - } + if (dataDir.empty()) + dataDir = getHome() + "/.local/share"; return dataDir; } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index a9950f830c..5a9c9513fd 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -110,6 +110,9 @@ void deletePath(const Path & path, unsigned long long & bytesFreed); Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix", bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755); +/* Return $HOME or the user's home directory from /etc/passwd. */ +Path getHome(); + /* Return $XDG_CACHE_HOME or $HOME/.cache. */ Path getCacheDir(); diff --git a/src/nix-channel/nix-channel.cc b/src/nix-channel/nix-channel.cc index 0f50f6242c..2aaae2f471 100755 --- a/src/nix-channel/nix-channel.cc +++ b/src/nix-channel/nix-channel.cc @@ -169,9 +169,7 @@ int main(int argc, char ** argv) setenv("NIX_DOWNLOAD_CACHE", channelCache.c_str(), 1); // Figure out the name of the `.nix-channels' file to use - auto home = getEnv("HOME"); - if (home.empty()) - throw Error("$HOME not set"); + auto home = getHome(); channelsList = home + "/.nix-channels"; nixDefExpr = home + "/.nix-defexpr"; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 908c09bc8c..da39bf36ab 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -192,17 +192,9 @@ static void loadDerivations(EvalState & state, Path nixExprPath, } -static Path getHomeDir() -{ - Path homeDir(getEnv("HOME", "")); - if (homeDir == "") throw Error("HOME environment variable not set"); - return homeDir; -} - - static Path getDefNixExprPath() { - return getHomeDir() + "/.nix-defexpr"; + return getHome() + "/.nix-defexpr"; } @@ -1188,7 +1180,7 @@ static void opSwitchProfile(Globals & globals, Strings opFlags, Strings opArgs) throw UsageError(format("exactly one argument expected")); Path profile = absPath(opArgs.front()); - Path profileLink = getHomeDir() + "/.nix-profile"; + Path profileLink = getHome() + "/.nix-profile"; switchLink(profileLink, profile); } @@ -1413,7 +1405,7 @@ int main(int argc, char * * argv) globals.profile = getEnv("NIX_PROFILE", ""); if (globals.profile == "") { - Path profileLink = getHomeDir() + "/.nix-profile"; + Path profileLink = getHome() + "/.nix-profile"; globals.profile = pathExists(profileLink) ? absPath(readLink(profileLink), dirOf(profileLink)) : canonPath(settings.nixStateDir + "/profiles/default"); -- cgit 1.4.1