2 * SPDX-License-Identifier: GPL-2.0-or-later
4 * This file may be redistributed under the terms of the GNU Public
7 * Based on linux-perf/git scm
9 * Some modifications and simplifications for util-linux
10 * by Davidlohr Bueso <dave@xxxxxxx> - March 2012.
17 #include <sys/types.h>
28 #define NULL_DEVICE "/dev/null"
30 static const char *pager_argv
[] = { "sh", "-c", NULL
, NULL
};
32 struct child_process
{
41 struct sigaction orig_sigint
;
42 struct sigaction orig_sighup
;
43 struct sigaction orig_sigterm
;
44 struct sigaction orig_sigquit
;
45 struct sigaction orig_sigpipe
;
48 void (*preexec_cb
)(void);
50 static struct child_process pager_process
;
52 static inline void close_pair(int fd
[2])
58 static int start_command(struct child_process
*cmd
)
64 * In case of errors we must keep the promise to close FDs
65 * that have been passed in via ->in and ->out.
67 need_in
= !cmd
->no_stdin
&& cmd
->in
< 0;
81 dup2(fdin
[0], STDIN_FILENO
);
83 } else if (cmd
->in
> 0) {
84 dup2(cmd
->in
, STDIN_FILENO
);
91 execvp(cmd
->argv
[0], (char *const*) cmd
->argv
);
92 errexec(cmd
->argv
[0]);
98 else if (0 <= cmd
->in
)
105 else if (0 <= cmd
->in
)
110 static int wait_or_whine(pid_t pid
)
114 pid_t waiting
= waitpid(pid
, &status
, 0);
119 ul_sig_err(EXIT_FAILURE
, "waitpid failed");
123 if (WIFSIGNALED(status
))
126 if (!WIFEXITED(status
))
128 code
= WEXITSTATUS(status
);
140 static int finish_command(struct child_process
*cmd
)
142 return wait_or_whine(cmd
->pid
);
145 static void pager_preexec_less(void)
148 * Work around bug in "less" by not starting it until we
154 FD_SET(STDIN_FILENO
, &in
);
157 select(STDIN_FILENO
+ 1, &in
, NULL
, &ex
, NULL
);
159 if (setenv("LESS", "FRSX", 0) != 0)
160 warn(_("failed to set the %s environment variable"), "LESS");
163 static void wait_for_pager(void)
165 if (pager_process
.pid
== 0)
168 /* signal EOF to pager */
169 close(STDOUT_FILENO
);
170 close(STDERR_FILENO
);
171 finish_command(&pager_process
);
174 static void wait_for_pager_signal(int signo
)
180 static int has_command(const char *cmd
)
192 b
= strtok(c
, " "); /* cmd may contain options */
197 rc
= access(b
, X_OK
) == 0;
201 path
= getenv("PATH");
208 for (s
= strtok(p
, ":"); s
; s
= strtok(NULL
, ":")) {
209 int fd
= open(s
, O_RDONLY
|O_CLOEXEC
);
212 rc
= faccessat(fd
, b
, X_OK
, 0) == 0;
221 /*fprintf(stderr, "has PAGER '%s': rc=%d\n", cmd, rc);*/
225 static void __setup_pager(void)
227 const char *pager
= getenv("PAGER");
230 if (!isatty(STDOUT_FILENO
))
235 else if (!*pager
|| !strcmp(pager
, "cat"))
238 if (!has_command(pager
))
241 /* spawn the pager */
242 pager_argv
[2] = pager
;
243 pager_process
.argv
= pager_argv
;
244 pager_process
.in
= -1;
246 if (!strncmp(pager
, "less", 4))
247 pager_process
.preexec_cb
= pager_preexec_less
;
249 pager_process
.preexec_cb
= NULL
;
251 if (start_command(&pager_process
))
254 /* original process continues, but writes to the pipe */
255 dup2(pager_process
.in
, STDOUT_FILENO
);
256 setvbuf(stdout
, NULL
, _IOLBF
, 0);
257 if (isatty(STDERR_FILENO
)) {
258 dup2(pager_process
.in
, STDERR_FILENO
);
259 setvbuf(stderr
, NULL
, _IOLBF
, 0);
261 close(pager_process
.in
);
263 memset(&sa
, 0, sizeof(sa
));
264 sa
.sa_handler
= wait_for_pager_signal
;
266 /* this makes sure that the parent terminates after the pager */
267 sigaction(SIGINT
, &sa
, &pager_process
.orig_sigint
);
268 sigaction(SIGHUP
, &sa
, &pager_process
.orig_sighup
);
269 sigaction(SIGTERM
, &sa
, &pager_process
.orig_sigterm
);
270 sigaction(SIGQUIT
, &sa
, &pager_process
.orig_sigquit
);
271 sigaction(SIGPIPE
, &sa
, &pager_process
.orig_sigpipe
);
274 /* Setup pager and redirects output to the $PAGER. The pager is closed at exit.
276 void pager_redirect(void)
278 if (pager_process
.pid
)
279 return; /* already running */
283 atexit(wait_for_pager
);
286 /* Setup pager and redirect output, the pager may be closed by pager_close().
288 void pager_open(void)
290 if (pager_process
.pid
)
291 return; /* already running */
293 pager_process
.org_out
= dup(STDOUT_FILENO
);
294 pager_process
.org_err
= dup(STDERR_FILENO
);
299 /* Close pager and restore original std{out,err}.
301 void pager_close(void)
303 if (pager_process
.pid
== 0)
308 /* restore original output */
309 dup2(pager_process
.org_out
, STDOUT_FILENO
);
310 dup2(pager_process
.org_err
, STDERR_FILENO
);
312 close(pager_process
.org_out
);
313 close(pager_process
.org_err
);
315 /* restore original segnals setting */
316 sigaction(SIGINT
, &pager_process
.orig_sigint
, NULL
);
317 sigaction(SIGHUP
, &pager_process
.orig_sighup
, NULL
);
318 sigaction(SIGTERM
, &pager_process
.orig_sigterm
, NULL
);
319 sigaction(SIGQUIT
, &pager_process
.orig_sigquit
, NULL
);
320 sigaction(SIGPIPE
, &pager_process
.orig_sigpipe
, NULL
);
322 memset(&pager_process
, 0, sizeof(pager_process
));
325 #ifdef TEST_PROGRAM_PAGER
329 int main(int argc
__attribute__ ((__unused__
)),
330 char *argv
[] __attribute__ ((__unused__
)))
335 for (i
= 0; i
< MAX
; i
++)
339 #endif /* TEST_PROGRAM_PAGER */