Branch data Line data Source code
1 : : /*
2 : : * libdpkg - Debian packaging suite library routines
3 : : * pager.c - pager execution support
4 : : *
5 : : * Copyright © 2018 Guillem Jover <guillem@debian.org>
6 : : *
7 : : * This is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU General Public License as published by
9 : : * the Free Software Foundation; either version 2 of the License, or
10 : : * (at your option) any later version.
11 : : *
12 : : * This is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : : * GNU General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU General Public License
18 : : * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 : : */
20 : :
21 : : #include <config.h>
22 : : #include <compat.h>
23 : :
24 : : #include <sys/types.h>
25 : :
26 : : #include <stdbool.h>
27 : : #include <stdlib.h>
28 : : #include <string.h>
29 : : #include <signal.h>
30 : : #include <unistd.h>
31 : :
32 : : #include <dpkg/dpkg.h>
33 : : #include <dpkg/i18n.h>
34 : : #include <dpkg/string.h>
35 : : #include <dpkg/subproc.h>
36 : : #include <dpkg/command.h>
37 : : #include <dpkg/pager.h>
38 : :
39 : : static bool pager_enabled = true;
40 : :
41 : : void
42 : 0 : pager_enable(bool enable)
43 : : {
44 : 0 : pager_enabled = enable;
45 : 0 : }
46 : :
47 : : /**
48 : : * Get a suitable pager.
49 : : *
50 : : * @return A string representing a pager.
51 : : */
52 : : const char *
53 : 3 : pager_get_exec(void)
54 : : {
55 : : const char *pager;
56 : :
57 [ + - + + ]: 3 : if (!isatty(0) || !isatty(1))
58 : 1 : return CAT;
59 : :
60 : 2 : pager = getenv("DPKG_PAGER");
61 [ + - ]: 2 : if (str_is_unset(pager))
62 : 2 : pager = getenv("PAGER");
63 [ + + ]: 2 : if (str_is_unset(pager))
64 : 1 : pager = DPKG_DEFAULT_PAGER;
65 : :
66 : 2 : return pager;
67 : : }
68 : :
69 : : struct pager {
70 : : bool used;
71 : : const char *desc;
72 : : pid_t pid;
73 : : struct sigaction sigpipe;
74 : : int stdout_old;
75 : : int pipe[2];
76 : : };
77 : :
78 : : struct pager *
79 : 0 : pager_spawn(const char *desc)
80 : : {
81 : : struct sigaction sa;
82 : : struct pager *pager;
83 : : const char *exec;
84 : :
85 : 0 : pager = m_calloc(1, sizeof(*pager));
86 [ # # # # ]: 0 : pager->used = isatty(0) && isatty(1);
87 : 0 : pager->desc = desc;
88 : :
89 : 0 : exec = pager_get_exec();
90 [ # # ]: 0 : if (strcmp(exec, CAT) == 0)
91 : 0 : pager->used = false;
92 : :
93 [ # # ]: 0 : if (!pager_enabled)
94 : 0 : pager->used = false;
95 : :
96 [ # # ]: 0 : if (!pager->used)
97 : 0 : return pager;
98 : :
99 : 0 : m_pipe(pager->pipe);
100 : :
101 : 0 : memset(&sa, 0, sizeof(sa));
102 : 0 : sigemptyset(&sa.sa_mask);
103 : 0 : sa.sa_handler = SIG_IGN;
104 : 0 : sa.sa_flags = 0;
105 : :
106 : 0 : sigaction(SIGPIPE, &sa, &pager->sigpipe);
107 : :
108 : 0 : pager->pid = subproc_fork();
109 [ # # ]: 0 : if (pager->pid == 0) {
110 : : /* Set better defaults for less if not already set. */
111 : 0 : setenv("LESS", "-FRSXMQ", 0);
112 : :
113 : 0 : m_dup2(pager->pipe[0], 0);
114 : 0 : close(pager->pipe[0]);
115 : 0 : close(pager->pipe[1]);
116 : :
117 : 0 : command_shell(exec, desc);
118 : : }
119 : :
120 : 0 : pager->stdout_old = m_dup(1);
121 : 0 : m_dup2(pager->pipe[1], 1);
122 : 0 : close(pager->pipe[0]);
123 : 0 : close(pager->pipe[1]);
124 : :
125 : : /* Force the output to fully buffered, because originally stdout was
126 : : * a tty, so it was set as line buffered. This way we send as much as
127 : : * possible to the pager, which will handle the output by itself. */
128 : 0 : setvbuf(stdout, NULL, _IOFBF, 0);
129 : :
130 : 0 : return pager;
131 : : }
132 : :
133 : : void
134 : 0 : pager_reap(struct pager *pager)
135 : : {
136 [ # # ]: 0 : if (!pager->used)
137 : 0 : return;
138 : :
139 : 0 : m_dup2(pager->stdout_old, 1);
140 : 0 : subproc_reap(pager->pid, pager->desc, SUBPROC_NOPIPE);
141 : :
142 : 0 : sigaction(SIGPIPE, &pager->sigpipe, NULL);
143 : :
144 : 0 : free(pager);
145 : : }
|