aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2017-01-19 16:58:39 +0100
committerEelco Dolstra <edolstra@gmail.com>2017-01-19 17:16:14 +0100
commit21948deed99a3295e4d5666e027a6ca42dc00b40 (patch)
tree62579dc51fdee152a67486d428f39ecceb84f08e /src/libutil
parent63e10b4d28e64107e51207f292ab0093a95c1bc6 (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.cc33
-rw-r--r--src/libutil/util.hh11
2 files changed, 16 insertions, 28 deletions
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index d79cb5c13..e94575828 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 67d289168..d00f9c645 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();