Restore default signal handling in child processes

In particular, this fixes Ctrl-C in nix-shell sessions.
This commit is contained in:
Eelco Dolstra 2017-02-01 13:00:21 +01:00
parent 583ff4ec46
commit 7a65b2470e
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
7 changed files with 33 additions and 35 deletions

View file

@ -30,6 +30,7 @@ static std::pair<FdSink, FdSource> connect(const string & conn)
throw SysError("dupping stdin"); throw SysError("dupping stdin");
if (dup2(from.writeSide, STDOUT_FILENO) == -1) if (dup2(from.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout"); throw SysError("dupping stdout");
restoreSignals();
execlp("ssh", "ssh", "-x", "-T", conn.c_str(), "nix-store --serve", NULL); execlp("ssh", "ssh", "-x", "-T", conn.c_str(), "nix-store --serve", NULL);
throw SysError("executing ssh"); throw SysError("executing ssh");
}); });

View file

@ -119,15 +119,9 @@ void initNix()
startSignalHandlerThread(); startSignalHandlerThread();
/* Ignore SIGPIPE. */ /* Reset SIGCHLD to its default. */
struct sigaction act; struct sigaction act;
sigemptyset(&act.sa_mask); sigemptyset(&act.sa_mask);
act.sa_handler = SIG_IGN;
act.sa_flags = 0;
if (sigaction(SIGPIPE, &act, 0))
throw SysError("ignoring SIGPIPE");
/* Reset SIGCHLD to its default. */
act.sa_handler = SIG_DFL; act.sa_handler = SIG_DFL;
act.sa_flags = 0; act.sa_flags = 0;
if (sigaction(SIGCHLD, &act, 0)) if (sigaction(SIGCHLD, &act, 0))
@ -252,7 +246,7 @@ void printVersion(const string & programName)
void showManPage(const string & name) void showManPage(const string & name)
{ {
restoreSIGPIPE(); restoreSignals();
execlp("man", "man", name.c_str(), NULL); execlp("man", "man", name.c_str(), NULL);
throw SysError(format("command man %1% failed") % name.c_str()); throw SysError(format("command man %1% failed") % name.c_str());
} }
@ -305,16 +299,6 @@ RunPager::RunPager()
if (!pager) pager = getenv("PAGER"); if (!pager) pager = getenv("PAGER");
if (pager && ((string) pager == "" || (string) pager == "cat")) return; if (pager && ((string) pager == "" || (string) pager == "cat")) return;
/* Ignore SIGINT. The pager will handle it (and we'll get
SIGPIPE). */
struct sigaction act;
act.sa_handler = SIG_IGN;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
if (sigaction(SIGINT, &act, 0)) throw SysError("ignoring SIGINT");
restoreSIGPIPE();
Pipe toPager; Pipe toPager;
toPager.create(); toPager.create();
@ -323,6 +307,7 @@ RunPager::RunPager()
throw SysError("dupping stdin"); throw SysError("dupping stdin");
if (!getenv("LESS")) if (!getenv("LESS"))
setenv("LESS", "FRSXMK", 1); setenv("LESS", "FRSXMK", 1);
restoreSignals();
if (pager) if (pager)
execl("/bin/sh", "sh", "-c", pager, NULL); execl("/bin/sh", "sh", "-c", pager, NULL);
execlp("pager", "pager", NULL); execlp("pager", "pager", NULL);
@ -331,6 +316,8 @@ RunPager::RunPager()
throw SysError(format("executing %1%") % pager); throw SysError(format("executing %1%") % pager);
}); });
pid.setKillSignal(SIGINT);
if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1) if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
throw SysError("dupping stdout"); throw SysError("dupping stdout");
} }
@ -344,10 +331,14 @@ RunPager::~RunPager()
close(STDOUT_FILENO); close(STDOUT_FILENO);
pid.wait(); pid.wait();
} }
} catch (...) {
try {
pid.kill(true);
} catch (...) { } catch (...) {
ignoreException(); ignoreException();
} }
} }
}
string showBytes(unsigned long long bytes) string showBytes(unsigned long long bytes)

View file

