]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/pager.c
unit: unify how we assing slices to units
[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
LP
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"
0b452006 30#include "process-util.h"
1968a360 31#include "macro.h"
288a74cc 32#include "terminal-util.h"
ce30c8dc 33#include "signal-util.h"
6a7c676c 34#include "copy.h"
1968a360
LP
35
36static pid_t pager_pid = 0;
37
919ce0b7 38noreturn static void pager_fallback(void) {
6a7c676c 39 int r;
46e65dcc 40
6a7c676c
LP
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");
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 86
ce30c8dc
LP
87 (void) reset_all_signal_handlers();
88 (void) reset_signal_mask();
89
1968a360 90 dup2(fd[0], STDIN_FILENO);
3d94f76c 91 safe_close_pair(fd);
1968a360 92
f366d58d
JD
93 less_opts = getenv("SYSTEMD_LESS");
94 if (!less_opts)
95 less_opts = "FRSXMK";
1b12a7b5 96 if (jump_to_end)
63c372cb 97 less_opts = strjoina(less_opts, " +G");
f366d58d 98 setenv("LESS", less_opts, 1);
1968a360
LP
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);
1968a360 124
4a8e40eb
MS
125 pager_fallback();
126 /* not reached */
1968a360
LP
127 }
128
129 /* Return in the parent */
4a62c710
MS
130 if (dup2(fd[1], STDOUT_FILENO) < 0)
131 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
8b5264aa
LP
132 if (dup2(fd[1], STDERR_FILENO) < 0)
133 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
1968a360 134
3d94f76c 135 safe_close_pair(fd);
f89a3b6f 136 return 1;
1968a360
LP
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);
8b5264aa
LP
146 stdout = NULL;
147
148 fclose(stderr);
149 stderr = NULL;
150
1968a360 151 kill(pager_pid, SIGCONT);
c73d180d 152 (void) wait_for_terminate(pager_pid, NULL);
1968a360
LP
153 pager_pid = 0;
154}
f89a3b6f
LP
155
156bool pager_have(void) {
157 return pager_pid > 0;
158}
78002a67
ZJS
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();
4a62c710
MS
185 if (pid < 0)
186 return log_error_errno(errno, "Failed to fork: %m");
78002a67
ZJS
187
188 if (pid == 0) {
189 /* Child */
ce30c8dc
LP
190
191 (void) reset_all_signal_handlers();
192 (void) reset_signal_mask();
193
78002a67
ZJS
194 if (null_stdio) {
195 r = make_null_stdio();
196 if (r < 0) {
da927ba9 197 log_error_errno(r, "Failed to kill stdio: %m");
78002a67
ZJS
198 _exit(EXIT_FAILURE);
199 }
200 }
201
202 execvp(args[0], (char**) args);
56f64d95 203 log_error_errno(errno, "Failed to execute man: %m");
78002a67
ZJS
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}