mirror of
https://github.com/NixOS/nix
synced 2025-07-06 21:41:48 +02:00
Merge remote-tracking branch 'origin/master' into source-path
This commit is contained in:
commit
01232358ff
101 changed files with 1785 additions and 834 deletions
71
src/libutil/config-impl.hh
Normal file
71
src/libutil/config-impl.hh
Normal file
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
@ -202,12 +219,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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -28,6 +28,7 @@ enum struct ExperimentalFeature
|
|||
AutoAllocateUids,
|
||||
Cgroups,
|
||||
DiscardReferences,
|
||||
DaemonTrustOverride,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,13 +1747,39 @@ void triggerInterrupt()
|
|||
}
|
||||
|
||||
static sigset_t savedSignalMask;
|
||||
static bool savedSignalMaskIsSet = false;
|
||||
|
||||
void setChildSignalMask(sigset_t * sigs)
|
||||
{
|
||||
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();
|
||||
|
||||
if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask))
|
||||
throw SysError("querying signal mask");
|
||||
saveSignalMask();
|
||||
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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() { };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue