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
);
89 execvp(cmd
->argv
[0], (char *const*) cmd
->argv
);
90 errexec(cmd
->argv
[0]);
96 else if (0 <= cmd
->in
)
103 else if (0 <= cmd
->in
)
108 static int wait_or_whine(pid_t pid
)
112 pid_t waiting
= waitpid(pid
, &status
, 0);
117 ul_sig_err(EXIT_FAILURE
, "waitpid failed");
121 if (WIFSIGNALED(status
))
124 if (!WIFEXITED(status
))
126 code
= WEXITSTATUS(status
);
138 static int finish_command(struct child_process
*cmd
)
140 return wait_or_whine(cmd
->pid
);
143 static void pager_preexec(void)
146 * Work around bug in "less" by not starting it until we
152 FD_SET(STDIN_FILENO
, &in
);
155 select(STDIN_FILENO
+ 1, &in
, NULL
, &ex
, NULL
);
157 if (setenv("LESS", "FRSX", 0) != 0)
158 warn(_("failed to set the %s environment variable"), "LESS");
161 static void wait_for_pager(void)
163 if (pager_process
.pid
== 0)
166 /* signal EOF to pager */
167 close(STDOUT_FILENO
);
168 close(STDERR_FILENO
);
169 finish_command(&pager_process
);
172 static void wait_for_pager_signal(int signo
)
179 static int has_command(const char *cmd
)
191 b
= strtok(c
, " "); /* cmd may contain options */
196 rc
= access(b
, X_OK
) == 0;
200 path
= getenv("PATH");
207 for (s
= strtok(p
, ":"); s
; s
= strtok(NULL
, ":")) {
208 int fd
= open(s
, O_RDONLY
|O_CLOEXEC
);
211 rc
= faccessat(fd
, b
, X_OK
, 0) == 0;
220 /*fprintf(stderr, "has PAGER '%s': rc=%d\n", cmd, rc);*/
224 static void __setup_pager(void)
226 const char *pager
= getenv("PAGER");
229 if (!isatty(STDOUT_FILENO
))
234 else if (!*pager
|| !strcmp(pager
, "cat"))
237 if (!has_command(pager
))
240 /* spawn the pager */
241 pager_argv
[2] = pager
;
242 pager_process
.argv
= pager_argv
;
243 pager_process
.in
= -1;
244 pager_process
.preexec_cb
= pager_preexec
;
246 if (start_command(&pager_process
))
249 /* original process continues, but writes to the pipe */
250 dup2(pager_process
.in
, STDOUT_FILENO
);
251 setvbuf(stdout
, NULL
, _IOLBF
, 0);
252 if (isatty(STDERR_FILENO
)) {
253 dup2(pager_process
.in
, STDERR_FILENO
);
254 setvbuf(stderr
, NULL
, _IOLBF
, 0);
256 close(pager_process
.in
);
258 memset(&sa
, 0, sizeof(sa
));
259 sa
.sa_handler
= wait_for_pager_signal
;
261 /* this makes sure that the parent terminates after the pager */
262 sigaction(SIGINT
, &sa
, &pager_process
.orig_sigint
);
263 sigaction(SIGHUP
, &sa
, &pager_process
.orig_sighup
);
264 sigaction(SIGTERM
, &sa
, &pager_process
.orig_sigterm
);
265 sigaction(SIGQUIT
, &sa
, &pager_process
.orig_sigquit
);
266 sigaction(SIGPIPE
, &sa
, &pager_process
.orig_sigpipe
);
269 /* Setup pager and redirects output to the $PAGER. The pager is closed at exit.
271 void pager_redirect(void)
273 if (pager_process
.pid
)
274 return; /* already running */
278 atexit(wait_for_pager
);
281 /* Setup pager and redirect output, the pager may be closed by pager_close().
283 void pager_open(void)
285 if (pager_process
.pid
)
286 return; /* already running */
288 pager_process
.org_out
= dup(STDOUT_FILENO
);
289 pager_process
.org_err
= dup(STDERR_FILENO
);
294 /* Close pager and restore original std{out,err}.
296 void pager_close(void)
298 if (pager_process
.pid
== 0)
303 /* restore original output */
304 dup2(pager_process
.org_out
, STDOUT_FILENO
);
305 dup2(pager_process
.org_err
, STDERR_FILENO
);
307 close(pager_process
.org_out
);
308 close(pager_process
.org_err
);
310 /* restore original segnals setting */
311 sigaction(SIGINT
, &pager_process
.orig_sigint
, NULL
);
312 sigaction(SIGHUP
, &pager_process
.orig_sighup
, NULL
);
313 sigaction(SIGTERM
, &pager_process
.orig_sigterm
, NULL
);
314 sigaction(SIGQUIT
, &pager_process
.orig_sigquit
, NULL
);
315 sigaction(SIGPIPE
, &pager_process
.orig_sigpipe
, NULL
);
317 memset(&pager_process
, 0, sizeof(pager_process
));
320 #ifdef TEST_PROGRAM_PAGER
324 int main(int argc
__attribute__ ((__unused__
)),
325 char *argv
[] __attribute__ ((__unused__
)))
330 for (i
= 0; i
< MAX
; i
++)
334 #endif /* TEST_PROGRAM_PAGER */