aboutsummaryrefslogtreecommitdiff
path: root/src/libmain
diff options
context:
space:
mode:
authorJohn Ericson <git@JohnEricson.me>2022-10-28 23:22:18 +0100
committerGitHub <noreply@github.com>2022-10-28 23:22:18 +0100
commit13f2a6f38db44385ae1c7d3d01170149de328abb (patch)
tree9184949feb826f00b7a5bc4175dda4667055e44c /src/libmain
parent12461e246b02371c6b6981b4e65985e9397474e1 (diff)
parentb7e8a3bf4cbb2448db860f65ea13ef2c64b6883b (diff)
Merge branch 'master' into indexed-store-path-outputs
Diffstat (limited to 'src/libmain')
-rw-r--r--src/libmain/common-args.cc1
-rw-r--r--src/libmain/common-args.hh1
-rw-r--r--src/libmain/loggers.cc7
-rw-r--r--src/libmain/progress-bar.cc66
-rw-r--r--src/libmain/progress-bar.hh4
-rw-r--r--src/libmain/shared.cc23
-rw-r--r--src/libmain/shared.hh20
-rw-r--r--src/libmain/stack.cc12
8 files changed, 102 insertions, 32 deletions
diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc
index 12f5403ea..f92920d18 100644
--- a/src/libmain/common-args.cc
+++ b/src/libmain/common-args.cc
@@ -32,6 +32,7 @@ MixCommonArgs::MixCommonArgs(const std::string & programName)
addFlag({
.longName = "option",
.description = "Set the Nix configuration setting *name* to *value* (overriding `nix.conf`).",
+ .category = miscCategory,
.labels = {"name", "value"},
.handler = {[](std::string name, std::string value) {
try {
diff --git a/src/libmain/common-args.hh b/src/libmain/common-args.hh
index 25453b8c6..f180d83ce 100644
--- a/src/libmain/common-args.hh
+++ b/src/libmain/common-args.hh
@@ -6,6 +6,7 @@ namespace nix {
//static constexpr auto commonArgsCategory = "Miscellaneous common options";
static constexpr auto loggingCategory = "Logging-related options";
+static constexpr auto miscCategory = "Miscellaneous global options";
class MixCommonArgs : public virtual Args
{
diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc
index cdf23859b..cda5cb939 100644
--- a/src/libmain/loggers.cc
+++ b/src/libmain/loggers.cc
@@ -30,8 +30,11 @@ Logger * makeDefaultLogger() {
return makeJSONLogger(*makeSimpleLogger(true));
case LogFormat::bar:
return makeProgressBar();
- case LogFormat::barWithLogs:
- return makeProgressBar(true);
+ case LogFormat::barWithLogs: {
+ auto logger = makeProgressBar();
+ logger->setPrintBuildLogs(true);
+ return logger;
+ }
default:
abort();
}
diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc
index f4306ab91..961f4e18a 100644
--- a/src/libmain/progress-bar.cc
+++ b/src/libmain/progress-bar.cc
@@ -8,6 +8,7 @@
#include <map>
#include <thread>
#include <iostream>
+#include <chrono>
namespace nix {
@@ -48,6 +49,7 @@ private:
bool visible = true;
ActivityId parent;
std::optional<std::string> name;
+ std::chrono::time_point<std::chrono::steady_clock> startTime;
};
struct ActivitiesByType
@@ -79,22 +81,22 @@ private:
std::condition_variable quitCV, updateCV;
- bool printBuildLogs;
+ bool printBuildLogs = false;
bool isTTY;
public:
- ProgressBar(bool printBuildLogs, bool isTTY)
- : printBuildLogs(printBuildLogs)
- , isTTY(isTTY)
+ ProgressBar(bool isTTY)
+ : isTTY(isTTY)
{
state_.lock()->active = isTTY;
updateThread = std::thread([&]() {
auto state(state_.lock());
+ auto nextWakeup = std::chrono::milliseconds::max();
while (state->active) {
if (!state->haveUpdate)
- state.wait(updateCV);
- draw(*state);
+ state.wait_for(updateCV, nextWakeup);
+ nextWakeup = draw(*state);
state.wait_for(quitCV, std::chrono::milliseconds(50));
}
});
@@ -118,7 +120,8 @@ public:
updateThread.join();
}
- bool isVerbose() override {
+ bool isVerbose() override
+ {
return printBuildLogs;
}
@@ -159,11 +162,13 @@ public:
if (lvl <= verbosity && !s.empty() && type != actBuildWaiting)
log(*state, lvl, s + "...");
- state->activities.emplace_back(ActInfo());
+ state->activities.emplace_back(ActInfo {
+ .s = s,
+ .type = type,
+ .parent = parent,
+ .startTime = std::chrono::steady_clock::now()
+ });
auto i = std::prev(state->activities.end());
- i->s = s;
- i->type = type;
- i->parent = parent;
state->its.emplace(act, i);
state->activitiesByType[type].its.emplace(act, i);
@@ -327,10 +332,12 @@ public:
updateCV.notify_one();
}
- void draw(State & state)
+ std::chrono::milliseconds draw(State & state)
{
+ auto nextWakeup = std::chrono::milliseconds::max();
+
state.haveUpdate = false;
- if (!state.active) return;
+ if (!state.active) return nextWakeup;
std::string line;
@@ -341,12 +348,25 @@ public:
line += "]";
}
+ auto now = std::chrono::steady_clock::now();
+
if (!state.activities.empty()) {
if (!status.empty()) line += " ";
auto i = state.activities.rbegin();
- while (i != state.activities.rend() && (!i->visible || (i->s.empty() && i->lastLine.empty())))
+ while (i != state.activities.rend()) {
+ if (i->visible && (!i->s.empty() || !i->lastLine.empty())) {
+ /* Don't show activities until some time has
+ passed, to avoid displaying very short
+ activities. */
+ auto delay = std::chrono::milliseconds(10);
+ if (i->startTime + delay < now)
+ break;
+ else
+ nextWakeup = std::min(nextWakeup, std::chrono::duration_cast<std::chrono::milliseconds>(delay - (now - i->startTime)));
+ }
++i;
+ }
if (i != state.activities.rend()) {
line += i->s;
@@ -366,6 +386,8 @@ public:
if (width <= 0) width = std::numeric_limits<decltype(width)>::max();
writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K");
+
+ return nextWakeup;
}
std::string getStatus(State & state)
@@ -480,19 +502,21 @@ public:
draw(*state);
return s[0];
}
+
+ void setPrintBuildLogs(bool printBuildLogs) override
+ {
+ this->printBuildLogs = printBuildLogs;
+ }
};
-Logger * makeProgressBar(bool printBuildLogs)
+Logger * makeProgressBar()
{
- return new ProgressBar(
- printBuildLogs,
- shouldANSI()
- );
+ return new ProgressBar(shouldANSI());
}
-void startProgressBar(bool printBuildLogs)
+void startProgressBar()
{
- logger = makeProgressBar(printBuildLogs);
+ logger = makeProgressBar();
}
void stopProgressBar()
diff --git a/src/libmain/progress-bar.hh b/src/libmain/progress-bar.hh
index 7f0dafecf..3a76f8448 100644
--- a/src/libmain/progress-bar.hh
+++ b/src/libmain/progress-bar.hh
@@ -4,9 +4,9 @@
namespace nix {
-Logger * makeProgressBar(bool printBuildLogs = false);
+Logger * makeProgressBar();
-void startProgressBar(bool printBuildLogs = false);
+void startProgressBar();
void stopProgressBar();
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index 31454e49d..c1cf38565 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -4,6 +4,7 @@
#include "gc-store.hh"
#include "util.hh"
#include "loggers.hh"
+#include "progress-bar.hh"
#include <algorithm>
#include <cctype>
@@ -181,8 +182,9 @@ void initNix()
/* Reset SIGCHLD to its default. */
struct sigaction act;
sigemptyset(&act.sa_mask);
- act.sa_handler = SIG_DFL;
act.sa_flags = 0;
+
+ act.sa_handler = SIG_DFL;
if (sigaction(SIGCHLD, &act, 0))
throw SysError("resetting SIGCHLD");
@@ -194,9 +196,20 @@ void initNix()
/* HACK: on darwin, we need can’t use sigprocmask with SIGWINCH.
* Instead, add a dummy sigaction handler, and signalHandlerThread
* can handle the rest. */
- struct sigaction sa;
- sa.sa_handler = sigHandler;
- if (sigaction(SIGWINCH, &sa, 0)) throw SysError("handling SIGWINCH");
+ act.sa_handler = sigHandler;
+ if (sigaction(SIGWINCH, &act, 0)) throw SysError("handling SIGWINCH");
+
+ /* Disable SA_RESTART for interrupts, so that system calls on this thread
+ * error with EINTR like they do on Linux.
+ * Most signals on BSD systems default to SA_RESTART on, but Nix
+ * expects EINTR from syscalls to properly exit. */
+ act.sa_handler = SIG_DFL;
+ if (sigaction(SIGINT, &act, 0)) throw SysError("handling SIGINT");
+ if (sigaction(SIGTERM, &act, 0)) throw SysError("handling SIGTERM");
+ if (sigaction(SIGHUP, &act, 0)) throw SysError("handling SIGHUP");
+ if (sigaction(SIGPIPE, &act, 0)) throw SysError("handling SIGPIPE");
+ if (sigaction(SIGQUIT, &act, 0)) throw SysError("handling SIGQUIT");
+ if (sigaction(SIGTRAP, &act, 0)) throw SysError("handling SIGTRAP");
#endif
/* Register a SIGSEGV handler to detect stack overflows. */
@@ -410,6 +423,8 @@ RunPager::RunPager()
if (!pager) pager = getenv("PAGER");
if (pager && ((std::string) pager == "" || (std::string) pager == "cat")) return;
+ stopProgressBar();
+
Pipe toPager;
toPager.create();
diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh
index 0cc56d47d..3c37fd627 100644
--- a/src/libmain/shared.hh
+++ b/src/libmain/shared.hh
@@ -113,5 +113,25 @@ struct PrintFreed
/* Install a SIGSEGV handler to detect stack overflows. */
void detectStackOverflow();
+/* Pluggable behavior to run in case of a stack overflow.
+
+ Default value: defaultStackOverflowHandler.
+
+ This is called by the handler installed by detectStackOverflow().
+
+ This gives Nix library consumers a limit opportunity to report the error
+ condition. The handler should exit the process.
+ See defaultStackOverflowHandler() for a reference implementation.
+
+ NOTE: Use with diligence, because this runs in the signal handler, with very
+ limited stack space and a potentially a corrupted heap, all while the failed
+ thread is blocked indefinitely. All functions called must be reentrant. */
+extern std::function<void(siginfo_t * info, void * ctx)> stackOverflowHandler;
+
+/* The default, robust implementation of stackOverflowHandler.
+
+ Prints an error message directly to stderr using a syscall instead of the
+ logger. Exits the process immediately after. */
+void defaultStackOverflowHandler(siginfo_t * info, void * ctx);
}
diff --git a/src/libmain/stack.cc b/src/libmain/stack.cc
index b0a4a4c5d..10f71c1dc 100644
--- a/src/libmain/stack.cc
+++ b/src/libmain/stack.cc
@@ -1,4 +1,5 @@
#include "error.hh"
+#include "shared.hh"
#include <cstring>
#include <cstddef>
@@ -29,9 +30,7 @@ static void sigsegvHandler(int signo, siginfo_t * info, void * ctx)
ptrdiff_t diff = (char *) info->si_addr - sp;
if (diff < 0) diff = -diff;
if (diff < 4096) {
- char msg[] = "error: stack overflow (possible infinite recursion)\n";
- [[gnu::unused]] auto res = write(2, msg, strlen(msg));
- _exit(1); // maybe abort instead?
+ nix::stackOverflowHandler(info, ctx);
}
}
@@ -67,5 +66,12 @@ void detectStackOverflow()
#endif
}
+std::function<void(siginfo_t * info, void * ctx)> stackOverflowHandler(defaultStackOverflowHandler);
+
+void defaultStackOverflowHandler(siginfo_t * info, void * ctx) {
+ char msg[] = "error: stack overflow (possible infinite recursion)\n";
+ [[gnu::unused]] auto res = write(2, msg, strlen(msg));
+ _exit(1); // maybe abort instead?
+}
}