]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/pager.c
sd-*.h: clean up exported (or to-be-exported) header files
[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"
1968a360 29#include "macro.h"
07630cea 30#include "process-util.h"
ce30c8dc 31#include "signal-util.h"
07630cea
LP
32#include "string-util.h"
33#include "terminal-util.h"
34#include "util.h"
35#include "pager.h"
1968a360
LP
36
37static pid_t pager_pid = 0;
38
919ce0b7 39noreturn static void pager_fallback(void) {
6a7c676c 40 int r;
46e65dcc 41
59f448cf 42 r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, false);
6a7c676c
LP
43 if (r < 0) {
44 log_error_errno(r, "Internal pager failed: %m");
4a8e40eb
MS
45 _exit(EXIT_FAILURE);
46 }
46e65dcc 47
4a8e40eb
MS
48 _exit(EXIT_SUCCESS);
49}
50
1b12a7b5 51int pager_open(bool jump_to_end) {
bcbd61db 52 _cleanup_close_pair_ int fd[2] = { -1, -1 };
1968a360
LP
53 const char *pager;
54 pid_t parent_pid;
55
56 if (pager_pid > 0)
f89a3b6f 57 return 1;
1968a360 58
8481248b 59 if (!on_tty())
f89a3b6f 60 return 0;
729e3769 61
bcbd61db
LP
62 pager = getenv("SYSTEMD_PAGER");
63 if (!pager)
64 pager = getenv("PAGER");
65
66 /* If the pager is explicitly turned off, honour it */
67 if (pager && (pager[0] == 0 || streq(pager, "cat")))
68 return 0;
69
1968a360
LP
70 /* Determine and cache number of columns before we spawn the
71 * pager so that we get the value from the actual tty */
bcbd61db 72 (void) columns();
1968a360 73
4a62c710
MS
74 if (pipe(fd) < 0)
75 return log_error_errno(errno, "Failed to create pager pipe: %m");
1968a360
LP
76
77 parent_pid = getpid();
78
79 pager_pid = fork();
bcbd61db 80 if (pager_pid < 0)
65359589 81 return log_error_errno(errno, "Failed to fork pager: %m");
1968a360
LP
82
83 /* In the child start the pager */
84 if (pager_pid == 0) {
a1b4e6e9 85 const char* less_opts, *less_charset;
1968a360 86
ce30c8dc
LP
87 (void) reset_all_signal_handlers();
88 (void) reset_signal_mask();
89
bcbd61db 90 (void) dup2(fd[0], STDIN_FILENO);
3d94f76c 91 safe_close_pair(fd);
1968a360 92
a1b4e6e9 93 /* Initialize a good set of less options */
f366d58d
JD
94 less_opts = getenv("SYSTEMD_LESS");
95 if (!less_opts)
96 less_opts = "FRSXMK";
1b12a7b5 97 if (jump_to_end)
63c372cb 98 less_opts = strjoina(less_opts, " +G");
f366d58d 99 setenv("LESS", less_opts, 1);
1968a360 100
a1b4e6e9
LP
101 /* Initialize a good charset for less. This is
102 * particularly important if we output UTF-8
103 * characters. */
104 less_charset = getenv("SYSTEMD_LESSCHARSET");
105 if (!less_charset && is_locale_utf8())
106 less_charset = "utf-8";
107 if (less_charset)
108 setenv("LESSCHARSET", less_charset, 1);
109
1968a360
LP
110 /* Make sure the pager goes away when the parent dies */
111 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
112 _exit(EXIT_FAILURE);
113
114 /* Check whether our parent died before we were able
115 * to set the death signal */
116 if (getppid() != parent_pid)
117 _exit(EXIT_SUCCESS);
118
119 if (pager) {
120 execlp(pager, pager, NULL);
121 execl("/bin/sh", "sh", "-c", pager, NULL);
122 }
123
124 /* Debian's alternatives command for pagers is
125 * called 'pager'. Note that we do not call
126 * sensible-pagers here, since that is just a
127 * shell script that implements a logic that
128 * is similar to this one anyway, but is
129 * Debian-specific. */
130 execlp("pager", "pager", NULL);
131
132 execlp("less", "less", NULL);
133 execlp("more", "more", NULL);
1968a360 134
4a8e40eb
MS
135 pager_fallback();
136 /* not reached */
1968a360
LP
137 }
138
139 /* Return in the parent */
4a62c710
MS
140 if (dup2(fd[1], STDOUT_FILENO) < 0)
141 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
8b5264aa
LP
142 if (dup2(fd[1], STDERR_FILENO) < 0)
143 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
1968a360 144
f89a3b6f 145 return 1;
1968a360
LP
146}
147
148void pager_close(void) {
149
150 if (pager_pid <= 0)
151 return;
152
153 /* Inform pager that we are done */
74ca738f
LP
154 stdout = safe_fclose(stdout);
155 stderr = safe_fclose(stderr);
8b5264aa 156
74ca738f 157 (void) kill(pager_pid, SIGCONT);
c73d180d 158 (void) wait_for_terminate(pager_pid, NULL);
1968a360
LP
159 pager_pid = 0;
160}
f89a3b6f
LP
161
162bool pager_have(void) {
163 return pager_pid > 0;
164}
78002a67
ZJS
165
166int show_man_page(const char *desc, bool null_stdio) {
167 const char *args[4] = { "man", NULL, NULL, NULL };
168 char *e = NULL;
169 pid_t pid;
170 size_t k;
171 int r;
172 siginfo_t status;
173
174 k = strlen(desc);
175
176 if (desc[k-1] == ')')
177 e = strrchr(desc, '(');
178
179 if (e) {
180 char *page = NULL, *section = NULL;
181
182 page = strndupa(desc, e - desc);
183 section = strndupa(e + 1, desc + k - e - 2);
184
185 args[1] = section;
186 args[2] = page;
187 } else
188 args[1] = desc;
189
190 pid = fork();
4a62c710
MS
191 if (pid < 0)
192 return log_error_errno(errno, "Failed to fork: %m");
78002a67
ZJS
193
194 if (pid == 0) {
195 /* Child */
ce30c8dc
LP
196
197 (void) reset_all_signal_handlers();
198 (void) reset_signal_mask();
199
78002a67
ZJS
200 if (null_stdio) {
201 r = make_null_stdio();
202 if (r < 0) {
da927ba9 203 log_error_errno(r, "Failed to kill stdio: %m");
78002a67
ZJS
204 _exit(EXIT_FAILURE);
205 }
206 }
207
208 execvp(args[0], (char**) args);
56f64d95 209 log_error_errno(errno, "Failed to execute man: %m");
78002a67
ZJS
210 _exit(EXIT_FAILURE);
211 }
212
213 r = wait_for_terminate(pid, &status);
214 if (r < 0)
215 return r;
216
217 log_debug("Exit code %i status %i", status.si_code, status.si_status);
218 return status.si_status;
219}