]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/pretty-print.c
Split out pretty-print.c and move pager.c and main-func.h to shared/
[thirdparty/systemd.git] / src / shared / pretty-print.c
CommitLineData
294bf0c3
ZJS
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <sys/utsname.h>
4#include <errno.h>
5#include <stdio.h>
6
7#include "alloc-util.h"
8#include "conf-files.h"
9#include "def.h"
10#include "env-util.h"
11#include "fd-util.h"
12#include "fileio.h"
13#include "pager.h"
14#include "path-util.h"
15#include "pretty-print.h"
16#include "string-util.h"
17#include "strv.h"
18#include "terminal-util.h"
19#include "util.h"
20
21static bool urlify_enabled(void) {
22 static int cached_urlify_enabled = -1;
23
24 /* Unfortunately 'less' doesn't support links like this yet 😭, hence let's disable this as long as there's a
25 * pager in effect. Let's drop this check as soon as less got fixed a and enough time passed so that it's safe
26 * to assume that a link-enabled 'less' version has hit most installations. */
27
28 if (cached_urlify_enabled < 0) {
29 int val;
30
31 val = getenv_bool("SYSTEMD_URLIFY");
32 if (val >= 0)
33 cached_urlify_enabled = val;
34 else
35 cached_urlify_enabled = colors_enabled() && !pager_have();
36 }
37
38 return cached_urlify_enabled;
39}
40
41int terminal_urlify(const char *url, const char *text, char **ret) {
42 char *n;
43
44 assert(url);
45
46 /* Takes an URL and a pretty string and formats it as clickable link for the terminal. See
47 * https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda for details. */
48
49 if (isempty(text))
50 text = url;
51
52 if (urlify_enabled())
53 n = strjoin("\x1B]8;;", url, "\a", text, "\x1B]8;;\a");
54 else
55 n = strdup(text);
56 if (!n)
57 return -ENOMEM;
58
59 *ret = n;
60 return 0;
61}
62
63int terminal_urlify_path(const char *path, const char *text, char **ret) {
64 _cleanup_free_ char *absolute = NULL;
65 struct utsname u;
66 const char *url;
67 int r;
68
69 assert(path);
70
71 /* Much like terminal_urlify() above, but takes a file system path as input
72 * and turns it into a proper file:// URL first. */
73
74 if (isempty(path))
75 return -EINVAL;
76
77 if (isempty(text))
78 text = path;
79
80 if (!urlify_enabled()) {
81 char *n;
82
83 n = strdup(text);
84 if (!n)
85 return -ENOMEM;
86
87 *ret = n;
88 return 0;
89 }
90
91 if (uname(&u) < 0)
92 return -errno;
93
94 if (!path_is_absolute(path)) {
95 r = path_make_absolute_cwd(path, &absolute);
96 if (r < 0)
97 return r;
98
99 path = absolute;
100 }
101
102 /* As suggested by https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda, let's include the local
103 * hostname here. Note that we don't use gethostname_malloc() or gethostname_strict() since we are interested
104 * in the raw string the kernel has set, whatever it may be, under the assumption that terminals are not overly
105 * careful with validating the strings either. */
106
107 url = strjoina("file://", u.nodename, path);
108
109 return terminal_urlify(url, text, ret);
110}
111
112int terminal_urlify_man(const char *page, const char *section, char **ret) {
113 const char *url, *text;
114
115 url = strjoina("man:", page, "(", section, ")");
116 text = strjoina(page, "(", section, ") man page");
117
118 return terminal_urlify(url, text, ret);
119}
120
121static int cat_file(const char *filename, bool newline) {
122 _cleanup_fclose_ FILE *f = NULL;
123 _cleanup_free_ char *urlified = NULL;
124 int r;
125
126 f = fopen(filename, "re");
127 if (!f)
128 return -errno;
129
130 r = terminal_urlify_path(filename, NULL, &urlified);
131 if (r < 0)
132 return r;
133
134 printf("%s%s# %s%s\n",
135 newline ? "\n" : "",
136 ansi_highlight_blue(),
137 urlified,
138 ansi_normal());
139 fflush(stdout);
140
141 for (;;) {
142 _cleanup_free_ char *line = NULL;
143
144 r = read_line(f, LONG_LINE_MAX, &line);
145 if (r < 0)
146 return log_error_errno(r, "Failed to read \"%s\": %m", filename);
147 if (r == 0)
148 break;
149
150 puts(line);
151 }
152
153 return 0;
154}
155
156int cat_files(const char *file, char **dropins, CatFlags flags) {
157 char **path;
158 int r;
159
160 if (file) {
161 r = cat_file(file, false);
162 if (r == -ENOENT && (flags & CAT_FLAGS_MAIN_FILE_OPTIONAL))
163 printf("%s# config file %s not found%s\n",
164 ansi_highlight_magenta(),
165 file,
166 ansi_normal());
167 else if (r < 0)
168 return log_warning_errno(r, "Failed to cat %s: %m", file);
169 }
170
171 STRV_FOREACH(path, dropins) {
172 r = cat_file(*path, file || path != dropins);
173 if (r < 0)
174 return log_warning_errno(r, "Failed to cat %s: %m", *path);
175 }
176
177 return 0;
178}
179
180void print_separator(void) {
181
182 /* Outputs a separator line that resolves to whitespace when copied from the terminal. We do that by outputting
183 * one line filled with spaces with ANSI underline set, followed by a second (empty) line. */
184
185 if (underline_enabled()) {
186 size_t i, c;
187
188 c = columns();
189
190 flockfile(stdout);
191 fputs_unlocked(ANSI_UNDERLINE, stdout);
192
193 for (i = 0; i < c; i++)
194 fputc_unlocked(' ', stdout);
195
196 fputs_unlocked(ANSI_NORMAL "\n\n", stdout);
197 funlockfile(stdout);
198 } else
199 fputs("\n\n", stdout);
200}
201
202int conf_files_cat(const char *root, const char *name) {
203 _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
204 _cleanup_free_ char *path = NULL;
205 const char *dir;
206 char **t;
207 int r;
208
209 NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) {
210 assert(endswith(dir, "/"));
211 r = strv_extendf(&dirs, "%s%s.d", dir, name);
212 if (r < 0)
213 return log_error_errno(r, "Failed to build directory list: %m");
214 }
215
216 r = conf_files_list_strv(&files, ".conf", root, 0, (const char* const*) dirs);
217 if (r < 0)
218 return log_error_errno(r, "Failed to query file list: %m");
219
220 path = path_join(root, "/etc", name);
221 if (!path)
222 return log_oom();
223
224 if (DEBUG_LOGGING) {
225 log_debug("Looking for configuration in:");
226 log_debug(" %s", path);
227 STRV_FOREACH(t, dirs)
228 log_debug(" %s/*.conf", *t);
229 }
230
231 /* show */
232 return cat_files(path, files, CAT_FLAGS_MAIN_FILE_OPTIONAL);
233}