From d9187b4ee0d2343b71eab830936200f8a5ab7be0 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Sun, 18 Jan 2026 23:44:36 +0100 Subject: [PATCH] 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 --- lix/libexec/meson.build | 6 ++++++ lix/libexec/run-pager.cc | 19 +++++++++++++++++ lix/libmain/shared.cc | 45 ++++++++++++++++++++-------------------- 3 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 lix/libexec/run-pager.cc diff --git a/lix/libexec/meson.build b/lix/libexec/meson.build index e69de29bb..d61b44985 100644 --- a/lix/libexec/meson.build +++ b/lix/libexec/meson.build @@ -0,0 +1,6 @@ +run_pager = executable( + 'run-pager', + files('run-pager.cc'), + install : true, + install_dir : libexecdir / 'lix', +) diff --git a/lix/libexec/run-pager.cc b/lix/libexec/run-pager.cc new file mode 100644 index 000000000..10be232cb --- /dev/null +++ b/lix/libexec/run-pager.cc @@ -0,0 +1,19 @@ +#include "common.hh" + +LIBEXEC_HELPER(0) + +int helperMain(const char * name, std::span 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"); +} diff --git a/lix/libmain/shared.cc b/lix/libmain/shared.cc index fb8ca1cc7..ad81064eb 100644 --- a/lix/libmain/shared.cc +++ b/lix/libmain/shared.cc @@ -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 #include +#include #include #include #include @@ -373,7 +376,7 @@ int handleExceptions(const std::string & programName, std::function fun) return 0; } -static std::pair startPager() +static std::pair startPager() { if (!isOutputARealTerminal(StandardOutputStream::Stdout)) { return {}; @@ -384,27 +387,18 @@ static std::pair 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 fn) @@ -422,15 +416,22 @@ void withPager(kj::Function 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()