@ -400,6 +400,8 @@ void Goal::trace(const format & f)
/* Common initialisation performed in child processes. */ /* Common initialisation performed in child processes. */
static void commonChildInit(Pipe & logPipe) static void commonChildInit(Pipe & logPipe)
{ {
restoreSignals();
/* Put the child in a separate session (and thus a separate /* Put the child in a separate session (and thus a separate
process group) so that it has no controlling terminal (meaning process group) so that it has no controlling terminal (meaning
that e.g. ssh cannot open /dev/tty) and it doesn't receive that e.g. ssh cannot open /dev/tty) and it doesn't receive
@ -2662,8 +2664,6 @@ void DerivationGoal::runChild()
for (auto & i : drv->args) for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites)); args.push_back(rewriteStrings(i, inputRewrites));
restoreSIGPIPE();
/* Indicate that we managed to set up the build environment. */ /* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, string("\1\n")); writeFull(STDERR_FILENO, string("\1\n"));

View file

@ -91,6 +91,7 @@ ref<RemoteStore::Connection> SSHStore::openConnection()
{ {
if ((pid_t) sshMaster == -1) { if ((pid_t) sshMaster == -1) {
sshMaster = startProcess([&]() { sshMaster = startProcess([&]() {
restoreSignals();
if (key.empty()) if (key.empty())
execlp("ssh", "ssh", "-N", "-M", "-S", socketPath.c_str(), uri.c_str(), NULL); execlp("ssh", "ssh", "-N", "-M", "-S", socketPath.c_str(), uri.c_str(), NULL);
else else

View file

@ -860,6 +860,8 @@ string runProgram(Path program, bool searchPath, const Strings & args,
Strings args_(args); Strings args_(args);
args_.push_front(program); args_.push_front(program);
restoreSignals();
if (searchPath) if (searchPath)
execvp(program.c_str(), stringsToCharPtrs(args_).data()); execvp(program.c_str(), stringsToCharPtrs(args_).data());
else else
@ -909,16 +911,6 @@ void closeOnExec(int fd)
} }
void restoreSIGPIPE()
{
struct sigaction act;
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
if (sigaction(SIGPIPE, &act, 0)) throw SysError("resetting SIGPIPE");
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -1218,19 +1210,31 @@ void triggerInterrupt()
} }
} }
static sigset_t savedSignalMask;
void startSignalHandlerThread() void startSignalHandlerThread()
{ {
if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask))
throw SysError("quering signal mask");
sigset_t set; sigset_t set;
sigemptyset(&set); sigemptyset(&set);
sigaddset(&set, SIGINT); sigaddset(&set, SIGINT);
sigaddset(&set, SIGTERM); sigaddset(&set, SIGTERM);
sigaddset(&set, SIGHUP); sigaddset(&set, SIGHUP);
sigaddset(&set, SIGPIPE);
if (pthread_sigmask(SIG_BLOCK, &set, nullptr)) if (pthread_sigmask(SIG_BLOCK, &set, nullptr))
throw SysError("blocking signals"); throw SysError("blocking signals");
std::thread(signalHandlerThread, set).detach(); std::thread(signalHandlerThread, set).detach();
} }
void restoreSignals()
{
if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr))
throw SysError("restoring signals");
}
/* RAII helper to automatically deregister a callback. */ /* RAII helper to automatically deregister a callback. */
struct InterruptCallbackImpl : InterruptCallback struct InterruptCallbackImpl : InterruptCallback
{ {

View file

@ -256,10 +256,6 @@ void closeMostFDs(const set<int> & exceptions);
/* Set the close-on-exec flag for the given file descriptor. */ /* Set the close-on-exec flag for the given file descriptor. */
void closeOnExec(int fd); void closeOnExec(int fd);
/* Restore default handling of SIGPIPE, otherwise some programs will
randomly say "Broken pipe". */
void restoreSIGPIPE();
/* User interruption. */ /* User interruption. */
@ -423,6 +419,9 @@ void callSuccess(
on the current thread (and thus any threads created by it). */ on the current thread (and thus any threads created by it). */
void startSignalHandlerThread(); void startSignalHandlerThread();
/* Restore default signal handling. */
void restoreSignals();
struct InterruptCallback struct InterruptCallback
{ {
virtual ~InterruptCallback() { }; virtual ~InterruptCallback() { };

View file

@ -452,6 +452,8 @@ int main(int argc, char ** argv)
auto argPtrs = stringsToCharPtrs(args); auto argPtrs = stringsToCharPtrs(args);
restoreSignals();
execvp(getEnv("NIX_BUILD_SHELL", "bash").c_str(), argPtrs.data()); execvp(getEnv("NIX_BUILD_SHELL", "bash").c_str(), argPtrs.data());
throw SysError("executing shell"); throw SysError("executing shell");