diff --git a/src/nix/doctor.cc b/src/nix/doctor.cc index 88c6b78ba..b1039f779 100644 --- a/src/nix/doctor.cc +++ b/src/nix/doctor.cc @@ -1,14 +1,27 @@ +#include +#include +#include #include +#include + #include "command.hh" +#include "eval-settings.hh" +#include "eval.hh" +#include "filetransfer.hh" #include "logging.hh" #include "serve-protocol.hh" #include "shared.hh" #include "store-api.hh" +#include "split.hh" #include "local-fs-store.hh" #include "util.hh" #include "worker-protocol.hh" +namespace fs = std::filesystem; + +using namespace std::literals::string_view_literals; + using namespace nix; namespace { @@ -61,6 +74,36 @@ struct CmdDoctor : StoreCommand void run(ref store) override { logger->log("Running checks against store uri: " + store->getUri()); + std::cout << "- system: " << evalSettings.getCurrentSystem() << "\n"; + + if (fs::exists("/etc/os-release")) { + std::string hostOs = this->getOsDescription(); + std::cout << "- host os: " << hostOs << "\n"; + } + + // Now check if multi-user. + // FIXME: how actually do we check this? + bool isMultiUser = settings.buildUsersGroup != "" || store->getUri() == "daemon"; + std::cout << "- multi-user?: " << (isMultiUser ? "yes" : "no") << "\n"; + std::cout << "- sandbox: " << settings.sandboxMode.to_string() << "\n"; + std::cout << "- version: "; + // FIXME: nix-env is what nix-info uses. Is there any reason for us to do the same? + try { + // XXX + printVersion("nix-env:"); + } catch (Exit const &) { + // Ignore. + } + + std::cout << "- : "; + evalSettings.pureEval = false; + fileTransferSettings.tries = 0; // FIXME + EvalState evalState(SearchPath{}, store); + auto *nixpkgsSearch = evalState.parseExprFromString("", CanonPath::fromCwd()); + Value nixpkgsValue; + evalState.eval(nixpkgsSearch, nixpkgsValue); + //evalState.forceValue(nixpkgsValue, noPos); + std::cout << printValue(evalState, nixpkgsValue) << "\n"; if (store.dynamic_pointer_cast()) { success &= checkNixInPath(); @@ -73,6 +116,80 @@ struct CmdDoctor : StoreCommand throw Exit(2); } + std::string getOsDescription() const + { + constexpr std::string_view NAME = "NAME="; + constexpr std::string_view VERSION = "VERSION="; + constexpr std::string_view PRETTY_NAME = "PRETTY_NAME="; + constexpr std::string_view BUILD_ID = "BUILD_ID="; + + constexpr auto dequoteStrip = [](std::string_view s) -> std::string_view { + return stripChars(s, "\" \n\r"); + }; + + std::vector parts; + parts.reserve(4); + + if (auto unameCmd = which("uname")) { + assert(fs::path(*unameCmd).is_absolute()); + try { + // -sr -> --kernel-name --kernel-release + auto unameOutput = runProgram(*unameCmd, false, {"-sr"}); + parts.emplace_back(trim(unameOutput)); + } catch (ExecError const & e) { + printError("could not run uname to get OS info: %s", e.what()); + } + } else { + printError("could not get OS info: uname not found in PATH"); + } + + std::optional name; + std::optional version; + std::optional prettyName; + std::optional buildId; + + try { + std::string const contents = readFile("/etc/os-release"); + + for (std::string_view const line : splitBy(contents, "\n")) { + if (line.starts_with(NAME)) { + name = dequoteStrip(line.substr(NAME.size())); + } + + if (line.starts_with(VERSION)) { + version = dequoteStrip(line.substr(VERSION.size())); + } + + if (line.starts_with(PRETTY_NAME)) { + prettyName = dequoteStrip(line.substr(PRETTY_NAME.size())); + } + + if (line.starts_with(BUILD_ID)) { + buildId = dequoteStrip(line.substr(BUILD_ID.size())); + } + } + + // Use pretty name, falling back to name and version. + if (prettyName.has_value()) { + parts.emplace_back(*prettyName); + } else if (name.has_value()) { + parts.emplace_back(*name); + if (version.has_value()) { + parts.emplace_back(*version); + } + } + + if (buildId.has_value()) { + parts.emplace_back(*buildId); + } + + } catch (SysError const & e) { + printError("could not determine OS from /etc/os-release: %s", e.what()); + } + + return boost::algorithm::join(parts, ", "); + } + bool checkNixInPath() { PathSet dirs;