#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
-#include "util.h"
static pid_t pager_pid = 0;
_noreturn_ static void pager_fallback(void) {
int r;
- r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, 0);
+ r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, UINT64_MAX, 0);
if (r < 0) {
log_error_errno(r, "Internal pager failed: %m");
_exit(EXIT_FAILURE);
return r;
}
-int pager_open(PagerFlags flags) {
- _cleanup_close_pair_ int fd[2] = { -1, -1 }, exe_name_pipe[2] = { -1, -1 };
+void pager_open(PagerFlags flags) {
+ _cleanup_close_pair_ int fd[2] = EBADF_PAIR, exe_name_pipe[2] = EBADF_PAIR;
_cleanup_strv_free_ char **pager_args = NULL;
+ _cleanup_free_ char *l = NULL;
const char *pager, *less_opts;
int r;
if (flags & PAGER_DISABLE)
- return 0;
+ return;
if (pager_pid > 0)
- return 1;
+ return;
if (terminal_is_dumb())
- return 0;
+ return;
if (!is_main_thread())
- return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Pager invoked from wrong thread.");
+ return (void) log_error_errno(SYNTHETIC_ERRNO(EPERM), "Pager invoked from wrong thread.");
pager = getenv("SYSTEMD_PAGER");
if (!pager)
if (pager) {
pager_args = strv_split(pager, WHITESPACE);
if (!pager_args)
- return log_oom();
+ return (void) log_oom();
/* If the pager is explicitly turned off, honour it */
if (strv_isempty(pager_args) || strv_equal(pager_args, STRV_MAKE("cat")))
- return 0;
+ return;
}
/* Determine and cache number of columns/lines before we spawn the pager so that we get the value from the
(void) lines();
if (pipe2(fd, O_CLOEXEC) < 0)
- return log_error_errno(errno, "Failed to create pager pipe: %m");
+ return (void) log_error_errno(errno, "Failed to create pager pipe: %m");
/* This is a pipe to feed the name of the executed pager binary into the parent */
if (pipe2(exe_name_pipe, O_CLOEXEC) < 0)
- return log_error_errno(errno, "Failed to create exe_name pipe: %m");
+ return (void) log_error_errno(errno, "Failed to create exe_name pipe: %m");
/* Initialize a good set of less options */
less_opts = getenv("SYSTEMD_LESS");
if (!less_opts)
less_opts = "FRSXMK";
- if (flags & PAGER_JUMP_TO_END)
- less_opts = strjoina(less_opts, " +G");
+ if (flags & PAGER_JUMP_TO_END) {
+ l = strjoin(less_opts, " +G");
+ if (!l)
+ return (void) log_oom();
+ less_opts = l;
+ }
/* We set SIGINT as PR_DEATHSIG signal here, to match the "K" parameter we set in $LESS, which enables SIGINT behaviour. */
r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGINT|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pager_pid);
if (r < 0)
- return r;
+ return;
if (r == 0) {
- const char *less_charset, *exe;
+ const char *less_charset;
/* In the child start the pager */
* pager. If they didn't, use secure mode when under euid is changed. If $SYSTEMD_PAGERSECURE
* wasn't explicitly set, and we autodetect the need for secure mode, only use the pager we
* know to be good. */
- int use_secure_mode = getenv_bool_secure("SYSTEMD_PAGERSECURE");
+ int use_secure_mode = secure_getenv_bool("SYSTEMD_PAGERSECURE");
bool trust_pager = use_secure_mode >= 0;
if (use_secure_mode == -ENXIO) {
uid_t uid;
/* We generally always set variables used by less, even if we end up using a different pager.
* They shouldn't hurt in any case, and ideally other pagers would look at them too. */
- if (use_secure_mode)
- r = setenv("LESSSECURE", "1", 1);
- else
- r = unsetenv("LESSSECURE");
+ r = set_unset_env("LESSSECURE", use_secure_mode ? "1" : NULL, true);
if (r < 0) {
- log_error_errno(errno, "Failed to adjust environment variable LESSSECURE: %m");
+ log_error_errno(r, "Failed to adjust environment variable LESSSECURE: %m");
_exit(EXIT_FAILURE);
}
* secure mode. Thus, start the pager specified through
* envvars only when $SYSTEMD_PAGERSECURE was explicitly set
* as well. */
- r = loop_write(exe_name_pipe[1], pager_args[0], strlen(pager_args[0]) + 1, false);
+ r = loop_write(exe_name_pipe[1], pager_args[0], strlen(pager_args[0]) + 1);
if (r < 0) {
log_error_errno(r, "Failed to write pager name to socket: %m");
_exit(EXIT_FAILURE);
/* Debian's alternatives command for pagers is called 'pager'. Note that we do not call
* sensible-pagers here, since that is just a shell script that implements a logic that is
* similar to this one anyway, but is Debian-specific. */
- FOREACH_STRING(exe, "pager", "less", "more") {
- /* Only less implements secure mode right now. */
- if (use_secure_mode && !streq(exe, "less"))
+ static const char* pagers[] = { "pager", "less", "more", "(built-in)" };
+
+ for (unsigned i = 0; i < ELEMENTSOF(pagers); i++) {
+ /* Only less (and our trivial fallback) implement secure mode right now. */
+ if (use_secure_mode && !STR_IN_SET(pagers[i], "less", "(built-in)"))
continue;
- r = loop_write(exe_name_pipe[1], exe, strlen(exe) + 1, false);
- if (r < 0) {
+ r = loop_write(exe_name_pipe[1], pagers[i], strlen(pagers[i]) + 1);
+ if (r < 0) {
log_error_errno(r, "Failed to write pager name to socket: %m");
_exit(EXIT_FAILURE);
}
- execlp(exe, exe, NULL);
- log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
- "Failed to execute '%s', using next fallback pager: %m", exe);
- }
- /* Our builtin is also very secure. */
- r = loop_write(exe_name_pipe[1], "(built-in)", strlen("(built-in)") + 1, false);
- if (r < 0) {
- log_error_errno(r, "Failed to write pager name to socket: %m");
- _exit(EXIT_FAILURE);
+ if (i < ELEMENTSOF(pagers) - 1) {
+ execlp(pagers[i], pagers[i], NULL);
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to execute '%s', will try '%s' next: %m", pagers[i], pagers[i+1]);
+ } else {
+ /* Close pipe to signal the parent to start sending data */
+ safe_close_pair(exe_name_pipe);
+ pager_fallback();
+ assert_not_reached();
+ }
}
- /* Close pipe to signal the parent to start sending data */
- safe_close_pair(exe_name_pipe);
- pager_fallback();
- /* not reached */
}
/* Return in the parent */
stored_stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 3);
if (dup2(fd[1], STDOUT_FILENO) < 0) {
stored_stdout = safe_close(stored_stdout);
- return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
+ return (void) log_error_errno(errno, "Failed to duplicate pager pipe: %m");
}
stdout_redirected = true;
stored_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3);
if (dup2(fd[1], STDERR_FILENO) < 0) {
stored_stderr = safe_close(stored_stderr);
- return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
+ return (void) log_error_errno(errno, "Failed to duplicate pager pipe: %m");
}
stderr_redirected = true;
exe_name_pipe[1] = safe_close(exe_name_pipe[1]);
r = no_quit_on_interrupt(TAKE_FD(exe_name_pipe[0]), less_opts);
- if (r < 0)
- return r;
if (r > 0)
- (void) ignore_signals(SIGINT, -1);
-
- return 1;
+ (void) ignore_signals(SIGINT);
}
void pager_close(void) {
stdout_redirected = stderr_redirected = false;
(void) kill(pager_pid, SIGCONT);
- (void) wait_for_terminate(pager_pid, NULL);
+ (void) wait_for_terminate(TAKE_PID(pager_pid), NULL);
pager_pid = 0;
}
if (e) {
char *page = NULL, *section = NULL;
- page = strndupa(desc, e - desc);
- section = strndupa(e + 1, desc + k - e - 2);
+ page = strndupa_safe(desc, e - desc);
+ section = strndupa_safe(e + 1, desc + k - e - 2);
args[1] = section;
args[2] = page;
} else
args[1] = desc;
- r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_NULL_STDIO : 0)|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
+ r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|(null_stdio ? FORK_REARRANGE_STDIO : 0)|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {