]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/shared/pager.c
unit: unify how we assing slices to units
[thirdparty/systemd.git] / src / shared / pager.c
... / ...
CommitLineData
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <fcntl.h>
23#include <stdlib.h>
24#include <unistd.h>
25#include <string.h>
26#include <sys/prctl.h>
27
28#include "pager.h"
29#include "util.h"
30#include "process-util.h"
31#include "macro.h"
32#include "terminal-util.h"
33#include "signal-util.h"
34#include "copy.h"
35
36static pid_t pager_pid = 0;
37
38noreturn static void pager_fallback(void) {
39 int r;
40
41 r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (off_t) -1, false);
42 if (r < 0) {
43 log_error_errno(r, "Internal pager failed: %m");
44 _exit(EXIT_FAILURE);
45 }
46
47 _exit(EXIT_SUCCESS);
48}
49
50int pager_open(bool jump_to_end) {
51 int fd[2];
52 const char *pager;
53 pid_t parent_pid;
54 int r;
55
56 if (pager_pid > 0)
57 return 1;
58
59 if ((pager = getenv("SYSTEMD_PAGER")) || (pager = getenv("PAGER")))
60 if (!*pager || streq(pager, "cat"))
61 return 0;
62
63 if (!on_tty())
64 return 0;
65
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 return log_error_errno(errno, "Failed to create pager pipe: %m");
72
73 parent_pid = getpid();
74
75 pager_pid = fork();
76 if (pager_pid < 0) {
77 r = -errno;
78 log_error_errno(errno, "Failed to fork pager: %m");
79 safe_close_pair(fd);
80 return r;
81 }
82
83 /* In the child start the pager */
84 if (pager_pid == 0) {
85 const char* less_opts;
86
87 (void) reset_all_signal_handlers();
88 (void) reset_signal_mask();
89
90 dup2(fd[0], STDIN_FILENO);
91 safe_close_pair(fd);
92
93 less_opts = getenv("SYSTEMD_LESS");
94 if (!less_opts)
95 less_opts = "FRSXMK";
96 if (jump_to_end)
97 less_opts = strjoina(less_opts, " +G");
98 setenv("LESS", less_opts, 1);
99
100 /* Make sure the pager goes away when the parent dies */
101 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
102 _exit(EXIT_FAILURE);
103
104 /* Check whether our parent died before we were able
105 * to set the death signal */
106 if (getppid() != parent_pid)
107 _exit(EXIT_SUCCESS);
108
109 if (pager) {
110 execlp(pager, pager, NULL);
111 execl("/bin/sh", "sh", "-c", pager, NULL);
112 }
113
114 /* Debian's alternatives command for pagers is
115 * called 'pager'. Note that we do not call
116 * sensible-pagers here, since that is just a
117 * shell script that implements a logic that
118 * is similar to this one anyway, but is
119 * Debian-specific. */
120 execlp("pager", "pager", NULL);
121
122 execlp("less", "less", NULL);
123 execlp("more", "more", NULL);
124
125 pager_fallback();
126 /* not reached */
127 }
128
129 /* Return in the parent */
130 if (dup2(fd[1], STDOUT_FILENO) < 0)
131 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
132 if (dup2(fd[1], STDERR_FILENO) < 0)
133 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
134
135 safe_close_pair(fd);
136 return 1;
137}
138
139void pager_close(void) {
140
141 if (pager_pid <= 0)
142 return;
143
144 /* Inform pager that we are done */
145 fclose(stdout);
146 stdout = NULL;
147
148 fclose(stderr);
149 stderr = NULL;
150
151 kill(pager_pid, SIGCONT);
152 (void) wait_for_terminate(pager_pid, NULL);
153 pager_pid = 0;
154}
155
156bool pager_have(void) {
157 return pager_pid > 0;
158}
159
160int show_man_page(const char *desc, bool null_stdio) {
161 const char *args[4] = { "man", NULL, NULL, NULL };
162 char *e = NULL;
163 pid_t pid;
164 size_t k;
165 int r;
166 siginfo_t status;
167
168 k = strlen(desc);
169
170 if (desc[k-1] == ')')
171 e = strrchr(desc, '(');
172
173 if (e) {
174 char *page = NULL, *section = NULL;
175
176 page = strndupa(desc, e - desc);
177 section = strndupa(e + 1, desc + k - e - 2);
178
179 args[1] = section;
180 args[2] = page;
181 } else
182 args[1] = desc;
183
184 pid = fork();
185 if (pid < 0)
186 return log_error_errno(errno, "Failed to fork: %m");
187
188 if (pid == 0) {
189 /* Child */
190
191 (void) reset_all_signal_handlers();
192 (void) reset_signal_mask();
193
194 if (null_stdio) {
195 r = make_null_stdio();
196 if (r < 0) {
197 log_error_errno(r, "Failed to kill stdio: %m");
198 _exit(EXIT_FAILURE);
199 }
200 }
201
202 execvp(args[0], (char**) args);
203 log_error_errno(errno, "Failed to execute man: %m");
204 _exit(EXIT_FAILURE);
205 }
206
207 r = wait_for_terminate(pid, &status);
208 if (r < 0)
209 return r;
210
211 log_debug("Exit code %i status %i", status.si_code, status.si_status);
212 return status.si_status;
213}