]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/pager.c
Merge pull request #2092 from poettering/dnssec2
[thirdparty/systemd.git] / src / shared / pager.c
CommitLineData
1968a360
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
1968a360
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
1968a360 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
1968a360
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
4a8e40eb 22#include <fcntl.h>
1968a360 23#include <stdlib.h>
1968a360
LP
24#include <string.h>
25#include <sys/prctl.h>
07630cea 26#include <unistd.h>
1968a360 27
07630cea 28#include "copy.h"
3ffd4af2 29#include "fd-util.h"
8752c575 30#include "locale-util.h"
1968a360 31#include "macro.h"
3ffd4af2 32#include "pager.h"
07630cea 33#include "process-util.h"
ce30c8dc 34#include "signal-util.h"
07630cea
LP
35#include "string-util.h"
36#include "terminal-util.h"
37#include "util.h"
1968a360
LP
38
39static pid_t pager_pid = 0;
40
919ce0b7 41noreturn static void pager_fallback(void) {
6a7c676c 42 int r;
46e65dcc 43
59f448cf 44 r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, false);
6a7c676c
LP
45 if (r < 0) {
46 log_error_errno(r, "Internal pager failed: %m");
4a8e40eb
MS
47 _exit(EXIT_FAILURE);
48 }
46e65dcc 49
4a8e40eb
MS
50 _exit(EXIT_SUCCESS);
51}
52
1b12a7b5 53int pager_open(bool jump_to_end) {
bcbd61db 54 _cleanup_close_pair_ int fd[2] = { -1, -1 };
1968a360
LP
55 const char *pager;
56 pid_t parent_pid;
57
58 if (pager_pid > 0)
f89a3b6f 59 return 1;
1968a360 60
8481248b 61 if (!on_tty())
f89a3b6f 62 return 0;
729e3769 63
bcbd61db
LP
64 pager = getenv("SYSTEMD_PAGER");
65 if (!pager)
66 pager = getenv("PAGER");
67
68 /* If the pager is explicitly turned off, honour it */
69 if (pager && (pager[0] == 0 || streq(pager, "cat")))
70 return 0;
71
1968a360
LP
72 /* Determine and cache number of columns before we spawn the
73 * pager so that we get the value from the actual tty */
bcbd61db 74 (void) columns();
1968a360 75
4a62c710
MS
76 if (pipe(fd) < 0)
77 return log_error_errno(errno, "Failed to create pager pipe: %m");
1968a360
LP
78
79 parent_pid = getpid();
80
81 pager_pid = fork();
bcbd61db 82 if (pager_pid < 0)
65359589 83 return log_error_errno(errno, "Failed to fork pager: %m");
1968a360
LP
84
85 /* In the child start the pager */
86 if (pager_pid == 0) {
a1b4e6e9 87 const char* less_opts, *less_charset;
1968a360 88
ce30c8dc
LP
89 (void) reset_all_signal_handlers();
90 (void) reset_signal_mask();
91
bcbd61db 92 (void) dup2(fd[0], STDIN_FILENO);
3d94f76c 93 safe_close_pair(fd);
1968a360 94
a1b4e6e9 95 /* Initialize a good set of less options */
f366d58d
JD
96 less_opts = getenv("SYSTEMD_LESS");
97 if (!less_opts)
98 less_opts = "FRSXMK";
1b12a7b5 99 if (jump_to_end)
63c372cb 100 less_opts = strjoina(less_opts, " +G");
f366d58d 101 setenv("LESS", less_opts, 1);
1968a360 102
a1b4e6e9
LP
103 /* Initialize a good charset for less. This is
104 * particularly important if we output UTF-8
105 * characters. */
106 less_charset = getenv("SYSTEMD_LESSCHARSET");
107 if (!less_charset && is_locale_utf8())
108 less_charset = "utf-8";
109 if (less_charset)
110 setenv("LESSCHARSET", less_charset, 1);
111
1968a360
LP
112 /* Make sure the pager goes away when the parent dies */
113 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
114 _exit(EXIT_FAILURE);
115
116 /* Check whether our parent died before we were able
117 * to set the death signal */
118 if (getppid() != parent_pid)
119 _exit(EXIT_SUCCESS);
120
121 if (pager) {
122 execlp(pager, pager, NULL);
123 execl("/bin/sh", "sh", "-c", pager, NULL);
124 }
125
126 /* Debian's alternatives command for pagers is
127 * called 'pager'. Note that we do not call
128 * sensible-pagers here, since that is just a
129 * shell script that implements a logic that
130 * is similar to this one anyway, but is
131 * Debian-specific. */
132 execlp("pager", "pager", NULL);
133
134 execlp("less", "less", NULL);
135 execlp("more", "more", NULL);
1968a360 136
4a8e40eb
MS
137 pager_fallback();
138 /* not reached */
1968a360
LP
139 }
140
141 /* Return in the parent */
4a62c710
MS
142 if (dup2(fd[1], STDOUT_FILENO) < 0)
143 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
8b5264aa
LP
144 if (dup2(fd[1], STDERR_FILENO) < 0)
145 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
1968a360 146
f89a3b6f 147 return 1;
1968a360
LP
148}
149
150void pager_close(void) {
151
152 if (pager_pid <= 0)
153 return;
154
155 /* Inform pager that we are done */
74ca738f
LP
156 stdout = safe_fclose(stdout);
157 stderr = safe_fclose(stderr);
8b5264aa 158
74ca738f 159 (void) kill(pager_pid, SIGCONT);
c73d180d 160 (void) wait_for_terminate(pager_pid, NULL);
1968a360
LP
161 pager_pid = 0;
162}
f89a3b6f
LP
163
164bool pager_have(void) {
165 return pager_pid > 0;
166}
78002a67
ZJS
167
168int show_man_page(const char *desc, bool null_stdio) {
169 const char *args[4] = { "man", NULL, NULL, NULL };
170 char *e = NULL;
171 pid_t pid;
172 size_t k;
173 int r;
174 siginfo_t status;
175
176 k = strlen(desc);
177
178 if (desc[k-1] == ')')
179 e = strrchr(desc, '(');
180
181 if (e) {
182 char *page = NULL, *section = NULL;
183
184 page = strndupa(desc, e - desc);
185 section = strndupa(e + 1, desc + k - e - 2);
186
187 args[1] = section;
188 args[2] = page;
189 } else
190 args[1] = desc;
191
192 pid = fork();
4a62c710
MS
193 if (pid < 0)
194 return log_error_errno(errno, "Failed to fork: %m");
78002a67
ZJS
195
196 if (pid == 0) {
197 /* Child */
ce30c8dc
LP
198
199 (void) reset_all_signal_handlers();
200 (void) reset_signal_mask();
201
78002a67
ZJS
202 if (null_stdio) {
203 r = make_null_stdio();
204 if (r < 0) {
da927ba9 205 log_error_errno(r, "Failed to kill stdio: %m");
78002a67
ZJS
206 _exit(EXIT_FAILURE);
207 }
208 }
209
210 execvp(args[0], (char**) args);
56f64d95 211 log_error_errno(errno, "Failed to execute man: %m");
78002a67
ZJS
212 _exit(EXIT_FAILURE);
213 }
214
215 r = wait_for_terminate(pid, &status);
216 if (r < 0)
217 return r;
218
219 log_debug("Exit code %i status %i", status.si_code, status.si_status);
220 return status.si_status;
221}