#include #include "command.hh" #include "common-args.hh" #include "eval.hh" #include "globals.hh" #include "legacy.hh" #include "shared.hh" #include "store-api.hh" #include "filetransfer.hh" #include "finally.hh" #include "loggers.hh" #include "markdown.hh" #include #include #include #include #include #include extern std::string chrootHelperName; void chrootHelper(int argc, char * * argv); namespace nix { /* Check if we have a non-loopback/link-local network interface. */ static bool haveInternet() { struct ifaddrs * addrs; if (getifaddrs(&addrs)) return true; Finally free([&]() { freeifaddrs(addrs); }); for (auto i = addrs; i; i = i->ifa_next) { if (!i->ifa_addr) continue; if (i->ifa_addr->sa_family == AF_INET) { if (ntohl(((sockaddr_in *) i->ifa_addr)->sin_addr.s_addr) != INADDR_LOOPBACK) { return true; } } else if (i->ifa_addr->sa_family == AF_INET6) { if (!IN6_IS_ADDR_LOOPBACK(&((sockaddr_in6 *) i->ifa_addr)->sin6_addr) && !IN6_IS_ADDR_LINKLOCAL(&((sockaddr_in6 *) i->ifa_addr)->sin6_addr)) return true; } } return false; } std::string programPath; char * * savedArgv; struct HelpRequested { }; struct NixArgs : virtual MultiCommand, virtual MixCommonArgs { bool printBuildLogs = false; bool useNet = true; bool refresh = false; bool showVersion = false; NixArgs() : MultiCommand(RegisterCommand::getCommandsFor({})), MixCommonArgs("nix") { categories.clear(); categories[Command::catDefault] = "Main commands"; categories[catSecondary] = "Infrequently used commands"; categories[catUtility] = "Utility/scripting commands"; categories[catNixInstallation] = "Commands for upgrading or troubleshooting your Nix installation"; addFlag({ .longName = "help", .description = "Show usage information.", .handler = {[&]() { throw HelpRequested(); }}, }); addFlag({ .longName = "print-build-logs", .shortName = 'L', .description = "Print full build logs on standard error.", .category = loggingCategory, .handler = {[&]() {setLogFormat(LogFormat::barWithLogs); }}, }); addFlag({ .longName = "version", .description = "Show version information.", .handler = {[&]() { showVersion = true; }}, }); addFlag({ .longName = "offline", .aliases = {"no-net"}, // FIXME: remove .description = "Disable substituters and consider all previously downloaded files up-to-date.", .handler = {[&]() { useNet = false; }}, }); addFlag({ .longName = "refresh", .description = "Consider all previously downloaded files out-of-date.", .handler = {[&]() { refresh = true; }}, }); } std::map> aliases = { {"add-to-store", {"store", "add-path"}}, {"cat-nar", {"nar", "cat"}}, {"cat-store", {"store", "cat"}}, {"copy-sigs", {"store", "copy-sigs"}}, {"dev-shell", {"develop"}}, {"diff-closures", {"store", "diff-closures"}}, {"dump-path", {"store", "dump-path"}}, {"hash-file", {"hash", "file"}}, {"hash-path", {"hash", "path"}}, {"ls-nar", {"nar", "ls"}}, {"ls-store", {"store", "ls"}}, {"make-content-addressable", {"store", "make-content-addressable"}}, {"optimise-store", {"store", "optimise"}}, {"ping-store", {"store", "ping"}}, {"sign-paths", {"store", "sign"}}, {"to-base16", {"hash", "to-base16"}}, {"to-base32", {"hash", "to-base32"}}, {"to-base64", {"hash", "to-base64"}}, {"verify", {"store", "verify"}}, }; bool aliasUsed = false; Strings::iterator rewriteArgs(Strings & args, Strings::iterator pos) override { if (aliasUsed || command || pos == args.end()) return pos; auto arg = *pos; auto i = aliases.find(arg); if (i == aliases.end()) return pos; warn("'%s' is a deprecated alias for '%s'", arg, concatStringsSep(" ", i->second)); pos = args.erase(pos); for (auto j = i->second.rbegin(); j != i->second.rend(); ++j) pos = args.insert(pos, *j); aliasUsed = true; return pos; } std::string description() override { return "a tool for reproducible and declarative configuration management"; } std::string doc() override { return #include "nix.md" ; } // Plugins may add new subcommands. void pluginsInited() override { commands = RegisterCommand::getCommandsFor({}); } }; /* Render the help for the specified subcommand to stdout using lowdown. */ static void showHelp(std::vector subcommand, MultiCommand & toplevel) { auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", concatStringsSep("-", subcommand)); evalSettings.restrictEval = false; evalSettings.pureEval = false; EvalState state({}, openStore("dummy://")); auto vGenerateManpage = state.allocValue(); state.eval(state.parseExprFromString( #include "generate-manpage.nix.gen.hh" , "/"), *vGenerateManpage); auto vUtils = state.allocValue(); state.cacheFile( "/utils.nix", "/utils.nix", state.parseExprFromString( #include "utils.nix.gen.hh" , "/"), *vUtils); auto attrs = state.buildBindings(16); attrs.alloc("command").mkString(toplevel.toJSON().dump()); auto vRes = state.allocValue(); state.callFunction(*vGenerateManpage, state.allocValue()->mkAttrs(attrs), *vRes, noPos); auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md")); if (!attr) throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); auto markdown = state.forceString(*attr->value); RunPager pager; std::cout << renderMarkdownToTerminal(markdown) << "\n"; } struct CmdHelp : Command { std::vector subcommand; CmdHelp() { expectArgs({ .label = "subcommand", .handler = {&subcommand}, }); } std::string description() override { return "show help about `nix` or a particular subcommand"; } std::string doc() override { return #include "help.md" ; } void run() override { assert(parent); MultiCommand * toplevel = parent; while (toplevel->parent) toplevel = toplevel->parent; showHelp(subcommand, *toplevel); } }; static auto rCmdHelp = registerCommand("help"); void mainWrapped(int argc, char * * argv) { savedArgv = argv; /* The chroot helper needs to be run before any threads have been started. */ if (argc > 0 && argv[0] == chrootHelperName) { chrootHelper(argc, argv); return; } initNix(); initGC(); #if __linux__ if (getuid() == 0) { try { saveMountNamespace(); if (unshare(CLONE_NEWNS) == -1) throw SysError("setting up a private mount namespace"); } catch (Error & e) { } } #endif programPath = argv[0]; auto programName = std::string(baseNameOf(programPath)); { auto legacy = (*RegisterLegacyCommand::commands)[programName]; if (legacy) return legacy(argc, argv); } evalSettings.pureEval = true; setLogFormat("bar"); settings.verboseBuild = false; if (isatty(STDERR_FILENO)) { verbosity = lvlNotice; } else { verbosity = lvlInfo; } Finally f([] { logger->stop(); }); NixArgs args; if (argc == 2 && std::string(argv[1]) == "__dump-args") { std::cout << args.toJSON().dump() << "\n"; return; } if (argc == 2 && std::string(argv[1]) == "__dump-builtins") { evalSettings.pureEval = false; EvalState state({}, openStore("dummy://")); auto res = nlohmann::json::object(); auto builtins = state.baseEnv.values[0]->attrs; for (auto & builtin : *builtins) { auto b = nlohmann::json::object(); if (!builtin.value->isPrimOp()) continue; auto primOp = builtin.value->primOp; if (!primOp->doc) continue; b["arity"] = primOp->arity; b["args"] = primOp->args; b["doc"] = trim(stripIndentation(primOp->doc)); res[(std::string) builtin.name] = std::move(b); } std::cout << res.dump() << "\n"; return; } Finally printCompletions([&]() { if (completions) { switch (completionType) { case ctNormal: std::cout << "normal\n"; break; case ctFilenames: std::cout << "filenames\n"; break; case ctAttrs: std::cout << "attrs\n"; break; } for (auto & s : *completions) std::cout << s.completion << "\t" << s.description << "\n"; } }); try { args.parseCmdline(argvToStrings(argc, argv)); } catch (HelpRequested &) { std::vector subcommand; MultiCommand * command = &args; while (command) { if (command && command->command) { subcommand.push_back(command->command->first); command = dynamic_cast(&*command->command->second); } else break; } showHelp(subcommand, args); return; } catch (UsageError &) { if (!completions) throw; } if (completions) return; if (args.showVersion) { printVersion(programName); return; } if (!args.command) throw UsageError("no subcommand specified"); if (args.command->first != "repl" && args.command->first != "doctor" && args.command->first != "upgrade-nix") settings.requireExperimentalFeature(Xp::NixCommand); if (args.useNet && !haveInternet()) { warn("you don't have Internet access; disabling some network-dependent features"); args.useNet = false; } if (!args.useNet) { // FIXME: should check for command line overrides only. if (!settings.useSubstitutes.overridden) settings.useSubstitutes = false; if (!settings.tarballTtl.overridden) settings.tarballTtl = std::numeric_limits::max(); if (!fileTransferSettings.tries.overridden) fileTransferSettings.tries = 0; if (!fileTransferSettings.connectTimeout.overridden) fileTransferSettings.connectTimeout = 1; } if (args.refresh) { settings.tarballTtl = 0; settings.ttlNegativeNarInfoCache = 0; settings.ttlPositiveNarInfoCache = 0; } args.command->second->prepare(); args.command->second->run(); } } int main(int argc, char * * argv) { // Increase the default stack size for the evaluator and for // libstdc++'s std::regex. nix::setStackSize(64 * 1024 * 1024); return nix::handleExceptions(argv[0], [&]() { nix::mainWrapped(argc, argv); }); }