aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/config.cc231
-rw-r--r--src/libutil/config.hh192
-rw-r--r--src/libutil/hash.cc2
-rw-r--r--src/libutil/json.cc43
-rw-r--r--src/libutil/json.hh11
-rw-r--r--src/libutil/lazy.hh48
-rw-r--r--src/libutil/logging.cc22
-rw-r--r--src/libutil/logging.hh99
-rw-r--r--src/libutil/pool.hh16
-rw-r--r--src/libutil/types.hh19
-rw-r--r--src/libutil/util.cc64
-rw-r--r--src/libutil/util.hh15
12 files changed, 666 insertions, 96 deletions
diff --git a/src/libutil/config.cc b/src/libutil/config.cc
new file mode 100644
index 000000000..f7a46bfee
--- /dev/null
+++ b/src/libutil/config.cc
@@ -0,0 +1,231 @@
+#include "config.hh"
+#include "args.hh"
+#include "json.hh"
+
+namespace nix {
+
+void Config::set(const std::string & name, const std::string & value)
+{
+ auto i = _settings.find(name);
+ if (i == _settings.end())
+ throw UsageError("unknown setting '%s'", name);
+ i->second.setting->set(value);
+ i->second.setting->overriden = true;
+}
+
+void Config::addSetting(AbstractSetting * setting)
+{
+ _settings.emplace(setting->name, Config::SettingData(false, setting));
+ for (auto & alias : setting->aliases)
+ _settings.emplace(alias, Config::SettingData(true, setting));
+
+ bool set = false;
+
+ auto i = initials.find(setting->name);
+ if (i != initials.end()) {
+ setting->set(i->second);
+ setting->overriden = true;
+ initials.erase(i);
+ set = true;
+ }
+
+ for (auto & alias : setting->aliases) {
+ auto i = initials.find(alias);
+ if (i != initials.end()) {
+ if (set)
+ warn("setting '%s' is set, but it's an alias of '%s' which is also set",
+ alias, setting->name);
+ else {
+ setting->set(i->second);
+ setting->overriden = true;
+ initials.erase(i);
+ set = true;
+ }
+ }
+ }
+}
+
+void Config::warnUnknownSettings()
+{
+ for (auto & i : initials)
+ warn("unknown setting '%s'", i.first);
+}
+
+StringMap Config::getSettings(bool overridenOnly)
+{
+ StringMap res;
+ for (auto & opt : _settings)
+ if (!opt.second.isAlias && (!overridenOnly || opt.second.setting->overriden))
+ res.emplace(opt.first, opt.second.setting->to_string());
+ return res;
+}
+
+void Config::applyConfigFile(const Path & path, bool fatal)
+{
+ try {
+ string contents = readFile(path);
+
+ unsigned int pos = 0;
+
+ while (pos < contents.size()) {
+ string line;
+ while (pos < contents.size() && contents[pos] != '\n')
+ line += contents[pos++];
+ pos++;
+
+ string::size_type hash = line.find('#');
+ if (hash != string::npos)
+ line = string(line, 0, hash);
+
+ vector<string> tokens = tokenizeString<vector<string> >(line);
+ if (tokens.empty()) continue;
+
+ if (tokens.size() < 2 || tokens[1] != "=")
+ throw UsageError("illegal configuration line ‘%1%’ in ‘%2%’", line, path);
+
+ string name = tokens[0];
+
+ vector<string>::iterator i = tokens.begin();
+ advance(i, 2);
+
+ try {
+ set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow
+ } catch (UsageError & e) {
+ if (fatal) throw;
+ warn("in configuration file '%s': %s", path, e.what());
+ }
+ };
+ } catch (SysError &) { }
+}
+
+void Config::resetOverriden()
+{
+ for (auto & s : _settings)
+ s.second.setting->overriden = false;
+}
+
+void Config::toJSON(JSONObject & out)
+{
+ for (auto & s : _settings)
+ if (!s.second.isAlias) {
+ JSONObject out2(out.object(s.first));
+ out2.attr("description", s.second.setting->description);
+ JSONPlaceholder out3(out2.placeholder("value"));
+ s.second.setting->toJSON(out3);
+ }
+}
+
+AbstractSetting::AbstractSetting(
+ const std::string & name,
+ const std::string & description,
+ const std::set<std::string> & aliases)
+ : name(name), description(description), aliases(aliases)
+{
+}
+
+void AbstractSetting::toJSON(JSONPlaceholder & out)
+{
+ out.write(to_string());
+}
+
+template<typename T>
+void BaseSetting<T>::toJSON(JSONPlaceholder & out)
+{
+ out.write(value);
+}
+
+template<> void BaseSetting<std::string>::set(const std::string & str)
+{
+ value = str;
+}
+
+template<> std::string BaseSetting<std::string>::to_string()
+{
+ return value;
+}
+
+template<typename T>
+void BaseSetting<T>::set(const std::string & str)
+{
+ static_assert(std::is_integral<T>::value, "Integer required.");
+ if (!string2Int(str, value))
+ throw UsageError("setting '%s' has invalid value '%s'", name, str);
+}
+
+template<typename T>
+std::string BaseSetting<T>::to_string()
+{
+ static_assert(std::is_integral<T>::value, "Integer required.");
+ return std::to_string(value);
+}
+
+template<> void BaseSetting<bool>::set(const std::string & str)
+{
+ if (str == "true" || str == "yes" || str == "1")
+ value = true;
+ else if (str == "false" || str == "no" || str == "0")
+ value = false;
+ else
+ throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str);
+}
+
+template<> std::string BaseSetting<bool>::to_string()
+{
+ return value ? "true" : "false";
+}
+
+template<> void BaseSetting<Strings>::set(const std::string & str)
+{
+ value = tokenizeString<Strings>(str);
+}
+
+template<> std::string BaseSetting<Strings>::to_string()
+{
+ return concatStringsSep(" ", value);
+}
+
+template<> void BaseSetting<Strings>::toJSON(JSONPlaceholder & out)
+{
+ JSONList list(out.list());
+ for (auto & s : value)
+ list.elem(s);
+}
+
+template<> void BaseSetting<StringSet>::set(const std::string & str)
+{
+ value = tokenizeString<StringSet>(str);
+}
+
+template<> std::string BaseSetting<StringSet>::to_string()
+{
+ return concatStringsSep(" ", value);
+}
+
+template<> void BaseSetting<StringSet>::toJSON(JSONPlaceholder & out)
+{
+ JSONList list(out.list());
+ for (auto & s : value)
+ list.elem(s);
+}
+
+template class BaseSetting<int>;
+template class BaseSetting<unsigned int>;
+template class BaseSetting<long>;
+template class BaseSetting<unsigned long>;
+template class BaseSetting<long long>;
+template class BaseSetting<unsigned long long>;
+template class BaseSetting<bool>;
+template class BaseSetting<std::string>;
+
+void PathSetting::set(const std::string & str)
+{
+ if (str == "") {
+ if (allowEmpty)
+ value = "";
+ else
+ throw UsageError("setting '%s' cannot be empty", name);
+ } else
+ value = canonPath(str);
+}
+
+}
diff --git a/src/libutil/config.hh b/src/libutil/config.hh
new file mode 100644
index 000000000..77620d47d
--- /dev/null
+++ b/src/libutil/config.hh
@@ -0,0 +1,192 @@
+#include <map>
+#include <set>
+
+#include "types.hh"
+
+#pragma once
+
+namespace nix {
+
+class Args;
+class AbstractSetting;
+class JSONPlaceholder;
+class JSONObject;
+
+/* A class to simplify providing configuration settings. The typical
+ use is to inherit Config and add Setting<T> members:
+
+ class MyClass : private Config
+ {
+ Setting<int> foo{this, 123, "foo", "the number of foos to use"};
+ Setting<std::string> bar{this, "blabla", "bar", "the name of the bar"};
+
+ MyClass() : Config(readConfigFile("/etc/my-app.conf"))
+ {
+ std::cout << foo << "\n"; // will print 123 unless overriden
+ }
+ };
+*/
+
+class Config
+{
+ friend class AbstractSetting;
+
+ struct SettingData
+ {
+ bool isAlias;
+ AbstractSetting * setting;
+ SettingData(bool isAlias, AbstractSetting * setting)
+ : isAlias(isAlias), setting(setting)
+ { }
+ };
+
+ std::map<std::string, SettingData> _settings;
+
+ StringMap initials;
+
+public:
+
+ Config(const StringMap & initials)
+ : initials(initials)
+ { }
+
+ void set(const std::string & name, const std::string & value);
+
+ void addSetting(AbstractSetting * setting);
+
+ void warnUnknownSettings();
+
+ StringMap getSettings(bool overridenOnly = false);
+
+ void applyConfigFile(const Path & path, bool fatal = false);
+
+ void resetOverriden();
+
+ void toJSON(JSONObject & out);
+};
+
+class AbstractSetting
+{
+ friend class Config;
+
+public:
+
+ const std::string name;
+ const std::string description;
+ const std::set<std::string> aliases;
+
+ int created = 123;
+
+ bool overriden = false;
+
+protected:
+
+ AbstractSetting(
+ const std::string & name,
+ const std::string & description,
+ const std::set<std::string> & aliases);
+
+ virtual ~AbstractSetting()
+ {
+ // Check against a gcc miscompilation causing our constructor
+ // not to run (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80431).
+ assert(created == 123);
+ }
+
+ virtual void set(const std::string & value) = 0;
+
+ virtual std::string to_string() = 0;
+
+ virtual void toJSON(JSONPlaceholder & out);
+
+ bool isOverriden() { return overriden; }
+};
+
+/* A setting of type T. */
+template<typename T>
+class BaseSetting : public AbstractSetting
+{
+protected:
+
+ T value;
+
+public:
+
+ BaseSetting(const T & def,
+ const std::string & name,
+ const std::string & description,
+ const std::set<std::string> & aliases = {})
+ : AbstractSetting(name, description, aliases)
+ , value(def)
+ { }
+
+ operator const T &() const { return value; }
+ operator T &() { return value; }
+ const T & get() const { return value; }
+ bool operator ==(const T & v2) const { return value == v2; }
+ bool operator !=(const T & v2) const { return value != v2; }
+ void operator =(const T & v) { assign(v); }
+ virtual void assign(const T & v) { value = v; }
+
+ void set(const std::string & str) override;
+
+ std::string to_string() override;
+
+ void toJSON(JSONPlaceholder & out) override;
+};
+
+template<typename T>
+std::ostream & operator <<(std::ostream & str, const BaseSetting<T> & opt)
+{
+ str << (const T &) opt;
+ return str;
+}
+
+template<typename T>
+bool operator ==(const T & v1, const BaseSetting<T> & v2) { return v1 == (const T &) v2; }
+
+template<typename T>
+class Setting : public BaseSetting<T>
+{
+public:
+ Setting(Config * options,
+ const T & def,
+ const std::string & name,
+ const std::string & description,
+ const std::set<std::string> & aliases = {})
+ : BaseSetting<T>(def, name, description, aliases)
+ {
+ options->addSetting(this);
+ }
+
+ void operator =(const T & v) { this->assign(v); }
+};
+
+/* A special setting for Paths. These are automatically canonicalised
+ (e.g. "/foo//bar/" becomes "/foo/bar"). */
+class PathSetting : public BaseSetting<Path>
+{
+ bool allowEmpty;
+
+public:
+
+ PathSetting(Config * options,
+ bool allowEmpty,
+ const Path & def,
+ const std::string & name,
+ const std::string & description,
+ const std::set<std::string> & aliases = {})
+ : BaseSetting<Path>(def, name, description, aliases)
+ , allowEmpty(allowEmpty)
+ {
+ options->addSetting(this);
+ }
+
+ void set(const std::string & str) override;
+
+ Path operator +(const char * p) const { return value + p; }
+
+ void operator =(const Path & v) { this->assign(v); }
+};
+
+}
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index 9f4afd93c..fa1bb5d97 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -224,7 +224,7 @@ static void start(HashType ht, Ctx & ctx)
static void update(HashType ht, Ctx & ctx,
- const unsigned char * bytes, unsigned int len)
+ const unsigned char * bytes, size_t len)
{
if (ht == htMD5) MD5_Update(&ctx.md5, bytes, len);
else if (ht == htSHA1) SHA1_Update(&ctx.sha1, bytes, len);
diff --git a/src/libutil/json.cc b/src/libutil/json.cc
index 6023d1d4f..b8b8ef9c8 100644
--- a/src/libutil/json.cc
+++ b/src/libutil/json.cc
@@ -19,49 +19,32 @@ void toJSON(std::ostream & str, const char * start, const char * end)
str << '"';
}
-void toJSON(std::ostream & str, const std::string & s)
-{
- toJSON(str, s.c_str(), s.c_str() + s.size());
-}
-
void toJSON(std::ostream & str, const char * s)
{
if (!s) str << "null"; else toJSON(str, s, s + strlen(s));
}
-void toJSON(std::ostream & str, unsigned long long n)
-{
- str << n;
-}
-
-void toJSON(std::ostream & str, unsigned long n)
-{
- str << n;
-}
-
-void toJSON(std::ostream & str, long n)
-{
- str << n;
-}
+template<> void toJSON<int>(std::ostream & str, const int & n) { str << n; }
+template<> void toJSON<unsigned int>(std::ostream & str, const unsigned int & n) { str << n; }
+template<> void toJSON<long>(std::ostream & str, const long & n) { str << n; }
+template<> void toJSON<unsigned long>(std::ostream & str, const unsigned long & n) { str << n; }
+template<> void toJSON<long long>(std::ostream & str, const long long & n) { str << n; }
+template<> void toJSON<unsigned long long>(std::ostream & str, const unsigned long long & n) { str << n; }
+template<> void toJSON<float>(std::ostream & str, const float & n) { str << n; }
-void toJSON(std::ostream & str, unsigned int n)
+template<> void toJSON<std::string>(std::ostream & str, const std::string & s)
{
- str << n;
-}
-
-void toJSON(std::ostream & str, int n)
-{
- str << n;
+ toJSON(str, s.c_str(), s.c_str() + s.size());
}
-void toJSON(std::ostream & str, double f)
+template<> void toJSON<bool>(std::ostream & str, const bool & b)
{
- str << f;
+ str << (b ? "true" : "false");
}
-void toJSON(std::ostream & str, bool b)
+template<> void toJSON<std::nullptr_t>(std::ostream & str, const std::nullptr_t & b)
{
- str << (b ? "true" : "false");
+ str << "null";
}
JSONWriter::JSONWriter(std::ostream & str, bool indent)
diff --git a/src/libutil/json.hh b/src/libutil/json.hh
index 03eecb732..595e9bbe3 100644
--- a/src/libutil/json.hh
+++ b/src/libutil/json.hh
@@ -7,15 +7,10 @@
namespace nix {
void toJSON(std::ostream & str, const char * start, const char * end);
-void toJSON(std::ostream & str, const std::string & s);
void toJSON(std::ostream & str, const char * s);
-void toJSON(std::ostream & str, unsigned long long n);
-void toJSON(std::ostream & str, unsigned long n);
-void toJSON(std::ostream & str, long n);
-void toJSON(std::ostream & str, unsigned int n);
-void toJSON(std::ostream & str, int n);
-void toJSON(std::ostream & str, double f);
-void toJSON(std::ostream & str, bool b);
+
+template<typename T>
+void toJSON(std::ostream & str, const T & n);
class JSONWriter
{
diff --git a/src/libutil/lazy.hh b/src/libutil/lazy.hh
new file mode 100644
index 000000000..d073e486c
--- /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/logging.cc b/src/libutil/logging.cc
index d9e8d22d7..2d0acca24 100644
--- a/src/libutil/logging.cc
+++ b/src/libutil/logging.cc
@@ -1,9 +1,16 @@
#include "logging.hh"
#include "util.hh"
+#include <atomic>
+
namespace nix {
-Logger * logger = 0;
+Logger * logger = makeDefaultLogger();
+
+void Logger::warn(const std::string & msg)
+{
+ log(lvlInfo, ANSI_RED "warning:" ANSI_NORMAL " " + msg);
+}
class SimpleLogger : public Logger
{
@@ -37,12 +44,7 @@ public:
writeToStderr(prefix + (tty ? fs.s : filterANSIEscapes(fs.s)) + "\n");
}
- void startActivity(Activity & activity, Verbosity lvl, const FormatOrString & fs) override
- {
- log(lvl, fs);
- }
-
- void stopActivity(Activity & activity) override
+ void event(const Event & ev) override
{
}
};
@@ -52,7 +54,7 @@ Verbosity verbosity = lvlInfo;
void warnOnce(bool & haveWarned, const FormatOrString & fs)
{
if (!haveWarned) {
- printError(format("warning: %1%") % fs.s);
+ warn(fs.s);
haveWarned = true;
}
}
@@ -74,4 +76,8 @@ Logger * makeDefaultLogger()
return new SimpleLogger();
}
+std::atomic<uint64_t> Activity::nextId{(uint64_t) getpid() << 32};
+
+Activity::Activity() : id(nextId++) { };
+
}
diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh
index 3f8366479..ddfc336fe 100644
--- a/src/libutil/logging.hh
+++ b/src/libutil/logging.hh
@@ -13,7 +13,64 @@ typedef enum {
lvlVomit
} Verbosity;
-class Activity;
+class Activity
+{
+ static std::atomic<uint64_t> nextId;
+public:
+ typedef uint64_t t;
+ const t id;
+ Activity();
+ Activity(const Activity & act) : id(act.id) { };
+ Activity(uint64_t id) : id(id) { };
+};
+
+typedef enum {
+ evBuildCreated = 0,
+ evBuildStarted = 1,
+ evBuildOutput = 2,
+ evBuildFinished = 3,
+ evDownloadCreated = 4,
+ evDownloadDestroyed = 5,
+ evDownloadProgress = 6,
+ evDownloadSucceeded = 7,
+ evSubstitutionCreated = 8,
+ evSubstitutionStarted = 9,
+ evSubstitutionFinished = 10,
+} EventType;
+
+struct Event
+{
+ struct Field
+ {
+ // FIXME: use std::variant.
+ enum { tInt, tString } type;
+ uint64_t i = 0;
+ std::string s;
+ Field(const std::string & s) : type(tString), s(s) { }
+ Field(const char * s) : type(tString), s(s) { }
+ Field(const uint64_t & i) : type(tInt), i(i) { }
+ Field(const Activity & act) : type(tInt), i(act.id) { }
+ };
+
+ typedef std::vector<Field> Fields;
+
+ EventType type;
+ Fields fields;
+
+ std::string getS(size_t n) const
+ {
+ assert(n < fields.size());
+ assert(fields[n].type == Field::tString);
+ return fields[n].s;
+ }
+
+ uint64_t getI(size_t n) const
+ {
+ assert(n < fields.size());
+ assert(fields[n].type == Field::tInt);
+ return fields[n].i;
+ }
+};
class Logger
{
@@ -30,34 +87,18 @@ public:
log(lvlInfo, fs);
}
- virtual void setExpected(const std::string & label, uint64_t value = 1) { }
- virtual void setProgress(const std::string & label, uint64_t value = 1) { }
- virtual void incExpected(const std::string & label, uint64_t value = 1) { }
- virtual void incProgress(const std::string & label, uint64_t value = 1) { }
+ virtual void warn(const std::string & msg);
-private:
-
- virtual void startActivity(Activity & activity, Verbosity lvl, const FormatOrString & fs) = 0;
-
- virtual void stopActivity(Activity & activity) = 0;
-
-};
-
-class Activity
-{
-public:
- Logger & logger;
-
- Activity(Logger & logger, Verbosity lvl, const FormatOrString & fs)
- : logger(logger)
+ template<typename... Args>
+ void event(EventType type, const Args & ... args)
{
- logger.startActivity(*this, lvl, fs);
+ Event ev;
+ ev.type = type;
+ nop{(ev.fields.emplace_back(Event::Field(args)), 1)...};
+ event(ev);
}
- ~Activity()
- {
- logger.stopActivity(*this);
- }
+ virtual void event(const Event & ev) = 0;
};
extern Logger * logger;
@@ -82,6 +123,14 @@ extern Verbosity verbosity; /* suppress msgs > this */
#define debug(args...) printMsg(lvlDebug, args)
#define vomit(args...) printMsg(lvlVomit, args)
+template<typename... Args>
+inline void warn(const std::string & fs, Args... args)
+{
+ boost::format f(fs);
+ nop{boost::io::detail::feed(f, args)...};
+ logger->warn(f.str());
+}
+
void warnOnce(bool & haveWarned, const FormatOrString & fs);
void writeToStderr(const string & s);
diff --git a/src/libutil/pool.hh b/src/libutil/pool.hh
index 20df21948..703309002 100644
--- a/src/libutil/pool.hh
+++ b/src/libutil/pool.hh
@@ -68,6 +68,22 @@ public:
state_->max = max;
}
+ void incCapacity()
+ {
+ auto state_(state.lock());
+ state_->max++;
+ /* we could wakeup here, but this is only used when we're
+ * about to nest Pool usages, and we want to save the slot for
+ * the nested use if we can
+ */
+ }
+
+ void decCapacity()
+ {
+ auto state_(state.lock());
+ state_->max--;
+ }
+
~Pool()
{
auto state_(state.lock());
diff --git a/src/libutil/types.hh b/src/libutil/types.hh
index 97d79af9b..9f32d31ad 100644
--- a/src/libutil/types.hh
+++ b/src/libutil/types.hh
@@ -7,6 +7,7 @@
#include <list>
#include <set>
#include <memory>
+#include <map>
#include <boost/format.hpp>
@@ -31,6 +32,11 @@ using std::vector;
using boost::format;
+/* A variadic template that does nothing. Useful to call a function
+ for all variadic arguments but ignoring the result. */
+struct nop { template<typename... T> nop(T...) {} };
+
+
struct FormatOrString
{
string s;
@@ -45,16 +51,6 @@ struct FormatOrString
... a_n’. However, ‘fmt(s)’ is equivalent to ‘s’ (so no %-expansion
takes place). */
-inline void formatHelper(boost::format & f)
-{
-}
-
-template<typename T, typename... Args>
-inline void formatHelper(boost::format & f, T x, Args... args)
-{
- formatHelper(f % x, args...);
-}
-
inline std::string fmt(const std::string & s)
{
return s;
@@ -74,7 +70,7 @@ template<typename... Args>
inline std::string fmt(const std::string & fs, Args... args)
{
boost::format f(fs);
- formatHelper(f, args...);
+ nop{boost::io::detail::feed(f, args)...};
return f.str();
}
@@ -141,6 +137,7 @@ private:
typedef list<string> Strings;
typedef set<string> StringSet;
+typedef std::map<std::string, std::string> StringMap;
/* Paths are just strings. */
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index a640a64c7..16f4b232e 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>
@@ -96,6 +99,8 @@ Path absPath(Path path, Path dir)
Path canonPath(const Path & path, bool resolveSymlinks)
{
+ assert(path != "");
+
string s;
if (path[0] != '/')
@@ -367,7 +372,7 @@ void deletePath(const Path & path)
void deletePath(const Path & path, unsigned long long & bytesFreed)
{
- Activity act(*logger, lvlDebug, format("recursively deleting path ‘%1%’") % path);
+ //Activity act(*logger, lvlDebug, format("recursively deleting path ‘%1%’") % path);
bytesFreed = 0;
_deletePath(path, bytesFreed);
}
@@ -415,18 +420,50 @@ 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;
}
+Path getConfigDir()
+{
+ Path configDir = getEnv("XDG_CONFIG_HOME");
+ if (configDir.empty())
+ configDir = getHome() + "/.config";
+ return configDir;
+}
+
+
+Path getDataDir()
+{
+ Path dataDir = getEnv("XDG_DATA_HOME");
+ if (dataDir.empty())
+ dataDir = getHome() + "/.local/share";
+ return dataDir;
+}
+
+
Paths createDirs(const Path & path)
{
Paths created;
@@ -932,7 +969,12 @@ void closeOnExec(int fd)
bool _isInterrupted = false;
-thread_local bool interruptThrown = false;
+static thread_local bool interruptThrown = false;
+
+void setInterruptThrown()
+{
+ interruptThrown = true;
+}
void _interrupted()
{
@@ -1047,9 +1089,9 @@ bool statusOk(int status)
}
-bool hasPrefix(const string & s, const string & suffix)
+bool hasPrefix(const string & s, const string & prefix)
{
- return s.compare(0, suffix.size(), suffix) == 0;
+ return s.compare(0, prefix.size(), prefix) == 0;
}
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 0e6941e4a..7ea32e8d9 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -110,9 +110,18 @@ 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 the path to $XDG_CACHE_HOME/.cache. */
+/* Return $HOME or the user's home directory from /etc/passwd. */
+Path getHome();
+
+/* Return $XDG_CACHE_HOME or $HOME/.cache. */
Path getCacheDir();
+/* Return $XDG_CONFIG_HOME or $HOME/.config. */
+Path getConfigDir();
+
+/* Return $XDG_DATA_HOME or $HOME/.local/share. */
+Path getDataDir();
+
/* Create a directory and all its parents, if necessary. Returns the
list of created directories, in order of creation. */
Paths createDirs(const Path & path);
@@ -264,7 +273,7 @@ void closeOnExec(int fd);
extern bool _isInterrupted;
-extern thread_local bool interruptThrown;
+void setInterruptThrown();
void _interrupted();
@@ -355,6 +364,8 @@ void ignoreException();
#define ANSI_NORMAL "\e[0m"
#define ANSI_BOLD "\e[1m"
#define ANSI_RED "\e[31;1m"
+#define ANSI_GREEN "\e[32;1m"
+#define ANSI_BLUE "\e[34;1m"
/* Filter out ANSI escape codes from the given string. If ‘nixOnly’ is