1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/prctl.h>
33 #include "locale-util.h"
37 #include "process-util.h"
38 #include "signal-util.h"
39 #include "string-util.h"
41 #include "terminal-util.h"
43 static pid_t pager_pid = 0;
45 static int stored_stdout = -1;
46 static int stored_stderr = -1;
47 static bool stdout_redirected = false;
48 static bool stderr_redirected = false;
50 _noreturn_ static void pager_fallback(void) {
53 r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, 0);
55 log_error_errno(r, "Internal pager failed: %m");
62 int pager_open(bool no_pager, bool jump_to_end) {
63 _cleanup_close_pair_ int fd[2] = { -1, -1 };
73 if (terminal_is_dumb())
76 if (!is_main_thread())
79 pager = getenv("SYSTEMD_PAGER");
81 pager = getenv("PAGER");
83 /* If the pager is explicitly turned off, honour it */
84 if (pager && STR_IN_SET(pager, "", "cat"))
87 /* Determine and cache number of columns/lines before we spawn the pager so that we get the value from the
92 if (pipe2(fd, O_CLOEXEC) < 0)
93 return log_error_errno(errno, "Failed to create pager pipe: %m");
95 r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pager_pid);
99 const char* less_opts, *less_charset;
101 /* In the child start the pager */
103 (void) dup2(fd[0], STDIN_FILENO);
106 /* Initialize a good set of less options */
107 less_opts = getenv("SYSTEMD_LESS");
109 less_opts = "FRSXMK";
111 less_opts = strjoina(less_opts, " +G");
112 if (setenv("LESS", less_opts, 1) < 0)
115 /* Initialize a good charset for less. This is
116 * particularly important if we output UTF-8
118 less_charset = getenv("SYSTEMD_LESSCHARSET");
119 if (!less_charset && is_locale_utf8())
120 less_charset = "utf-8";
122 setenv("LESSCHARSET", less_charset, 1) < 0)
126 execlp(pager, pager, NULL);
127 execl("/bin/sh", "sh", "-c", pager, NULL);
130 /* Debian's alternatives command for pagers is
131 * called 'pager'. Note that we do not call
132 * sensible-pagers here, since that is just a
133 * shell script that implements a logic that
134 * is similar to this one anyway, but is
135 * Debian-specific. */
136 execlp("pager", "pager", NULL);
138 execlp("less", "less", NULL);
139 execlp("more", "more", NULL);
145 /* Return in the parent */
146 stored_stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 3);
147 if (dup2(fd[1], STDOUT_FILENO) < 0) {
148 stored_stdout = safe_close(stored_stdout);
149 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
151 stdout_redirected = true;
153 stored_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3);
154 if (dup2(fd[1], STDERR_FILENO) < 0) {
155 stored_stderr = safe_close(stored_stderr);
156 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
158 stderr_redirected = true;
163 void pager_close(void) {
168 /* Inform pager that we are done */
169 (void) fflush(stdout);
170 if (stdout_redirected)
171 if (stored_stdout < 0 || dup2(stored_stdout, STDOUT_FILENO) < 0)
172 (void) close(STDOUT_FILENO);
173 stored_stdout = safe_close(stored_stdout);
174 (void) fflush(stderr);
175 if (stderr_redirected)
176 if (stored_stderr < 0 || dup2(stored_stderr, STDERR_FILENO) < 0)
177 (void) close(STDERR_FILENO);
178 stored_stderr = safe_close(stored_stderr);
179 stdout_redirected = stderr_redirected = false;
181 (void) kill(pager_pid, SIGCONT);
182 (void) wait_for_terminate(pager_pid, NULL);
186 bool pager_have(void) {
187 return pager_pid > 0;
190 #if 0 /// UNNEEDED by elogind
191 int show_man_page(const char *desc, bool null_stdio) {
192 const char *args[4] = { "man", NULL, NULL, NULL };
200 if (desc[k-1] == ')')
201 e = strrchr(desc, '(');
204 char *page = NULL, *section = NULL;
206 page = strndupa(desc, e - desc);
207 section = strndupa(e + 1, desc + k - e - 2);
214 r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_NULL_STDIO : 0)|FORK_LOG, &pid);
219 execvp(args[0], (char**) args);
220 log_error_errno(errno, "Failed to execute man: %m");
224 return wait_for_terminate_and_check(NULL, pid, 0);