Merge pull request #8049 from edolstra/unexpected-eof
Fix "unexpected EOF" errors on macOS
This commit is contained in:
commit
7f46ebcf90
|
@ -27,18 +27,6 @@ run_test () {
|
||||||
|
|
||||||
run_test
|
run_test
|
||||||
|
|
||||||
# Hack: Retry the test if it fails with “unexpected EOF reading a line” as these
|
|
||||||
# appear randomly without anyone knowing why.
|
|
||||||
# See https://github.com/NixOS/nix/issues/3605 for more info
|
|
||||||
if [[ $status -ne 0 && $status -ne 99 && \
|
|
||||||
"$(uname)" == "Darwin" && \
|
|
||||||
"$log" =~ "unexpected EOF reading a line" \
|
|
||||||
]]; then
|
|
||||||
echo "$post_run_msg [${yellow}FAIL$normal] (possibly flaky, so will be retried)"
|
|
||||||
echo "$log" | sed 's/^/ /'
|
|
||||||
run_test
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $status -eq 0 ]; then
|
if [ $status -eq 0 ]; then
|
||||||
echo "$post_run_msg [${green}PASS$normal]"
|
echo "$post_run_msg [${green}PASS$normal]"
|
||||||
elif [ $status -eq 99 ]; then
|
elif [ $status -eq 99 ]; then
|
||||||
|
|
|
@ -35,7 +35,7 @@ HookInstance::HookInstance()
|
||||||
/* Fork the hook. */
|
/* Fork the hook. */
|
||||||
pid = startProcess([&]() {
|
pid = startProcess([&]() {
|
||||||
|
|
||||||
commonChildInit(fromHook);
|
commonChildInit(fromHook.writeSide.get());
|
||||||
|
|
||||||
if (chdir("/") == -1) throw SysError("changing into /");
|
if (chdir("/") == -1) throw SysError("changing into /");
|
||||||
|
|
||||||
|
|
|
@ -292,7 +292,7 @@ void LocalDerivationGoal::closeReadPipes()
|
||||||
if (hook) {
|
if (hook) {
|
||||||
DerivationGoal::closeReadPipes();
|
DerivationGoal::closeReadPipes();
|
||||||
} else
|
} else
|
||||||
builderOut.readSide = -1;
|
builderOut.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -802,15 +802,13 @@ void LocalDerivationGoal::startBuilder()
|
||||||
/* Create the log file. */
|
/* Create the log file. */
|
||||||
Path logFile = openLogFile();
|
Path logFile = openLogFile();
|
||||||
|
|
||||||
/* Create a pipe to get the output of the builder. */
|
/* Create a pseudoterminal to get the output of the builder. */
|
||||||
//builderOut.create();
|
builderOut = posix_openpt(O_RDWR | O_NOCTTY);
|
||||||
|
if (!builderOut)
|
||||||
builderOut.readSide = posix_openpt(O_RDWR | O_NOCTTY);
|
|
||||||
if (!builderOut.readSide)
|
|
||||||
throw SysError("opening pseudoterminal master");
|
throw SysError("opening pseudoterminal master");
|
||||||
|
|
||||||
// FIXME: not thread-safe, use ptsname_r
|
// FIXME: not thread-safe, use ptsname_r
|
||||||
std::string slaveName(ptsname(builderOut.readSide.get()));
|
std::string slaveName = ptsname(builderOut.get());
|
||||||
|
|
||||||
if (buildUser) {
|
if (buildUser) {
|
||||||
if (chmod(slaveName.c_str(), 0600))
|
if (chmod(slaveName.c_str(), 0600))
|
||||||
|
@ -821,35 +819,14 @@ void LocalDerivationGoal::startBuilder()
|
||||||
}
|
}
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
else {
|
else {
|
||||||
if (grantpt(builderOut.readSide.get()))
|
if (grantpt(builderOut.get()))
|
||||||
throw SysError("granting access to pseudoterminal slave");
|
throw SysError("granting access to pseudoterminal slave");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if 0
|
if (unlockpt(builderOut.get()))
|
||||||
// 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");
|
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");
|
|
||||||
|
|
||||||
buildResult.startTime = time(0);
|
buildResult.startTime = time(0);
|
||||||
|
|
||||||
/* Fork a child to build the package. */
|
/* Fork a child to build the package. */
|
||||||
|
@ -897,7 +874,11 @@ void LocalDerivationGoal::startBuilder()
|
||||||
|
|
||||||
usingUserNamespace = userNamespacesSupported();
|
usingUserNamespace = userNamespacesSupported();
|
||||||
|
|
||||||
|
Pipe sendPid;
|
||||||
|
sendPid.create();
|
||||||
|
|
||||||
Pid helper = startProcess([&]() {
|
Pid helper = startProcess([&]() {
|
||||||
|
sendPid.readSide.close();
|
||||||
|
|
||||||
/* Drop additional groups here because we can't do it
|
/* Drop additional groups here because we can't do it
|
||||||
after we've created the new user namespace. FIXME:
|
after we've created the new user namespace. FIXME:
|
||||||
|
@ -917,13 +898,14 @@ void LocalDerivationGoal::startBuilder()
|
||||||
if (usingUserNamespace)
|
if (usingUserNamespace)
|
||||||
options.cloneFlags |= CLONE_NEWUSER;
|
options.cloneFlags |= CLONE_NEWUSER;
|
||||||
|
|
||||||
pid_t child = startProcess([&]() { runChild(); }, options);
|
pid_t child = startProcess([&]() { runChild(slaveName); }, options);
|
||||||
|
|
||||||
writeFull(builderOut.writeSide.get(),
|
writeFull(sendPid.writeSide.get(), fmt("%d\n", child));
|
||||||
fmt("%d %d\n", usingUserNamespace, child));
|
|
||||||
_exit(0);
|
_exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sendPid.writeSide.close();
|
||||||
|
|
||||||
if (helper.wait() != 0)
|
if (helper.wait() != 0)
|
||||||
throw Error("unable to start build process");
|
throw Error("unable to start build process");
|
||||||
|
|
||||||
|
@ -935,10 +917,9 @@ void LocalDerivationGoal::startBuilder()
|
||||||
userNamespaceSync.writeSide = -1;
|
userNamespaceSync.writeSide = -1;
|
||||||
});
|
});
|
||||||
|
|
||||||
auto ss = tokenizeString<std::vector<std::string>>(readLine(builderOut.readSide.get()));
|
auto ss = tokenizeString<std::vector<std::string>>(readLine(sendPid.readSide.get()));
|
||||||
assert(ss.size() == 2);
|
assert(ss.size() == 1);
|
||||||
usingUserNamespace = ss[0] == "1";
|
pid = string2Int<pid_t>(ss[0]).value();
|
||||||
pid = string2Int<pid_t>(ss[1]).value();
|
|
||||||
|
|
||||||
if (usingUserNamespace) {
|
if (usingUserNamespace) {
|
||||||
/* Set the UID/GID mapping of the builder's user namespace
|
/* Set the UID/GID mapping of the builder's user namespace
|
||||||
|
@ -993,21 +974,20 @@ void LocalDerivationGoal::startBuilder()
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
pid = startProcess([&]() {
|
pid = startProcess([&]() {
|
||||||
runChild();
|
runChild(slaveName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parent */
|
/* parent */
|
||||||
pid.setSeparatePG(true);
|
pid.setSeparatePG(true);
|
||||||
builderOut.writeSide = -1;
|
worker.childStarted(shared_from_this(), {builderOut.get()}, true, true);
|
||||||
worker.childStarted(shared_from_this(), {builderOut.readSide.get()}, true, true);
|
|
||||||
|
|
||||||
/* Check if setting up the build environment failed. */
|
/* Check if setting up the build environment failed. */
|
||||||
std::vector<std::string> msgs;
|
std::vector<std::string> msgs;
|
||||||
while (true) {
|
while (true) {
|
||||||
std::string msg = [&]() {
|
std::string msg = [&]() {
|
||||||
try {
|
try {
|
||||||
return readLine(builderOut.readSide.get());
|
return readLine(builderOut.get());
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
auto status = pid.wait();
|
auto status = pid.wait();
|
||||||
e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)",
|
e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)",
|
||||||
|
@ -1019,7 +999,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
}();
|
}();
|
||||||
if (msg.substr(0, 1) == "\2") break;
|
if (msg.substr(0, 1) == "\2") break;
|
||||||
if (msg.substr(0, 1) == "\1") {
|
if (msg.substr(0, 1) == "\1") {
|
||||||
FdSource source(builderOut.readSide.get());
|
FdSource source(builderOut.get());
|
||||||
auto ex = readError(source);
|
auto ex = readError(source);
|
||||||
ex.addTrace({}, "while setting up the build environment");
|
ex.addTrace({}, "while setting up the build environment");
|
||||||
throw ex;
|
throw ex;
|
||||||
|
@ -1640,7 +1620,7 @@ void setupSeccomp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalDerivationGoal::runChild()
|
void LocalDerivationGoal::runChild(const Path & slaveName)
|
||||||
{
|
{
|
||||||
/* Warning: in the child we should absolutely not make any SQLite
|
/* Warning: in the child we should absolutely not make any SQLite
|
||||||
calls! */
|
calls! */
|
||||||
|
@ -1649,7 +1629,22 @@ void LocalDerivationGoal::runChild()
|
||||||
|
|
||||||
try { /* child */
|
try { /* child */
|
||||||
|
|
||||||
commonChildInit(builderOut);
|
/* Open the slave side of the pseudoterminal. */
|
||||||
|
AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY);
|
||||||
|
if (!builderOut)
|
||||||
|
throw SysError("opening pseudoterminal slave");
|
||||||
|
|
||||||
|
// Put the pt into raw mode to prevent \n -> \r\n translation.
|
||||||
|
struct termios term;
|
||||||
|
if (tcgetattr(builderOut.get(), &term))
|
||||||
|
throw SysError("getting pseudoterminal attributes");
|
||||||
|
|
||||||
|
cfmakeraw(&term);
|
||||||
|
|
||||||
|
if (tcsetattr(builderOut.get(), TCSANOW, &term))
|
||||||
|
throw SysError("putting pseudoterminal into raw mode");
|
||||||
|
|
||||||
|
commonChildInit(builderOut.get());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setupSeccomp();
|
setupSeccomp();
|
||||||
|
@ -2891,7 +2886,7 @@ void LocalDerivationGoal::deleteTmpDir(bool force)
|
||||||
bool LocalDerivationGoal::isReadDesc(int fd)
|
bool LocalDerivationGoal::isReadDesc(int fd)
|
||||||
{
|
{
|
||||||
return (hook && DerivationGoal::isReadDesc(fd)) ||
|
return (hook && DerivationGoal::isReadDesc(fd)) ||
|
||||||
(!hook && fd == builderOut.readSide.get());
|
(!hook && fd == builderOut.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,9 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
/* The path of the temporary directory in the sandbox. */
|
/* The path of the temporary directory in the sandbox. */
|
||||||
Path tmpDirInSandbox;
|
Path tmpDirInSandbox;
|
||||||
|
|
||||||
/* Pipe for the builder's standard output/error. */
|
/* Master side of the pseudoterminal used for the builder's
|
||||||
Pipe builderOut;
|
standard output/error. */
|
||||||
|
AutoCloseFD builderOut;
|
||||||
|
|
||||||
/* Pipe for synchronising updates to the builder namespaces. */
|
/* Pipe for synchronising updates to the builder namespaces. */
|
||||||
Pipe userNamespaceSync;
|
Pipe userNamespaceSync;
|
||||||
|
@ -168,7 +169,7 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
int getChildStatus() override;
|
int getChildStatus() override;
|
||||||
|
|
||||||
/* Run the builder's process. */
|
/* Run the builder's process. */
|
||||||
void runChild();
|
void runChild(const std::string & slaveName);
|
||||||
|
|
||||||
/* Check that the derivation outputs all exist and register them
|
/* Check that the derivation outputs all exist and register them
|
||||||
as valid. */
|
as valid. */
|
||||||
|
|
|
@ -1968,7 +1968,7 @@ std::string showBytes(uint64_t bytes)
|
||||||
|
|
||||||
|
|
||||||
// FIXME: move to libstore/build
|
// FIXME: move to libstore/build
|
||||||
void commonChildInit(Pipe & logPipe)
|
void commonChildInit(int stderrFd)
|
||||||
{
|
{
|
||||||
logger = makeSimpleLogger();
|
logger = makeSimpleLogger();
|
||||||
|
|
||||||
|
@ -1983,7 +1983,7 @@ void commonChildInit(Pipe & logPipe)
|
||||||
throw SysError("creating a new session");
|
throw SysError("creating a new session");
|
||||||
|
|
||||||
/* Dup the write side of the logger pipe into stderr. */
|
/* Dup the write side of the logger pipe into stderr. */
|
||||||
if (dup2(logPipe.writeSide.get(), STDERR_FILENO) == -1)
|
if (dup2(stderrFd, STDERR_FILENO) == -1)
|
||||||
throw SysError("cannot pipe standard error into log file");
|
throw SysError("cannot pipe standard error into log file");
|
||||||
|
|
||||||
/* Dup stderr to stdout. */
|
/* Dup stderr to stdout. */
|
||||||
|
|
|
@ -704,7 +704,7 @@ typedef std::function<bool(const Path & path)> PathFilter;
|
||||||
extern PathFilter defaultPathFilter;
|
extern PathFilter defaultPathFilter;
|
||||||
|
|
||||||
/* Common initialisation performed in child processes. */
|
/* Common initialisation performed in child processes. */
|
||||||
void commonChildInit(Pipe & logPipe);
|
void commonChildInit(int stderrFd);
|
||||||
|
|
||||||
/* Create a Unix domain socket. */
|
/* Create a Unix domain socket. */
|
||||||
AutoCloseFD createUnixDomainSocket();
|
AutoCloseFD createUnixDomainSocket();
|
||||||
|
|
Loading…
Reference in a new issue