chiark / gitweb /
core: move config_parse_set_status() into load-fragment.c
[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 <sys/types.h>
23 #include <fcntl.h>
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
33 static pid_t pager_pid = 0;
34
35 noreturn static void pager_fallback(void) {
36         ssize_t n;
37
38         do {
39                 n = splice(STDIN_FILENO, NULL, STDOUT_FILENO, NULL, 64*1024, 0);
40         } while (n > 0);
41
42         if (n < 0) {
43                 log_error("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         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                 log_error("Failed to create pager pipe: %m");
72                 return -errno;
73         }
74
75         parent_pid = getpid();
76
77         pager_pid = fork();
78         if (pager_pid < 0) {
79                 r = -errno;
80                 log_error("Failed to fork pager: %m");
81                 close_pipe(fd);
82                 return r;
83         }
84
85         /* In the child start the pager */
86         if (pager_pid == 0) {
87                 const char* less_opts;
88
89                 dup2(fd[0], STDIN_FILENO);
90                 close_pipe(fd);
91
92                 less_opts = getenv("SYSTEMD_LESS");
93                 if (!less_opts)
94                         less_opts = "FRSXMK";
95                 if (jump_to_end)
96                         less_opts = strappenda(less_opts, " +G");
97                 setenv("LESS", less_opts, 1);
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);
123
124                 pager_fallback();
125                 /* not reached */
126         }
127
128         /* Return in the parent */
129         if (dup2(fd[1], STDOUT_FILENO) < 0) {
130                 log_error("Failed to duplicate pager pipe: %m");
131                 return -errno;
132         }
133
134         close_pipe(fd);
135         return 1;
136 }
137
138 void 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 }
149
150 bool pager_have(void) {
151         return pager_pid > 0;
152 }