It is considered bad form to use things from includes in headers, as these directives propagate to everywhere else and can make it confusing. types.hh (which is includes almost literally everywhere) had some of these directives, which this commit removes.
387 lines
10 KiB
C++
387 lines
10 KiB
C++
#include "shared.hh"
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <csignal>
|
|
#include <cstdlib>
|
|
#include <exception>
|
|
#include <iostream>
|
|
#include <mutex>
|
|
#include <utility>
|
|
|
|
#include <glog/logging.h>
|
|
#include <openssl/crypto.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "globals.hh"
|
|
#include "store-api.hh"
|
|
#include "util.hh"
|
|
|
|
namespace nix {
|
|
|
|
static bool gcWarning = true;
|
|
|
|
void printGCWarning() {
|
|
if (!gcWarning) {
|
|
return;
|
|
}
|
|
|
|
static bool haveWarned = false;
|
|
if (!haveWarned) {
|
|
haveWarned = true;
|
|
LOG(WARNING) << "you did not specify '--add-root'; "
|
|
<< "the result might be removed by the garbage collector";
|
|
}
|
|
}
|
|
|
|
void printMissing(const ref<Store>& store, const PathSet& paths) {
|
|
unsigned long long downloadSize;
|
|
unsigned long long narSize;
|
|
PathSet willBuild;
|
|
PathSet willSubstitute;
|
|
PathSet unknown;
|
|
store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize,
|
|
narSize);
|
|
printMissing(store, willBuild, willSubstitute, unknown, downloadSize,
|
|
narSize);
|
|
}
|
|
|
|
void printMissing(const ref<Store>& store, const PathSet& willBuild,
|
|
const PathSet& willSubstitute, const PathSet& unknown,
|
|
unsigned long long downloadSize, unsigned long long narSize) {
|
|
if (!willBuild.empty()) {
|
|
LOG(INFO) << "these derivations will be built:";
|
|
Paths sorted = store->topoSortPaths(willBuild);
|
|
reverse(sorted.begin(), sorted.end());
|
|
for (auto& i : sorted) {
|
|
LOG(INFO) << " " << i;
|
|
}
|
|
}
|
|
|
|
if (!willSubstitute.empty()) {
|
|
LOG(INFO) << "these paths will be fetched ("
|
|
<< (downloadSize / (1024.0 * 1024.0)) << " MiB download, "
|
|
<< (narSize / (1024.0 * 1024.0)) << "MiB unpacked):";
|
|
|
|
for (auto& i : willSubstitute) {
|
|
LOG(INFO) << i;
|
|
}
|
|
}
|
|
|
|
if (!unknown.empty()) {
|
|
LOG(INFO) << "don't know how to build these paths"
|
|
<< (settings.readOnlyMode
|
|
? " (may be caused by read-only store access)"
|
|
: "")
|
|
<< ":";
|
|
|
|
for (auto& i : unknown) {
|
|
LOG(INFO) << i;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string getArg(const std::string& opt, Strings::iterator& i,
|
|
const Strings::iterator& end) {
|
|
++i;
|
|
if (i == end) {
|
|
throw UsageError(format("'%1%' requires an argument") % opt);
|
|
}
|
|
|
|
return *i;
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10101000L
|
|
/* OpenSSL is not thread-safe by default - it will randomly crash
|
|
unless the user supplies a mutex locking function. So let's do
|
|
that. */
|
|
static std::vector<std::mutex> opensslLocks;
|
|
|
|
static void opensslLockCallback(int mode, int type, const char* file,
|
|
int line) {
|
|
if (mode & CRYPTO_LOCK)
|
|
opensslLocks[type].lock();
|
|
else
|
|
opensslLocks[type].unlock();
|
|
}
|
|
#endif
|
|
|
|
static void sigHandler(int signo) {}
|
|
|
|
void initNix() {
|
|
/* Turn on buffering for cerr. */
|
|
#if HAVE_PUBSETBUF
|
|
static char buf[1024];
|
|
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
|
|
#endif
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10101000L
|
|
/* Initialise OpenSSL locking. */
|
|
opensslLocks = std::vector<std::mutex>(CRYPTO_num_locks());
|
|
CRYPTO_set_locking_callback(opensslLockCallback);
|
|
#endif
|
|
|
|
loadConfFile();
|
|
|
|
startSignalHandlerThread();
|
|
|
|
/* Reset SIGCHLD to its default. */
|
|
struct sigaction act;
|
|
sigemptyset(&act.sa_mask);
|
|
act.sa_handler = SIG_DFL;
|
|
act.sa_flags = 0;
|
|
if (sigaction(SIGCHLD, &act, nullptr) != 0) {
|
|
throw SysError("resetting SIGCHLD");
|
|
}
|
|
|
|
/* Install a dummy SIGUSR1 handler for use with pthread_kill(). */
|
|
act.sa_handler = sigHandler;
|
|
if (sigaction(SIGUSR1, &act, nullptr) != 0) {
|
|
throw SysError("handling SIGUSR1");
|
|
}
|
|
|
|
/* Register a SIGSEGV handler to detect stack overflows. */
|
|
detectStackOverflow();
|
|
|
|
/* There is no privacy in the Nix system ;-) At least not for
|
|
now. In particular, store objects should be readable by
|
|
everybody. */
|
|
umask(0022);
|
|
|
|
/* Initialise the PRNG. */
|
|
struct timeval tv;
|
|
gettimeofday(&tv, nullptr);
|
|
srandom(tv.tv_usec);
|
|
}
|
|
|
|
LegacyArgs::LegacyArgs(
|
|
const std::string& programName,
|
|
std::function<bool(Strings::iterator& arg, const Strings::iterator& end)>
|
|
parseArg)
|
|
: MixCommonArgs(programName), parseArg(std::move(parseArg)) {
|
|
mkFlag()
|
|
.longName("no-build-output")
|
|
.shortName('Q')
|
|
.description("do not show build output")
|
|
.set(&settings.verboseBuild, false);
|
|
|
|
mkFlag()
|
|
.longName("keep-failed")
|
|
.shortName('K')
|
|
.description("keep temporary directories of failed builds")
|
|
.set(&(bool&)settings.keepFailed, true);
|
|
|
|
mkFlag()
|
|
.longName("keep-going")
|
|
.shortName('k')
|
|
.description("keep going after a build fails")
|
|
.set(&(bool&)settings.keepGoing, true);
|
|
|
|
mkFlag()
|
|
.longName("fallback")
|
|
.description("build from source if substitution fails")
|
|
.set(&(bool&)settings.tryFallback, true);
|
|
|
|
auto intSettingAlias = [&](char shortName, const std::string& longName,
|
|
const std::string& description,
|
|
const std::string& dest) {
|
|
mkFlag<unsigned int>(shortName, longName, description, [=](unsigned int n) {
|
|
settings.set(dest, std::to_string(n));
|
|
});
|
|
};
|
|
|
|
intSettingAlias(0, "cores",
|
|
"maximum number of CPU cores to use inside a build", "cores");
|
|
intSettingAlias(0, "max-silent-time",
|
|
"number of seconds of silence before a build is killed",
|
|
"max-silent-time");
|
|
intSettingAlias(0, "timeout", "number of seconds before a build is killed",
|
|
"timeout");
|
|
|
|
mkFlag(0, "readonly-mode", "do not write to the Nix store",
|
|
&settings.readOnlyMode);
|
|
|
|
mkFlag(0, "no-gc-warning", "disable warning about not using '--add-root'",
|
|
&gcWarning, false);
|
|
|
|
mkFlag()
|
|
.longName("store")
|
|
.label("store-uri")
|
|
.description("URI of the Nix store to use")
|
|
.dest(&(std::string&)settings.storeUri);
|
|
}
|
|
|
|
bool LegacyArgs::processFlag(Strings::iterator& pos, Strings::iterator end) {
|
|
if (MixCommonArgs::processFlag(pos, end)) {
|
|
return true;
|
|
}
|
|
bool res = parseArg(pos, end);
|
|
if (res) {
|
|
++pos;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool LegacyArgs::processArgs(const Strings& args, bool finish) {
|
|
if (args.empty()) {
|
|
return true;
|
|
}
|
|
assert(args.size() == 1);
|
|
Strings ss(args);
|
|
auto pos = ss.begin();
|
|
if (!parseArg(pos, ss.end())) {
|
|
throw UsageError(format("unexpected argument '%1%'") % args.front());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void parseCmdLine(
|
|
int argc, char** argv,
|
|
std::function<bool(Strings::iterator& arg, const Strings::iterator& end)>
|
|
parseArg) {
|
|
parseCmdLine(baseNameOf(argv[0]), argvToStrings(argc, argv),
|
|
std::move(parseArg));
|
|
}
|
|
|
|
void parseCmdLine(
|
|
const std::string& programName, const Strings& args,
|
|
std::function<bool(Strings::iterator& arg, const Strings::iterator& end)>
|
|
parseArg) {
|
|
LegacyArgs(programName, std::move(parseArg)).parseCmdline(args);
|
|
}
|
|
|
|
void printVersion(const std::string& programName) {
|
|
std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
|
|
|
|
// TODO(tazjin): figure out what the fuck this is
|
|
/*if (verbosity > lvlInfo) {
|
|
Strings cfg;
|
|
#if HAVE_BOEHMGC
|
|
cfg.push_back("gc");
|
|
#endif
|
|
#if HAVE_SODIUM
|
|
cfg.push_back("signed-caches");
|
|
#endif
|
|
std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n";
|
|
std::cout << "Configuration file: " << settings.nixConfDir + "/nix.conf"
|
|
<< "\n";
|
|
std::cout << "Store directory: " << settings.nixStore << "\n";
|
|
std::cout << "State directory: " << settings.nixStateDir << "\n";
|
|
} */
|
|
throw Exit();
|
|
}
|
|
|
|
void showManPage(const std::string& name) {
|
|
restoreSignals();
|
|
setenv("MANPATH", settings.nixManDir.c_str(), 1);
|
|
execlp("man", "man", name.c_str(), nullptr);
|
|
throw SysError(format("command 'man %1%' failed") % name.c_str());
|
|
}
|
|
|
|
int handleExceptions(const std::string& programName,
|
|
const std::function<void()>& fun) {
|
|
ReceiveInterrupts receiveInterrupts; // FIXME: need better place for this
|
|
|
|
std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
|
|
try {
|
|
try {
|
|
fun();
|
|
} catch (...) {
|
|
/* Subtle: we have to make sure that any `interrupted'
|
|
condition is discharged before we reach printMsg()
|
|
below, since otherwise it will throw an (uncaught)
|
|
exception. */
|
|
setInterruptThrown();
|
|
throw;
|
|
}
|
|
} catch (Exit& e) {
|
|
return e.status;
|
|
} catch (UsageError& e) {
|
|
LOG(INFO) << e.what();
|
|
LOG(INFO) << "Try '" << programName << " "
|
|
<< " --help' for more information.";
|
|
return 1;
|
|
} catch (BaseError& e) {
|
|
LOG(ERROR) << error << (settings.showTrace ? e.prefix() : "") << e.msg();
|
|
if (!e.prefix().empty() && !settings.showTrace) {
|
|
LOG(INFO) << "(use '--show-trace' to show detailed location information)";
|
|
}
|
|
return e.status;
|
|
} catch (std::bad_alloc& e) {
|
|
LOG(ERROR) << error << "failed to allocate: " << e.what();
|
|
return 1;
|
|
} catch (std::exception& e) {
|
|
LOG(ERROR) << error << e.what();
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
RunPager::RunPager() {
|
|
if (isatty(STDOUT_FILENO) == 0) {
|
|
return;
|
|
}
|
|
char* pager = getenv("NIX_PAGER");
|
|
if (pager == nullptr) {
|
|
pager = getenv("PAGER");
|
|
}
|
|
if (pager && ((std::string)pager == "" || (std::string)pager == "cat")) {
|
|
return;
|
|
}
|
|
|
|
Pipe toPager;
|
|
toPager.create();
|
|
|
|
pid = startProcess([&]() {
|
|
if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1) {
|
|
throw SysError("dupping stdin");
|
|
}
|
|
if (getenv("LESS") == nullptr) {
|
|
setenv("LESS", "FRSXMK", 1);
|
|
}
|
|
restoreSignals();
|
|
if (pager != nullptr) {
|
|
execl("/bin/sh", "sh", "-c", pager, nullptr);
|
|
}
|
|
execlp("pager", "pager", nullptr);
|
|
execlp("less", "less", nullptr);
|
|
execlp("more", "more", nullptr);
|
|
throw SysError(format("executing '%1%'") % pager);
|
|
});
|
|
|
|
pid.setKillSignal(SIGINT);
|
|
|
|
if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1) {
|
|
throw SysError("dupping stdout");
|
|
}
|
|
}
|
|
|
|
RunPager::~RunPager() {
|
|
try {
|
|
if (pid != -1) {
|
|
std::cout.flush();
|
|
close(STDOUT_FILENO);
|
|
pid.wait();
|
|
}
|
|
} catch (...) {
|
|
ignoreException();
|
|
}
|
|
}
|
|
|
|
std::string showBytes(unsigned long long bytes) {
|
|
return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str();
|
|
}
|
|
|
|
PrintFreed::~PrintFreed() {
|
|
if (show) {
|
|
std::cout << format("%1% store paths deleted, %2% freed\n") %
|
|
results.paths.size() % showBytes(results.bytesFreed);
|
|
}
|
|
}
|
|
|
|
Exit::~Exit() = default;
|
|
|
|
} // namespace nix
|