chiark / gitweb /
Prep v227: Removed src/shared/cgroup-show.[hc], it is not needed.
[elogind.git] / src / shared / pager.c
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
36 static pid_t pager_pid = 0;
37
38 noreturn static void pager_fallback(void) {
39         int r;
40
41         r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_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
50 int pager_open(bool jump_to_end) {
51         _cleanup_close_pair_ int fd[2] = { -1, -1 };
52         const char *pager;
53         pid_t parent_pid;
54
55         if (pager_pid > 0)
56                 return 1;
57
58         if (!on_tty())
59                         return 0;
60
61         pager = getenv("SYSTEMD_PAGER");
62         if (!pager)
63                 pager = getenv("PAGER");
64
65         /* If the pager is explicitly turned off, honour it */
66         if (pager && (pager[0] == 0 || streq(pager, "cat")))
67                 return 0;
68
69         /* Determine and cache number of columns before we spawn the
70          * pager so that we get the value from the actual tty */
71         (void) columns();
72
73         if (pipe(fd) < 0)
74                 return log_error_errno(errno, "Failed to create pager pipe: %m");
75
76         parent_pid = getpid();
77
78         pager_pid = fork();
79         if (pager_pid < 0)
80                 return log_error_errno(errno, "Failed to fork pager: %m");
81
82         /* In the child start the pager */
83         if (pager_pid == 0) {
84                 const char* less_opts, *less_charset;
85
86                 (void) reset_all_signal_handlers();
87                 (void) reset_signal_mask();
88
89                 (void) dup2(fd[0], STDIN_FILENO);
90                 safe_close_pair(fd);
91
92                 /* Initialize a good set of less options */
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                 /* Initialize a good charset for less. This is
101                  * particularly important if we output UTF-8
102                  * characters. */
103                 less_charset = getenv("SYSTEMD_LESSCHARSET");
104                 if (!less_charset && is_locale_utf8())
105                         less_charset = "utf-8";
106                 if (less_charset)
107                         setenv("LESSCHARSET", less_charset, 1);
108
109                 /* Make sure the pager goes away when the parent dies */
110                 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
111                         _exit(EXIT_FAILURE);
112
113                 /* Check whether our parent died before we were able
114                  * to set the death signal */
115                 if (getppid() != parent_pid)
116                         _exit(EXIT_SUCCESS);
117
118                 if (pager) {
119                         execlp(pager, pager, NULL);
120                         execl("/bin/sh", "sh", "-c", pager, NULL);
121                 }
122
123                 /* Debian's alternatives command for pagers is
124                  * called 'pager'. Note that we do not call
125                  * sensible-pagers here, since that is just a
126                  * shell script that implements a logic that
127                  * is similar to this one anyway, but is
128                  * Debian-specific. */
129                 execlp("pager", "pager", NULL);
130
131                 execlp("less", "less", NULL);
132                 execlp("more", "more", NULL);
133
134                 pager_fallback();
135                 /* not reached */
136         }
137
138         /* Return in the parent */
139         if (dup2(fd[1], STDOUT_FILENO) < 0)
140                 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
141         if (dup2(fd[1], STDERR_FILENO) < 0)
142                 return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
143
144         return 1;
145 }
146
147 void pager_close(void) {
148
149         if (pager_pid <= 0)
150                 return;
151
152         /* Inform pager that we are done */
153         stdout = safe_fclose(stdout);
154         stderr = safe_fclose(stderr);
155
156         (void) kill(pager_pid, SIGCONT);
157         (void) wait_for_terminate(pager_pid, NULL);
158         pager_pid = 0;
159 }
160
161 bool pager_have(void) {
162         return pager_pid > 0;
163 }
164
165 /// UNNEEDED by elogind
166 #if 0
167 int show_man_page(const char *desc, bool null_stdio) {
168         const char *args[4] = { "man", NULL, NULL, NULL };
169         char *e = NULL;
170         pid_t pid;
171         size_t k;
172         int r;
173         siginfo_t status;
174
175         k = strlen(desc);
176
177         if (desc[k-1] == ')')
178                 e = strrchr(desc, '(');
179
180         if (e) {
181                 char *page = NULL, *section = NULL;
182
183                 page = strndupa(desc, e - desc);
184                 section = strndupa(e + 1, desc + k - e - 2);
185
186                 args[1] = section;
187                 args[2] = page;
188         } else
189                 args[1] = desc;
190
191         pid = fork();
192         if (pid < 0)
193                 return log_error_errno(errno, "Failed to fork: %m");
194
195         if (pid == 0) {
196                 /* Child */
197
198                 (void) reset_all_signal_handlers();
199                 (void) reset_signal_mask();
200
201                 if (null_stdio) {
202                         r = make_null_stdio();
203                         if (r < 0) {
204                                 log_error_errno(r, "Failed to kill stdio: %m");
205                                 _exit(EXIT_FAILURE);
206                         }
207                 }
208
209                 execvp(args[0], (char**) args);
210                 log_error_errno(errno, "Failed to execute man: %m");
211                 _exit(EXIT_FAILURE);
212         }
213
214         r = wait_for_terminate(pid, &status);
215         if (r < 0)
216                 return r;
217
218         log_debug("Exit code %i status %i", status.si_code, status.si_status);
219         return status.si_status;
220 }
221 #endif // 0