aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2023-04-24 13:20:36 +0200
committerEelco Dolstra <edolstra@gmail.com>2023-04-24 13:20:36 +0200
commit01232358ffd78600d170ca5c1526a7031f6f2762 (patch)
treebabf00196894ec8b79fb963419fe4a6d44e56db4 /src/libutil
parentcb2615cf4735cf28a6e538544c9abbf40cdd24a9 (diff)
parent7474a90db69813d051ab1bef35c7d0ab958d9ccd (diff)
Merge remote-tracking branch 'origin/master' into source-path
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/config-impl.hh71
-rw-r--r--src/libutil/config.cc94
-rw-r--r--src/libutil/config.hh46
-rw-r--r--src/libutil/experimental-features.cc12
-rw-r--r--src/libutil/experimental-features.hh1
-rw-r--r--src/libutil/hash.cc2
-rw-r--r--src/libutil/tests/config.cc2
-rw-r--r--src/libutil/util.cc47
-rw-r--r--src/libutil/util.hh20
9 files changed, 247 insertions, 48 deletions
diff --git a/src/libutil/config-impl.hh b/src/libutil/config-impl.hh
new file mode 100644
index 000000000..b6cae5ec3
--- /dev/null
+++ b/src/libutil/config-impl.hh
@@ -0,0 +1,71 @@
+#pragma once
+/**
+ * @file
+ *
+ * Template implementations (as opposed to mere declarations).
+ *
+ * One only needs to include this when one is declaring a
+ * `BaseClass<CustomType>` setting, or as derived class of such an
+ * instantiation.
+ */
+
+#include "config.hh"
+
+namespace nix {
+
+template<> struct BaseSetting<Strings>::trait
+{
+ static constexpr bool appendable = true;
+};
+template<> struct BaseSetting<StringSet>::trait
+{
+ static constexpr bool appendable = true;
+};
+template<> struct BaseSetting<StringMap>::trait
+{
+ static constexpr bool appendable = true;
+};
+template<> struct BaseSetting<std::set<ExperimentalFeature>>::trait
+{
+ static constexpr bool appendable = true;
+};
+
+template<typename T>
+struct BaseSetting<T>::trait
+{
+ static constexpr bool appendable = false;
+};
+
+template<typename T>
+bool BaseSetting<T>::isAppendable()
+{
+ return trait::appendable;
+}
+
+template<> void BaseSetting<Strings>::appendOrSet(Strings && newValue, bool append);
+template<> void BaseSetting<StringSet>::appendOrSet(StringSet && newValue, bool append);
+template<> void BaseSetting<StringMap>::appendOrSet(StringMap && newValue, bool append);
+template<> void BaseSetting<std::set<ExperimentalFeature>>::appendOrSet(std::set<ExperimentalFeature> && newValue, bool append);
+
+template<typename T>
+void BaseSetting<T>::appendOrSet(T && newValue, bool append)
+{
+ static_assert(!trait::appendable, "using default `appendOrSet` implementation with an appendable type");
+ assert(!append);
+ value = std::move(newValue);
+}
+
+template<typename T>
+void BaseSetting<T>::set(const std::string & str, bool append)
+{
+ if (experimentalFeatureSettings.isEnabled(experimentalFeature))
+ appendOrSet(parse(str), append);
+ else {
+ assert(experimentalFeature);
+ warn("Ignoring setting '%s' because experimental feature '%s' is not enabled",
+ name,
+ showExperimentalFeature(*experimentalFeature));
+ }
+}
+
+}
diff --git a/src/libutil/config.cc b/src/libutil/config.cc
index a42f3a849..085a884dc 100644
--- a/src/libutil/config.cc
+++ b/src/libutil/config.cc
@@ -3,6 +3,8 @@
#include "abstract-setting-to-json.hh"
#include "experimental-features.hh"
+#include "config-impl.hh"
+
#include <nlohmann/json.hpp>
namespace nix {
@@ -80,6 +82,8 @@ void Config::getSettings(std::map<std::string, SettingInfo> & res, bool overridd
void AbstractConfig::applyConfig(const std::string & contents, const std::string & path) {
unsigned int pos = 0;
+ std::vector<std::pair<std::string, std::string>> parsedContents;
+
while (pos < contents.size()) {
std::string line;
while (pos < contents.size() && contents[pos] != '\n')
@@ -125,8 +129,21 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string
auto i = tokens.begin();
advance(i, 2);
- set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow
+ parsedContents.push_back({
+ name,
+ concatStringsSep(" ", Strings(i, tokens.end())),
+ });
};
+
+ // First apply experimental-feature related settings
+ for (auto & [name, value] : parsedContents)
+ if (name == "experimental-features" || name == "extra-experimental-features")
+ set(name, value);
+
+ // Then apply other settings
+ for (auto & [name, value] : parsedContents)
+ if (name != "experimental-features" && name != "extra-experimental-features")
+ set(name, value);
}
void AbstractConfig::applyConfigFile(const Path & path)
@@ -203,12 +220,6 @@ void AbstractSetting::convertToArg(Args & args, const std::string & category)
}
template<typename T>
-bool BaseSetting<T>::isAppendable()
-{
- return false;
-}
-
-template<typename T>
void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
{
args.addFlag({
@@ -231,9 +242,9 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
});
}
-template<> void BaseSetting<std::string>::set(const std::string & str, bool append)
+template<> std::string BaseSetting<std::string>::parse(const std::string & str) const
{
- value = str;
+ return str;
}
template<> std::string BaseSetting<std::string>::to_string() const
@@ -242,11 +253,11 @@ template<> std::string BaseSetting<std::string>::to_string() const
}
template<typename T>
-void BaseSetting<T>::set(const std::string & str, bool append)
+T BaseSetting<T>::parse(const std::string & str) const
{
static_assert(std::is_integral<T>::value, "Integer required.");
if (auto n = string2Int<T>(str))
- value = *n;
+ return *n;
else
throw UsageError("setting '%s' has invalid value '%s'", name, str);
}
@@ -258,12 +269,12 @@ std::string BaseSetting<T>::to_string() const
return std::to_string(value);
}
-template<> void BaseSetting<bool>::set(const std::string & str, bool append)
+template<> bool BaseSetting<bool>::parse(const std::string & str) const
{
if (str == "true" || str == "yes" || str == "1")
- value = true;
+ return true;
else if (str == "false" || str == "no" || str == "0")
- value = false;
+ return false;
else
throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str);
}
@@ -291,16 +302,15 @@ template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string &
});
}
-template<> void BaseSetting<Strings>::set(const std::string & str, bool append)
+template<> Strings BaseSetting<Strings>::parse(const std::string & str) const
{
- auto ss = tokenizeString<Strings>(str);
- if (!append) value.clear();
- for (auto & s : ss) value.push_back(std::move(s));
+ return tokenizeString<Strings>(str);
}
-template<> bool BaseSetting<Strings>::isAppendable()
+template<> void BaseSetting<Strings>::appendOrSet(Strings && newValue, bool append)
{
- return true;
+ if (!append) value.clear();
+ for (auto && s : std::move(newValue)) value.push_back(std::move(s));
}
template<> std::string BaseSetting<Strings>::to_string() const
@@ -308,16 +318,16 @@ template<> std::string BaseSetting<Strings>::to_string() const
return concatStringsSep(" ", value);
}
-template<> void BaseSetting<StringSet>::set(const std::string & str, bool append)
+template<> StringSet BaseSetting<StringSet>::parse(const std::string & str) const
{
- if (!append) value.clear();
- for (auto & s : tokenizeString<StringSet>(str))
- value.insert(s);
+ return tokenizeString<StringSet>(str);
}
-template<> bool BaseSetting<StringSet>::isAppendable()
+template<> void BaseSetting<StringSet>::appendOrSet(StringSet && newValue, bool append)
{
- return true;
+ if (!append) value.clear();
+ for (auto && s : std::move(newValue))
+ value.insert(s);
}
template<> std::string BaseSetting<StringSet>::to_string() const
@@ -325,21 +335,24 @@ template<> std::string BaseSetting<StringSet>::to_string() const
return concatStringsSep(" ", value);
}
-template<> void BaseSetting<std::set<ExperimentalFeature>>::set(const std::string & str, bool append)
+template<> std::set<ExperimentalFeature> BaseSetting<std::set<ExperimentalFeature>>::parse(const std::string & str) const
{
- if (!append) value.clear();
+ std::set<ExperimentalFeature> res;
for (auto & s : tokenizeString<StringSet>(str)) {
auto thisXpFeature = parseExperimentalFeature(s);
if (thisXpFeature)
- value.insert(thisXpFeature.value());
+ res.insert(thisXpFeature.value());
else
warn("unknown experimental feature '%s'", s);
}
+ return res;
}
-template<> bool BaseSetting<std::set<ExperimentalFeature>>::isAppendable()
+template<> void BaseSetting<std::set<ExperimentalFeature>>::appendOrSet(std::set<ExperimentalFeature> && newValue, bool append)
{
- return true;
+ if (!append) value.clear();
+ for (auto && s : std::move(newValue))
+ value.insert(s);
}
template<> std::string BaseSetting<std::set<ExperimentalFeature>>::to_string() const
@@ -350,20 +363,23 @@ template<> std::string BaseSetting<std::set<ExperimentalFeature>>::to_string() c
return concatStringsSep(" ", stringifiedXpFeatures);
}
-template<> void BaseSetting<StringMap>::set(const std::string & str, bool append)
+template<> StringMap BaseSetting<StringMap>::parse(const std::string & str) const
{
- if (!append) value.clear();
+ StringMap res;
for (auto & s : tokenizeString<Strings>(str)) {
auto eq = s.find_first_of('=');
if (std::string::npos != eq)
- value.emplace(std::string(s, 0, eq), std::string(s, eq + 1));
+ res.emplace(std::string(s, 0, eq), std::string(s, eq + 1));
// else ignored
}
+ return res;
}
-template<> bool BaseSetting<StringMap>::isAppendable()
+template<> void BaseSetting<StringMap>::appendOrSet(StringMap && newValue, bool append)
{
- return true;
+ if (!append) value.clear();
+ for (auto && [k, v] : std::move(newValue))
+ value.emplace(std::move(k), std::move(v));
}
template<> std::string BaseSetting<StringMap>::to_string() const
@@ -387,15 +403,15 @@ template class BaseSetting<StringSet>;
template class BaseSetting<StringMap>;
template class BaseSetting<std::set<ExperimentalFeature>>;
-void PathSetting::set(const std::string & str, bool append)
+Path PathSetting::parse(const std::string & str) const
{
if (str == "") {
if (allowEmpty)
- value = "";
+ return "";
else
throw UsageError("setting '%s' cannot be empty", name);
} else
- value = canonPath(str);
+ return canonPath(str);
}
bool GlobalConfig::set(const std::string & name, const std::string & value)
diff --git a/src/libutil/config.hh b/src/libutil/config.hh
index 162626791..2675baed7 100644
--- a/src/libutil/config.hh
+++ b/src/libutil/config.hh
@@ -215,8 +215,11 @@ protected:
virtual void set(const std::string & value, bool append = false) = 0;
- virtual bool isAppendable()
- { return false; }
+ /**
+ * Whether the type is appendable; i.e. whether the `append`
+ * parameter to `set()` is allowed to be `true`.
+ */
+ virtual bool isAppendable() = 0;
virtual std::string to_string() const = 0;
@@ -241,6 +244,23 @@ protected:
const T defaultValue;
const bool documentDefault;
+ /**
+ * Parse the string into a `T`.
+ *
+ * Used by `set()`.
+ */
+ virtual T parse(const std::string & str) const;
+
+ /**
+ * Append or overwrite `value` with `newValue`.
+ *
+ * Some types to do not support appending in which case `append`
+ * should never be passed. The default handles this case.
+ *
+ * @param append Whether to append or overwrite.
+ */
+ virtual void appendOrSet(T && newValue, bool append);
+
public:
BaseSetting(const T & def,
@@ -268,9 +288,25 @@ public:
template<typename U>
void setDefault(const U & v) { if (!overridden) value = v; }
- void set(const std::string & str, bool append = false) override;
+ /**
+ * Require any experimental feature the setting depends on
+ *
+ * Uses `parse()` to get the value from `str`, and `appendOrSet()`
+ * to set it.
+ */
+ void set(const std::string & str, bool append = false) override final;
- bool isAppendable() override;
+ /**
+ * C++ trick; This is template-specialized to compile-time indicate whether
+ * the type is appendable.
+ */
+ struct trait;
+
+ /**
+ * Always defined based on the C++ magic
+ * with `trait` above.
+ */
+ bool isAppendable() override final;
virtual void override(const T & v)
{
@@ -336,7 +372,7 @@ public:
options->addSetting(this);
}
- void set(const std::string & str, bool append = false) override;
+ Path parse(const std::string & str) const override;
Path operator +(const char * p) const { return value + p; }
diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc
index 5b4418714..bd1899662 100644
--- a/src/libutil/experimental-features.cc
+++ b/src/libutil/experimental-features.cc
@@ -12,7 +12,7 @@ struct ExperimentalFeatureDetails
std::string_view description;
};
-constexpr std::array<ExperimentalFeatureDetails, 11> xpFeatureDetails = {{
+constexpr std::array<ExperimentalFeatureDetails, 12> xpFeatureDetails = {{
{
.tag = Xp::CaDerivations,
.name = "ca-derivations",
@@ -189,6 +189,16 @@ constexpr std::array<ExperimentalFeatureDetails, 11> xpFeatureDetails = {{
runtime dependencies.
)",
},
+ {
+ .tag = Xp::DaemonTrustOverride,
+ .name = "daemon-trust-override",
+ .description = R"(
+ Allow forcing trusting or not trusting clients with
+ `nix-daemon`. This is useful for testing, but possibly also
+ useful for various experiments with `nix-daemon --stdio`
+ networking.
+ )",
+ },
}};
static_assert(
diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh
index 8ef66263a..3c00bc4e5 100644
--- a/src/libutil/experimental-features.hh
+++ b/src/libutil/experimental-features.hh
@@ -28,6 +28,7 @@ enum struct ExperimentalFeature
AutoAllocateUids,
Cgroups,
DiscardReferences,
+ DaemonTrustOverride,
};
/**
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index 0c8f5727d..2c36d9d94 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -1,6 +1,7 @@
#include <iostream>
#include <cstring>
+#include <openssl/crypto.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
@@ -16,7 +17,6 @@
namespace nix {
-
static size_t regularHashSize(HashType type) {
switch (type) {
case htMD5: return md5HashSize;
diff --git a/src/libutil/tests/config.cc b/src/libutil/tests/config.cc
index f250e934e..886e70da5 100644
--- a/src/libutil/tests/config.cc
+++ b/src/libutil/tests/config.cc
@@ -82,6 +82,7 @@ namespace nix {
TestSetting() : AbstractSetting("test", "test", {}) {}
void set(const std::string & value, bool append) override {}
std::string to_string() const override { return {}; }
+ bool isAppendable() override { return false; }
};
Config config;
@@ -90,6 +91,7 @@ namespace nix {
ASSERT_FALSE(config.set("test", "value"));
config.addSetting(&setting);
ASSERT_TRUE(config.set("test", "value"));
+ ASSERT_FALSE(config.set("extra-test", "value"));
}
TEST(Config, withInitialValue) {
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 843a10eab..21d1c8dcd 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -47,6 +47,9 @@ extern char * * environ __attribute__((weak));
namespace nix {
+void initLibUtil() {
+}
+
std::optional<std::string> getEnv(const std::string & key)
{
char * value = getenv(key.c_str());
@@ -1744,14 +1747,40 @@ void triggerInterrupt()
}
static sigset_t savedSignalMask;
+static bool savedSignalMaskIsSet = false;
-void startSignalHandlerThread()
+void setChildSignalMask(sigset_t * sigs)
{
- updateWindowSize();
+ assert(sigs); // C style function, but think of sigs as a reference
+
+#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
+ sigemptyset(&savedSignalMask);
+ // There's no "assign" or "copy" function, so we rely on (math) idempotence
+ // of the or operator: a or a = a.
+ sigorset(&savedSignalMask, sigs, sigs);
+#else
+ // Without sigorset, our best bet is to assume that sigset_t is a type that
+ // can be assigned directly, such as is the case for a sigset_t defined as
+ // an integer type.
+ savedSignalMask = *sigs;
+#endif
+
+ savedSignalMaskIsSet = true;
+}
+void saveSignalMask() {
if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask))
throw SysError("querying signal mask");
+ savedSignalMaskIsSet = true;
+}
+
+void startSignalHandlerThread()
+{
+ updateWindowSize();
+
+ saveSignalMask();
+
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
@@ -1767,6 +1796,20 @@ void startSignalHandlerThread()
static void restoreSignals()
{
+ // If startSignalHandlerThread wasn't called, that means we're not running
+ // in a proper libmain process, but a process that presumably manages its
+ // own signal handlers. Such a process should call either
+ // - initNix(), to be a proper libmain process
+ // - startSignalHandlerThread(), to resemble libmain regarding signal
+ // handling only
+ // - saveSignalMask(), for processes that define their own signal handling
+ // thread
+ // TODO: Warn about this? Have a default signal mask? The latter depends on
+ // whether we should generally inherit signal masks from the caller.
+ // I don't know what the larger unix ecosystem expects from us here.
+ if (!savedSignalMaskIsSet)
+ return;
+
if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr))
throw SysError("restoring signals");
}
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 85ab77b1b..040fed68f 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -32,6 +32,7 @@ namespace nix {
struct Sink;
struct Source;
+void initLibUtil();
/**
* The system for which Nix is compiled.
@@ -445,6 +446,8 @@ void setStackSize(size_t stackSize);
/**
* Restore the original inherited Unix process context (such as signal
* masks, stack size).
+
+ * See startSignalHandlerThread(), saveSignalMask().
*/
void restoreProcessContext(bool restoreMounts = true);
@@ -814,9 +817,26 @@ class Callback;
/**
* Start a thread that handles various signals. Also block those signals
* on the current thread (and thus any threads created by it).
+ * Saves the signal mask before changing the mask to block those signals.
+ * See saveSignalMask().
*/
void startSignalHandlerThread();
+/**
+ * Saves the signal mask, which is the signal mask that nix will restore
+ * before creating child processes.
+ * See setChildSignalMask() to set an arbitrary signal mask instead of the
+ * current mask.
+ */
+void saveSignalMask();
+
+/**
+ * Sets the signal mask. Like saveSignalMask() but for a signal set that doesn't
+ * necessarily match the current thread's mask.
+ * See saveSignalMask() to set the saved mask to the current mask.
+ */
+void setChildSignalMask(sigset_t *sigs);
+
struct InterruptCallback
{
virtual ~InterruptCallback() { };