This source file includes following definitions.
- pager_fallback
- pager_open
- pager_close
- pager_have
- show_man_page
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/prctl.h>
#include "pager.h"
#include "util.h"
#include "macro.h"
static pid_t pager_pid = 0;
noreturn static void pager_fallback(void) {
ssize_t n;
do {
n = splice(STDIN_FILENO, NULL, STDOUT_FILENO, NULL, 64*1024, 0);
} while (n > 0);
if (n < 0) {
log_error("Internal pager failed: %m");
_exit(EXIT_FAILURE);
}
_exit(EXIT_SUCCESS);
}
int pager_open(bool jump_to_end) {
int fd[2];
const char *pager;
pid_t parent_pid;
int r;
if (pager_pid > 0)
return 1;
if ((pager = getenv("SYSTEMD_PAGER")) || (pager = getenv("PAGER")))
if (!*pager || streq(pager, "cat"))
return 0;
if (!on_tty())
return 0;
columns();
if (pipe(fd) < 0) {
log_error("Failed to create pager pipe: %m");
return -errno;
}
parent_pid = getpid();
pager_pid = fork();
if (pager_pid < 0) {
r = -errno;
log_error("Failed to fork pager: %m");
safe_close_pair(fd);
return r;
}
if (pager_pid == 0) {
const char* less_opts;
dup2(fd[0], STDIN_FILENO);
safe_close_pair(fd);
less_opts = getenv("SYSTEMD_LESS");
if (!less_opts)
less_opts = "FRSXMK";
if (jump_to_end)
less_opts = strappenda(less_opts, " +G");
setenv("LESS", less_opts, 1);
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
_exit(EXIT_FAILURE);
if (getppid() != parent_pid)
_exit(EXIT_SUCCESS);
if (pager) {
execlp(pager, pager, NULL);
execl("/bin/sh", "sh", "-c", pager, NULL);
}
execlp("pager", "pager", NULL);
execlp("less", "less", NULL);
execlp("more", "more", NULL);
pager_fallback();
}
if (dup2(fd[1], STDOUT_FILENO) < 0) {
log_error("Failed to duplicate pager pipe: %m");
return -errno;
}
safe_close_pair(fd);
return 1;
}
void pager_close(void) {
if (pager_pid <= 0)
return;
fclose(stdout);
kill(pager_pid, SIGCONT);
wait_for_terminate(pager_pid, NULL);
pager_pid = 0;
}
bool pager_have(void) {
return pager_pid > 0;
}
int show_man_page(const char *desc, bool null_stdio) {
const char *args[4] = { "man", NULL, NULL, NULL };
char *e = NULL;
pid_t pid;
size_t k;
int r;
siginfo_t status;
k = strlen(desc);
if (desc[k-1] == ')')
e = strrchr(desc, '(');
if (e) {
char *page = NULL, *section = NULL;
page = strndupa(desc, e - desc);
section = strndupa(e + 1, desc + k - e - 2);
args[1] = section;
args[2] = page;
} else
args[1] = desc;
pid = fork();
if (pid < 0) {
log_error("Failed to fork: %m");
return -errno;
}
if (pid == 0) {
if (null_stdio) {
r = make_null_stdio();
if (r < 0) {
log_error("Failed to kill stdio: %s", strerror(-r));
_exit(EXIT_FAILURE);
}
}
execvp(args[0], (char**) args);
log_error("Failed to execute man: %m");
_exit(EXIT_FAILURE);
}
r = wait_for_terminate(pid, &status);
if (r < 0)
return r;
log_debug("Exit code %i status %i", status.si_code, status.si_status);
return status.si_status;
}