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) {
case -1:
throw SysError("unable to fork");
case 0:
try {
restoreAffinity();
if (dup2(to.readSide, STDIN_FILENO) == -1) if (dup2(to.readSide, STDIN_FILENO) == -1)
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");
execlp("ssh" execlp("ssh", "ssh", "-x", "-T", conn.c_str(), "nix-store --serve", NULL);
, "ssh"
, "-x"
, "-T"
, conn.c_str()
, "nix-store --serve"
, NULL);
throw SysError("executing ssh"); 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,14 +602,7 @@ HookInstance::HookInstance()
builderOut.create(); builderOut.create();
/* Fork the hook. */ /* Fork the hook. */
pid = fork(); pid = startProcess([&]() {
switch (pid) {
case -1:
throw SysError("unable to fork");
case 0:
try { /* child */
commonChildInit(fromHook); commonChildInit(fromHook);
@ -630,14 +623,8 @@ HookInstance::HookInstance()
NULL); NULL);
throw SysError(format("executing `%1%'") % buildHook); 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,15 +2768,7 @@ 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) {
case -1:
throw SysError("unable to fork");
case 0:
try { /* child */
commonChildInit(logPipe); commonChildInit(logPipe);
@ -2799,14 +2778,8 @@ void SubstitutionGoal::tryToRun()
execv(sub.c_str(), (char * *) argArr); execv(sub.c_str(), (char * *) argArr);
throw SysError(format("executing `%1%'") % sub); 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,16 +1083,7 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
setSubstituterEnv(); setSubstituterEnv();
run.pid = fork(); run.pid = startProcess([&]() {
switch (run.pid) {
case -1:
throw SysError("unable to fork");
case 0: /* child */
try {
restoreAffinity();
if (dup2(toPipe.readSide, STDIN_FILENO) == -1) if (dup2(toPipe.readSide, STDIN_FILENO) == -1)
throw SysError("dupping stdin"); throw SysError("dupping stdin");
if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1) if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1)
@ -1101,13 +1092,7 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
throw SysError("dupping stderr"); throw SysError("dupping stderr");
execl(substituter.c_str(), substituter.c_str(), "--query", NULL); execl(substituter.c_str(), substituter.c_str(), "--query", NULL);
throw SysError(format("executing `%1%'") % substituter); 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,25 +809,17 @@ 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:
throw SysError("unable to fork");
case 0:
try { /* child */
if (setuid(uid) == -1) if (setuid(uid) == -1)
throw SysError("setting uid"); throw SysError("setting uid");
while (true) { 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
@ -830,14 +830,9 @@ void killUser(uid_t uid)
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); _exit(0);
} });
/* parent */
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,16 +881,7 @@ string runProgram(Path program, bool searchPath, const Strings & args)
pipe.create(); pipe.create();
/* Fork. */ /* Fork. */
Pid pid; Pid pid = startProcess([&]() {
pid = fork();
switch (pid) {
case -1:
throw SysError("unable to fork");
case 0: /* child */
try {
if (dup2(pipe.writeSide, STDOUT_FILENO) == -1) if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout"); throw SysError("dupping stdout");
@ -884,15 +889,9 @@ string runProgram(Path program, bool searchPath, const Strings & args)
execvp(program.c_str(), (char * *) &cargs[0]); execvp(program.c_str(), (char * *) &cargs[0]);
else else
execv(program.c_str(), (char * *) &cargs[0]); execv(program.c_str(), (char * *) &cargs[0]);
throw SysError(format("executing `%1%'") % program); 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,17 +872,7 @@ 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();
switch (child) {
case -1:
throw SysError("unable to fork");
case 0:
try { /* child */
/* Background the daemon. */ /* Background the daemon. */
if (setsid() == -1) if (setsid() == -1)
throw SysError(format("creating a new session")); throw SysError(format("creating a new session"));
@ -901,11 +891,8 @@ static void daemonLoop()
to.fd = remote; to.fd = remote;
processConnection(trusted); processConnection(trusted);
} catch (std::exception & e) { _exit(0);
writeToStderr("unexpected Nix daemon error: " + string(e.what()) + "\n"); }, "unexpected Nix daemon error: ");
}
exit(0);
}
} 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();
switch (pid) {
case -1:
throw SysError("unable to fork");
case 0: /* child */
try {
fromDecompressor.readSide.close(); fromDecompressor.readSide.close();
if (dup2(fromDecompressor.writeSide, STDOUT_FILENO) == -1) if (dup2(fromDecompressor.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout"); throw SysError("dupping stdout");
// FIXME: use absolute path. // FIXME: use absolute path.
execlp(compression.c_str(), compression.c_str(), "-d", NULL); execlp(compression.c_str(), compression.c_str(), "-d", NULL);
throw SysError(format("executing `%1%'") % compression); 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();