libmain: run pagers with a libexec helper

this is mostly a test and example for the libexec helper infrastructure,
but it also lets us simplify pager launching until we we can more easily
handle executable-not-found errors the launch fallbacks would cause when
using runProgram2 instead of fork. ideally we'd use `posix_spawn` later.

fixes #1104

Change-Id: Ia33cc12e8a9d60ffad6f5c055bb1b8b596810e64
This commit is contained in:
eldritch horrors
2026-01-20 14:02:12 +00:00
parent 9921615410
commit d9187b4ee0
3 changed files with 48 additions and 22 deletions
+6
View File
@@ -0,0 +1,6 @@
run_pager = executable(
'run-pager',
files('run-pager.cc'),
install : true,
install_dir : libexecdir / 'lix',
)
+19
View File
@@ -0,0 +1,19 @@
#include "common.hh"
LIBEXEC_HELPER(0)
int helperMain(const char * name, std::span<char *> args) noexcept
{
auto pager = args.empty() ? nullptr : args[0];
if (!getenv("LESS")) {
setenv("LESS", "FRSXMK", 1);
}
if (pager) {
execl("/bin/sh", "sh", "-c", pager, nullptr);
}
execlp("pager", "pager", nullptr);
execlp("less", "less", nullptr);
execlp("more", "more", nullptr);
die("could not find a pager to run, please set PAGER or NIX_PAGER");
}
+23 -22
View File
@@ -12,6 +12,8 @@
#include "lix/libutil/log-format.hh"
#include "lix/libutil/config.hh"
#include "lix/libutil/logging.hh"
#include "lix/libutil/file-descriptor.hh"
#include "lix/libutil/processes.hh"
#include "lix/libutil/result.hh"
#include "lix/libutil/signals.hh"
#include "lix/libmain/loggers.hh"
@@ -26,6 +28,7 @@
#include <cstdlib>
#include <kj/async-io.h>
#include <kj/common.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -373,7 +376,7 @@ int handleExceptions(const std::string & programName, std::function<int()> fun)
return 0;
}
static std::pair<Pid, AutoCloseFD> startPager()
static std::pair<RunningHelper, AutoCloseFD> startPager()
{
if (!isOutputARealTerminal(StandardOutputStream::Stdout)) {
return {};
@@ -384,27 +387,18 @@ static std::pair<Pid, AutoCloseFD> startPager()
return {};
}
logger->pause();
Pipe toPager;
toPager.create();
auto pid = startProcess([&]() {
if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1)
throw SysError("dupping stdin");
if (!getenv("LESS"))
setenv("LESS", "FRSXMK", 1);
restoreProcessContext();
if (pager)
execl("/bin/sh", "sh", "-c", pager, nullptr);
execlp("pager", "pager", nullptr);
execlp("less", "less", nullptr);
execlp("more", "more", nullptr);
throw SysError("executing '%1%'", pager);
});
auto helper = runHelper(
"run-pager",
{
.args = pager ? Strings{pager} : Strings{},
.redirections = {{.dup = STDIN_FILENO, .from = toPager.readSide.get()}},
}
);
pid.setKillSignal(SIGINT);
return {std::move(pid), std::move(toPager.writeSide)};
return {std::move(helper), std::move(toPager.writeSide)};
}
void withPager(kj::Function<void(Pager &)> fn)
@@ -422,15 +416,22 @@ void withPager(kj::Function<void(Pager &)> fn)
}
};
auto [pagerPid, pagerPipe] = startPager();
logger->pause();
KJ_DEFER(logger->resume());
auto [pagerProc, pagerPipe] = startPager();
KJ_DEFER({
if (pagerPid) {
logger->resume();
if (pagerProc) {
pagerProc.kill();
}
});
PagerImpl pager{pagerPid ? pagerPid.get() : STDOUT_FILENO};
PagerImpl pager{pagerProc ? pagerPipe.get() : STDOUT_FILENO};
fn(pager);
pagerPipe.close();
if (pagerProc) {
pagerProc.waitAndCheck();
}
}
PrintFreed::~PrintFreed()