Merge pull request #2878 from NixOS/run-in-pts

Run builds in a pseudo-terminal
This commit is contained in:
Eelco Dolstra 2019-06-17 14:44:40 +02:00 committed by GitHub
commit 4b214e6e45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -38,6 +38,7 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <cstring> #include <cstring>
#include <termios.h>
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
@ -1558,8 +1559,8 @@ void DerivationGoal::buildDone()
if (hook) { if (hook) {
hook->builderOut.readSide = -1; hook->builderOut.readSide = -1;
hook->fromHook.readSide = -1; hook->fromHook.readSide = -1;
} } else
else builderOut.readSide = -1; builderOut.readSide = -1;
/* Close the log file. */ /* Close the log file. */
closeLogFile(); closeLogFile();
@ -2181,7 +2182,48 @@ void DerivationGoal::startBuilder()
Path logFile = openLogFile(); Path logFile = openLogFile();
/* Create a pipe to get the output of the builder. */ /* Create a pipe to get the output of the builder. */
builderOut.create(); //builderOut.create();
builderOut.readSide = posix_openpt(O_RDWR | O_NOCTTY);
if (!builderOut.readSide)
throw SysError("opening pseudoterminal master");
std::string slaveName(ptsname(builderOut.readSide.get()));
if (buildUser) {
if (chmod(slaveName.c_str(), 0600))
throw SysError("changing mode of pseudoterminal slave");
if (chown(slaveName.c_str(), buildUser->getUID(), 0))
throw SysError("changing owner of pseudoterminal slave");
} else {
if (grantpt(builderOut.readSide.get()))
throw SysError("granting access to pseudoterminal slave");
}
#if 0
// Mount the pt in the sandbox so that the "tty" command works.
// FIXME: this doesn't work with the new devpts in the sandbox.
if (useChroot)
dirsInChroot[slaveName] = {slaveName, false};
#endif
if (unlockpt(builderOut.readSide.get()))
throw SysError("unlocking pseudoterminal");
builderOut.writeSide = open(slaveName.c_str(), O_RDWR | O_NOCTTY);
if (!builderOut.writeSide)
throw SysError("opening pseudoterminal slave");
// Put the pt into raw mode to prevent \n -> \r\n translation.
struct termios term;
if (tcgetattr(builderOut.writeSide.get(), &term))
throw SysError("getting pseudoterminal attributes");
cfmakeraw(&term);
if (tcsetattr(builderOut.writeSide.get(), TCSANOW, &term))
throw SysError("putting pseudoterminal into raw mode");
result.startTime = time(0); result.startTime = time(0);
@ -2406,6 +2448,9 @@ void DerivationGoal::initEnv()
may change that in the future. So tell the builder which file may change that in the future. So tell the builder which file
descriptor to use for that. */ descriptor to use for that. */
env["NIX_LOG_FD"] = "2"; env["NIX_LOG_FD"] = "2";
/* Trigger colored output in various tools. */
env["TERM"] = "xterm-256color";
} }
@ -4361,14 +4406,15 @@ void Worker::waitForInput()
for (auto & k : fds2) { for (auto & k : fds2) {
if (FD_ISSET(k, &fds)) { if (FD_ISSET(k, &fds)) {
ssize_t rd = read(k, buffer.data(), buffer.size()); ssize_t rd = read(k, buffer.data(), buffer.size());
if (rd == -1) { // FIXME: is there a cleaner way to handle pt close
if (errno != EINTR) // than EIO? Is this even standard?
throw SysError(format("reading from %1%") if (rd == 0 || (rd == -1 && errno == EIO)) {
% goal->getName());
} else if (rd == 0) {
debug(format("%1%: got EOF") % goal->getName()); debug(format("%1%: got EOF") % goal->getName());
goal->handleEOF(k); goal->handleEOF(k);
j->fds.erase(k); j->fds.erase(k);
} else if (rd == -1) {
if (errno != EINTR)
throw SysError("%s: read failed", goal->getName());
} else { } else {
printMsg(lvlVomit, format("%1%: read %2% bytes") printMsg(lvlVomit, format("%1%: read %2% bytes")
% goal->getName() % rd); % goal->getName() % rd);