]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/pager.c
man: improve documentation for "indirect" unit file state a bit
[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
22#include <sys/types.h>
4a8e40eb 23#include <fcntl.h>
1968a360
LP
24#include <stdlib.h>
25#include <unistd.h>
26#include <string.h>
27#include <sys/prctl.h>
28
29#include "pager.h"
30#include "util.h"
31#include "macro.h"
32
33static pid_t pager_pid = 0;
34
919ce0b7 35noreturn static void pager_fallback(void) {
4a8e40eb 36 ssize_t n;
46e65dcc 37
4a8e40eb
MS
38 do {
39 n = splice(STDIN_FILENO, NULL, STDOUT_FILENO, NULL, 64*1024, 0);
40 } while (n > 0);
46e65dcc 41
4a8e40eb
MS
42 if (n < 0) {
43 log_error("Internal pager failed: %m");
44 _exit(EXIT_FAILURE);
45 }
46e65dcc 46
4a8e40eb
MS
47 _exit(EXIT_SUCCESS);
48}
49
1b12a7b5 50int pager_open(bool jump_to_end) {
1968a360
LP
51 int fd[2];
52 const char *pager;
53 pid_t parent_pid;
f89a3b6f 54 int r;
1968a360
LP
55
56 if (pager_pid > 0)
f89a3b6f 57 return 1;
1968a360 58
1968a360
LP
59 if ((pager = getenv("SYSTEMD_PAGER")) || (pager = getenv("PAGER")))
60 if (!*pager || streq(pager, "cat"))
f89a3b6f 61 return 0;
1968a360 62
8481248b 63 if (!on_tty())
f89a3b6f 64 return 0;
729e3769 65
1968a360
LP
66 /* Determine and cache number of columns before we spawn the
67 * pager so that we get the value from the actual tty */
68 columns();
69
70 if (pipe(fd) < 0) {
71 log_error("Failed to create pager pipe: %m");
f89a3b6f 72 return -errno;
1968a360
LP
73 }
74
75 parent_pid = getpid();
76
77 pager_pid = fork();
78 if (pager_pid < 0) {
f89a3b6f 79 r = -errno;
1968a360 80 log_error("Failed to fork pager: %m");
3d94f76c 81 safe_close_pair(fd);
f89a3b6f 82 return r;
1968a360
LP
83 }
84
85 /* In the child start the pager */
86 if (pager_pid == 0) {
f366d58d 87 const char* less_opts;
1968a360
LP
88
89 dup2(fd[0], STDIN_FILENO);
3d94f76c 90 safe_close_pair(fd);
1968a360 91
f366d58d
JD
92 less_opts = getenv("SYSTEMD_LESS");
93 if (!less_opts)
94 less_opts = "FRSXMK";
1b12a7b5 95 if (jump_to_end)
f366d58d
JD
96 less_opts = strappenda(less_opts, " +G");
97 setenv("LESS", less_opts, 1);
1968a360
LP
98
99 /* Make sure the pager goes away when the parent dies */
100 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
101 _exit(EXIT_FAILURE);
102
103 /* Check whether our parent died before we were able
104 * to set the death signal */
105 if (getppid() != parent_pid)
106 _exit(EXIT_SUCCESS);
107
108 if (pager) {
109 execlp(pager, pager, NULL);
110 execl("/bin/sh", "sh", "-c", pager, NULL);
111 }
112
113 /* Debian's alternatives command for pagers is
114 * called 'pager'. Note that we do not call
115 * sensible-pagers here, since that is just a
116 * shell script that implements a logic that
117 * is similar to this one anyway, but is
118 * Debian-specific. */
119 execlp("pager", "pager", NULL);
120
121 execlp("less", "less", NULL);
122 execlp("more", "more", NULL);
1968a360 123
4a8e40eb
MS
124 pager_fallback();
125 /* not reached */
1968a360
LP
126 }
127
128 /* Return in the parent */
fafb6ecc 129 if (dup2(fd[1], STDOUT_FILENO) < 0) {
1968a360 130 log_error("Failed to duplicate pager pipe: %m");
f89a3b6f 131 return -errno;
fafb6ecc 132 }
1968a360 133
3d94f76c 134 safe_close_pair(fd);
f89a3b6f 135 return 1;
1968a360
LP
136}
137
138void pager_close(void) {
139
140 if (pager_pid <= 0)
141 return;
142
143 /* Inform pager that we are done */
144 fclose(stdout);
145 kill(pager_pid, SIGCONT);
146 wait_for_terminate(pager_pid, NULL);
147 pager_pid = 0;
148}
f89a3b6f
LP
149
150bool pager_have(void) {
151 return pager_pid > 0;
152}
78002a67
ZJS
153
154int show_man_page(const char *desc, bool null_stdio) {
155 const char *args[4] = { "man", NULL, NULL, NULL };
156 char *e = NULL;
157 pid_t pid;
158 size_t k;
159 int r;
160 siginfo_t status;
161
162 k = strlen(desc);
163
164 if (desc[k-1] == ')')
165 e = strrchr(desc, '(');
166
167 if (e) {
168 char *page = NULL, *section = NULL;
169
170 page = strndupa(desc, e - desc);
171 section = strndupa(e + 1, desc + k - e - 2);
172
173 args[1] = section;
174 args[2] = page;
175 } else
176 args[1] = desc;
177
178 pid = fork();
179 if (pid < 0) {
180 log_error("Failed to fork: %m");
181 return -errno;
182 }
183
184 if (pid == 0) {
185 /* Child */
186 if (null_stdio) {
187 r = make_null_stdio();
188 if (r < 0) {
189 log_error("Failed to kill stdio: %s", strerror(-r));
190 _exit(EXIT_FAILURE);
191 }
192 }
193
194 execvp(args[0], (char**) args);
195 log_error("Failed to execute man: %m");
196 _exit(EXIT_FAILURE);
197 }
198
199 r = wait_for_terminate(pid, &status);
200 if (r < 0)
201 return r;
202
203 log_debug("Exit code %i status %i", status.si_code, status.si_status);
204 return status.si_status;
205}