diff options
Diffstat (limited to 'src')
31 files changed, 788 insertions, 460 deletions
diff --git a/src/boost/format/feed_args.hpp b/src/boost/format/feed_args.hpp index 3d0b47b4a12e..cdd57fdf2bf1 100644 --- a/src/boost/format/feed_args.hpp +++ b/src/boost/format/feed_args.hpp @@ -41,6 +41,13 @@ namespace { std::streamsize w, const char c, std::ios::fmtflags f, + bool center) + __attribute__ ((unused)); + + void do_pad( std::string & s, + std::streamsize w, + const char c, + std::ios::fmtflags f, bool center) // applies centered / left / right padding to the string s. // Effects : string s is padded. diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d418ab4e43aa..5e1ae63c4824 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -202,7 +202,7 @@ void initGC() GC_INIT(); - GC_oom_fn = oomHandler; + GC_set_oom_fn(oomHandler); /* Set the initial heap size to something fairly big (25% of physical RAM, up to a maximum of 384 MiB) so that in most cases @@ -299,7 +299,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store) { countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0"; - restricted = settings.get("restrict-eval", false); + restricted = settings.restrictEval; assert(gcInitialised); diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index 620050a13b05..daa3258f0d3c 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -6,8 +6,6 @@ libexpr_DIR := $(d) libexpr_SOURCES := $(wildcard $(d)/*.cc) $(wildcard $(d)/primops/*.cc) $(d)/lexer-tab.cc $(d)/parser-tab.cc -libexpr_CXXFLAGS := -Wno-deprecated-register - libexpr_LIBS = libutil libstore libformat libexpr_LDFLAGS = diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc index 98693d78a7f4..9a7a893138de 100644 --- a/src/libmain/common-args.cc +++ b/src/libmain/common-args.cc @@ -22,7 +22,11 @@ MixCommonArgs::MixCommonArgs(const string & programName) [](Strings ss) { auto name = ss.front(); ss.pop_front(); auto value = ss.front(); - settings.set(name, value); + try { + settings.set(name, value); + } catch (UsageError & e) { + warn(e.what()); + } }); } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index a720afd6cdd4..4747b9bf9b4c 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -106,8 +106,6 @@ void initNix() std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf)); #endif - logger = makeDefaultLogger(); - /* Initialise OpenSSL locking. */ opensslLocks = std::vector<std::mutex>(CRYPTO_num_locks()); CRYPTO_set_locking_callback(opensslLockCallback); @@ -140,9 +138,6 @@ void initNix() struct timeval tv; gettimeofday(&tv, 0); srandom(tv.tv_usec); - - if (char *pack = getenv("_NIX_OPTIONS")) - settings.unpack(pack); } @@ -158,13 +153,13 @@ struct LegacyArgs : public MixCommonArgs &settings.verboseBuild, false); mkFlag('K', "keep-failed", "keep temporary directories of failed builds", - &settings.keepFailed); + &(bool&) settings.keepFailed); mkFlag('k', "keep-going", "keep going after a build fails", - &settings.keepGoing); + &(bool&) settings.keepGoing); mkFlag(0, "fallback", "build from source if substitution fails", []() { - settings.set("build-fallback", "true"); + settings.tryFallback = true; }); mkFlag1('j', "max-jobs", "jobs", "maximum number of parallel builds", [=](std::string s) { @@ -186,7 +181,7 @@ struct LegacyArgs : public MixCommonArgs &settings.readOnlyMode); mkFlag(0, "no-build-hook", "disable use of the build hook mechanism", - &settings.useBuildHook, false); + &(bool&) settings.useBuildHook, false); mkFlag(0, "show-trace", "show Nix expression stack trace in evaluation errors", &settings.showTrace); @@ -220,7 +215,6 @@ void parseCmdLine(int argc, char * * argv, std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg) { LegacyArgs(baseNameOf(argv[0]), parseArg).parseCmdline(argvToStrings(argc, argv)); - settings.update(); } diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 25ad0d75b70a..b536c6c00044 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -79,10 +79,7 @@ struct BinaryCacheStoreAccessor : public FSAccessor BinaryCacheStore::BinaryCacheStore(const Params & params) : Store(params) - , compression(get(params, "compression", "xz")) - , writeNARListing(get(params, "write-nar-listing", "0") == "1") { - auto secretKeyFile = get(params, "secret-key", ""); if (secretKeyFile != "") secretKey = std::unique_ptr<SecretKey>(new SecretKey(readFile(secretKeyFile))); diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh index d42b1abd2455..5c2d0acfdbb7 100644 --- a/src/libstore/binary-cache-store.hh +++ b/src/libstore/binary-cache-store.hh @@ -13,13 +13,15 @@ struct NarInfo; class BinaryCacheStore : public Store { -private: +public: - std::unique_ptr<SecretKey> secretKey; + const Setting<std::string> compression{this, "xz", "compression", "NAR compression method ('xz', 'bzip2', or 'none')"}; + const Setting<bool> writeNARListing{this, false, "write-nar-listing", "whether to write a JSON file listing the files in each NAR"}; + const Setting<Path> secretKeyFile{this, "", "secret-key", "path to secret key used to sign the binary cache"}; - std::string compression; +private: - bool writeNARListing; + std::unique_ptr<SecretKey> secretKey; protected: diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 968e291129f3..9bf1ab5aa581 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -34,13 +34,6 @@ #include <pwd.h> #include <grp.h> -/* chroot-like behavior from Apple's sandbox */ -#if __APPLE__ - #define DEFAULT_ALLOWED_IMPURE_PREFIXES "/System/Library /usr/lib /dev /bin/sh" -#else - #define DEFAULT_ALLOWED_IMPURE_PREFIXES "" -#endif - /* Includes required for chroot support. */ #if __linux__ #include <sys/socket.h> @@ -469,7 +462,7 @@ UserLock::UserLock() assert(settings.buildUsersGroup != ""); /* Get the members of the build-users-group. */ - struct group * gr = getgrnam(settings.buildUsersGroup.c_str()); + struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str()); if (!gr) throw Error(format("the group ‘%1%’ specified in ‘build-users-group’ does not exist") % settings.buildUsersGroup); @@ -1279,7 +1272,7 @@ void DerivationGoal::inputsRealised() /* Don't repeat fixed-output derivations since they're already verified by their output hash.*/ - nrRounds = fixedOutput ? 1 : settings.get("build-repeat", 0) + 1; + nrRounds = fixedOutput ? 1 : settings.buildRepeat + 1; /* Okay, try to build. Note that here we don't wait for a build slot to become available, since we don't need one if there is a @@ -1697,12 +1690,7 @@ void DerivationGoal::startBuilder() /* Are we doing a chroot build? */ { - string x = settings.get("build-use-sandbox", - /* deprecated alias */ - settings.get("build-use-chroot", string("false"))); - if (x != "true" && x != "false" && x != "relaxed") - throw Error("option ‘build-use-sandbox’ must be set to one of ‘true’, ‘false’ or ‘relaxed’"); - if (x == "true") { + if (settings.sandboxMode == smEnabled) { if (get(drv->env, "__noChroot") == "1") throw Error(format("derivation ‘%1%’ has ‘__noChroot’ set, " "but that's not allowed when ‘build-use-sandbox’ is ‘true’") % drvPath); @@ -1713,9 +1701,9 @@ void DerivationGoal::startBuilder() #endif useChroot = true; } - else if (x == "false") + else if (settings.sandboxMode == smDisabled) useChroot = false; - else if (x == "relaxed") + else if (settings.sandboxMode == smRelaxed) useChroot = !fixedOutput && get(drv->env, "__noChroot") != "1"; } @@ -1756,21 +1744,10 @@ void DerivationGoal::startBuilder() if (useChroot) { - string defaultChrootDirs; -#if __linux__ - if (worker.store.isInStore(BASH_PATH)) - defaultChrootDirs = "/bin/sh=" BASH_PATH; -#endif - /* Allow a user-configurable set of directories from the host file system. */ - PathSet dirs = tokenizeString<StringSet>( - settings.get("build-sandbox-paths", - /* deprecated alias with lower priority */ - settings.get("build-chroot-dirs", defaultChrootDirs))); - PathSet dirs2 = tokenizeString<StringSet>( - settings.get("build-extra-chroot-dirs", - settings.get("build-extra-sandbox-paths", string("")))); + PathSet dirs = settings.sandboxPaths; + PathSet dirs2 = settings.extraSandboxPaths; dirs.insert(dirs2.begin(), dirs2.end()); dirsInChroot.clear(); @@ -1802,8 +1779,7 @@ void DerivationGoal::startBuilder() for (auto & i : closure) dirsInChroot[i] = i; - string allowed = settings.get("allowed-impure-host-deps", string(DEFAULT_ALLOWED_IMPURE_PREFIXES)); - PathSet allowedPaths = tokenizeString<StringSet>(allowed); + PathSet allowedPaths = settings.allowedImpureHostPrefixes; /* This works like the above, except on a per-derivation level */ Strings impurePaths = tokenizeString<Strings>(get(drv->env, "__impureHostDeps")); @@ -1823,7 +1799,7 @@ void DerivationGoal::startBuilder() } } if (!found) - throw Error(format("derivation ‘%1%’ requested impure path ‘%2%’, but it was not in allowed-impure-host-deps (‘%3%’)") % drvPath % i % allowed); + throw Error(format("derivation ‘%1%’ requested impure path ‘%2%’, but it was not in allowed-impure-host-deps") % drvPath % i); dirsInChroot[i] = i; } @@ -2444,7 +2420,7 @@ void DerivationGoal::runChild() /* Mount a new tmpfs on /dev/shm to ensure that whatever the builder puts in /dev/shm is cleaned up automatically. */ if (pathExists("/dev/shm") && mount("none", (chrootRootDir + "/dev/shm").c_str(), "tmpfs", 0, - fmt("size=%s", settings.get("sandbox-dev-shm-size", std::string("50%"))).c_str()) == -1) + fmt("size=%s", settings.sandboxShmSize).c_str()) == -1) throw SysError("mounting /dev/shm"); /* Mount a new devpts on /dev/pts. Note that this @@ -2602,7 +2578,7 @@ void DerivationGoal::runChild() sandboxProfile += "(version 1)\n"; /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */ - if (settings.get("darwin-log-sandbox-violations", false)) { + if (settings.darwinLogSandboxViolations) { sandboxProfile += "(deny default)\n"; } else { sandboxProfile += "(deny default (with no-log))\n"; @@ -2749,7 +2725,7 @@ void DerivationGoal::registerOutputs() InodesSeen inodesSeen; Path checkSuffix = ".check"; - bool runDiffHook = settings.get("run-diff-hook", false); + bool runDiffHook = settings.runDiffHook; bool keepPreviousRound = settings.keepFailed || runDiffHook; /* Check whether the output paths were created, and grep each @@ -2990,7 +2966,7 @@ void DerivationGoal::registerOutputs() ? fmt("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round", i->path, drvPath, prev) : fmt("output ‘%1%’ of ‘%2%’ differs from previous round", i->path, drvPath); - auto diffHook = settings.get("diff-hook", std::string("")); + auto diffHook = settings.diffHook; if (prevExists && diffHook != "" && runDiffHook) { try { auto diff = runProgram(diffHook, true, {prev, i->path}); @@ -3001,7 +2977,7 @@ void DerivationGoal::registerOutputs() } } - if (settings.get("enforce-determinism", true)) + if (settings.enforceDeterminism) throw NotDeterministic(msg); printError(msg); @@ -3051,13 +3027,11 @@ Path DerivationGoal::openLogFile() string baseName = baseNameOf(drvPath); /* Create a log file. */ - Path dir = (format("%1%/%2%/%3%/") % worker.store.logDir % worker.store.drvsLogDir % string(baseName, 0, 2)).str(); + Path dir = fmt("%s/%s/%s/", worker.store.logDir, worker.store.drvsLogDir, string(baseName, 0, 2)); createDirs(dir); - Path logFileName = (format("%1%/%2%%3%") - % dir - % string(baseName, 2) - % (settings.compressLog ? ".bz2" : "")).str(); + Path logFileName = fmt("%s/%s%s", dir, string(baseName, 2), + settings.compressLog ? ".bz2" : ""); fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666); if (!fdLogFile) throw SysError(format("creating log file ‘%1%’") % logFileName); @@ -3402,10 +3376,10 @@ void SubstitutionGoal::tryToRun() trace("trying to run"); /* Make sure that we are allowed to start a build. Note that even - is maxBuildJobs == 0 (no local builds allowed), we still allow + if maxBuildJobs == 0 (no local builds allowed), we still allow a substituter to run. This is because substitutions cannot be distributed to another machine via the build hook. */ - if (worker.getNrLocalBuilds() >= (settings.maxBuildJobs == 0 ? 1 : settings.maxBuildJobs)) { + if (worker.getNrLocalBuilds() >= std::min(1U, (unsigned int) settings.maxBuildJobs)) { worker.waitForBuildSlot(shared_from_this()); return; } @@ -3686,7 +3660,7 @@ void Worker::run(const Goals & _topGoals) if (!children.empty() || !waitingForAWhile.empty()) waitForInput(); else { - if (awake.empty() && settings.maxBuildJobs == 0) throw Error( + if (awake.empty() && 0 == settings.maxBuildJobs) throw Error( "unable to start any build; either increase ‘--max-jobs’ " "or enable distributed builds"); assert(!awake.empty()); @@ -3723,9 +3697,9 @@ void Worker::waitForInput() auto nearest = steady_time_point::max(); // nearest deadline for (auto & i : children) { if (!i.respectTimeouts) continue; - if (settings.maxSilentTime != 0) + if (0 != settings.maxSilentTime) nearest = std::min(nearest, i.lastOutput + std::chrono::seconds(settings.maxSilentTime)); - if (settings.buildTimeout != 0) + if (0 != settings.buildTimeout) nearest = std::min(nearest, i.timeStarted + std::chrono::seconds(settings.buildTimeout)); } if (nearest != steady_time_point::max()) { @@ -3803,7 +3777,7 @@ void Worker::waitForInput() } if (goal->getExitCode() == Goal::ecBusy && - settings.maxSilentTime != 0 && + 0 != settings.maxSilentTime && j->respectTimeouts && after - j->lastOutput >= std::chrono::seconds(settings.maxSilentTime)) { @@ -3814,7 +3788,7 @@ void Worker::waitForInput() } else if (goal->getExitCode() == Goal::ecBusy && - settings.buildTimeout != 0 && + 0 != settings.buildTimeout && j->respectTimeouts && after - j->timeStarted >= std::chrono::seconds(settings.buildTimeout)) { diff --git a/src/libstore/crypto.cc b/src/libstore/crypto.cc index 0fc86a1fe921..f56a6adab9c9 100644 --- a/src/libstore/crypto.cc +++ b/src/libstore/crypto.cc @@ -105,14 +105,12 @@ PublicKeys getDefaultPublicKeys() // FIXME: filter duplicates - for (auto s : settings.get("binary-cache-public-keys", - Strings{"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="})) - { + for (auto s : settings.binaryCachePublicKeys.get()) { PublicKey key(s); publicKeys.emplace(key.name, key); } - for (auto secretKeyFile : settings.get("secret-key-files", Strings())) { + for (auto secretKeyFile : settings.secretKeyFiles.get()) { try { SecretKey secretKey(readFile(secretKeyFile)); publicKeys.emplace(secretKey.name, secretKey.toPublicKey()); diff --git a/src/libstore/download.cc b/src/libstore/download.cc index f8f578695033..42fd05bd80d7 100644 --- a/src/libstore/download.cc +++ b/src/libstore/download.cc @@ -251,7 +251,7 @@ struct CurlDownloader : public Downloader /* If no file exist in the specified path, curl continues to work anyway as if netrc support was disabled. */ - curl_easy_setopt(req, CURLOPT_NETRC_FILE, settings.netrcFile.c_str()); + curl_easy_setopt(req, CURLOPT_NETRC_FILE, settings.netrcFile.get().c_str()); curl_easy_setopt(req, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); result.data = std::make_shared<std::string>(); @@ -369,9 +369,9 @@ struct CurlDownloader : public Downloader curl_multi_setopt(curlm, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); #endif curl_multi_setopt(curlm, CURLMOPT_MAX_TOTAL_CONNECTIONS, - settings.get("binary-caches-parallel-connections", 25)); + settings.binaryCachesParallelConnections.get()); - enableHttp2 = settings.get("enable-http2", true); + enableHttp2 = settings.enableHttp2; wakeupPipe.create(); fcntl(wakeupPipe.readSide.get(), F_SETFL, O_NONBLOCK); @@ -611,7 +611,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa string expectedETag; - int ttl = settings.get("tarball-ttl", 60 * 60); + int ttl = settings.tarballTtl; bool skip = false; if (pathExists(fileLink) && pathExists(dataFile)) { diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 8c900be77b8f..bb61daa51642 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -1,6 +1,7 @@ #include "globals.hh" #include "util.hh" #include "archive.hh" +#include "args.hh" #include <algorithm> #include <map> @@ -17,255 +18,98 @@ namespace nix { must be deleted and recreated on startup.) */ #define DEFAULT_SOCKET_PATH "/daemon-socket/socket" +/* chroot-like behavior from Apple's sandbox */ +#if __APPLE__ + #define DEFAULT_ALLOWED_IMPURE_PREFIXES "/System/Library /usr/lib /dev /bin/sh" +#else + #define DEFAULT_ALLOWED_IMPURE_PREFIXES "" +#endif Settings settings; - Settings::Settings() + : Config({}) + , nixPrefix(NIX_PREFIX) + , nixStore(canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)))) + , nixDataDir(canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR))) + , nixLogDir(canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR))) + , nixStateDir(canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR))) + , nixConfDir(canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR))) + , nixLibexecDir(canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR))) + , nixBinDir(canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR))) + , nixDaemonSocketFile(canonPath(nixStateDir + DEFAULT_SOCKET_PATH)) { - nixPrefix = NIX_PREFIX; - nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR))); - nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR)); - nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR)); - nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR)); - nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR)); - nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR)); - nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR)); - nixDaemonSocketFile = canonPath(nixStateDir + DEFAULT_SOCKET_PATH); - - // should be set with the other config options, but depends on nixLibexecDir -#ifdef __APPLE__ - preBuildHook = nixLibexecDir + "/nix/resolve-system-dependencies"; -#endif - - keepFailed = false; - keepGoing = false; - tryFallback = false; - maxBuildJobs = 1; - buildCores = std::max(1U, std::thread::hardware_concurrency()); - readOnlyMode = false; - thisSystem = SYSTEM; - maxSilentTime = 0; - buildTimeout = 0; - useBuildHook = true; - reservedSize = 8 * 1024 * 1024; - fsyncMetadata = true; - useSQLiteWAL = true; - syncBeforeRegistering = false; - useSubstitutes = true; buildUsersGroup = getuid() == 0 ? "nixbld" : ""; - useSshSubstituter = true; - impersonateLinux26 = false; - keepLog = true; - compressLog = true; - maxLogSize = 0; - pollInterval = 5; - checkRootReachability = false; - gcKeepOutputs = false; - gcKeepDerivations = true; - autoOptimiseStore = false; - envKeepDerivations = false; lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1"; - showTrace = false; - enableNativeCode = false; - netrcFile = fmt("%s/%s", nixConfDir, "netrc"); caFile = getEnv("NIX_SSL_CERT_FILE", getEnv("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt")); - enableImportFromDerivation = true; -} +#if __linux__ + sandboxPaths = tokenizeString<StringSet>("/bin/sh=" BASH_PATH); +#endif + + allowedImpureHostPrefixes = tokenizeString<StringSet>(DEFAULT_ALLOWED_IMPURE_PREFIXES); +} void Settings::loadConfFile() { - Path settingsFile = (format("%1%/%2%") % nixConfDir % "nix.conf").str(); - if (!pathExists(settingsFile)) return; - string contents = readFile(settingsFile); - - 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 Error(format("illegal configuration line ‘%1%’ in ‘%2%’") % line % settingsFile); - - string name = tokens[0]; - - vector<string>::iterator i = tokens.begin(); - advance(i, 2); - settings[name] = concatStringsSep(" ", Strings(i, tokens.end())); // FIXME: slow - }; + applyConfigFile(nixConfDir + "/nix.conf"); } - void Settings::set(const string & name, const string & value) { - settings[name] = value; overrides[name] = value; + Config::set(name, value); } - -string Settings::get(const string & name, const string & def) -{ - auto i = settings.find(name); - if (i == settings.end()) return def; - return i->second; -} - - -Strings Settings::get(const string & name, const Strings & def) -{ - auto i = settings.find(name); - if (i == settings.end()) return def; - return tokenizeString<Strings>(i->second); -} - - -bool Settings::get(const string & name, bool def) +StringMap Settings::getOverrides() { - bool res = def; - _get(res, name); - return res; + return overrides; } - -int Settings::get(const string & name, int def) +unsigned int Settings::getDefaultCores() { - int res = def; - _get(res, name); - return res; -} - - -void Settings::update() -{ - _get(tryFallback, "build-fallback"); - - auto s = get("build-max-jobs", std::string("1")); - if (s == "auto") - maxBuildJobs = std::max(1U, std::thread::hardware_concurrency()); - else - if (!string2Int(s, maxBuildJobs)) - throw Error("configuration setting ‘build-max-jobs’ should be ‘auto’ or an integer"); - - _get(buildCores, "build-cores"); - _get(thisSystem, "system"); - _get(maxSilentTime, "build-max-silent-time"); - _get(buildTimeout, "build-timeout"); - _get(reservedSize, "gc-reserved-space"); - _get(fsyncMetadata, "fsync-metadata"); - _get(useSQLiteWAL, "use-sqlite-wal"); - _get(syncBeforeRegistering, "sync-before-registering"); - _get(useSubstitutes, "build-use-substitutes"); - _get(buildUsersGroup, "build-users-group"); - _get(impersonateLinux26, "build-impersonate-linux-26"); - _get(keepLog, "build-keep-log"); - _get(compressLog, "build-compress-log"); - _get(maxLogSize, "build-max-log-size"); - _get(pollInterval, "build-poll-interval"); - _get(checkRootReachability, "gc-check-reachability"); - _get(gcKeepOutputs, "gc-keep-outputs"); - _get(gcKeepDerivations, "gc-keep-derivations"); - _get(autoOptimiseStore, "auto-optimise-store"); - _get(envKeepDerivations, "env-keep-derivations"); - _get(sshSubstituterHosts, "ssh-substituter-hosts"); - _get(useSshSubstituter, "use-ssh-substituter"); - _get(enableNativeCode, "allow-unsafe-native-code-during-evaluation"); - _get(useCaseHack, "use-case-hack"); - _get(preBuildHook, "pre-build-hook"); - _get(keepGoing, "keep-going"); - _get(keepFailed, "keep-failed"); - _get(netrcFile, "netrc-file"); - _get(enableImportFromDerivation, "allow-import-from-derivation"); + return std::max(1U, std::thread::hardware_concurrency()); } +const string nixVersion = PACKAGE_VERSION; -void Settings::_get(string & res, const string & name) +template<> void Setting<SandboxMode>::set(const std::string & str) { - SettingsMap::iterator i = settings.find(name); - if (i == settings.end()) return; - res = i->second; + if (str == "true") value = smEnabled; + else if (str == "relaxed") value = smRelaxed; + else if (str == "false") value = smDisabled; + else throw UsageError("option '%s' has invalid value '%s'", name, str); } - -void Settings::_get(bool & res, const string & name) +template<> std::string Setting<SandboxMode>::to_string() { - SettingsMap::iterator i = settings.find(name); - if (i == settings.end()) return; - if (i->second == "true") res = true; - else if (i->second == "false") res = false; - else throw Error(format("configuration option ‘%1%’ should be either ‘true’ or ‘false’, not ‘%2%’") - % name % i->second); + if (value == smEnabled) return "true"; + else if (value == smRelaxed) return "relaxed"; + else if (value == smDisabled) return "false"; + else abort(); } - -void Settings::_get(StringSet & res, const string & name) +template<> void Setting<unsigned int, Settings::MaxBuildJobsTag>::set(const std::string & str) { - SettingsMap::iterator i = settings.find(name); - if (i == settings.end()) return; - res.clear(); - Strings ss = tokenizeString<Strings>(i->second); - res.insert(ss.begin(), ss.end()); + 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); } -void Settings::_get(Strings & res, const string & name) +template<> std::string Setting<unsigned int, Settings::MaxBuildJobsTag>::to_string() { - SettingsMap::iterator i = settings.find(name); - if (i == settings.end()) return; - res = tokenizeString<Strings>(i->second); + return std::to_string(value); } - -template<class N> void Settings::_get(N & res, const string & name) +template<> void Setting<bool, Settings::CaseHackTag>::set(const std::string & str) { - SettingsMap::iterator i = settings.find(name); - if (i == settings.end()) return; - if (!string2Int(i->second, res)) - throw Error(format("configuration setting ‘%1%’ should have an integer value") % name); + value = parseBool(str); + nix::useCaseHack = true; } - -string Settings::pack() +template<> std::string Setting<bool, Settings::CaseHackTag>::to_string() { - string s; - for (auto & i : settings) { - if (i.first.find('\n') != string::npos || - i.first.find('=') != string::npos || - i.second.find('\n') != string::npos) - throw Error("illegal option name/value"); - s += i.first; s += '='; s += i.second; s += '\n'; - } - return s; + return printBool(value); } - -void Settings::unpack(const string & pack) { - Strings lines = tokenizeString<Strings>(pack, "\n"); - for (auto & i : lines) { - string::size_type eq = i.find('='); - if (eq == string::npos) - throw Error("illegal option name/value"); - set(i.substr(0, eq), i.substr(eq + 1)); - } -} - - -Settings::SettingsMap Settings::getOverrides() -{ - return overrides; -} - - -const string nixVersion = PACKAGE_VERSION; - - } diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index ccec300f776e..f3a6038cafae 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -1,7 +1,7 @@ #pragma once #include "types.hh" -#include "logging.hh" +#include "config.hh" #include <map> #include <sys/types.h> @@ -9,40 +9,33 @@ namespace nix { +typedef enum { smEnabled, smRelaxed, smDisabled } SandboxMode; -struct Settings { +extern bool useCaseHack; // FIXME - typedef std::map<string, string> SettingsMap; +class Settings : public Config { - Settings(); - - void loadConfFile(); - - void set(const string & name, const string & value); + StringMap overrides; - string get(const string & name, const string & def); + unsigned int getDefaultCores(); - Strings get(const string & name, const Strings & def); +public: - bool get(const string & name, bool def); - - int get(const string & name, int def); + Settings(); - void update(); + void loadConfFile(); - string pack(); + void set(const string & name, const string & value); - void unpack(const string & pack); + StringMap getOverrides(); - SettingsMap getOverrides(); + Path nixPrefix; /* The directory where we store sources and derived files. */ Path nixStore; Path nixDataDir; /* !!! fix */ - Path nixPrefix; - /* The directory where we log various operations. */ Path nixLogDir; @@ -61,17 +54,14 @@ struct Settings { /* File name of the socket the daemon listens to. */ Path nixDaemonSocketFile; - /* Whether to keep temporary directories of failed builds. */ - bool keepFailed; + Setting<bool> keepFailed{this, false, "keep-failed", + "Whether to keep temporary directories of failed builds."}; - /* Whether to keep building subgoals when a sibling (another - subgoal of the same goal) fails. */ - bool keepGoing; + Setting<bool> keepGoing{this, false, "keep-going", + "Whether to keep building derivations when another build fails."}; - /* Whether, if we cannot realise the known closure corresponding - to a derivation, we should try to normalise the derivation - instead. */ - bool tryFallback; + Setting<bool> tryFallback{this, false, "build-fallback", + "Whether to fall back to building when substitution fails."}; /* Whether to show build log output in real time. */ bool verboseBuild = true; @@ -80,132 +70,206 @@ struct Settings { the log to show if a build fails. */ size_t logLines = 10; - /* Maximum number of parallel build jobs. 0 means unlimited. */ - unsigned int maxBuildJobs; + struct MaxBuildJobsTag { }; + Setting<unsigned int, MaxBuildJobsTag> maxBuildJobs{this, 1, "build-max-jobs", + "Maximum number of parallel build jobs. \"auto\" means use number of cores."}; - /* Number of CPU cores to utilize in parallel within a build, - i.e. by passing this number to Make via '-j'. 0 means that the - number of actual CPU cores on the local host ought to be - auto-detected. */ - unsigned int buildCores; + Setting<unsigned int> buildCores{this, getDefaultCores(), "build-cores", + "Number of CPU cores to utilize in parallel within a build, " + "i.e. by passing this number to Make via '-j'. 0 means that the " + "number of actual CPU cores on the local host ought to be " + "auto-detected."}; /* Read-only mode. Don't copy stuff to the store, don't change the database. */ - bool readOnlyMode; - - /* The canonical system name, as returned by config.guess. */ - string thisSystem; + bool readOnlyMode = false; - /* The maximum time in seconds that a builer can go without - producing any output on stdout/stderr before it is killed. 0 - means infinity. */ - time_t maxSilentTime; + Setting<std::string> thisSystem{this, SYSTEM, "system", + "The canonical Nix system name."}; - /* The maximum duration in seconds that a builder can run. 0 - means infinity. */ - time_t buildTimeout; + Setting<time_t> maxSilentTime{this, 0, "build-max-silent-time", + "The maximum time in seconds that a builer can go without " + "producing any output on stdout/stderr before it is killed. " + "0 means infinity."}; - /* Whether to use build hooks (for distributed builds). Sometimes - users want to disable this from the command-line. */ - bool useBuildHook; + Setting<time_t> buildTimeout{this, 0, "build-timeout", + "The maximum duration in seconds that a builder can run. " + "0 means infinity."}; - /* Amount of reserved space for the garbage collector - (/nix/var/nix/db/reserved). */ - off_t reservedSize; + Setting<bool> useBuildHook{this, true, "remote-builds", + "Whether to use build hooks (for distributed builds)."}; - /* Whether SQLite should use fsync. */ - bool fsyncMetadata; + Setting<off_t> reservedSize{this, 8 * 1024 * 1024, "gc-reserved-space", + "Amount of reserved disk space for the garbage collector."}; - /* Whether SQLite should use WAL mode. */ - bool useSQLiteWAL; + Setting<bool> fsyncMetadata{this, true, "fsync-metadata", + "Whether SQLite should use fsync()."}; - /* Whether to call sync() before registering a path as valid. */ - bool syncBeforeRegistering; + Setting<bool> useSQLiteWAL{this, true, "use-sqlite-wal", + "Whether SQLite should use WAL mode."}; - /* Whether to use substitutes. */ - bool useSubstitutes; + Setting<bool> syncBeforeRegistering{this, false, "sync-before-registering", + "Whether to call sync() before registering a path as valid."}; - /* The Unix group that contains the build users. */ - string buildUsersGroup; + Setting<bool> useSubstitutes{this, true, "build-use-substitutes", + "Whether to use substitutes."}; - /* Set of ssh connection strings for the ssh substituter */ - Strings sshSubstituterHosts; + Setting<std::string> buildUsersGroup{this, "", "build-users-group", + "The Unix group that contains the build users."}; - /* Whether to use the ssh substituter at all */ - bool useSshSubstituter; + Setting<bool> impersonateLinux26{this, false, "build-impersonate-linux-26", + "Whether to impersonate a Linux 2.6 machine on newer kernels."}; - /* Whether to impersonate a Linux 2.6 machine on newer kernels. */ - bool impersonateLinux26; + Setting<bool> keepLog{this, true, "build-keep-log", + "Whether to store build logs."}; - /* Whether to store build logs. */ - bool keepLog; + Setting<bool> compressLog{this, true, "build-compress-log", + "Whether to compress logs."}; - /* Whether to compress logs. */ - bool compressLog; - - /* Maximum number of bytes a builder can write to stdout/stderr - before being killed (0 means no limit). */ - unsigned long maxLogSize; + Setting<unsigned long> maxLogSize{this, 0, "build-max-log-size", + "Maximum number of bytes a builder can write to stdout/stderr " + "before being killed (0 means no limit)."}; /* When build-repeat > 0 and verboseBuild == true, whether to print repeated builds (i.e. builds other than the first one) to stderr. Hack to prevent Hydra logs from being polluted. */ bool printRepeatedBuilds = true; - /* How often (in seconds) to poll for locks. */ - unsigned int pollInterval; + Setting<unsigned int> pollInterval{this, 5, "build-poll-interval", + "How often (in seconds) to poll for locks."}; - /* Whether to check if new GC roots can in fact be found by the - garbage collector. */ - bool checkRootReachability; + Setting<bool> checkRootReachability{this, false, "gc-check-reachability", + "Whether to check if new GC roots can in fact be found by the " + "garbage collector."}; - /* Whether the garbage collector should keep outputs of live - derivations. */ - bool gcKeepOutputs; + Setting<bool> gcKeepOutputs{this, false, "gc-keep-outputs", + "Whether the garbage collector should keep outputs of live derivations."}; - /* Whether the garbage collector should keep derivers of live - paths. */ - bool gcKeepDerivations; + Setting<bool> gcKeepDerivations{this, true, "gc-keep-derivations", + "Whether the garbage collector should keep derivers of live paths."}; - /* Whether to automatically replace files with identical contents - with hard links. */ - bool autoOptimiseStore; + Setting<bool> autoOptimiseStore{this, false, "auto-optimise-store", + "Whether to automatically replace files with identical contents with hard links."}; - /* Whether to add derivations as a dependency of user environments - (to prevent them from being GCed). */ - bool envKeepDerivations; + Setting<bool> envKeepDerivations{this, false, "env-keep-derivations", + "Whether to add derivations as a dependency of user environments " + "(to prevent them from being GCed)."}; /* Whether to lock the Nix client and worker to the same CPU. */ bool lockCPU; /* Whether to show a stack trace if Nix evaluation fails. */ - bool showTrace; + bool showTrace = false; + + Setting<bool> enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", + "Whether builtin functions that allow executing native code should be enabled."}; + + Setting<SandboxMode> sandboxMode{this, smDisabled, "build-use-sandbox", + "Whether to enable sandboxed builds. Can be \"true\", \"false\" or \"relaxed\".", + {"build-use-chroot"}}; + + Setting<PathSet> sandboxPaths{this, {}, "build-sandbox-paths", + "The paths to make available inside the build sandbox.", + {"build-chroot-dirs"}}; + + Setting<PathSet> extraSandboxPaths{this, {}, "build-extra-sandbox-paths", + "Additional paths to make available inside the build sandbox.", + {"build-extra-chroot-dirs"}}; + + Setting<bool> restrictEval{this, false, "restrict-eval", + "Whether to restrict file system access to paths in $NIX_PATH, " + "and to disallow fetching files from the network."}; - /* Whether native-code enabling primops should be enabled */ - bool enableNativeCode; + Setting<size_t> buildRepeat{this, 0, "build-repeat", + "The number of times to repeat a build in order to verify determinism."}; - /* The hook to run just before a build to set derivation-specific - build settings */ - Path preBuildHook; +#if __linux__ + Setting<std::string> sandboxShmSize{this, "50%", "sandbox-dev-shm-size", + "The size of /dev/shm in the build sandbox."}; +#endif - /* Path to the netrc file used to obtain usernames/passwords for - downloads. */ - Path netrcFile; + Setting<PathSet> allowedImpureHostPrefixes{this, {}, "allowed-impure-host-deps", + "Which prefixes to allow derivations to ask for access to (primarily for Darwin)."}; + +#if __APPLE__ + Setting<bool> darwinLogSandboxViolations{this, false, "darwin-log-sandbox-violations", + "Whether to log Darwin sandbox access violations to the system log."}; +#endif + + Setting<bool> runDiffHook{this, false, "run-diff-hook", + "Whether to run the program specified by the diff-hook setting " + "repeated builds produce a different result. Typically used to " + "plug in diffoscope."}; + + PathSetting diffHook{this, true, "", "diff-hook", + "A program that prints out the differences between the two paths " + "specified on its command line."}; + + Setting<bool> enforceDeterminism{this, true, "enforce-determinism", + "Whether to fail if repeated builds produce different output."}; + + Setting<Strings> binaryCachePublicKeys{this, + {"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="}, + "binary-cache-public-keys", + "Trusted public keys for secure substitution."}; + + 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<bool> enableHttp2{this, true, "enable-http2", + "Whether to enable HTTP/2 support."}; + + Setting<unsigned int> tarballTtl{this, 60 * 60, "tarball-ttl", + "How soon to expire files fetched by builtins.fetchTarball and builtins.fetchurl."}; + + Setting<std::string> signedBinaryCaches{this, "*", "signed-binary-caches", + "Obsolete."}; + + Setting<Strings> substituters{this, + nixStore == "/nix/store" ? Strings{"https://cache.nixos.org/"} : Strings(), + "substituters", + "The URIs of substituters (such as https://cache.nixos.org/).", + {"binary-caches"}}; + + // FIXME: provide a way to add to option values. + Setting<Strings> extraSubstituters{this, {}, "extra-substituters", + "Additional URIs of substituters.", + {"extra-binary-caches"}}; + + Setting<Strings> trustedUsers{this, {"root"}, "trusted-users", + "Which users or groups are trusted to ask the daemon to do unsafe things."}; + + /* ?Who we trust to use the daemon in safe ways */ + Setting<Strings> allowedUsers{this, {"*"}, "allowed-users", + "Which users or groups are allowed to connect to the daemon."}; + + Setting<bool> printMissing{this, true, "print-missing", + "Whether to print what paths need to be built or downloaded."}; + + Setting<std::string> preBuildHook{this, +#if __APPLE__ + nixLibexecDir + "/nix/resolve-system-dependencies", +#else + "", +#endif + "pre-build-hook", + "A program to run just before a build to set derivation-specific build settings."}; + + Setting<std::string> netrcFile{this, fmt("%s/%s", nixConfDir, "netrc"), "netrc-file", + "Path to the netrc file used to obtain usernames/passwords for downloads."}; /* Path to the SSL CA file used */ Path caFile; - /* Whether we allow import-from-derivation */ - bool enableImportFromDerivation; - -private: - SettingsMap settings, overrides; + Setting<bool> enableImportFromDerivation{this, true, "allow-import-from-derivation", + "Whether the evaluator allows importing the result of a derivation."}; - void _get(string & res, const string & name); - void _get(bool & res, const string & name); - void _get(StringSet & res, const string & name); - void _get(Strings & res, const string & name); - template<class N> void _get(N & res, const string & name); + struct CaseHackTag { }; + Setting<bool, CaseHackTag> useCaseHack{this, nix::useCaseHack, "use-case-hack", + "Whether to enable a Darwin-specific hack for dealing with file name collisions."}; }; diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 0e838846c794..befc560bfcec 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -12,6 +12,10 @@ static std::string uriScheme = "ssh://"; struct LegacySSHStore : public Store { + const Setting<int> maxConnections{this, 1, "max-connections", "maximum number of concurrent SSH connections"}; + const Setting<Path> sshKey{this, "", "ssh-key", "path to an SSH private key"}; + const Setting<bool> compress{this, false, "compress", "whether to compress the connection"}; + struct Connection { std::unique_ptr<SSHMaster::Connection> sshConn; @@ -29,16 +33,16 @@ struct LegacySSHStore : public Store : Store(params) , host(host) , connections(make_ref<Pool<Connection>>( - std::max(1, std::stoi(get(params, "max-connections", "1"))), + std::max(1, (int) maxConnections), [this]() { return openConnection(); }, [](const ref<Connection> & r) { return true; } )) , master( host, - get(params, "ssh-key", ""), + sshKey, // Use SSH master only if using more than 1 connection. connections->capacity() > 1, - get(params, "compress", "") == "true") + compress) { } diff --git a/src/libstore/local-fs-store.cc b/src/libstore/local-fs-store.cc index 57e1b8a09fe6..bf247903c9db 100644 --- a/src/libstore/local-fs-store.cc +++ b/src/libstore/local-fs-store.cc @@ -9,9 +9,6 @@ namespace nix { LocalFSStore::LocalFSStore(const Params & params) : Store(params) - , rootDir(get(params, "root")) - , stateDir(canonPath(get(params, "state", rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir))) - , logDir(canonPath(get(params, "log", rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir))) { } @@ -88,6 +85,8 @@ void LocalFSStore::narFromPath(const Path & path, Sink & sink) const string LocalFSStore::drvsLogDir = "drvs"; + + std::shared_ptr<std::string> LocalFSStore::getBuildLog(const Path & path_) { auto path(path_); @@ -110,8 +109,8 @@ std::shared_ptr<std::string> LocalFSStore::getBuildLog(const Path & path_) Path logPath = j == 0 - ? (format("%1%/%2%/%3%/%4%") % logDir % drvsLogDir % string(baseName, 0, 2) % string(baseName, 2)).str() - : (format("%1%/%2%/%3%") % logDir % drvsLogDir % baseName).str(); + ? fmt("%s/%s/%s/%s", logDir, drvsLogDir, string(baseName, 0, 2), string(baseName, 2)) + : fmt("%s/%s/%s", logDir, drvsLogDir, baseName); Path logBz2Path = logPath + ".bz2"; if (pathExists(logPath)) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 8610841d7229..5a98454ab38e 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -38,13 +38,14 @@ namespace nix { LocalStore::LocalStore(const Params & params) : Store(params) , LocalFSStore(params) - , realStoreDir(get(params, "real", rootDir != "" ? rootDir + "/nix/store" : storeDir)) + , realStoreDir_{this, false, rootDir != "" ? rootDir + "/nix/store" : storeDir, "real", + "physical path to the Nix store"} + , realStoreDir(realStoreDir_) , dbDir(stateDir + "/db") , linksDir(realStoreDir + "/.links") , reservedPath(dbDir + "/reserved") , schemaPath(dbDir + "/schema") , trashDir(realStoreDir + "/trash") - , requireSigs(trim(settings.get("signed-binary-caches", std::string("*"))) != "") // FIXME: rename option , publicKeys(getDefaultPublicKeys()) { auto state(_state.lock()); @@ -74,7 +75,7 @@ LocalStore::LocalStore(const Params & params) mode_t perm = 01775; - struct group * gr = getgrnam(settings.buildUsersGroup.c_str()); + struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str()); if (!gr) printError(format("warning: the group ‘%1%’ specified in ‘build-users-group’ does not exist") % settings.buildUsersGroup); @@ -1332,9 +1333,9 @@ void LocalStore::signPathInfo(ValidPathInfo & info) { // FIXME: keep secret keys in memory. - auto secretKeyFiles = settings.get("secret-key-files", Strings()); + auto secretKeyFiles = settings.secretKeyFiles; - for (auto & secretKeyFile : secretKeyFiles) { + for (auto & secretKeyFile : secretKeyFiles.get()) { SecretKey secretKey(readFile(secretKeyFile)); info.sign(secretKey); } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 750da0c142d3..f2c40e96464b 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -67,6 +67,8 @@ private: public: + PathSetting realStoreDir_; + const Path realStoreDir; const Path dbDir; const Path linksDir; @@ -76,7 +78,9 @@ public: private: - bool requireSigs; + Setting<bool> requireSigs{(Store*) this, + settings.signedBinaryCaches != "", // FIXME + "require-sigs", "whether store paths should have a trusted signature on import"}; PublicKeys publicKeys; diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index c9c590787450..da3c8eb8d89d 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -43,7 +43,7 @@ template Paths readStorePaths(Store & store, Source & from); RemoteStore::RemoteStore(const Params & params) : Store(params) , connections(make_ref<Pool<Connection>>( - std::max(1, std::stoi(get(params, "max-connections", "1"))), + std::max(1, (int) maxConnections), [this]() { return openConnectionWrapper(); }, [](const ref<Connection> & r) { return r->to.good() && r->from.good(); } )) @@ -166,9 +166,7 @@ void RemoteStore::setOptions(Connection & conn) << settings.useSubstitutes; if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 12) { - Settings::SettingsMap overrides = settings.getOverrides(); - if (overrides["ssh-auth-sock"] == "") - overrides["ssh-auth-sock"] = getEnv("SSH_AUTH_SOCK"); + StringMap overrides = settings.getOverrides(); conn.to << overrides.size(); for (auto & i : overrides) conn.to << i.first << i.second; diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index db8da7eaa8c5..479cf3a7909d 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -22,6 +22,9 @@ class RemoteStore : public virtual Store { public: + const Setting<int> maxConnections{(Store*) this, 1, + "max-connections", "maximum number of concurrent connections to the Nix daemon"}; + RemoteStore(const Params & params); /* Implementations of abstract store API methods. */ diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 3053f908c4e2..245455296013 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -125,22 +125,22 @@ S3Helper::DownloadResult S3Helper::getObject( struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore { + const Setting<std::string> region{this, Aws::Region::US_EAST_1, "region", {"aws-region"}}; + const Setting<std::string> narinfoCompression{this, "", "narinfo-compression", "compression method for .narinfo files"}; + const Setting<std::string> lsCompression{this, "", "ls-compression", "compression method for .ls files"}; + const Setting<std::string> logCompression{this, "", "log-compression", "compression method for log/* files"}; + std::string bucketName; Stats stats; S3Helper s3Helper; - std::string narinfoCompression, lsCompression, logCompression; - S3BinaryCacheStoreImpl( const Params & params, const std::string & bucketName) : S3BinaryCacheStore(params) , bucketName(bucketName) - , s3Helper(get(params, "aws-region", Aws::Region::US_EAST_1)) - , narinfoCompression(get(params, "narinfo-compression", "")) - , lsCompression(get(params, "ls-compression", "")) - , logCompression(get(params, "log-compression", "")) + , s3Helper(region) { diskCache = getNarInfoDiskCache(); } diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 2a81a8b1ebe5..bb536fadfd51 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -14,16 +14,19 @@ class SSHStore : public RemoteStore { public: + const Setting<Path> sshKey{(Store*) this, "", "ssh-key", "path to an SSH private key"}; + const Setting<bool> compress{(Store*) this, false, "compress", "whether to compress the connection"}; + SSHStore(const std::string & host, const Params & params) : Store(params) , RemoteStore(params) , host(host) , master( host, - get(params, "ssh-key", ""), + sshKey, // Use SSH master only if using more than 1 connection. connections->capacity() > 1, - get(params, "compress", "") == "true") + compress) { } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 53c802044ea6..835bbb90e0bb 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -241,8 +241,8 @@ Path Store::computeStorePathForText(const string & name, const string & s, Store::Store(const Params & params) - : storeDir(get(params, "store", settings.nixStore)) - , state({std::stoi(get(params, "path-info-cache-size", "65536"))}) + : Config(params) + , state({(size_t) pathInfoCacheSize}) { } @@ -718,7 +718,10 @@ ref<Store> openStore(const std::string & uri, const Store::Params & params) { for (auto fun : *RegisterStoreImplementation::implementations) { auto store = fun(uri, params); - if (store) return ref<Store>(store); + if (store) { + store->warnUnknownSettings(); + return ref<Store>(store); + } } throw Error(format("don't know how to open Nix store ‘%s’") % uri); @@ -779,14 +782,10 @@ std::list<ref<Store>> getDefaultSubstituters() state->stores.push_back(openStore(uri)); }; - Strings defaultSubstituters; - if (settings.nixStore == "/nix/store") - defaultSubstituters.push_back("https://cache.nixos.org/"); - - for (auto uri : settings.get("substituters", settings.get("binary-caches", defaultSubstituters))) + for (auto uri : settings.substituters.get()) addStore(uri); - for (auto uri : settings.get("extra-binary-caches", Strings())) + for (auto uri : settings.extraSubstituters.get()) addStore(uri); state->done = true; diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index c0a52145af53..067309c9e956 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -6,6 +6,7 @@ #include "lru-cache.hh" #include "sync.hh" #include "globals.hh" +#include "config.hh" #include <atomic> #include <limits> @@ -224,13 +225,17 @@ struct BuildResult }; -class Store : public std::enable_shared_from_this<Store> +class Store : public std::enable_shared_from_this<Store>, public Config { public: typedef std::map<std::string, std::string> Params; - const Path storeDir; + const PathSetting storeDir_{this, false, settings.nixStore, + "store", "path to the Nix store"}; + const Path storeDir = storeDir_; + + const Setting<int> pathInfoCacheSize{this, 65536, "path-info-cache-size", "size of the in-memory store path information cache"}; protected: @@ -585,9 +590,19 @@ protected: class LocalFSStore : public virtual Store { public: - const Path rootDir; - const Path stateDir; - const Path logDir; + + // FIXME: the (Store*) cast works around a bug in gcc that causes + // it to emit the call to the Option constructor. Clang works fine + // either way. + const PathSetting rootDir{(Store*) this, true, "", + "root", "directory prefixed to all other paths"}; + const PathSetting stateDir{(Store*) this, false, + rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir, + "state", "directory where Nix will store state"}; + const PathSetting logDir{(Store*) this, false, + rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir, + "log", "directory where Nix will store state"}; + const static string drvsLogDir; LocalFSStore(const Params & params); diff --git a/src/libutil/config.cc b/src/libutil/config.cc new file mode 100644 index 000000000000..e7a810cec4d2 --- /dev/null +++ b/src/libutil/config.cc @@ -0,0 +1,194 @@ +#include "config.hh" +#include "args.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); +} + +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); + 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); + initials.erase(i); + set = true; + } + } + } +} + +void Config::warnUnknownSettings() +{ + for (auto & i : initials) + warn("unknown setting '%s'", i.first); +} + +StringMap Config::getSettings() +{ + StringMap res; + for (auto & opt : _settings) + if (!opt.second.isAlias) + 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 &) { } +} + +AbstractSetting::AbstractSetting( + const std::string & name, + const std::string & description, + const std::set<std::string> & aliases) + : name(name), description(description), aliases(aliases) +{ +} + +template<> void Setting<std::string>::set(const std::string & str) +{ + value = str; +} + +template<> std::string Setting<std::string>::to_string() +{ + return value; +} + +template<typename T, typename Tag> +void Setting<T, Tag>::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() +{ + static_assert(std::is_integral<T>::value, "Integer required."); + return std::to_string(value); +} + +bool AbstractSetting::parseBool(const std::string & str) +{ + if (str == "true" || str == "yes" || str == "1") + return true; + else if (str == "false" || str == "no" || str == "0") + return false; + else + throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str); +} + +template<> void Setting<bool>::set(const std::string & str) +{ + value = parseBool(str); +} + +std::string AbstractSetting::printBool(bool b) +{ + return b ? "true" : "false"; +} + + +template<> std::string Setting<bool>::to_string() +{ + return printBool(value); +} + +template<> void Setting<Strings>::set(const std::string & str) +{ + value = tokenizeString<Strings>(str); +} + +template<> std::string Setting<Strings>::to_string() +{ + return concatStringsSep(" ", value); +} + +template<> void Setting<StringSet>::set(const std::string & str) +{ + value = tokenizeString<StringSet>(str); +} + +template<> std::string Setting<StringSet>::to_string() +{ + return concatStringsSep(" ", value); +} + +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>; + +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 000000000000..6c8612f675c7 --- /dev/null +++ b/src/libutil/config.hh @@ -0,0 +1,163 @@ +#include <map> +#include <set> + +#include "types.hh" + +#pragma once + +namespace nix { + +class Args; +class AbstractSetting; + +/* 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 = false; + AbstractSetting * 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(); + + void applyConfigFile(const Path & path, bool fatal = false); +}; + +class AbstractSetting +{ + friend class Config; + +public: + + const std::string name; + const std::string description; + const std::set<std::string> aliases; + + int created = 123; + +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. + assert(created == 123); + } + + virtual void set(const std::string & value) = 0; + + virtual std::string to_string() = 0; + + bool parseBool(const std::string & str); + std::string printBool(bool b); +}; + +struct DefaultSettingTag { }; + +/* A setting of type T. */ +template<typename T, typename Tag = DefaultSettingTag> +class Setting : public AbstractSetting +{ +protected: + + T value; + +public: + + Setting(Config * options, + 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 set(const std::string & str) override; + + std::string to_string() override; +}; + +template<typename T> +std::ostream & operator <<(std::ostream & str, const Setting<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; } + +/* A special setting for Paths. These are automatically canonicalised + (e.g. "/foo//bar/" becomes "/foo/bar"). */ +class PathSetting : public Setting<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 = {}) + : Setting<Path>(options, def, name, description, aliases) + , allowEmpty(allowEmpty) + { + set(value); + } + + void set(const std::string & str) override; + + Path operator +(const char * p) const { return value + p; } +}; + +} diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index d9e8d22d7685..afcc2ec58543 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -3,7 +3,12 @@ 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 { @@ -52,7 +57,7 @@ Verbosity verbosity = lvlInfo; void warnOnce(bool & haveWarned, const FormatOrString & fs) { if (!haveWarned) { - printError(format("warning: %1%") % fs.s); + warn(fs.s); haveWarned = true; } } diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index 3f83664794f7..81aebccdca45 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -30,6 +30,8 @@ public: log(lvlInfo, fs); } + virtual void warn(const std::string & msg); + 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) { } @@ -82,6 +84,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); + formatHelper(f, args...); + logger->warn(f.str()); +} + void warnOnce(bool & haveWarned, const FormatOrString & fs); void writeToStderr(const string & s); diff --git a/src/libutil/types.hh b/src/libutil/types.hh index 97d79af9b5d6..1429c238513b 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> @@ -141,6 +142,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 a640a64c724e..0bd51afd1a9f 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -96,6 +96,8 @@ Path absPath(Path path, Path dir) Path canonPath(const Path & path, bool resolveSymlinks) { + assert(path != ""); + string s; if (path[0] != '/') diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc index 8786e2561b9c..1389353bb5d8 100644 --- a/src/nix-daemon/nix-daemon.cc +++ b/src/nix-daemon/nix-daemon.cc @@ -436,30 +436,31 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe } case wopSetOptions: { - from >> settings.keepFailed; - from >> settings.keepGoing; - settings.set("build-fallback", readInt(from) ? "true" : "false"); + settings.keepFailed = readInt(from); + settings.keepGoing = readInt(from); + settings.tryFallback = readInt(from); verbosity = (Verbosity) readInt(from); - settings.set("build-max-jobs", std::to_string(readInt(from))); - settings.set("build-max-silent-time", std::to_string(readInt(from))); + settings.maxBuildJobs = readInt(from); + settings.maxSilentTime = readInt(from); settings.useBuildHook = readInt(from) != 0; settings.verboseBuild = lvlError == (Verbosity) readInt(from); readInt(from); // obsolete logType readInt(from); // obsolete printBuildTrace - settings.set("build-cores", std::to_string(readInt(from))); - settings.set("build-use-substitutes", readInt(from) ? "true" : "false"); + settings.buildCores = readInt(from); + settings.useSubstitutes = readInt(from); 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); - if (name == "build-timeout" || name == "use-ssh-substituter") - settings.set(name, value); - else - settings.set(trusted ? name : "untrusted-" + name, value); + try { + if (trusted || name == "build-timeout") + settings.set(name, value); + } catch (UsageError & e) { + warn(e.what()); + } } } - settings.update(); startWork(); stopWork(); break; @@ -878,8 +879,8 @@ static void daemonLoop(char * * argv) struct group * gr = peer.gidKnown ? getgrgid(peer.gid) : 0; string group = gr ? gr->gr_name : std::to_string(peer.gid); - Strings trustedUsers = settings.get("trusted-users", Strings({"root"})); - Strings allowedUsers = settings.get("allowed-users", Strings({"*"})); + Strings trustedUsers = settings.trustedUsers; + Strings allowedUsers = settings.allowedUsers; if (matchUser(user, group, trustedUsers)) trusted = true; diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 3dc167191c83..9131b74dfb41 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -145,7 +145,7 @@ static void opRealise(Strings opFlags, Strings opArgs) unknown = PathSet(); } - if (settings.get("print-missing", true)) + if (settings.printMissing) printMissing(ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize); if (dryRun) return; @@ -795,11 +795,11 @@ static void opServe(Strings opFlags, Strings opArgs) settings.maxSilentTime = readInt(in); settings.buildTimeout = readInt(in); if (GET_PROTOCOL_MINOR(clientVersion) >= 2) - in >> settings.maxLogSize; + settings.maxLogSize = readNum<unsigned long>(in); if (GET_PROTOCOL_MINOR(clientVersion) >= 3) { - settings.set("build-repeat", std::to_string(readInt(in))); - settings.set("enforce-determinism", readInt(in) != 0 ? "true" : "false"); - settings.set("run-diff-hook", "true"); + settings.buildRepeat = readInt(in); + settings.enforceDeterminism = readInt(in); + settings.runDiffHook = readInt(in); } settings.printRepeatedBuilds = false; }; diff --git a/src/nix/show-config.cc b/src/nix/show-config.cc new file mode 100644 index 000000000000..ba39e2bb29b3 --- /dev/null +++ b/src/nix/show-config.cc @@ -0,0 +1,43 @@ +#include "command.hh" +#include "common-args.hh" +#include "installables.hh" +#include "shared.hh" +#include "store-api.hh" +#include "json.hh" + +using namespace nix; + +struct CmdShowConfig : Command +{ + bool json = false; + + CmdShowConfig() + { + mkFlag(0, "json", "produce JSON output", &json); + } + + std::string name() override + { + return "show-config"; + } + + std::string description() override + { + return "show the Nix configuration"; + } + + void run() override + { + 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); + } else { + for (auto & s : settings.getSettings()) + std::cout << s.first + " = " + s.second + "\n"; + } + } +}; + +static RegisterCommand r1(make_ref<CmdShowConfig>()); |