diff options
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/lazy.hh | 48 | ||||
-rw-r--r-- | src/libutil/util.cc | 45 | ||||
-rw-r--r-- | src/libutil/util.hh | 3 |
3 files changed, 79 insertions, 17 deletions
diff --git a/src/libutil/lazy.hh b/src/libutil/lazy.hh new file mode 100644 index 000000000000..d073e486c2eb --- /dev/null +++ b/src/libutil/lazy.hh @@ -0,0 +1,48 @@ +#include <exception> +#include <functional> +#include <mutex> + +namespace nix { + +/* A helper class for lazily-initialized variables. + + Lazy<T> 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<typename T> +class Lazy +{ + + typedef std::function<T()> 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 98c0aff1e722..1d1f68fc8452 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 <thread> #include <future> -#include <sys/wait.h> -#include <unistd.h> #include <fcntl.h> #include <limits.h> +#include <pwd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> #ifdef __APPLE__ #include <sys/syscall.h> @@ -417,14 +420,28 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix, } +static Lazy<Path> 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 a9950f830c50..5a9c9513fd5c 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(); |