diff options
-rw-r--r-- | doc/manual/expressions/advanced-attributes.xml | 18 | ||||
-rw-r--r-- | doc/manual/expressions/builtins.xml | 29 | ||||
-rw-r--r-- | doc/manual/release-notes/rl-2.2.xml | 4 | ||||
-rw-r--r-- | release-common.nix | 2 | ||||
-rw-r--r-- | shell.nix | 2 | ||||
-rw-r--r-- | src/libexpr/get-drvs.hh | 2 | ||||
-rw-r--r-- | src/libexpr/lexer.l | 33 | ||||
-rw-r--r-- | src/libstore/build.cc | 63 | ||||
-rw-r--r-- | src/libstore/globals.cc | 6 | ||||
-rw-r--r-- | src/libstore/globals.hh | 8 | ||||
-rw-r--r-- | src/libstore/legacy-ssh-store.cc | 6 | ||||
-rw-r--r-- | src/libstore/local-store.cc | 6 | ||||
-rw-r--r-- | src/libstore/local-store.hh | 2 | ||||
-rw-r--r-- | src/libstore/remote-store.cc | 7 | ||||
-rw-r--r-- | src/libstore/remote-store.hh | 4 | ||||
-rw-r--r-- | src/libstore/s3-binary-cache-store.cc | 91 | ||||
-rw-r--r-- | src/libstore/store-api.cc | 10 | ||||
-rw-r--r-- | src/libstore/store-api.hh | 6 | ||||
-rw-r--r-- | src/libutil/util.cc | 9 | ||||
-rw-r--r-- | src/libutil/util.hh | 3 | ||||
-rw-r--r-- | src/nix/doctor.cc | 124 | ||||
-rw-r--r-- | src/nix/local.mk | 2 | ||||
-rw-r--r-- | tests/check-refs.sh | 2 | ||||
-rw-r--r-- | tests/check-reqs.nix | 2 | ||||
-rw-r--r-- | tests/check-reqs.sh | 2 | ||||
-rw-r--r-- | tests/common.sh.in | 15 | ||||
-rw-r--r-- | tests/init.sh | 1 |
27 files changed, 335 insertions, 124 deletions
diff --git a/doc/manual/expressions/advanced-attributes.xml b/doc/manual/expressions/advanced-attributes.xml index 2af7a51acfbb..a9b97b91a0a2 100644 --- a/doc/manual/expressions/advanced-attributes.xml +++ b/doc/manual/expressions/advanced-attributes.xml @@ -312,9 +312,7 @@ big = "a very long string"; <varlistentry><term><varname>preferLocalBuild</varname></term> <listitem><para>If this attribute is set to - <literal>true</literal>, it has two effects. First, the - derivation will always be built, not substituted, even if a - substitute is available. Second, if <link + <literal>true</literal> and <link linkend="chap-distributed-builds">distributed building is enabled</link>, then, if possible, the derivaton will be built locally instead of forwarded to a remote machine. This is @@ -324,6 +322,20 @@ big = "a very long string"; </varlistentry> + + <varlistentry><term><varname>allowSubstitutes</varname></term> + + <listitem><para>If this attribute is set to + <literal>false</literal>, then Nix will always build this + derivation; it will not try to substitute its outputs. This is + useful for very trivial derivations (such as + <function>writeText</function> in Nixpkgs) that are cheaper to + build locally than to substitute from a binary + cache.</para></listitem> + + </varlistentry> + + </variablelist> </section> diff --git a/doc/manual/expressions/builtins.xml b/doc/manual/expressions/builtins.xml index 8d12da9b1356..03a60a3c4c74 100644 --- a/doc/manual/expressions/builtins.xml +++ b/doc/manual/expressions/builtins.xml @@ -1077,22 +1077,8 @@ Evaluates to <literal>[ "foo" ]</literal>. <replaceable>path</replaceable></term> <listitem><para>Return <literal>true</literal> if the path - <replaceable>path</replaceable> exists, and - <literal>false</literal> otherwise. One application of this - function is to conditionally include a Nix expression containing - user configuration: - -<programlisting> -let - fileName = builtins.getEnv "CONFIG_FILE"; - config = - if fileName != "" && builtins.pathExists (builtins.toPath fileName) - then import (builtins.toPath fileName) - else { someSetting = false; }; <lineannotation># default configuration</lineannotation> -in config.someSetting</programlisting> - - (Note that <envar>CONFIG_FILE</envar> must be an absolute path for - this to work.)</para></listitem> + <replaceable>path</replaceable> exists at evaluation time, and + <literal>false</literal> otherwise.</para></listitem> </varlistentry> @@ -1409,13 +1395,10 @@ in foo</programlisting> <varlistentry xml:id='builtin-toPath'> <term><function>builtins.toPath</function> <replaceable>s</replaceable></term> - <listitem><para>Convert the string value - <replaceable>s</replaceable> into a path value. The string - <replaceable>s</replaceable> must represent an absolute path - (i.e., must start with <literal>/</literal>). The path need not - exist. The resulting path is canonicalised, e.g., - <literal>builtins.toPath "//foo/xyzzy/../bar/"</literal> returns - <literal>/foo/bar</literal>.</para></listitem> + <listitem><para> DEPRECATED. Use <literal>/. + "/path"</literal> + to convert a string into an absolute path. For relative paths, + use <literal>./. + "/path"</literal>. + </para></listitem> </varlistentry> diff --git a/doc/manual/release-notes/rl-2.2.xml b/doc/manual/release-notes/rl-2.2.xml index bc28a56c9401..abe9b49adadd 100644 --- a/doc/manual/release-notes/rl-2.2.xml +++ b/doc/manual/release-notes/rl-2.2.xml @@ -19,6 +19,10 @@ </para> </listitem> + <listitem> + <para>Sandbox builds are now enabled by default on Linux.</para> + </listitem> + </itemizedlist> </section> diff --git a/release-common.nix b/release-common.nix index ace2a4f9b91f..9f584993daa1 100644 --- a/release-common.nix +++ b/release-common.nix @@ -64,10 +64,12 @@ rec { apis = ["s3" "transfer"]; customMemoryManagement = false; }).overrideDerivation (args: { + /* patches = args.patches or [] ++ [ (fetchpatch { url = https://github.com/edolstra/aws-sdk-cpp/commit/3e07e1f1aae41b4c8b340735ff9e8c735f0c063f.patch; sha256 = "1pij0v449p166f9l29x7ppzk8j7g9k9mp15ilh5qxp29c7fnvxy2"; }) ]; + */ })); perlDeps = diff --git a/shell.nix b/shell.nix index c04bcd151309..817684b7646e 100644 --- a/shell.nix +++ b/shell.nix @@ -1,6 +1,6 @@ { useClang ? false }: -with import (builtins.fetchGit { url = https://github.com/NixOS/nixpkgs-channels.git; ref = "nixos-18.03"; }) {}; +with import (builtins.fetchGit { url = https://github.com/NixOS/nixpkgs-channels.git; ref = "nixos-18.09"; }) {}; with import ./release-common.nix { inherit pkgs; }; diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index 4d9128e3f448..daaa635fe1b1 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -44,7 +44,7 @@ public: string queryDrvPath() const; string queryOutPath() const; string queryOutputName() const; - /** Return the list of outputs. The "outputs to install" are determined by `mesa.outputsToInstall`. */ + /** Return the list of outputs. The "outputs to install" are determined by `meta.outputsToInstall`. */ Outputs queryOutputs(bool onlyOutputsToInstall = false); StringSet queryMetaNames(); diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index a052447d3dce..c34e5c383923 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -6,9 +6,9 @@ %option nounput noyy_top_state +%s DEFAULT %x STRING %x IND_STRING -%x INSIDE_DOLLAR_CURLY %{ @@ -99,8 +99,6 @@ URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~ %% -<INITIAL,INSIDE_DOLLAR_CURLY>{ - if { return IF; } then { return THEN; } @@ -140,17 +138,19 @@ or { return OR_KW; } return FLOAT; } -\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; } -} +\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; } -\} { return '}'; } -<INSIDE_DOLLAR_CURLY>\} { POP_STATE(); return '}'; } -\{ { return '{'; } -<INSIDE_DOLLAR_CURLY>\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return '{'; } +\} { /* State INITIAL only exists at the bottom of the stack and is + used as a marker. DEFAULT replaces it everywhere else. + Popping when in INITIAL state causes an empty stack exception, + so don't */ + if (YYSTATE != INITIAL) + POP_STATE(); + return '}'; + } +\{ { PUSH_STATE(DEFAULT); return '{'; } -<INITIAL,INSIDE_DOLLAR_CURLY>\" { - PUSH_STATE(STRING); return '"'; - } +\" { PUSH_STATE(STRING); return '"'; } <STRING>([^\$\"\\]|\$[^\{\"\\]|\\{ANY}|\$\\{ANY})*\$/\" | <STRING>([^\$\"\\]|\$[^\{\"\\]|\\{ANY}|\$\\{ANY})+ { /* It is impossible to match strings ending with '$' with one @@ -159,7 +159,7 @@ or { return OR_KW; } yylval->e = unescapeStr(data->symbols, yytext, yyleng); return STR; } -<STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; } +<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; } <STRING>\" { POP_STATE(); return '"'; } <STRING>\$|\\|\$\\ { /* This can only occur when we reach EOF, otherwise the above @@ -169,7 +169,7 @@ or { return OR_KW; } return STR; } -<INITIAL,INSIDE_DOLLAR_CURLY>\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; } +\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; } <IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ { yylval->e = new ExprIndStr(yytext); return IND_STR; @@ -187,14 +187,13 @@ or { return OR_KW; } yylval->e = unescapeStr(data->symbols, yytext + 2, yyleng - 2); return IND_STR; } -<IND_STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; } +<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; } <IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; } <IND_STRING>\' { yylval->e = new ExprIndStr("'"); return IND_STR; } -<INITIAL,INSIDE_DOLLAR_CURLY>{ {PATH} { if (yytext[yyleng-1] == '/') throw ParseError("path '%s' has a trailing slash", yytext); @@ -219,7 +218,5 @@ or { return OR_KW; } return (unsigned char) yytext[0]; } -} - %% diff --git a/src/libstore/build.cc b/src/libstore/build.cc index cf4218a261fa..676ad5856b13 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -3310,6 +3310,7 @@ void DerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & outputs) struct Checks { + bool ignoreSelfRefs = false; std::experimental::optional<uint64_t> maxSize, maxClosureSize; std::experimental::optional<Strings> allowedReferences, allowedRequisites, disallowedReferences, disallowedRequisites; }; @@ -3345,35 +3346,6 @@ void DerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & outputs) return std::make_pair(pathsDone, closureSize); }; - auto checkRefs = [&](const std::experimental::optional<Strings> & value, bool allowed, bool recursive) - { - if (!value) return; - - PathSet spec = parseReferenceSpecifiers(worker.store, *drv, *value); - - PathSet used = recursive ? getClosure(info.path).first : info.references; - - PathSet badPaths; - - for (auto & i : used) - if (allowed) { - if (spec.find(i) == spec.end()) - badPaths.insert(i); - } else { - if (spec.find(i) != spec.end()) - badPaths.insert(i); - } - - if (!badPaths.empty()) { - string badPathsStr; - for (auto & i : badPaths) { - badPathsStr += "\n "; - badPathsStr += i; - } - throw BuildError("output '%s' is not allowed to refer to the following paths:%s", info.path, badPathsStr); - } - }; - auto applyChecks = [&](const Checks & checks) { if (checks.maxSize && info.narSize > *checks.maxSize) @@ -3387,6 +3359,38 @@ void DerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & outputs) info.path, closureSize, *checks.maxClosureSize); } + auto checkRefs = [&](const std::experimental::optional<Strings> & value, bool allowed, bool recursive) + { + if (!value) return; + + PathSet spec = parseReferenceSpecifiers(worker.store, *drv, *value); + + PathSet used = recursive ? getClosure(info.path).first : info.references; + + if (recursive && checks.ignoreSelfRefs) + used.erase(info.path); + + PathSet badPaths; + + for (auto & i : used) + if (allowed) { + if (!spec.count(i)) + badPaths.insert(i); + } else { + if (spec.count(i)) + badPaths.insert(i); + } + + if (!badPaths.empty()) { + string badPathsStr; + for (auto & i : badPaths) { + badPathsStr += "\n "; + badPathsStr += i; + } + throw BuildError("output '%s' is not allowed to refer to the following paths:%s", info.path, badPathsStr); + } + }; + checkRefs(checks.allowedReferences, true, false); checkRefs(checks.allowedRequisites, true, true); checkRefs(checks.disallowedReferences, false, false); @@ -3435,6 +3439,7 @@ void DerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & outputs) } else { // legacy non-structured-attributes case Checks checks; + checks.ignoreSelfRefs = true; checks.allowedReferences = parsedDrv->getStringsAttr("allowedReferences"); checks.allowedRequisites = parsedDrv->getStringsAttr("allowedRequisites"); checks.disallowedReferences = parsedDrv->getStringsAttr("disallowedReferences"); diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index a9c07b23a6f3..1c2c08715a14 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -78,7 +78,11 @@ void loadConfFile() ~/.nix/nix.conf or the command line. */ globalConfig.resetOverriden(); - globalConfig.applyConfigFile(getConfigDir() + "/nix/nix.conf"); + auto dirs = getConfigDirs(); + // Iterate over them in reverse so that the ones appearing first in the path take priority + for (auto dir = dirs.rbegin(); dir != dirs.rend(); dir++) { + globalConfig.applyConfigFile(*dir + "/nix/nix.conf"); + } } unsigned int Settings::getDefaultCores() diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 6b3e204536f1..53efc6a90fb6 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -195,7 +195,13 @@ public: Setting<bool> showTrace{this, false, "show-trace", "Whether to show a stack trace on evaluation errors."}; - Setting<SandboxMode> sandboxMode{this, smDisabled, "sandbox", + Setting<SandboxMode> sandboxMode{this, + #if __linux__ + smEnabled + #else + smDisabled + #endif + , "sandbox", "Whether to enable sandboxed builds. Can be \"true\", \"false\" or \"relaxed\".", {"build-use-chroot", "build-use-sandbox"}}; diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 88d2574e86ef..26e1851981db 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -303,6 +303,12 @@ struct LegacySSHStore : public Store { auto conn(connections->get()); } + + unsigned int getProtocol() override + { + auto conn(connections->get()); + return conn->remoteVersion; + } }; static RegisterStoreImplementation regStore([]( diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 197b9d78995b..216f3417c4a8 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1338,6 +1338,12 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store, } +unsigned int LocalStore::getProtocol() +{ + return PROTOCOL_VERSION; +} + + #if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL) static void makeMutable(const Path & path) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 746bdbeed793..fce963433a5e 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -209,6 +209,8 @@ public: void registerValidPaths(const ValidPathInfos & infos); + unsigned int getProtocol() override; + void vacuumDB(); /* Repair the contents of the given path by redownloading it using diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index ef8b0e53b808..def140cfbe18 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -693,6 +693,13 @@ void RemoteStore::connect() } +unsigned int RemoteStore::getProtocol() +{ + auto conn(connections->get()); + return conn->daemonVersion; +} + + void RemoteStore::flushBadConnections() { connections->flushBad(); diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 7f9d7d1f56d6..4f554b5980e8 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -98,6 +98,8 @@ public: void connect() override; + unsigned int getProtocol() override; + void flushBadConnections(); protected: @@ -127,7 +129,7 @@ protected: ConnectionHandle getConnection(); - friend class ConnectionHandle; + friend struct ConnectionHandle; private: diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index ba11ce6bb6de..4f1e23198ffe 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -173,6 +173,8 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore 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"}; + const Setting<bool> multipartUpload{ + this, false, "multipart-upload", "whether to use multi-part uploads"}; const Setting<uint64_t> bufferSize{ this, 5 * 1024 * 1024, "buffer-size", "size (in bytes) of each part in multi-part uploads"}; @@ -261,48 +263,73 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore static std::shared_ptr<Aws::Utils::Threading::PooledThreadExecutor> executor = std::make_shared<Aws::Utils::Threading::PooledThreadExecutor>(maxThreads); - std::call_once(transferManagerCreated, [&]() { + std::call_once(transferManagerCreated, [&]() + { + if (multipartUpload) { + TransferManagerConfiguration transferConfig(executor.get()); + + transferConfig.s3Client = s3Helper.client; + transferConfig.bufferSize = bufferSize; + + transferConfig.uploadProgressCallback = + [](const TransferManager *transferManager, + const std::shared_ptr<const TransferHandle> + &transferHandle) + { + //FIXME: find a way to properly abort the multipart upload. + //checkInterrupt(); + debug("upload progress ('%s'): '%d' of '%d' bytes", + transferHandle->GetKey(), + transferHandle->GetBytesTransferred(), + transferHandle->GetBytesTotalSize()); + }; + + transferManager = TransferManager::Create(transferConfig); + } + }); - TransferManagerConfiguration transferConfig(executor.get()); + auto now1 = std::chrono::steady_clock::now(); - transferConfig.s3Client = s3Helper.client; - transferConfig.bufferSize = bufferSize; + if (transferManager) { - transferConfig.uploadProgressCallback = - [&](const TransferManager *transferManager, - const std::shared_ptr<const TransferHandle> - &transferHandle) - { - //FIXME: find a way to properly abort the multipart upload. - //checkInterrupt(); - debug("upload progress ('%s'): '%d' of '%d' bytes", - path, - transferHandle->GetBytesTransferred(), - transferHandle->GetBytesTotalSize()); - }; + if (contentEncoding != "") + throw Error("setting a content encoding is not supported with S3 multi-part uploads"); - transferManager = TransferManager::Create(transferConfig); - }); + std::shared_ptr<TransferHandle> transferHandle = + transferManager->UploadFile( + stream, bucketName, path, mimeType, + Aws::Map<Aws::String, Aws::String>(), + nullptr /*, contentEncoding */); - auto now1 = std::chrono::steady_clock::now(); + transferHandle->WaitUntilFinished(); - std::shared_ptr<TransferHandle> transferHandle = - transferManager->UploadFile( - stream, bucketName, path, mimeType, - Aws::Map<Aws::String, Aws::String>(), - nullptr, contentEncoding); + if (transferHandle->GetStatus() == TransferStatus::FAILED) + throw Error("AWS error: failed to upload 's3://%s/%s': %s", + bucketName, path, transferHandle->GetLastError().GetMessage()); - transferHandle->WaitUntilFinished(); + if (transferHandle->GetStatus() != TransferStatus::COMPLETED) + throw Error("AWS error: transfer status of 's3://%s/%s' in unexpected state", + bucketName, path); - if (transferHandle->GetStatus() == TransferStatus::FAILED) - throw Error("AWS error: failed to upload 's3://%s/%s': %s", - bucketName, path, transferHandle->GetLastError().GetMessage()); + } else { - if (transferHandle->GetStatus() != TransferStatus::COMPLETED) - throw Error("AWS error: transfer status of 's3://%s/%s' in unexpected state", - bucketName, path); + auto request = + Aws::S3::Model::PutObjectRequest() + .WithBucket(bucketName) + .WithKey(path); - printTalkative("upload of '%s' completed", path); + request.SetContentType(mimeType); + + if (contentEncoding != "") + request.SetContentEncoding(contentEncoding); + + auto stream = std::make_shared<istringstream_nocopy>(data); + + request.SetBody(stream); + + auto result = checkAws(fmt("AWS error uploading '%s'", path), + s3Helper.client->PutObject(request)); + } auto now2 = std::chrono::steady_clock::now(); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 92e2685f7f66..dc54c735fdb1 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -588,15 +588,19 @@ void copyStorePath(ref<Store> srcStore, ref<Store> dstStore, uint64_t total = 0; - // FIXME -#if 0 if (!info->narHash) { + StringSink sink; + srcStore->narFromPath({storePath}, sink); auto info2 = make_ref<ValidPathInfo>(*info); info2->narHash = hashString(htSHA256, *sink.s); if (!info->narSize) info2->narSize = sink.s->size(); + if (info->ultimate) info2->ultimate = false; info = info2; + + StringSource source(*sink.s); + dstStore->addToStore(*info, source, repair, checkSigs); + return; } -#endif if (info->ultimate) { auto info2 = make_ref<ValidPathInfo>(*info); diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 099818ed6f69..106b2be5e6b2 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -599,6 +599,12 @@ public: a notion of connection. Otherwise this is a no-op. */ virtual void connect() { }; + /* Get the protocol version of this store or it's connection. */ + virtual unsigned int getProtocol() + { + return 0; + }; + /* Get the priority of the store, used to order substituters. In particular, binary caches can specify a priority field in their "nix-cache-info" file. Lower value means higher priority. */ diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 03f0be705c1d..259eaf0a0dd3 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -496,6 +496,15 @@ Path getConfigDir() return configDir; } +std::vector<Path> getConfigDirs() +{ + Path configHome = getConfigDir(); + string configDirs = getEnv("XDG_CONFIG_DIRS"); + std::vector<Path> result = tokenizeString<std::vector<string>>(configDirs, ":"); + result.insert(result.begin(), configHome); + return result; +} + Path getDataDir() { diff --git a/src/libutil/util.hh b/src/libutil/util.hh index fc25d27758c7..bda87bee433e 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -131,6 +131,9 @@ Path getCacheDir(); /* Return $XDG_CONFIG_HOME or $HOME/.config. */ Path getConfigDir(); +/* Return the directories to search for user configuration files */ +std::vector<Path> getConfigDirs(); + /* Return $XDG_DATA_HOME or $HOME/.local/share. */ Path getDataDir(); diff --git a/src/nix/doctor.cc b/src/nix/doctor.cc new file mode 100644 index 000000000000..7b5444619470 --- /dev/null +++ b/src/nix/doctor.cc @@ -0,0 +1,124 @@ +#include "command.hh" +#include "serve-protocol.hh" +#include "shared.hh" +#include "store-api.hh" +#include "worker-protocol.hh" + +using namespace nix; + +std::string formatProtocol(unsigned int proto) +{ + if (proto) { + auto major = GET_PROTOCOL_MAJOR(proto) >> 8; + auto minor = GET_PROTOCOL_MINOR(proto); + return (format("%1%.%2%") % major % minor).str(); + } + return "unknown"; +} + +struct CmdDoctor : StoreCommand +{ + bool success = true; + + std::string name() override + { + return "doctor"; + } + + std::string description() override + { + return "check your system for potential problems"; + } + + void run(ref<Store> store) override + { + std::cout << "Store uri: " << store->getUri() << std::endl; + std::cout << std::endl; + + auto type = getStoreType(); + + if (type < tOther) { + success &= checkNixInPath(); + success &= checkProfileRoots(store); + } + success &= checkStoreProtocol(store->getProtocol()); + + if (!success) + throw Exit(2); + } + + bool checkNixInPath() + { + PathSet dirs; + + for (auto & dir : tokenizeString<Strings>(getEnv("PATH"), ":")) + if (pathExists(dir + "/nix-env")) + dirs.insert(dirOf(canonPath(dir + "/nix-env", true))); + + if (dirs.size() != 1) { + std::cout << "Warning: multiple versions of nix found in PATH." << std::endl; + std::cout << std::endl; + for (auto & dir : dirs) + std::cout << " " << dir << std::endl; + std::cout << std::endl; + return false; + } + + return true; + } + + bool checkProfileRoots(ref<Store> store) + { + PathSet dirs; + + for (auto & dir : tokenizeString<Strings>(getEnv("PATH"), ":")) { + Path profileDir = dirOf(dir); + try { + Path userEnv = canonPath(profileDir, true); + + if (store->isStorePath(userEnv) && hasSuffix(userEnv, "user-environment")) { + while (profileDir.find("/profiles/") == std::string::npos && isLink(profileDir)) + profileDir = absPath(readLink(profileDir), dirOf(profileDir)); + + if (profileDir.find("/profiles/") == std::string::npos) + dirs.insert(dir); + } + } catch (SysError &) {} + } + + if (!dirs.empty()) { + std::cout << "Warning: found profiles outside of " << settings.nixStateDir << "/profiles." << std::endl; + std::cout << "The generation this profile points to might not have a gcroot and could be" << std::endl; + std::cout << "garbage collected, resulting in broken symlinks." << std::endl; + std::cout << std::endl; + for (auto & dir : dirs) + std::cout << " " << dir << std::endl; + std::cout << std::endl; + return false; + } + + return true; + } + + bool checkStoreProtocol(unsigned int storeProto) + { + unsigned int clientProto = GET_PROTOCOL_MAJOR(SERVE_PROTOCOL_VERSION) == GET_PROTOCOL_MAJOR(storeProto) + ? SERVE_PROTOCOL_VERSION + : PROTOCOL_VERSION; + + if (clientProto != storeProto) { + std::cout << "Warning: protocol version of this client does not match the store." << std::endl; + std::cout << "While this is not necessarily a problem it's recommended to keep the client in" << std::endl; + std::cout << "sync with the daemon." << std::endl; + std::cout << std::endl; + std::cout << "Client protocol: " << formatProtocol(clientProto) << std::endl; + std::cout << "Store protocol: " << formatProtocol(storeProto) << std::endl; + std::cout << std::endl; + return false; + } + + return true; + } +}; + +static RegisterCommand r1(make_ref<CmdDoctor>()); diff --git a/src/nix/local.mk b/src/nix/local.mk index 168936314dcd..bdcca33d2a6f 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -21,6 +21,6 @@ nix_LIBS = libexpr libmain libstore libutil libformat nix_LDFLAGS = -pthread $(SODIUM_LIBS) $(foreach name, \ - nix-build nix-channel nix-collect-garbage nix-copy-closure nix-daemon nix-env nix-hash nix-instantiate nix-prefetch-rul nix-shell nix-shore, \ + nix-build nix-channel nix-collect-garbage nix-copy-closure nix-daemon nix-env nix-hash nix-instantiate nix-prefetch-url nix-shell nix-store, \ $(eval $(call install-symlink, nix, $(bindir)/$(name)))) $(eval $(call install-symlink, $(bindir)/nix, $(libexecdir)/nix/build-remote)) diff --git a/tests/check-refs.sh b/tests/check-refs.sh index 34ee22cfc8f7..16bbabc40985 100644 --- a/tests/check-refs.sh +++ b/tests/check-refs.sh @@ -1,5 +1,7 @@ source common.sh +clearStore + RESULT=$TEST_ROOT/result dep=$(nix-build -o $RESULT check-refs.nix -A dep) diff --git a/tests/check-reqs.nix b/tests/check-reqs.nix index 47b5b3d9c723..41436cb48e08 100644 --- a/tests/check-reqs.nix +++ b/tests/check-reqs.nix @@ -33,7 +33,7 @@ rec { }; # When specifying all the requisites, the build succeeds. - test1 = makeTest 1 [ "out" dep1 dep2 deps ]; + test1 = makeTest 1 [ dep1 dep2 deps ]; # But missing anything it fails. test2 = makeTest 2 [ dep2 deps ]; diff --git a/tests/check-reqs.sh b/tests/check-reqs.sh index 77689215def1..e9f65fc2a6d3 100644 --- a/tests/check-reqs.sh +++ b/tests/check-reqs.sh @@ -1,5 +1,7 @@ source common.sh +clearStore + RESULT=$TEST_ROOT/result nix-build -o $RESULT check-reqs.nix -A test1 diff --git a/tests/common.sh.in b/tests/common.sh.in index 2ee2f589dae4..6a523ca9d832 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -85,16 +85,13 @@ killDaemon() { trap "" EXIT } -canUseSandbox() { - if [[ $(uname) != Linux ]]; then return 1; fi - - if [ ! -L /proc/self/ns/user ]; then - echo "Kernel doesn't support user namespaces, skipping this test..." - return 1 - fi +if [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then + _canUseSandbox=1 +fi - if ! unshare --user true ; then - echo "Unprivileged user namespaces disabled by sysctl, skipping this test..." +canUseSandbox() { + if [[ ! $_canUseSandbox ]]; then + echo "Sandboxing not supported, skipping this test..." return 1 fi diff --git a/tests/init.sh b/tests/init.sh index e5353598bcc4..19a12c1e2d9e 100644 --- a/tests/init.sh +++ b/tests/init.sh @@ -16,6 +16,7 @@ mkdir "$NIX_CONF_DIR" cat > "$NIX_CONF_DIR"/nix.conf <<EOF build-users-group = keep-derivations = false +sandbox = false include nix.conf.extra EOF |