]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/pager.c
process-spec: add another flag FORK_WAIT to safe_fork()
[thirdparty/systemd.git] / src / shared / pager.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
1968a360
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
1968a360
LP
10 (at your option) any later version.
11
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
5430f7f2 15 Lesser General Public License for more details.
1968a360 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
1968a360
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
a8fbdf54
TA
21#include <errno.h>
22#include <signal.h>
23#include <stddef.h>
24#include <stdint.h>
25#include <stdio.h>
1968a360 26#include <stdlib.h>
1968a360
LP
27#include <string.h>
28#include <sys/prctl.h>
07630cea 29#include <unistd.h>
1968a360 30
07630cea 31#include "copy.h"
3ffd4af2 32#include "fd-util.h"
8752c575 33#include "locale-util.h"
a8fbdf54 34#include "log.h"
1968a360 35#include "macro.h"
3ffd4af2 36#include "pager.h"
07630cea 37#include "process-util.h"
ce30c8dc 38#include "signal-util.h"
07630cea 39#include "string-util.h"
57c9e047 40#include "strv.h"
07630cea 41#include "terminal-util.h"
1968a360
LP
42
43static pid_t pager_pid = 0;
44
919ce0b7 45noreturn static void pager_fallback(void) {
6a7c676c 46 int r;
46e65dcc 47
1c876927 48 r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, 0);
6a7c676c
LP
49 if (r < 0) {
50 log_error_errno(r, "Internal pager failed: %m");
4a8e40eb
MS
51 _exit(EXIT_FAILURE);
52 }
46e65dcc 53
4a8e40eb
MS
54 _exit(EXIT_SUCCESS);
55}
56
a45e7bb4
MS
57static int stored_stdout = -1;
58static int stored_stderr = -1;
59static bool stdout_redirected = false;
60static bool stderr_redirected = false;
61
ea4b98e6 62int pager_open(bool no_pager, bool jump_to_end) {
bcbd61db 63 _cleanup_close_pair_ int fd[2] = { -1, -1 };
1968a360 64 const char *pager;
4c253ed1 65 int r;
1968a360 66
ea4b98e6
AK
67 if (no_pager)
68 return 0;
69
1968a360 70 if (pager_pid > 0)
f89a3b6f 71 return 1;
1968a360 72
ac96418b 73 if (terminal_is_dumb())
f89a3b6f 74 return 0;
729e3769 75
bcbd61db
LP
76 pager = getenv("SYSTEMD_PAGER");
77 if (!pager)
78 pager = getenv("PAGER");
79
80 /* If the pager is explicitly turned off, honour it */
57c9e047 81 if (pager && STR_IN_SET(pager, "", "cat"))
bcbd61db
LP
82 return 0;
83
d13b5227
LP
84 /* Determine and cache number of columns/lines before we spawn the pager so that we get the value from the
85 * actual tty */
bcbd61db 86 (void) columns();
d13b5227 87 (void) lines();
1968a360 88
d262e99e 89 if (pipe2(fd, O_CLOEXEC) < 0)
4a62c710 90 return log_error_errno(errno, "Failed to create pager pipe: %m");
1968a360 91
b6e1fff1 92 r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pager_pid);
4c253ed1 93 if (r < 0)
b6e1fff1 94 return r;
4c253ed1 95 if (r == 0) {
a1b4e6e9 96 const char* less_opts, *less_charset;
1968a360 97
4c253ed1 98 /* In the child start the pager */
ce30c8dc 99
bcbd61db 100 (void) dup2(fd[0], STDIN_FILENO);
3d94f76c 101 safe_close_pair(fd);
1968a360 102
a1b4e6e9 103 /* Initialize a good set of less options */
f366d58d
JD
104 less_opts = getenv("SYSTEMD_LESS");
105 if (!less_opts)
106 less_opts = "FRSXMK";
1b12a7b5 107 if (jump_to_end)
63c372cb 108 less_opts = strjoina(less_opts, " +G");
0357fa0d
ZJS
109 if (setenv("LESS", less_opts, 1) < 0)
110 _exit(EXIT_FAILURE);
1968a360 111
a1b4e6e9
LP
112 /* Initialize a good charset for less. This is
113 * particularly important if we output UTF-8
114 * characters. */
115 less_charset = getenv("SYSTEMD_LESSCHARSET");
116 if (!less_charset && is_locale_utf8())
117 less_charset = "utf-8";
0357fa0d
ZJS
118 if (less_charset &&
119 setenv("LESSCHARSET", less_charset, 1) < 0)
120 _exit(EXIT_FAILURE);
a1b4e6e9 121
1968a360
LP
122 if (pager) {
123 execlp(pager, pager, NULL);
124 execl("/bin/sh", "sh", "-c", pager, NULL);
125 }
126
127 /* Debian's alternatives command for pagers is
128 * called 'pager'. Note that we do not call
129 * sensible-pagers here, since that is just a
130 * shell script that implements a logic that
131 * is similar to this one anyway, but is
132 * Debian-specific. */
133 execlp("pager", "pager", NULL);
134
135 execlp("less", "less", NULL);
136 execlp("more", "more", NULL);
1968a360 137
4a8e40eb
MS
138 pager_fallback();
139 /* not reached */
1968a360
LP
140 }
141
142 /* Return in the parent */
a45e7bb4
MS
143 stored_stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 3);
144 if (dup2(fd[1], STDOUT_FILENO) < 0) {
145 stored_stdout = safe_close(stored_stdout);
4a62c710 146 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
a45e7bb4
MS
147 }
148 stdout_redirected = true;
149
150 stored_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3);
151 if (dup2(fd[1], STDERR_FILENO) < 0) {
152 stored_stderr = safe_close(stored_stderr);
8b5264aa 153 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
a45e7bb4
MS
154 }
155 stderr_redirected = true;
1968a360 156
f89a3b6f 157 return 1;
1968a360
LP
158}
159
160void pager_close(void) {
161
162 if (pager_pid <= 0)
163 return;
164
165 /* Inform pager that we are done */
a45e7bb4 166 (void) fflush(stdout);
77018a8c
MS
167 if (stdout_redirected)
168 if (stored_stdout < 0 || dup2(stored_stdout, STDOUT_FILENO) < 0)
169 (void) close(STDOUT_FILENO);
a45e7bb4
MS
170 stored_stdout = safe_close(stored_stdout);
171 (void) fflush(stderr);
77018a8c
MS
172 if (stderr_redirected)
173 if (stored_stderr < 0 || dup2(stored_stderr, STDERR_FILENO) < 0)
174 (void) close(STDERR_FILENO);
a45e7bb4
MS
175 stored_stderr = safe_close(stored_stderr);
176 stdout_redirected = stderr_redirected = false;
8b5264aa 177
74ca738f 178 (void) kill(pager_pid, SIGCONT);
c73d180d 179 (void) wait_for_terminate(pager_pid, NULL);
1968a360
LP
180 pager_pid = 0;
181}
f89a3b6f
LP
182
183bool pager_have(void) {
184 return pager_pid > 0;
185}
78002a67
ZJS
186
187int show_man_page(const char *desc, bool null_stdio) {
188 const char *args[4] = { "man", NULL, NULL, NULL };
189 char *e = NULL;
190 pid_t pid;
191 size_t k;
192 int r;
193 siginfo_t status;
194
195 k = strlen(desc);
196
197 if (desc[k-1] == ')')
198 e = strrchr(desc, '(');
199
200 if (e) {
201 char *page = NULL, *section = NULL;
202
203 page = strndupa(desc, e - desc);
204 section = strndupa(e + 1, desc + k - e - 2);
205
206 args[1] = section;
207 args[2] = page;
208 } else
209 args[1] = desc;
210
b6e1fff1 211 r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_NULL_STDIO : 0)|FORK_LOG, &pid);
4c253ed1 212 if (r < 0)
b6e1fff1 213 return r;
4c253ed1 214 if (r == 0) {
78002a67 215 /* Child */
78002a67 216 execvp(args[0], (char**) args);
56f64d95 217 log_error_errno(errno, "Failed to execute man: %m");
78002a67
ZJS
218 _exit(EXIT_FAILURE);
219 }
220
221 r = wait_for_terminate(pid, &status);
222 if (r < 0)
223 return r;
224
225 log_debug("Exit code %i status %i", status.si_code, status.si_status);
226 return status.si_status;
227}