]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/pager.c
treewide: another round of simplifications
[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 42 if (n < 0) {
56f64d95 43 log_error_errno(errno, "Internal pager failed: %m");
4a8e40eb
MS
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
4a62c710
MS
70 if (pipe(fd) < 0)
71 return log_error_errno(errno, "Failed to create pager pipe: %m");
1968a360
LP
72
73 parent_pid = getpid();
74
75 pager_pid = fork();
76 if (pager_pid < 0) {
f89a3b6f 77 r = -errno;
56f64d95 78 log_error_errno(errno, "Failed to fork pager: %m");
3d94f76c 79 safe_close_pair(fd);
f89a3b6f 80 return r;
1968a360
LP
81 }
82
83 /* In the child start the pager */
84 if (pager_pid == 0) {
f366d58d 85 const char* less_opts;
1968a360
LP
86
87 dup2(fd[0], STDIN_FILENO);
3d94f76c 88 safe_close_pair(fd);
1968a360 89
f366d58d
JD
90 less_opts = getenv("SYSTEMD_LESS");
91 if (!less_opts)
92 less_opts = "FRSXMK";
1b12a7b5 93 if (jump_to_end)
f366d58d
JD
94 less_opts = strappenda(less_opts, " +G");
95 setenv("LESS", less_opts, 1);
1968a360
LP
96
97 /* Make sure the pager goes away when the parent dies */
98 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
99 _exit(EXIT_FAILURE);
100
101 /* Check whether our parent died before we were able
102 * to set the death signal */
103 if (getppid() != parent_pid)
104 _exit(EXIT_SUCCESS);
105
106 if (pager) {
107 execlp(pager, pager, NULL);
108 execl("/bin/sh", "sh", "-c", pager, NULL);
109 }
110
111 /* Debian's alternatives command for pagers is
112 * called 'pager'. Note that we do not call
113 * sensible-pagers here, since that is just a
114 * shell script that implements a logic that
115 * is similar to this one anyway, but is
116 * Debian-specific. */
117 execlp("pager", "pager", NULL);
118
119 execlp("less", "less", NULL);
120 execlp("more", "more", NULL);
1968a360 121
4a8e40eb
MS
122 pager_fallback();
123 /* not reached */
1968a360
LP
124 }
125
126 /* Return in the parent */
4a62c710
MS
127 if (dup2(fd[1], STDOUT_FILENO) < 0)
128 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
1968a360 129
3d94f76c 130 safe_close_pair(fd);
f89a3b6f 131 return 1;
1968a360
LP
132}
133
134void pager_close(void) {
135
136 if (pager_pid <= 0)
137 return;
138
139 /* Inform pager that we are done */
140 fclose(stdout);
141 kill(pager_pid, SIGCONT);
c73d180d 142 (void) wait_for_terminate(pager_pid, NULL);
1968a360
LP
143 pager_pid = 0;
144}
f89a3b6f
LP
145
146bool pager_have(void) {
147 return pager_pid > 0;
148}
78002a67
ZJS
149
150int show_man_page(const char *desc, bool null_stdio) {
151 const char *args[4] = { "man", NULL, NULL, NULL };
152 char *e = NULL;
153 pid_t pid;
154 size_t k;
155 int r;
156 siginfo_t status;
157
158 k = strlen(desc);
159
160 if (desc[k-1] == ')')
161 e = strrchr(desc, '(');
162
163 if (e) {
164 char *page = NULL, *section = NULL;
165
166 page = strndupa(desc, e - desc);
167 section = strndupa(e + 1, desc + k - e - 2);
168
169 args[1] = section;
170 args[2] = page;
171 } else
172 args[1] = desc;
173
174 pid = fork();
4a62c710
MS
175 if (pid < 0)
176 return log_error_errno(errno, "Failed to fork: %m");
78002a67
ZJS
177
178 if (pid == 0) {
179 /* Child */
180 if (null_stdio) {
181 r = make_null_stdio();
182 if (r < 0) {
da927ba9 183 log_error_errno(r, "Failed to kill stdio: %m");
78002a67
ZJS
184 _exit(EXIT_FAILURE);
185 }
186 }
187
188 execvp(args[0], (char**) args);
56f64d95 189 log_error_errno(errno, "Failed to execute man: %m");
78002a67
ZJS
190 _exit(EXIT_FAILURE);
191 }
192
193 r = wait_for_terminate(pid, &status);
194 if (r < 0)
195 return r;
196
197 log_debug("Exit code %i status %i", status.si_code, status.si_status);
198 return status.si_status;
199}