Refactoring: Move all fork handling into a higher-order function

C++11 lambdas ftw.
This commit is contained in:
Eelco Dolstra 2014-07-10 16:50:51 +02:00
parent 1114c7bd57
commit 8e9140cfde
7 changed files with 128 additions and 206 deletions

View file

@ -24,30 +24,14 @@ static std::pair<FdSink, FdSource> connect(const string & conn)
Pipe to, from; Pipe to, from;
to.create(); to.create();
from.create(); from.create();
pid_t child = fork(); startProcess([&]() {
switch (child) { if (dup2(to.readSide, STDIN_FILENO) == -1)
case -1: throw SysError("dupping stdin");
throw SysError("unable to fork"); if (dup2(from.writeSide, STDOUT_FILENO) == -1)
case 0: throw SysError("dupping stdout");
try { execlp("ssh", "ssh", "-x", "-T", conn.c_str(), "nix-store --serve", NULL);
restoreAffinity(); throw SysError("executing ssh");
if (dup2(to.readSide, STDIN_FILENO) == -1) });
throw SysError("dupping stdin");
if (dup2(from.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
execlp("ssh"
, "ssh"
, "-x"
, "-T"
, conn.c_str()
, "nix-store --serve"
, NULL);
throw SysError("executing ssh");
} catch (std::exception & e) {
std::cerr << "error: " << e.what() << std::endl;
}
_exit(1);
}
// If child exits unexpectedly, we'll EPIPE or EOF early. // If child exits unexpectedly, we'll EPIPE or EOF early.
// If we exit unexpectedly, child will EPIPE or EOF early. // If we exit unexpectedly, child will EPIPE or EOF early.
// So no need to keep track of it. // So no need to keep track of it.

View file

@ -602,42 +602,29 @@ HookInstance::HookInstance()
builderOut.create(); builderOut.create();
/* Fork the hook. */ /* Fork the hook. */
pid = fork(); pid = startProcess([&]() {
switch (pid) {
case -1: commonChildInit(fromHook);
throw SysError("unable to fork");
case 0: if (chdir("/") == -1) throw SysError("changing into `/");
try { /* child */
commonChildInit(fromHook); /* Dup the communication pipes. */
if (dup2(toHook.readSide, STDIN_FILENO) == -1)
throw SysError("dupping to-hook read side");
if (chdir("/") == -1) throw SysError("changing into `/"); /* Use fd 4 for the builder's stdout/stderr. */
if (dup2(builderOut.writeSide, 4) == -1)
throw SysError("dupping builder's stdout/stderr");
/* Dup the communication pipes. */ execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(),
if (dup2(toHook.readSide, STDIN_FILENO) == -1) (format("%1%") % settings.maxSilentTime).str().c_str(),
throw SysError("dupping to-hook read side"); (format("%1%") % settings.printBuildTrace).str().c_str(),
(format("%1%") % settings.buildTimeout).str().c_str(),
NULL);
/* Use fd 4 for the builder's stdout/stderr. */ throw SysError(format("executing `%1%'") % buildHook);
if (dup2(builderOut.writeSide, 4) == -1) });
throw SysError("dupping builder's stdout/stderr");
execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(),
(format("%1%") % settings.maxSilentTime).str().c_str(),
(format("%1%") % settings.printBuildTrace).str().c_str(),
(format("%1%") % settings.buildTimeout).str().c_str(),
NULL);
throw SysError(format("executing `%1%'") % buildHook);
} catch (std::exception & e) {
writeToStderr("build hook error: " + string(e.what()) + "\n");
}
_exit(1);
}
/* parent */
pid.setSeparatePG(true); pid.setSeparatePG(true);
pid.setKillSignal(SIGTERM); pid.setKillSignal(SIGTERM);
fromHook.writeSide.close(); fromHook.writeSide.close();
@ -2781,32 +2768,18 @@ void SubstitutionGoal::tryToRun()
const char * * argArr = strings2CharPtrs(args); const char * * argArr = strings2CharPtrs(args);
/* Fork the substitute program. */ /* Fork the substitute program. */
pid = fork(); pid = startProcess([&]() {
switch (pid) { commonChildInit(logPipe);
case -1: if (dup2(outPipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("unable to fork"); throw SysError("cannot dup output pipe into stdout");
case 0: execv(sub.c_str(), (char * *) argArr);
try { /* child */
commonChildInit(logPipe); throw SysError(format("executing `%1%'") % sub);
});
if (dup2(outPipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("cannot dup output pipe into stdout");
execv(sub.c_str(), (char * *) argArr);
throw SysError(format("executing `%1%'") % sub);
} catch (std::exception & e) {
writeToStderr("substitute error: " + string(e.what()) + "\n");
}
_exit(1);
}
/* parent */
pid.setSeparatePG(true); pid.setSeparatePG(true);
pid.setKillSignal(SIGTERM); pid.setKillSignal(SIGTERM);
outPipe.writeSide.close(); outPipe.writeSide.close();

View file

@ -1083,31 +1083,16 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
setSubstituterEnv(); setSubstituterEnv();
run.pid = fork(); run.pid = startProcess([&]() {
if (dup2(toPipe.readSide, STDIN_FILENO) == -1)
switch (run.pid) { throw SysError("dupping stdin");
if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1)
case -1: throw SysError("dupping stdout");
throw SysError("unable to fork"); if (dup2(errorPipe.writeSide, STDERR_FILENO) == -1)
throw SysError("dupping stderr");
case 0: /* child */ execl(substituter.c_str(), substituter.c_str(), "--query", NULL);
try { throw SysError(format("executing `%1%'") % substituter);
restoreAffinity(); });
if (dup2(toPipe.readSide, STDIN_FILENO) == -1)
throw SysError("dupping stdin");
if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
if (dup2(errorPipe.writeSide, STDERR_FILENO) == -1)
throw SysError("dupping stderr");
execl(substituter.c_str(), substituter.c_str(), "--query", NULL);
throw SysError(format("executing `%1%'") % substituter);
} catch (std::exception & e) {
std::cerr << "error: " << e.what() << std::endl;
}
_exit(1);
}
/* Parent. */
run.program = baseNameOf(substituter); run.program = baseNameOf(substituter);
run.to = toPipe.writeSide.borrow(); run.to = toPipe.writeSide.borrow();

View file

@ -1,5 +1,8 @@
#include "config.h" #include "config.h"
#include "util.hh"
#include "affinity.hh"
#include <iostream> #include <iostream>
#include <cerrno> #include <cerrno>
#include <cstdio> #include <cstdio>
@ -16,8 +19,6 @@
#include <sys/syscall.h> #include <sys/syscall.h>
#endif #endif
#include "util.hh"
extern char * * environ; extern char * * environ;
@ -714,6 +715,13 @@ Pid::Pid()
} }
Pid::Pid(pid_t pid)
{
Pid();
*this = pid;
}
Pid::~Pid() Pid::~Pid()
{ {
kill(); kill();
@ -801,43 +809,30 @@ void killUser(uid_t uid)
users to which the current process can send signals. So we users to which the current process can send signals. So we
fork a process, switch to uid, and send a mass kill. */ fork a process, switch to uid, and send a mass kill. */
Pid pid; Pid pid = startProcess([&]() {
pid = fork();
switch (pid) {
case -1: if (setuid(uid) == -1)
throw SysError("unable to fork"); throw SysError("setting uid");
case 0: while (true) {
try { /* child */
if (setuid(uid) == -1)
throw SysError("setting uid");
while (true) {
#ifdef __APPLE__ #ifdef __APPLE__
/* OSX's kill syscall takes a third parameter that, among other /* OSX's kill syscall takes a third parameter that, among
things, determines if kill(-1, signo) affects the calling other things, determines if kill(-1, signo) affects the
process. In the OSX libc, it's set to true, which means calling process. In the OSX libc, it's set to true,
"follow POSIX", which we don't want here which means "follow POSIX", which we don't want here
*/ */
if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break; if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break;
#else #else
if (kill(-1, SIGKILL) == 0) break; if (kill(-1, SIGKILL) == 0) break;
#endif #endif
if (errno == ESRCH) break; /* no more processes */ if (errno == ESRCH) break; /* no more processes */
if (errno != EINTR) if (errno != EINTR)
throw SysError(format("cannot kill processes for uid `%1%'") % uid); throw SysError(format("cannot kill processes for uid `%1%'") % uid);
}
} catch (std::exception & e) {
writeToStderr((format("killing processes belonging to uid `%1%': %2%\n") % uid % e.what()).str());
_exit(1);
} }
_exit(0);
}
/* parent */ _exit(0);
});
int status = pid.wait(true); int status = pid.wait(true);
if (status != 0) if (status != 0)
throw Error(format("cannot kill processes for uid `%1%': %2%") % uid % statusToString(status)); throw Error(format("cannot kill processes for uid `%1%': %2%") % uid % statusToString(status));
@ -852,6 +847,25 @@ void killUser(uid_t uid)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
pid_t startProcess(std::function<void()> fun, const string & errorPrefix)
{
pid_t pid = fork();
if (pid == -1) throw SysError("unable to fork");
if (pid == 0) {
try {
restoreAffinity();
fun();
} catch (std::exception & e) {
writeToStderr(errorPrefix + string(e.what()) + "\n");
}
_exit(1);
}
return pid;
}
string runProgram(Path program, bool searchPath, const Strings & args) string runProgram(Path program, bool searchPath, const Strings & args)
{ {
checkInterrupt(); checkInterrupt();
@ -867,32 +881,17 @@ string runProgram(Path program, bool searchPath, const Strings & args)
pipe.create(); pipe.create();
/* Fork. */ /* Fork. */
Pid pid; Pid pid = startProcess([&]() {
pid = fork(); if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
switch (pid) { if (searchPath)
execvp(program.c_str(), (char * *) &cargs[0]);
else
execv(program.c_str(), (char * *) &cargs[0]);
case -1: throw SysError(format("executing `%1%'") % program);
throw SysError("unable to fork"); });
case 0: /* child */
try {
if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
if (searchPath)
execvp(program.c_str(), (char * *) &cargs[0]);
else
execv(program.c_str(), (char * *) &cargs[0]);
throw SysError(format("executing `%1%'") % program);
} catch (std::exception & e) {
writeToStderr("error: " + string(e.what()) + "\n");
}
_exit(1);
}
/* Parent. */
pipe.writeSide.close(); pipe.writeSide.close();

View file

@ -7,6 +7,7 @@
#include <dirent.h> #include <dirent.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#include <functional>
#include <cstdio> #include <cstdio>
@ -237,6 +238,7 @@ class Pid
int killSignal; int killSignal;
public: public:
Pid(); Pid();
Pid(pid_t pid);
~Pid(); ~Pid();
void operator =(pid_t pid); void operator =(pid_t pid);
operator pid_t(); operator pid_t();
@ -252,6 +254,11 @@ public:
void killUser(uid_t uid); void killUser(uid_t uid);
/* Fork a process that runs the given function, and return the child
pid to the caller. */
pid_t startProcess(std::function<void()> fun, const string & errorPrefix = "error: ");
/* Run a program and return its stdout in a string (i.e., like the /* Run a program and return its stdout in a string (i.e., like the
shell backtick operator). */ shell backtick operator). */
string runProgram(Path program, bool searchPath = false, string runProgram(Path program, bool searchPath = false,

View file

@ -872,40 +872,27 @@ static void daemonLoop()
printMsg(lvlInfo, format("accepted connection from pid %1%, uid %2%") % clientPid % clientUid); printMsg(lvlInfo, format("accepted connection from pid %1%, uid %2%") % clientPid % clientUid);
/* Fork a child to handle the connection. */ /* Fork a child to handle the connection. */
pid_t child; startProcess([&]() {
child = fork(); /* Background the daemon. */
if (setsid() == -1)
throw SysError(format("creating a new session"));
switch (child) { /* Restore normal handling of SIGCHLD. */
setSigChldAction(false);
case -1: /* For debugging, stuff the pid into argv[1]. */
throw SysError("unable to fork"); if (clientPid != -1 && argvSaved[1]) {
string processName = int2String(clientPid);
case 0: strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
try { /* child */
/* Background the daemon. */
if (setsid() == -1)
throw SysError(format("creating a new session"));
/* Restore normal handling of SIGCHLD. */
setSigChldAction(false);
/* For debugging, stuff the pid into argv[1]. */
if (clientPid != -1 && argvSaved[1]) {
string processName = int2String(clientPid);
strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
}
/* Handle the connection. */
from.fd = remote;
to.fd = remote;
processConnection(trusted);
} catch (std::exception & e) {
writeToStderr("unexpected Nix daemon error: " + string(e.what()) + "\n");
} }
exit(0);
} /* Handle the connection. */
from.fd = remote;
to.fd = remote;
processConnection(trusted);
_exit(0);
}, "unexpected Nix daemon error: ");
} catch (Interrupted & e) { } catch (Interrupted & e) {
throw; throw;

View file

@ -939,27 +939,14 @@ static void opServe(Strings opFlags, Strings opArgs)
Pipe fromDecompressor; Pipe fromDecompressor;
fromDecompressor.create(); fromDecompressor.create();
Pid pid; Pid pid = startProcess([&]() {
pid = fork(); fromDecompressor.readSide.close();
if (dup2(fromDecompressor.writeSide, STDOUT_FILENO) == -1)
switch (pid) { throw SysError("dupping stdout");
// FIXME: use absolute path.
case -1: execlp(compression.c_str(), compression.c_str(), "-d", NULL);
throw SysError("unable to fork"); throw SysError(format("executing `%1%'") % compression);
});
case 0: /* child */
try {
fromDecompressor.readSide.close();
if (dup2(fromDecompressor.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
// FIXME: use absolute path.
execlp(compression.c_str(), compression.c_str(), "-d", NULL);
throw SysError(format("executing `%1%'") % compression);
} catch (std::exception & e) {
std::cerr << "error: " << e.what() << std::endl;
}
_exit(1);
}
fromDecompressor.writeSide.close(); fromDecompressor.writeSide.close();