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...)
This commit is contained in:
Eelco Dolstra 2014-12-05 20:34:41 +01:00
parent d51eed833a
commit d34d2b2bbf
3 changed files with 39 additions and 10 deletions

View file

@ -15,6 +15,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#include <spawn.h>
namespace nix { namespace nix {
@ -305,14 +306,35 @@ RunPager::RunPager()
Pipe toPager; Pipe toPager;
toPager.create(); toPager.create();
pid = startProcess([&]() { // FIXME: should do this in the child environment.
if (dup2(toPager.readSide, STDIN_FILENO) == -1)
throw SysError("dupping stdin");
if (!getenv("LESS")) if (!getenv("LESS"))
setenv("LESS", "FRSXMK", 1); setenv("LESS", "FRSXMK", 1);
execl("/bin/sh", "sh", "-c", pager.c_str(), NULL);
throw SysError(format("executing %1%") % pager); /* 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) if (dup2(toPager.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout"); throw SysError("dupping stdout");

View file

@ -73,6 +73,7 @@ class SysError : public Error
public: public:
int errNo; int errNo;
SysError(const FormatOrString & fs); SysError(const FormatOrString & fs);
SysError(int errNo, const FormatOrString & fs);
}; };

View file

@ -45,8 +45,14 @@ BaseError & BaseError::addPrefix(const FormatOrString & fs)
SysError::SysError(const FormatOrString & fs) SysError::SysError(const FormatOrString & fs)
: Error(format("%1%: %2%") % fs.s % strerror(errno)) : SysError(errno, fs)
, errNo(errno) {
}
SysError::SysError(int errNo, const FormatOrString & fs)
: Error(format("%1%: %2%") % fs.s % strerror(errNo))
, errNo(errNo)
{ {
} }