diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2017-01-19T15·58+0100 |
---|---|---|
committer | Eelco Dolstra <edolstra@gmail.com> | 2017-01-19T16·16+0100 |
commit | 21948deed99a3295e4d5666e027a6ca42dc00b40 (patch) | |
tree | 62579dc51fdee152a67486d428f39ecceb84f08e /src/libutil | |
parent | 63e10b4d28e64107e51207f292ab0093a95c1bc6 (diff) |
Kill builds when we get EOF on the log FD
This closes a long-time bug that allowed builds to hang Nix indefinitely (regardless of timeouts) simply by doing exec > /dev/null 2>&1; while true; do true; done Now, on EOF, we just send SIGKILL to the child to make sure it's really gone.
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/util.cc | 33 | ||||
-rw-r--r-- | src/libutil/util.hh | 11 |
2 files changed, 16 insertions, 28 deletions
diff --git a/src/libutil/util.cc b/src/libutil/util.cc index d79cb5c1333e..e9457582810a 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -648,26 +648,25 @@ void Pipe::create() Pid::Pid() - : pid(-1), separatePG(false), killSignal(SIGKILL) { } Pid::Pid(pid_t pid) - : pid(pid), separatePG(false), killSignal(SIGKILL) + : pid(pid) { } Pid::~Pid() { - kill(); + if (pid != -1) kill(); } void Pid::operator =(pid_t pid) { - if (this->pid != pid) kill(); + if (this->pid != -1 && this->pid != pid) kill(); this->pid = pid; killSignal = SIGKILL; // reset signal to default } @@ -679,9 +678,9 @@ Pid::operator pid_t() } -void Pid::kill(bool quiet) +int Pid::kill(bool quiet) { - if (pid == -1 || pid == 0) return; + assert(pid != -1); if (!quiet) printError(format("killing process %1%") % pid); @@ -692,32 +691,20 @@ void Pid::kill(bool quiet) if (::kill(separatePG ? -pid : pid, killSignal) != 0) printError((SysError(format("killing process %1%") % pid).msg())); - /* Wait until the child dies, disregarding the exit status. */ - int status; - while (waitpid(pid, &status, 0) == -1) { - checkInterrupt(); - if (errno != EINTR) { - printError( - (SysError(format("waiting for process %1%") % pid).msg())); - break; - } - } - - pid = -1; + return wait(); } -int Pid::wait(bool block) +int Pid::wait() { assert(pid != -1); while (1) { int status; - int res = waitpid(pid, &status, block ? 0 : WNOHANG); + int res = waitpid(pid, &status, 0); if (res == pid) { pid = -1; return status; } - if (res == 0 && !block) return -1; if (errno != EINTR) throw SysError("cannot get child exit status"); checkInterrupt(); @@ -782,7 +769,7 @@ void killUser(uid_t uid) _exit(0); }, options); - int status = pid.wait(true); + int status = pid.wait(); if (status != 0) throw Error(format("cannot kill processes for uid ‘%1%’: %2%") % uid % statusToString(status)); @@ -893,7 +880,7 @@ string runProgram(Path program, bool searchPath, const Strings & args, string result = drainFD(out.readSide.get()); /* Wait for the child to finish. */ - int status = pid.wait(true); + int status = pid.wait(); if (!statusOk(status)) throw ExecError(status, format("program ‘%1%’ %2%") % program % statusToString(status)); diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 67d2891688a2..d00f9c645712 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -192,17 +192,18 @@ typedef std::unique_ptr<DIR, DIRDeleter> AutoCloseDir; class Pid { - pid_t pid; - bool separatePG; - int killSignal; + pid_t pid = -1; + bool separatePG = false; + int killSignal = SIGKILL; public: Pid(); Pid(pid_t pid); ~Pid(); void operator =(pid_t pid); operator pid_t(); - void kill(bool quiet = false); - int wait(bool block); + int kill(bool quiet = false); + int wait(); + void setSeparatePG(bool separatePG); void setKillSignal(int signal); pid_t release(); |