diff options
-rw-r--r-- | Makefile.config.in | 1 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | doc/manual/command-ref/conf-file.xml | 72 | ||||
-rw-r--r-- | mk/lib.mk | 4 | ||||
-rw-r--r-- | src/libmain/shared.cc | 2 | ||||
-rw-r--r-- | src/libstore/gc.cc | 11 | ||||
-rw-r--r-- | src/libstore/globals.cc | 39 | ||||
-rw-r--r-- | src/libstore/globals.hh | 52 | ||||
-rw-r--r-- | src/libstore/local.mk | 3 | ||||
-rw-r--r-- | src/libstore/remote-store.cc | 2 | ||||
-rw-r--r-- | src/libutil/config.cc | 100 | ||||
-rw-r--r-- | src/libutil/config.hh | 64 | ||||
-rw-r--r-- | src/libutil/json.cc | 43 | ||||
-rw-r--r-- | src/libutil/json.hh | 11 | ||||
-rw-r--r-- | src/libutil/util.cc | 19 | ||||
-rw-r--r-- | src/libutil/util.hh | 7 | ||||
-rw-r--r-- | src/nix-daemon/nix-daemon.cc | 50 | ||||
-rw-r--r-- | src/nix/show-config.cc | 3 |
18 files changed, 310 insertions, 174 deletions
diff --git a/Makefile.config.in b/Makefile.config.in index 53dca1fcf10a..6948dad5a60b 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -15,6 +15,7 @@ SQLITE3_LIBS = @SQLITE3_LIBS@ bash = @bash@ bindir = @bindir@ bro = @bro@ +lsof = @lsof@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ diff --git a/configure.ac b/configure.ac index 3e6a894e3b10..c7026cf954dd 100644 --- a/configure.ac +++ b/configure.ac @@ -128,6 +128,7 @@ NEED_PROG(xz, xz) AC_PATH_PROG(dot, dot) AC_PATH_PROG(pv, pv, pv) AC_PATH_PROG(bro, bro, bro) +AC_PATH_PROG(lsof, lsof, lsof) NEED_PROG(cat, cat) diff --git a/doc/manual/command-ref/conf-file.xml b/doc/manual/command-ref/conf-file.xml index 3de9647aa4eb..616983bc7f0e 100644 --- a/doc/manual/command-ref/conf-file.xml +++ b/doc/manual/command-ref/conf-file.xml @@ -17,13 +17,32 @@ <refsection><title>Description</title> -<para>A number of persistent settings of Nix are stored in the file -<filename><replaceable>sysconfdir</replaceable>/nix/nix.conf</filename> or -<filename>$NIX_CONF_DIR/nix.conf</filename> if <envar>NIX_CONF_DIR</envar> is set. -This file is a list of <literal><replaceable>name</replaceable> = +<para>Nix reads settings from two configuration files:</para> + +<itemizedlist> + + <listitem> + <para>The system-wide configuration file + <filename><replaceable>sysconfdir</replaceable>/nix/nix.conf</filename> + (i.e. <filename>/etc/nix/nix.conf</filename> on most systems), or + <filename>$NIX_CONF_DIR/nix.conf</filename> if + <envar>NIX_CONF_DIR</envar> is set.</para> + </listitem> + + <listitem> + <para>The user configuration file + <filename>$XDG_CONFIG_HOME/nix/nix.conf</filename>, or + <filename>~/.config/nix/nix.conf</filename> if + <envar>XDG_CONFIG_HOME</envar> is not set.</para> + </listitem> + +</itemizedlist> + +<para>The configuration files consist of +<literal><replaceable>name</replaceable> = <replaceable>value</replaceable></literal> pairs, one per line. -Comments start with a <literal>#</literal> character. Here is an example -configuration file:</para> +Comments start with a <literal>#</literal> character. Here is an +example configuration file:</para> <programlisting> gc-keep-outputs = true # Nice for developers @@ -31,8 +50,9 @@ gc-keep-derivations = true # Idem env-keep-derivations = false </programlisting> -<para>You can override settings using the <option>--option</option> -flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para> +<para>You can override settings on the command line using the +<option>--option</option> flag, e.g. <literal>--option gc-keep-outputs +false</literal>.</para> <para>The following settings are currently available: @@ -334,15 +354,16 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para> </varlistentry> - <varlistentry><term><literal>binary-caches</literal></term> + <varlistentry><term><literal>substituters</literal></term> - <listitem><para>A list of URLs of binary caches, separated by + <listitem><para>A list of URLs of substituters, separated by whitespace. The default is <literal>https://cache.nixos.org</literal>.</para></listitem> </varlistentry> + <!-- <varlistentry><term><literal>binary-caches-files</literal></term> <listitem><para>A list of names of files that will be read to @@ -355,28 +376,28 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para> option to read files created by untrusted users!</para></listitem> </varlistentry> + --> - <varlistentry><term><literal>trusted-binary-caches</literal></term> + <varlistentry><term><literal>trusted-substituters</literal></term> - <listitem><para>A list of URLs of binary caches, separated by + <listitem><para>A list of URLs of substituters, separated by whitespace. These are not used by default, but can be enabled by users of the Nix daemon by specifying <literal>--option - binary-caches <replaceable>urls</replaceable></literal> on the + substituters <replaceable>urls</replaceable></literal> on the command line. Unprivileged users are only allowed to pass a - subset of the URLs listed in <literal>binary-caches</literal> and - <literal>trusted-binary-caches</literal>.</para></listitem> + subset of the URLs listed in <literal>substituters</literal> and + <literal>trusted-substituters</literal>.</para></listitem> </varlistentry> - <varlistentry><term><literal>extra-binary-caches</literal></term> + <varlistentry><term><literal>extra-substituters</literal></term> <listitem><para>Additional binary caches appended to those - specified in <option>binary-caches</option> and - <option>binary-caches-files</option>. When used by unprivileged - users, untrusted binary caches (i.e. those not listed in - <option>trusted-binary-caches</option>) are silently + specified in <option>substituters</option>. When used by + unprivileged users, untrusted substituters (i.e. those not listed + in <option>trusted-substituters</option>) are silently ignored.</para></listitem> </varlistentry> @@ -403,7 +424,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para> </varlistentry> - <varlistentry><term><literal>binary-caches-parallel-connections</literal></term> + <varlistentry><term><literal>http-connections</literal></term> <listitem><para>The maximum number of parallel TCP connections used to fetch files from binary caches and by other downloads. It @@ -412,15 +433,6 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para> </varlistentry> - <varlistentry><term><literal>verify-https-binary-caches</literal></term> - - <listitem><para>Whether HTTPS binary caches are required to have a - certificate that can be verified. Defaults to - <literal>true</literal>.</para></listitem> - - </varlistentry> - - <varlistentry><term><literal>netrc-file</literal></term> <listitem><para>If set to an absolute path to a <filename>netrc</filename> diff --git a/mk/lib.mk b/mk/lib.mk index bb82801d3b4e..1da51d879734 100644 --- a/mk/lib.mk +++ b/mk/lib.mk @@ -53,8 +53,8 @@ BUILD_SHARED_LIBS ?= 1 ifeq ($(BUILD_SHARED_LIBS), 1) ifeq (CYGWIN,$(findstring CYGWIN,$(OS))) - GLOBAL_CFLAGS += -U__STRICT_ANSI__ - GLOBAL_CXXFLAGS += -U__STRICT_ANSI__ + GLOBAL_CFLAGS += -U__STRICT_ANSI__ -D_GNU_SOURCE + GLOBAL_CXXFLAGS += -U__STRICT_ANSI__ -D_GNU_SOURCE else GLOBAL_CFLAGS += -fPIC GLOBAL_CXXFLAGS += -fPIC diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 4747b9bf9b4c..d6c1c0c9cb49 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -259,7 +259,7 @@ int handleExceptions(const string & programName, std::function<void()> fun) condition is discharged before we reach printMsg() below, since otherwise it will throw an (uncaught) exception. */ - interruptThrown = true; + setInterruptThrown(); throw; } } catch (Exit & e) { diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 0b03d61a789a..b6d462d2bffb 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -426,22 +426,27 @@ void LocalStore::findRuntimeRoots(PathSet & roots) throw SysError("iterating /proc"); } +#if !defined(__linux__) try { - auto lsofRegex = std::regex(R"(^n(/.*)$)"); + printError("RUN LSOF %s", LSOF); + std::regex lsofRegex(R"(^n(/.*)$)"); auto lsofLines = - tokenizeString<std::vector<string>>(runProgram("lsof", true, { "-n", "-w", "-F", "n" }), "\n"); + tokenizeString<std::vector<string>>(runProgram(LSOF, true, { "-n", "-w", "-F", "n" }), "\n"); for (const auto & line : lsofLines) { - auto match = std::smatch{}; + std::smatch match; if (std::regex_match(line, match, lsofRegex)) paths.emplace(match[1]); } } catch (ExecError & e) { /* lsof not installed, lsof failed */ } +#endif +#if defined(__linux__) readFileRoots("/proc/sys/kernel/modprobe", paths); readFileRoots("/proc/sys/kernel/fbsplash", paths); readFileRoots("/proc/sys/kernel/poweroff_cmd", paths); +#endif for (auto & i : paths) if (isInStore(i)) { diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index bb61daa51642..953bf6aaaa0a 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -53,19 +53,19 @@ Settings::Settings() void Settings::loadConfFile() { applyConfigFile(nixConfDir + "/nix.conf"); + + /* We only want to send overrides to the daemon, i.e. stuff from + ~/.nix/nix.conf or the command line. */ + resetOverriden(); + + applyConfigFile(getConfigDir() + "/nix/nix.conf"); } void Settings::set(const string & name, const string & value) { - overrides[name] = value; Config::set(name, value); } -StringMap Settings::getOverrides() -{ - return overrides; -} - unsigned int Settings::getDefaultCores() { return std::max(1U, std::thread::hardware_concurrency()); @@ -73,7 +73,7 @@ unsigned int Settings::getDefaultCores() const string nixVersion = PACKAGE_VERSION; -template<> void Setting<SandboxMode>::set(const std::string & str) +template<> void BaseSetting<SandboxMode>::set(const std::string & str) { if (str == "true") value = smEnabled; else if (str == "relaxed") value = smRelaxed; @@ -81,7 +81,7 @@ template<> void Setting<SandboxMode>::set(const std::string & str) else throw UsageError("option '%s' has invalid value '%s'", name, str); } -template<> std::string Setting<SandboxMode>::to_string() +template<> std::string BaseSetting<SandboxMode>::to_string() { if (value == smEnabled) return "true"; else if (value == smRelaxed) return "relaxed"; @@ -89,27 +89,16 @@ template<> std::string Setting<SandboxMode>::to_string() else abort(); } -template<> void Setting<unsigned int, Settings::MaxBuildJobsTag>::set(const std::string & str) +template<> void BaseSetting<SandboxMode>::toJSON(JSONPlaceholder & out) { - if (str == "auto") value = std::max(1U, std::thread::hardware_concurrency()); - else if (!string2Int(str, value)) - throw UsageError("configuration setting ‘%s’ should be ‘auto’ or an integer", name); + AbstractSetting::toJSON(out); } -template<> std::string Setting<unsigned int, Settings::MaxBuildJobsTag>::to_string() +void MaxBuildJobsSetting::set(const std::string & str) { - return std::to_string(value); -} - -template<> void Setting<bool, Settings::CaseHackTag>::set(const std::string & str) -{ - value = parseBool(str); - nix::useCaseHack = true; -} - -template<> std::string Setting<bool, Settings::CaseHackTag>::to_string() -{ - return printBool(value); + if (str == "auto") value = std::max(1U, std::thread::hardware_concurrency()); + else if (!string2Int(str, value)) + throw UsageError("configuration setting ‘%s’ should be ‘auto’ or an integer", name); } } diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 72863920de99..b4f44de2e65d 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -13,9 +13,40 @@ typedef enum { smEnabled, smRelaxed, smDisabled } SandboxMode; extern bool useCaseHack; // FIXME -class Settings : public Config { +struct CaseHackSetting : public BaseSetting<bool> +{ + CaseHackSetting(Config * options, + const std::string & name, + const std::string & description, + const std::set<std::string> & aliases = {}) + : BaseSetting<bool>(useCaseHack, name, description, aliases) + { + options->addSetting(this); + } + + void set(const std::string & str) override + { + BaseSetting<bool>::set(str); + nix::useCaseHack = true; + } +}; - StringMap overrides; +struct MaxBuildJobsSetting : public BaseSetting<unsigned int> +{ + MaxBuildJobsSetting(Config * options, + unsigned int def, + const std::string & name, + const std::string & description, + const std::set<std::string> & aliases = {}) + : BaseSetting<unsigned int>(def, name, description, aliases) + { + options->addSetting(this); + } + + void set(const std::string & str) override; +}; + +class Settings : public Config { unsigned int getDefaultCores(); @@ -27,8 +58,6 @@ public: void set(const string & name, const string & value); - StringMap getOverrides(); - Path nixPrefix; /* The directory where we store sources and derived files. */ @@ -70,8 +99,7 @@ public: the log to show if a build fails. */ size_t logLines = 10; - struct MaxBuildJobsTag { }; - Setting<unsigned int, MaxBuildJobsTag> maxBuildJobs{this, 1, "build-max-jobs", + MaxBuildJobsSetting maxBuildJobs{this, 1, "build-max-jobs", "Maximum number of parallel build jobs. \"auto\" means use number of cores."}; Setting<unsigned int> buildCores{this, getDefaultCores(), "build-cores", @@ -216,8 +244,9 @@ public: Setting<Strings> secretKeyFiles{this, {}, "secret-key-files", "Secret keys with which to sign local builds."}; - Setting<size_t> binaryCachesParallelConnections{this, 25, "binary-caches-parallel-connections", - "Number of parallel connections to binary caches."}; + Setting<size_t> binaryCachesParallelConnections{this, 25, "http-connections", + "Number of parallel HTTP connections.", + {"binary-caches-parallel-connections"}}; Setting<bool> enableHttp2{this, true, "enable-http2", "Whether to enable HTTP/2 support."}; @@ -239,6 +268,10 @@ public: "Additional URIs of substituters.", {"extra-binary-caches"}}; + Setting<StringSet> trustedSubstituters{this, {}, "trusted-substituters", + "Disabled substituters that may be enabled via the substituters option by untrusted users.", + {"trusted-binary-caches"}}; + Setting<Strings> trustedUsers{this, {"root"}, "trusted-users", "Which users or groups are trusted to ask the daemon to do unsafe things."}; @@ -267,8 +300,7 @@ public: Setting<bool> enableImportFromDerivation{this, true, "allow-import-from-derivation", "Whether the evaluator allows importing the result of a derivation."}; - struct CaseHackTag { }; - Setting<bool, CaseHackTag> useCaseHack{this, nix::useCaseHack, "use-case-hack", + CaseHackSetting useCaseHack{this, "use-case-hack", "Whether to enable a Darwin-specific hack for dealing with file name collisions."}; Setting<unsigned long> connectTimeout{this, 0, "connect-timeout", diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 9d5c04dca0c5..4da20330cf3f 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -27,7 +27,8 @@ libstore_CXXFLAGS = \ -DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \ -DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \ -DNIX_BIN_DIR=\"$(bindir)\" \ - -DBASH_PATH="\"$(bash)\"" + -DBASH_PATH="\"$(bash)\"" \ + -DLSOF=\"$(lsof)\" $(d)/local-store.cc: $(d)/schema.sql.hh diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index da3c8eb8d89d..bc9ef3d47c5e 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -166,7 +166,7 @@ void RemoteStore::setOptions(Connection & conn) << settings.useSubstitutes; if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 12) { - StringMap overrides = settings.getOverrides(); + auto overrides = settings.getSettings(true); conn.to << overrides.size(); for (auto & i : overrides) conn.to << i.first << i.second; diff --git a/src/libutil/config.cc b/src/libutil/config.cc index e7a810cec4d2..62c6433c741b 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -1,5 +1,6 @@ #include "config.hh" #include "args.hh" +#include "json.hh" namespace nix { @@ -9,6 +10,7 @@ void Config::set(const std::string & name, const std::string & value) 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) @@ -22,6 +24,7 @@ void Config::addSetting(AbstractSetting * setting) auto i = initials.find(setting->name); if (i != initials.end()) { setting->set(i->second); + setting->overriden = true; initials.erase(i); set = true; } @@ -34,6 +37,7 @@ void Config::addSetting(AbstractSetting * setting) alias, setting->name); else { setting->set(i->second); + setting->overriden = true; initials.erase(i); set = true; } @@ -47,11 +51,11 @@ void Config::warnUnknownSettings() warn("unknown setting '%s'", i.first); } -StringMap Config::getSettings() +StringMap Config::getSettings(bool overridenOnly) { StringMap res; for (auto & opt : _settings) - if (!opt.second.isAlias) + if (!opt.second.isAlias && (!overridenOnly || opt.second.setting->overriden)) res.emplace(opt.first, opt.second.setting->to_string()); return res; } @@ -94,6 +98,23 @@ void Config::applyConfigFile(const Path & path, bool fatal) } 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, @@ -102,83 +123,98 @@ AbstractSetting::AbstractSetting( { } -template<> void Setting<std::string>::set(const std::string & str) +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 Setting<std::string>::to_string() +template<> std::string BaseSetting<std::string>::to_string() { return value; } -template<typename T, typename Tag> -void Setting<T, Tag>::set(const std::string & str) +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, typename Tag> -std::string Setting<T, Tag>::to_string() +template<typename T> +std::string BaseSetting<T>::to_string() { static_assert(std::is_integral<T>::value, "Integer required."); return std::to_string(value); } -bool AbstractSetting::parseBool(const std::string & str) +template<> void BaseSetting<bool>::set(const std::string & str) { if (str == "true" || str == "yes" || str == "1") - return true; + value = true; else if (str == "false" || str == "no" || str == "0") - return false; + value = false; else throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str); } -template<> void Setting<bool>::set(const std::string & str) +template<> std::string BaseSetting<bool>::to_string() { - value = parseBool(str); + return value ? "true" : "false"; } -std::string AbstractSetting::printBool(bool b) +template<> void BaseSetting<Strings>::set(const std::string & str) { - return b ? "true" : "false"; + value = tokenizeString<Strings>(str); } - -template<> std::string Setting<bool>::to_string() +template<> std::string BaseSetting<Strings>::to_string() { - return printBool(value); + return concatStringsSep(" ", value); } -template<> void Setting<Strings>::set(const std::string & str) +template<> void BaseSetting<Strings>::toJSON(JSONPlaceholder & out) { - value = tokenizeString<Strings>(str); + JSONList list(out.list()); + for (auto & s : value) + list.elem(s); } -template<> std::string Setting<Strings>::to_string() +template<> void BaseSetting<StringSet>::set(const std::string & str) { - return concatStringsSep(" ", value); + value = tokenizeString<StringSet>(str); } -template<> void Setting<StringSet>::set(const std::string & str) +template<> std::string BaseSetting<StringSet>::to_string() { - value = tokenizeString<StringSet>(str); + return concatStringsSep(" ", value); } -template<> std::string Setting<StringSet>::to_string() +template<> void BaseSetting<StringSet>::toJSON(JSONPlaceholder & out) { - return concatStringsSep(" ", value); + JSONList list(out.list()); + for (auto & s : value) + list.elem(s); } -template class Setting<int>; -template class Setting<unsigned int>; -template class Setting<long>; -template class Setting<unsigned long>; -template class Setting<long long>; -template class Setting<unsigned long long>; +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>; void PathSetting::set(const std::string & str) { diff --git a/src/libutil/config.hh b/src/libutil/config.hh index 6c8612f675c7..91962109100d 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -9,6 +9,8 @@ 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: @@ -51,9 +53,13 @@ public: void warnUnknownSettings(); - StringMap getSettings(); + StringMap getSettings(bool overridenOnly = false); void applyConfigFile(const Path & path, bool fatal = false); + + void resetOverriden(); + + void toJSON(JSONObject & out); }; class AbstractSetting @@ -68,6 +74,8 @@ public: int created = 123; + bool overriden = false; + protected: AbstractSetting( @@ -78,7 +86,7 @@ protected: virtual ~AbstractSetting() { // Check against a gcc miscompilation causing our constructor - // not to run. + // not to run (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80431). assert(created == 123); } @@ -86,15 +94,14 @@ protected: virtual std::string to_string() = 0; - bool parseBool(const std::string & str); - std::string printBool(bool b); -}; + virtual void toJSON(JSONPlaceholder & out); -struct DefaultSettingTag { }; + bool isOverriden() { return overriden; } +}; /* A setting of type T. */ -template<typename T, typename Tag = DefaultSettingTag> -class Setting : public AbstractSetting +template<typename T> +class BaseSetting : public AbstractSetting { protected: @@ -102,42 +109,59 @@ protected: public: - Setting(Config * options, - const T & def, + BaseSetting(const T & def, const std::string & name, const std::string & description, const std::set<std::string> & aliases = {}) : AbstractSetting(name, description, aliases) , value(def) - { - options->addSetting(this); - } + { } 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) { value = v; } + 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 Setting<T> & opt) +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 Setting<T> & v2) { return v1 == (const T &) v2; } +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 Setting<Path> +class PathSetting : public BaseSetting<Path> { bool allowEmpty; @@ -149,15 +173,17 @@ public: const std::string & name, const std::string & description, const std::set<std::string> & aliases = {}) - : Setting<Path>(options, def, name, description, aliases) + : BaseSetting<Path>(def, name, description, aliases) , allowEmpty(allowEmpty) { - set(value); + 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/json.cc b/src/libutil/json.cc index 6023d1d4fb84..b8b8ef9c8cca 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 03eecb732586..595e9bbe3491 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/util.cc b/src/libutil/util.cc index 0bd51afd1a9f..88a2f752c023 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -429,6 +429,18 @@ 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"; + } + return configDir; +} + + Paths createDirs(const Path & path) { Paths created; @@ -934,7 +946,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() { diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 0e6941e4a8db..ae40dcd4cd2d 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -110,9 +110,12 @@ 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 $XDG_CACHE_HOME or $HOME/.cache. */ Path getCacheDir(); +/* Return $XDG_CONFIG_HOME or $HOME/.config. */ +Path getConfigDir(); + /* 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 +267,7 @@ void closeOnExec(int fd); extern bool _isInterrupted; -extern thread_local bool interruptThrown; +void setInterruptThrown(); void _interrupted(); diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc index 1389353bb5d8..07ad0b45b3e4 100644 --- a/src/nix-daemon/nix-daemon.cc +++ b/src/nix-daemon/nix-daemon.cc @@ -440,7 +440,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe settings.keepGoing = readInt(from); settings.tryFallback = readInt(from); verbosity = (Verbosity) readInt(from); - settings.maxBuildJobs = readInt(from); + settings.maxBuildJobs.assign(readInt(from)); settings.maxSilentTime = readInt(from); settings.useBuildHook = readInt(from) != 0; settings.verboseBuild = lvlError == (Verbosity) readInt(from); @@ -448,20 +448,56 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe readInt(from); // obsolete printBuildTrace settings.buildCores = readInt(from); settings.useSubstitutes = readInt(from); + + StringMap overrides; if (GET_PROTOCOL_MINOR(clientVersion) >= 12) { unsigned int n = readInt(from); for (unsigned int i = 0; i < n; i++) { string name = readString(from); string value = readString(from); - try { - if (trusted || name == "build-timeout") - settings.set(name, value); - } catch (UsageError & e) { - warn(e.what()); - } + overrides.emplace(name, value); } } + startWork(); + + for (auto & i : overrides) { + auto & name(i.first); + auto & value(i.second); + + auto setSubstituters = [&](Setting<Strings> & res) { + if (name != res.name && res.aliases.count(name) == 0) + return false; + StringSet trusted = settings.trustedSubstituters; + for (auto & s : settings.substituters.get()) + trusted.insert(s); + Strings subs; + auto ss = tokenizeString<Strings>(value); + for (auto & s : ss) + if (trusted.count(s)) + subs.push_back(s); + else + warn("ignoring untrusted substituter '%s'", s); + res = subs; + return true; + }; + + try { + if (trusted + || name == settings.buildTimeout.name + || name == settings.connectTimeout.name) + settings.set(name, value); + else if (setSubstituters(settings.substituters)) + ; + else if (setSubstituters(settings.extraSubstituters)) + ; + else + debug("ignoring untrusted setting '%s'", name); + } catch (UsageError & e) { + warn(e.what()); + } + } + stopWork(); break; } diff --git a/src/nix/show-config.cc b/src/nix/show-config.cc index ba39e2bb29b3..aade2adeace4 100644 --- a/src/nix/show-config.cc +++ b/src/nix/show-config.cc @@ -31,8 +31,7 @@ struct CmdShowConfig : Command if (json) { // FIXME: use appropriate JSON types (bool, ints, etc). JSONObject jsonObj(std::cout, true); - for (auto & s : settings.getSettings()) - jsonObj.attr(s.first, s.second); + settings.toJSON(jsonObj); } else { for (auto & s : settings.getSettings()) std::cout << s.first + " = " + s.second + "\n"; |