Use vfork

This commit is contained in:
Eelco Dolstra 2014-12-10 16:35:42 +01:00
parent b5ed5b6e66
commit 0e8fc118b3
3 changed files with 39 additions and 11 deletions

View file

@ -863,31 +863,46 @@ void killUser(uid_t uid)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
pid_t startProcess(std::function<void()> fun, /* Wrapper around vfork to prevent the child process from clobbering
bool dieWithParent, const string & errorPrefix, bool runExitHandlers) the caller's stack frame in the parent. */
static pid_t doFork(bool allowVfork, std::function<void()> fun) __attribute__((noinline));
static pid_t doFork(bool allowVfork, std::function<void()> fun)
{ {
#ifdef __linux__
pid_t pid = allowVfork ? vfork() : fork();
#else
pid_t pid = fork(); pid_t pid = fork();
if (pid == -1) throw SysError("unable to fork"); #endif
if (pid != 0) return pid;
fun();
abort();
}
if (pid == 0) {
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
{
auto wrapper = [&]() {
_writeToStderr = 0; _writeToStderr = 0;
try { try {
#if __linux__ #if __linux__
if (dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) if (options.dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
throw SysError("setting death signal"); throw SysError("setting death signal");
#endif #endif
restoreAffinity(); restoreAffinity();
fun(); fun();
} catch (std::exception & e) { } catch (std::exception & e) {
try { try {
std::cerr << errorPrefix << e.what() << "\n"; std::cerr << options.errorPrefix << e.what() << "\n";
} catch (...) { } } catch (...) { }
} catch (...) { } } catch (...) { }
if (runExitHandlers) if (options.runExitHandlers)
exit(1); exit(1);
else else
_exit(1); _exit(1);
} };
pid_t pid = doFork(options.allowVfork, wrapper);
if (pid == -1) throw SysError("unable to fork");
return pid; return pid;
} }

View file

@ -269,8 +269,16 @@ void killUser(uid_t uid);
/* Fork a process that runs the given function, and return the child /* Fork a process that runs the given function, and return the child
pid to the caller. */ pid to the caller. */
pid_t startProcess(std::function<void()> fun, bool dieWithParent = true, struct ProcessOptions
const string & errorPrefix = "error: ", bool runExitHandlers = false); {
string errorPrefix;
bool dieWithParent;
bool runExitHandlers;
bool allowVfork;
ProcessOptions() : errorPrefix("error: "), dieWithParent(true), runExitHandlers(false), allowVfork(true) { };
};
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
/* 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

View file

@ -799,6 +799,11 @@ static void daemonLoop(char * * argv)
% (peer.uidKnown ? user : "<unknown>")); % (peer.uidKnown ? user : "<unknown>"));
/* Fork a child to handle the connection. */ /* Fork a child to handle the connection. */
ProcessOptions options;
options.errorPrefix = "unexpected Nix daemon error: ";
options.dieWithParent = false;
options.runExitHandlers = true;
options.allowVfork = false;
startProcess([&]() { startProcess([&]() {
fdSocket.close(); fdSocket.close();
@ -821,7 +826,7 @@ static void daemonLoop(char * * argv)
processConnection(trusted); processConnection(trusted);
exit(0); exit(0);
}, false, "unexpected Nix daemon error: ", true); }, options);
} catch (Interrupted & e) { } catch (Interrupted & e) {
throw; throw;