From d34d2b2bbf784c0bb420a50905af25e02c6e4989 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 5 Dec 2014 20:34:41 +0100 Subject: [PATCH] Use posix_spawn to run the pager In low memory environments, "nix-env -qa" failed because the fork to run the pager hit the kernel's overcommit limits. Using posix_spawn gets around this. (Actually, you have to use posix_spawn with the undocumented POSIX_SPAWN_USEVFORK flag, otherwise it just uses fork/exec...) --- src/libmain/shared.cc | 38 ++++++++++++++++++++++++++++++-------- src/libutil/types.hh | 1 + src/libutil/util.cc | 10 ++++++++-- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index c4b5c210d..d0c75a82a 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -15,6 +15,7 @@ #include #include #include +#include namespace nix { @@ -305,14 +306,35 @@ RunPager::RunPager() Pipe toPager; toPager.create(); - pid = startProcess([&]() { - if (dup2(toPager.readSide, STDIN_FILENO) == -1) - throw SysError("dupping stdin"); - if (!getenv("LESS")) - setenv("LESS", "FRSXMK", 1); - execl("/bin/sh", "sh", "-c", pager.c_str(), NULL); - throw SysError(format("executing ‘%1%’") % pager); - }); + // FIXME: should do this in the child environment. + if (!getenv("LESS")) + setenv("LESS", "FRSXMK", 1); + + /* Start the pager using posix_spawn. */ + pid_t pid_; + const char * argv[] = { "sh", "-c", pager.c_str(), 0 }; + + posix_spawn_file_actions_t fileActions; + int err = posix_spawn_file_actions_init(&fileActions); + if (err) throw SysError(err, "creating POSIX file actions"); + err = posix_spawn_file_actions_adddup2(&fileActions, toPager.readSide, STDIN_FILENO); + if (err) throw SysError(err, "adding to POSIX file actions"); + + posix_spawnattr_t spawnAttrs; + err = posix_spawnattr_init(&spawnAttrs); + if (err) throw SysError(err, "creating POSIX spawn attrs"); +#ifdef POSIX_SPAWN_USEVFORK + err = posix_spawnattr_setflags(&spawnAttrs, POSIX_SPAWN_USEVFORK); + if (err) throw SysError(err, "setting POSIX spawn attr flag"); +#endif + + err = posix_spawn(&pid_, "/bin/sh", &fileActions, &spawnAttrs, (char * const *) argv, environ); + + posix_spawn_file_actions_destroy(&fileActions); + posix_spawnattr_destroy(&spawnAttrs); + + if (err) throw SysError(err, format("running ‘%1%’") % pager); + pid = pid_; if (dup2(toPager.writeSide, STDOUT_FILENO) == -1) throw SysError("dupping stdout"); diff --git a/src/libutil/types.hh b/src/libutil/types.hh index 160884ee1..030996a06 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -73,6 +73,7 @@ class SysError : public Error public: int errNo; SysError(const FormatOrString & fs); + SysError(int errNo, const FormatOrString & fs); }; diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 305e470eb..60be02cd4 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -45,8 +45,14 @@ BaseError & BaseError::addPrefix(const FormatOrString & fs) SysError::SysError(const FormatOrString & fs) - : Error(format("%1%: %2%") % fs.s % strerror(errno)) - , errNo(errno) + : SysError(errno, fs) +{ +} + + +SysError::SysError(int errNo, const FormatOrString & fs) + : Error(format("%1%: %2%") % fs.s % strerror(errNo)) + , errNo(errNo) { }