diff --git a/doc/manual/conf-file.xml b/doc/manual/conf-file.xml
index c37a2da47..c832108fe 100644
--- a/doc/manual/conf-file.xml
+++ b/doc/manual/conf-file.xml
@@ -148,6 +148,7 @@ flag, e.g. --option gc-keep-outputs false.
+
build-timeout
@@ -168,6 +169,20 @@ flag, e.g. --option gc-keep-outputs false.
+ build-max-log-size
+
+
+
+ This option defines the maximum number of bytes that a
+ builder can write to its stdout/stderr. If the builder exceeds
+ this limit, it’s killed. A value of 0 (the
+ default) means that there is no limit.
+
+
+
+
+
+
build-users-group
This options specifies the Unix group containing
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index b5d064e8c..25bf848ca 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -813,6 +813,9 @@ private:
BZFILE * bzLogFile;
AutoCloseFD fdLogFile;
+ /* Number of bytes received from the builder's stdout/stderr. */
+ unsigned long logSize;
+
/* Pipe for the builder's standard output/error. */
Pipe builderOut;
@@ -2403,6 +2406,8 @@ string drvsLogDir = "drvs";
Path DerivationGoal::openLogFile()
{
+ logSize = 0;
+
if (!settings.keepLog) return "";
string baseName = baseNameOf(drvPath);
@@ -2478,6 +2483,14 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
if ((hook && fd == hook->builderOut.readSide) ||
(!hook && fd == builderOut.readSide))
{
+ logSize += data.size();
+ if (settings.maxLogSize && logSize > settings.maxLogSize) {
+ printMsg(lvlError,
+ format("%1% killed after writing more than %2% bytes of log output")
+ % getName() % settings.maxLogSize);
+ cancel(true); // not really a timeout, but close enough
+ return;
+ }
if (verbosity >= settings.buildVerbosity)
writeToStderr(data);
if (bzLogFile) {
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index d17bd947d..aeb52e1a8 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -46,6 +46,7 @@ Settings::Settings()
impersonateLinux26 = false;
keepLog = true;
compressLog = true;
+ maxLogSize = 0;
cacheFailure = false;
pollInterval = 5;
checkRootReachability = false;
@@ -140,6 +141,7 @@ void Settings::update()
get(impersonateLinux26, "build-impersonate-linux-26");
get(keepLog, "build-keep-log");
get(compressLog, "build-compress-log");
+ get(maxLogSize, "build-max-log-size");
get(cacheFailure, "build-cache-failure");
get(pollInterval, "build-poll-interval");
get(checkRootReachability, "gc-check-reachability");
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index f129d9a11..50b61725c 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -153,6 +153,10 @@ struct Settings {
/* Whether to compress logs. */
bool compressLog;
+ /* Maximum number of bytes a builder can write to stdout/stderr
+ before being killed (0 means no limit). */
+ unsigned long maxLogSize;
+
/* Whether to cache build failures. */
bool cacheFailure;