2016-02-09 22:28:29 +02:00
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
#include "command.hh"
|
|
|
|
#include "common-args.hh"
|
|
|
|
#include "eval.hh"
|
|
|
|
#include "globals.hh"
|
|
|
|
#include "legacy.hh"
|
|
|
|
#include "shared.hh"
|
|
|
|
#include "store-api.hh"
|
2020-04-07 00:57:28 +03:00
|
|
|
#include "filetransfer.hh"
|
2017-08-29 17:18:00 +03:00
|
|
|
#include "finally.hh"
|
2020-06-05 18:01:02 +03:00
|
|
|
#include "loggers.hh"
|
2021-09-13 15:41:28 +03:00
|
|
|
#include "markdown.hh"
|
2016-02-09 22:28:29 +02:00
|
|
|
|
2019-06-17 10:57:22 +03:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <ifaddrs.h>
|
|
|
|
#include <netdb.h>
|
2019-11-01 16:09:42 +02:00
|
|
|
#include <netinet/in.h>
|
2019-06-17 10:57:22 +03:00
|
|
|
|
2020-08-17 18:44:52 +03:00
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
|
2017-08-29 14:21:07 +03:00
|
|
|
extern std::string chrootHelperName;
|
|
|
|
|
|
|
|
void chrootHelper(int argc, char * * argv);
|
|
|
|
|
2016-02-09 22:28:29 +02:00
|
|
|
namespace nix {
|
|
|
|
|
2019-06-17 10:57:22 +03:00
|
|
|
/* 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) {
|
2019-06-28 16:38:23 +03:00
|
|
|
if (!IN6_IS_ADDR_LOOPBACK(&((sockaddr_in6 *) i->ifa_addr)->sin6_addr) &&
|
|
|
|
!IN6_IS_ADDR_LINKLOCAL(&((sockaddr_in6 *) i->ifa_addr)->sin6_addr))
|
2019-06-17 10:57:22 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-31 17:14:47 +02:00
|
|
|
std::string programPath;
|
2021-01-14 01:05:04 +02:00
|
|
|
char * * savedArgv;
|
2018-01-31 17:14:47 +02:00
|
|
|
|
2021-01-25 15:38:15 +02:00
|
|
|
struct HelpRequested { };
|
|
|
|
|
2016-02-09 22:28:29 +02:00
|
|
|
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
|
|
|
{
|
2019-05-15 18:33:56 +03:00
|
|
|
bool printBuildLogs = false;
|
2019-06-17 10:57:22 +03:00
|
|
|
bool useNet = true;
|
2020-01-28 18:34:48 +02:00
|
|
|
bool refresh = false;
|
2021-02-17 18:11:14 +02:00
|
|
|
bool showVersion = false;
|
2019-05-15 18:33:56 +03:00
|
|
|
|
2020-12-03 18:55:27 +02:00
|
|
|
NixArgs() : MultiCommand(RegisterCommand::getCommandsFor({})), MixCommonArgs("nix")
|
2016-02-09 22:28:29 +02:00
|
|
|
{
|
2020-05-05 16:18:23 +03:00
|
|
|
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";
|
|
|
|
|
2020-05-04 23:40:19 +03:00
|
|
|
addFlag({
|
|
|
|
.longName = "help",
|
2021-01-13 15:18:04 +02:00
|
|
|
.description = "Show usage information.",
|
2021-01-25 15:38:15 +02:00
|
|
|
.handler = {[&]() { throw HelpRequested(); }},
|
2020-05-04 23:40:19 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "print-build-logs",
|
|
|
|
.shortName = 'L',
|
2021-01-13 15:18:04 +02:00
|
|
|
.description = "Print full build logs on standard error.",
|
2021-01-25 20:03:13 +02:00
|
|
|
.category = loggingCategory,
|
2020-06-05 18:01:02 +03:00
|
|
|
.handler = {[&]() {setLogFormat(LogFormat::barWithLogs); }},
|
2020-05-04 23:40:19 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "version",
|
2021-01-13 15:18:04 +02:00
|
|
|
.description = "Show version information.",
|
2021-02-17 18:11:14 +02:00
|
|
|
.handler = {[&]() { showVersion = true; }},
|
2020-05-04 23:40:19 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
2021-02-01 15:11:42 +02:00
|
|
|
.longName = "offline",
|
2021-02-07 21:44:56 +02:00
|
|
|
.aliases = {"no-net"}, // FIXME: remove
|
2021-01-13 15:18:04 +02:00
|
|
|
.description = "Disable substituters and consider all previously downloaded files up-to-date.",
|
2020-05-04 23:40:19 +03:00
|
|
|
.handler = {[&]() { useNet = false; }},
|
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "refresh",
|
2021-01-13 15:18:04 +02:00
|
|
|
.description = "Consider all previously downloaded files out-of-date.",
|
2020-05-04 23:40:19 +03:00
|
|
|
.handler = {[&]() { refresh = true; }},
|
|
|
|
});
|
2020-12-03 23:45:44 +02:00
|
|
|
}
|
2020-06-04 12:14:19 +03:00
|
|
|
|
2020-12-03 23:45:44 +02:00
|
|
|
std::map<std::string, std::vector<std::string>> aliases = {
|
2020-07-24 21:38:56 +03:00
|
|
|
{"add-to-store", {"store", "add-path"}},
|
|
|
|
{"cat-nar", {"nar", "cat"}},
|
|
|
|
{"cat-store", {"store", "cat"}},
|
|
|
|
{"copy-sigs", {"store", "copy-sigs"}},
|
2020-12-03 23:45:44 +02:00
|
|
|
{"dev-shell", {"develop"}},
|
2020-07-24 21:38:56 +03:00
|
|
|
{"diff-closures", {"store", "diff-closures"}},
|
|
|
|
{"dump-path", {"store", "dump-path"}},
|
2020-12-03 23:45:44 +02:00
|
|
|
{"hash-file", {"hash", "file"}},
|
|
|
|
{"hash-path", {"hash", "path"}},
|
2020-07-24 21:38:56 +03:00
|
|
|
{"ls-nar", {"nar", "ls"}},
|
|
|
|
{"ls-store", {"store", "ls"}},
|
|
|
|
{"make-content-addressable", {"store", "make-content-addressable"}},
|
|
|
|
{"optimise-store", {"store", "optimise"}},
|
|
|
|
{"ping-store", {"store", "ping"}},
|
2021-01-14 00:31:18 +02:00
|
|
|
{"sign-paths", {"store", "sign"}},
|
2020-12-03 23:45:44 +02:00
|
|
|
{"to-base16", {"hash", "to-base16"}},
|
|
|
|
{"to-base32", {"hash", "to-base32"}},
|
|
|
|
{"to-base64", {"hash", "to-base64"}},
|
2020-07-24 21:38:56 +03:00
|
|
|
{"verify", {"store", "verify"}},
|
2020-12-03 23:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
2016-02-09 22:28:29 +02:00
|
|
|
}
|
2017-06-07 17:49:54 +03:00
|
|
|
|
2020-08-18 16:15:35 +03:00
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "a tool for reproducible and declarative configuration management";
|
|
|
|
}
|
2020-12-23 19:33:42 +02:00
|
|
|
|
|
|
|
std::string doc() override
|
|
|
|
{
|
|
|
|
return
|
|
|
|
#include "nix.md"
|
|
|
|
;
|
|
|
|
}
|
2021-01-28 17:04:47 +02:00
|
|
|
|
|
|
|
// Plugins may add new subcommands.
|
|
|
|
void pluginsInited() override
|
|
|
|
{
|
|
|
|
commands = RegisterCommand::getCommandsFor({});
|
|
|
|
}
|
2016-02-09 22:28:29 +02:00
|
|
|
};
|
|
|
|
|
2021-09-13 15:41:28 +03:00
|
|
|
/* Render the help for the specified subcommand to stdout using
|
|
|
|
lowdown. */
|
|
|
|
static void showHelp(std::vector<std::string> subcommand, MultiCommand & toplevel)
|
2020-12-02 13:26:09 +02:00
|
|
|
{
|
2021-09-13 15:41:28 +03:00
|
|
|
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 vJson = state.allocValue();
|
|
|
|
mkString(*vJson, toplevel.toJSON().dump());
|
|
|
|
|
|
|
|
auto vRes = state.allocValue();
|
|
|
|
state.callFunction(*vGenerateManpage, *vJson, *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";
|
2020-12-02 13:26:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
struct CmdHelp : Command
|
|
|
|
{
|
|
|
|
std::vector<std::string> subcommand;
|
|
|
|
|
|
|
|
CmdHelp()
|
|
|
|
{
|
|
|
|
expectArgs({
|
|
|
|
.label = "subcommand",
|
|
|
|
.handler = {&subcommand},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
2020-12-10 19:40:16 +02:00
|
|
|
return "show help about `nix` or a particular subcommand";
|
2020-12-02 13:26:09 +02:00
|
|
|
}
|
|
|
|
|
2020-12-10 19:40:16 +02:00
|
|
|
std::string doc() override
|
2020-12-02 13:26:09 +02:00
|
|
|
{
|
2020-12-10 19:40:16 +02:00
|
|
|
return
|
|
|
|
#include "help.md"
|
|
|
|
;
|
2020-12-02 13:26:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
2021-09-13 15:41:28 +03:00
|
|
|
assert(parent);
|
|
|
|
MultiCommand * toplevel = parent;
|
|
|
|
while (toplevel->parent) toplevel = toplevel->parent;
|
|
|
|
showHelp(subcommand, *toplevel);
|
2020-12-02 13:26:09 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static auto rCmdHelp = registerCommand<CmdHelp>("help");
|
|
|
|
|
2016-02-09 22:28:29 +02:00
|
|
|
void mainWrapped(int argc, char * * argv)
|
|
|
|
{
|
2021-01-14 01:05:04 +02:00
|
|
|
savedArgv = argv;
|
|
|
|
|
2017-08-29 14:21:07 +03:00
|
|
|
/* The chroot helper needs to be run before any threads have been
|
|
|
|
started. */
|
|
|
|
if (argc > 0 && argv[0] == chrootHelperName) {
|
|
|
|
chrootHelper(argc, argv);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-09 22:28:29 +02:00
|
|
|
initNix();
|
|
|
|
initGC();
|
|
|
|
|
2018-01-31 17:14:47 +02:00
|
|
|
programPath = argv[0];
|
2019-12-05 20:11:09 +02:00
|
|
|
auto programName = std::string(baseNameOf(programPath));
|
2016-02-09 22:28:29 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
auto legacy = (*RegisterLegacyCommand::commands)[programName];
|
|
|
|
if (legacy) return legacy(argc, argv);
|
|
|
|
}
|
|
|
|
|
2020-11-27 12:19:36 +02:00
|
|
|
verbosity = lvlNotice;
|
2018-10-26 12:35:46 +03:00
|
|
|
settings.verboseBuild = false;
|
2019-02-12 21:35:03 +02:00
|
|
|
evalSettings.pureEval = true;
|
2018-10-26 12:35:46 +03:00
|
|
|
|
2020-06-05 18:01:02 +03:00
|
|
|
setLogFormat("bar");
|
|
|
|
|
|
|
|
Finally f([] { logger->stop(); });
|
|
|
|
|
2016-02-09 22:28:29 +02:00
|
|
|
NixArgs args;
|
|
|
|
|
2020-08-24 15:49:30 +03:00
|
|
|
if (argc == 2 && std::string(argv[1]) == "__dump-args") {
|
2020-08-17 18:44:52 +03:00
|
|
|
std::cout << args.toJSON().dump() << "\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-24 15:49:30 +03:00
|
|
|
if (argc == 2 && std::string(argv[1]) == "__dump-builtins") {
|
2020-09-16 17:56:28 +03:00
|
|
|
evalSettings.pureEval = false;
|
2020-08-24 19:54:16 +03:00
|
|
|
EvalState state({}, openStore("dummy://"));
|
2020-08-24 15:49:30 +03:00
|
|
|
auto res = nlohmann::json::object();
|
|
|
|
auto builtins = state.baseEnv.values[0]->attrs;
|
|
|
|
for (auto & builtin : *builtins) {
|
|
|
|
auto b = nlohmann::json::object();
|
2020-12-12 03:15:11 +02:00
|
|
|
if (!builtin.value->isPrimOp()) continue;
|
2020-08-24 15:49:30 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-05-10 21:32:21 +03:00
|
|
|
Finally printCompletions([&]()
|
|
|
|
{
|
|
|
|
if (completions) {
|
2020-05-10 22:35:07 +03:00
|
|
|
std::cout << (pathCompletions ? "filenames\n" : "no-filenames\n");
|
2020-05-10 21:32:21 +03:00
|
|
|
for (auto & s : *completions)
|
2020-10-09 10:39:51 +03:00
|
|
|
std::cout << s.completion << "\t" << s.description << "\n";
|
2020-05-10 21:32:21 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
try {
|
|
|
|
args.parseCmdline(argvToStrings(argc, argv));
|
2021-01-25 15:38:15 +02:00
|
|
|
} catch (HelpRequested &) {
|
|
|
|
std::vector<std::string> subcommand;
|
|
|
|
MultiCommand * command = &args;
|
|
|
|
while (command) {
|
|
|
|
if (command && command->command) {
|
|
|
|
subcommand.push_back(command->command->first);
|
|
|
|
command = dynamic_cast<MultiCommand *>(&*command->command->second);
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
2021-09-13 15:41:28 +03:00
|
|
|
showHelp(subcommand, args);
|
2021-01-25 15:38:15 +02:00
|
|
|
return;
|
2020-05-10 21:32:21 +03:00
|
|
|
} catch (UsageError &) {
|
|
|
|
if (!completions) throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (completions) return;
|
2019-10-16 18:45:09 +03:00
|
|
|
|
2021-02-17 18:11:14 +02:00
|
|
|
if (args.showVersion) {
|
|
|
|
printVersion(programName);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-25 15:38:15 +02:00
|
|
|
if (!args.command)
|
|
|
|
throw UsageError("no subcommand specified");
|
2016-02-09 22:28:29 +02:00
|
|
|
|
2020-05-07 17:46:25 +03:00
|
|
|
if (args.command->first != "repl"
|
|
|
|
&& args.command->first != "doctor"
|
|
|
|
&& args.command->first != "upgrade-nix")
|
|
|
|
settings.requireExperimentalFeature("nix-command");
|
|
|
|
|
2019-06-17 10:57:22 +03:00
|
|
|
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.
|
2021-03-26 17:14:38 +02:00
|
|
|
if (!settings.useSubstitutes.overridden)
|
2019-06-17 10:57:22 +03:00
|
|
|
settings.useSubstitutes = false;
|
2021-03-26 17:14:38 +02:00
|
|
|
if (!settings.tarballTtl.overridden)
|
2019-06-17 10:57:22 +03:00
|
|
|
settings.tarballTtl = std::numeric_limits<unsigned int>::max();
|
2021-03-26 17:14:38 +02:00
|
|
|
if (!fileTransferSettings.tries.overridden)
|
2020-04-07 00:43:43 +03:00
|
|
|
fileTransferSettings.tries = 0;
|
2021-03-26 17:14:38 +02:00
|
|
|
if (!fileTransferSettings.connectTimeout.overridden)
|
2020-04-07 00:43:43 +03:00
|
|
|
fileTransferSettings.connectTimeout = 1;
|
2019-06-17 10:57:22 +03:00
|
|
|
}
|
|
|
|
|
2021-01-18 15:38:31 +02:00
|
|
|
if (args.refresh) {
|
2020-01-28 18:34:48 +02:00
|
|
|
settings.tarballTtl = 0;
|
2021-01-18 15:38:31 +02:00
|
|
|
settings.ttlNegativeNarInfoCache = 0;
|
|
|
|
settings.ttlPositiveNarInfoCache = 0;
|
|
|
|
}
|
2020-01-28 18:34:48 +02:00
|
|
|
|
2020-05-05 16:18:23 +03:00
|
|
|
args.command->second->prepare();
|
|
|
|
args.command->second->run();
|
2016-02-09 22:28:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char * * argv)
|
|
|
|
{
|
2021-02-18 20:22:37 +02:00
|
|
|
// Increase the default stack size for the evaluator and for
|
|
|
|
// libstdc++'s std::regex.
|
2021-04-07 14:40:13 +03:00
|
|
|
nix::setStackSize(64 * 1024 * 1024);
|
2021-02-18 20:22:37 +02:00
|
|
|
|
2016-02-09 22:28:29 +02:00
|
|
|
return nix::handleExceptions(argv[0], [&]() {
|
|
|
|
nix::mainWrapped(argc, argv);
|
|
|
|
});
|
|
|
|
}
|