Branch data Line data Source code
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 : :
21 : 527 : static 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 [ + + ]: 527 : if (cached_urlify_enabled < 0) {
29 : : int val;
30 : :
31 : 499 : val = getenv_bool("SYSTEMD_URLIFY");
32 [ - + ]: 499 : if (val >= 0)
33 : 0 : cached_urlify_enabled = val;
34 : : else
35 [ - + # # ]: 499 : cached_urlify_enabled = colors_enabled() && !pager_have();
36 : : }
37 : :
38 : 527 : return cached_urlify_enabled;
39 : : }
40 : :
41 : 507 : int terminal_urlify(const char *url, const char *text, char **ret) {
42 : : char *n;
43 : :
44 [ - + ]: 507 : 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 [ - + ]: 507 : if (isempty(text))
50 : 0 : text = url;
51 : :
52 [ - + ]: 507 : if (urlify_enabled())
53 : 0 : n = strjoin("\x1B]8;;", url, "\a", text, "\x1B]8;;\a");
54 : : else
55 : 507 : n = strdup(text);
56 [ - + ]: 507 : if (!n)
57 : 0 : return -ENOMEM;
58 : :
59 : 507 : *ret = n;
60 : 507 : return 0;
61 : : }
62 : :
63 : 0 : int file_url_from_path(const char *path, char **ret) {
64 : 0 : _cleanup_free_ char *absolute = NULL;
65 : : struct utsname u;
66 : 0 : char *url = NULL;
67 : : int r;
68 : :
69 [ # # ]: 0 : if (uname(&u) < 0)
70 : 0 : return -errno;
71 : :
72 [ # # ]: 0 : if (!path_is_absolute(path)) {
73 : 0 : r = path_make_absolute_cwd(path, &absolute);
74 [ # # ]: 0 : if (r < 0)
75 : 0 : return r;
76 : :
77 : 0 : path = absolute;
78 : : }
79 : :
80 : : /* As suggested by https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda, let's include the local
81 : : * hostname here. Note that we don't use gethostname_malloc() or gethostname_strict() since we are interested
82 : : * in the raw string the kernel has set, whatever it may be, under the assumption that terminals are not overly
83 : : * careful with validating the strings either. */
84 : :
85 : 0 : url = strjoin("file://", u.nodename, path);
86 [ # # ]: 0 : if (!url)
87 : 0 : return -ENOMEM;
88 : :
89 : 0 : *ret = url;
90 : 0 : return 0;
91 : : }
92 : :
93 : 20 : int terminal_urlify_path(const char *path, const char *text, char **ret) {
94 : 20 : _cleanup_free_ char *url = NULL;
95 : : int r;
96 : :
97 [ - + ]: 20 : assert(path);
98 : :
99 : : /* Much like terminal_urlify() above, but takes a file system path as input
100 : : * and turns it into a proper file:// URL first. */
101 : :
102 [ - + ]: 20 : if (isempty(path))
103 : 0 : return -EINVAL;
104 : :
105 [ + + ]: 20 : if (isempty(text))
106 : 12 : text = path;
107 : :
108 [ + - ]: 20 : if (!urlify_enabled()) {
109 : : char *n;
110 : :
111 : 20 : n = strdup(text);
112 [ - + ]: 20 : if (!n)
113 : 0 : return -ENOMEM;
114 : :
115 : 20 : *ret = n;
116 : 20 : return 0;
117 : : }
118 : :
119 : 0 : r = file_url_from_path(path, &url);
120 [ # # ]: 0 : if (r < 0)
121 : 0 : return r;
122 : :
123 : 0 : return terminal_urlify(url, text, ret);
124 : : }
125 : :
126 : 491 : int terminal_urlify_man(const char *page, const char *section, char **ret) {
127 : : const char *url, *text;
128 : :
129 [ + + + - : 5401 : url = strjoina("man:", page, "(", section, ")");
- + - + +
+ + - ]
130 [ + + + - : 4419 : text = strjoina(page, "(", section, ") man page");
- + - + +
+ + - ]
131 : :
132 : 491 : return terminal_urlify(url, text, ret);
133 : : }
134 : :
135 : 20 : static int cat_file(const char *filename, bool newline) {
136 : 20 : _cleanup_fclose_ FILE *f = NULL;
137 : 20 : _cleanup_free_ char *urlified = NULL;
138 : : int r;
139 : :
140 : 20 : f = fopen(filename, "re");
141 [ + + ]: 20 : if (!f)
142 : 8 : return -errno;
143 : :
144 : 12 : r = terminal_urlify_path(filename, NULL, &urlified);
145 [ - + ]: 12 : if (r < 0)
146 : 0 : return r;
147 : :
148 [ + + ]: 12 : printf("%s%s# %s%s\n",
149 : : newline ? "\n" : "",
150 : : ansi_highlight_blue(),
151 : : urlified,
152 : : ansi_normal());
153 : 12 : fflush(stdout);
154 : :
155 : 204 : for (;;) {
156 [ + - + ]: 216 : _cleanup_free_ char *line = NULL;
157 : :
158 : 216 : r = read_line(f, LONG_LINE_MAX, &line);
159 [ - + ]: 216 : if (r < 0)
160 [ # # ]: 0 : return log_error_errno(r, "Failed to read \"%s\": %m", filename);
161 [ + + ]: 216 : if (r == 0)
162 : 12 : break;
163 : :
164 : 204 : puts(line);
165 : : }
166 : :
167 : 12 : return 0;
168 : : }
169 : :
170 : 12 : int cat_files(const char *file, char **dropins, CatFlags flags) {
171 : : char **path;
172 : : int r;
173 : :
174 [ + - ]: 12 : if (file) {
175 : 12 : r = cat_file(file, false);
176 [ + + + + ]: 12 : if (r == -ENOENT && (flags & CAT_FLAGS_MAIN_FILE_OPTIONAL))
177 : 4 : printf("%s# config file %s not found%s\n",
178 : : ansi_highlight_magenta(),
179 : : file,
180 : : ansi_normal());
181 [ + + ]: 8 : else if (r < 0)
182 [ + - ]: 4 : return log_warning_errno(r, "Failed to cat %s: %m", file);
183 : : }
184 : :
185 [ + + + + ]: 16 : STRV_FOREACH(path, dropins) {
186 [ - + # # ]: 8 : r = cat_file(*path, file || path != dropins);
187 [ - + ]: 8 : if (r < 0)
188 [ # # ]: 0 : return log_warning_errno(r, "Failed to cat %s: %m", *path);
189 : : }
190 : :
191 : 8 : return 0;
192 : : }
193 : :
194 : 4 : void print_separator(void) {
195 : :
196 : : /* Outputs a separator line that resolves to whitespace when copied from the terminal. We do that by outputting
197 : : * one line filled with spaces with ANSI underline set, followed by a second (empty) line. */
198 : :
199 [ - + ]: 4 : if (underline_enabled()) {
200 : : size_t i, c;
201 : :
202 : 0 : c = columns();
203 : :
204 : 0 : flockfile(stdout);
205 : 0 : fputs_unlocked(ANSI_UNDERLINE, stdout);
206 : :
207 [ # # ]: 0 : for (i = 0; i < c; i++)
208 : 0 : fputc_unlocked(' ', stdout);
209 : :
210 : 0 : fputs_unlocked(ANSI_NORMAL "\n\n", stdout);
211 : 0 : funlockfile(stdout);
212 : : } else
213 : 4 : fputs("\n\n", stdout);
214 : 4 : }
215 : :
216 : 0 : static int guess_type(const char **name, char ***prefixes, bool *is_collection, const char **extension) {
217 : : /* Try to figure out if name is like tmpfiles.d/ or systemd/system-presets/,
218 : : * i.e. a collection of directories without a main config file. */
219 : :
220 : 0 : _cleanup_free_ char *n = NULL;
221 : 0 : bool usr = false, run = false, coll = false;
222 : 0 : const char *ext = ".conf";
223 : : /* This is static so that the array doesn't get deallocated when we exit the function */
224 : : static const char* const std_prefixes[] = { CONF_PATHS(""), NULL };
225 : : static const char* const usr_prefixes[] = { CONF_PATHS_USR(""), NULL };
226 : : static const char* const run_prefixes[] = { "/run/", NULL };
227 : :
228 [ # # ]: 0 : if (path_equal(*name, "environment.d"))
229 : : /* Special case: we need to include /etc/environment in the search path, even
230 : : * though the whole concept is called environment.d. */
231 : 0 : *name = "environment";
232 : :
233 : 0 : n = strdup(*name);
234 [ # # ]: 0 : if (!n)
235 : 0 : return log_oom();
236 : :
237 : 0 : delete_trailing_chars(n, "/");
238 : :
239 [ # # ]: 0 : if (endswith(n, ".d"))
240 : 0 : coll = true;
241 : :
242 [ # # ]: 0 : if (path_equal(n, "environment"))
243 : 0 : usr = true;
244 : :
245 [ # # ]: 0 : if (path_equal(n, "udev/hwdb.d"))
246 : 0 : ext = ".hwdb";
247 : :
248 [ # # ]: 0 : if (path_equal(n, "udev/rules.d"))
249 : 0 : ext = ".rules";
250 : :
251 [ # # ]: 0 : if (path_equal(n, "kernel/install.d"))
252 : 0 : ext = ".install";
253 : :
254 [ # # ]: 0 : if (path_equal(n, "systemd/ntp-units.d")) {
255 : 0 : coll = true;
256 : 0 : ext = ".list";
257 : : }
258 : :
259 [ # # ]: 0 : if (path_equal(n, "systemd/relabel-extra.d")) {
260 : 0 : coll = run = true;
261 : 0 : ext = ".relabel";
262 : : }
263 : :
264 [ # # # # : 0 : if (PATH_IN_SET(n, "systemd/system-preset", "systemd/user-preset")) {
# # # # ]
265 : 0 : coll = true;
266 : 0 : ext = ".preset";
267 : : }
268 : :
269 [ # # ]: 0 : if (path_equal(n, "systemd/user-preset"))
270 : 0 : usr = true;
271 : :
272 [ # # # # ]: 0 : *prefixes = (char**) (usr ? usr_prefixes : run ? run_prefixes : std_prefixes);
273 : 0 : *is_collection = coll;
274 : 0 : *extension = ext;
275 : 0 : return 0;
276 : : }
277 : :
278 : 0 : int conf_files_cat(const char *root, const char *name) {
279 : 0 : _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
280 : 0 : _cleanup_free_ char *path = NULL;
281 : : char **prefixes, **prefix;
282 : : bool is_collection;
283 : : const char *extension;
284 : : char **t;
285 : : int r;
286 : :
287 : 0 : r = guess_type(&name, &prefixes, &is_collection, &extension);
288 [ # # ]: 0 : if (r < 0)
289 : 0 : return r;
290 : :
291 [ # # # # ]: 0 : STRV_FOREACH(prefix, prefixes) {
292 [ # # ]: 0 : assert(endswith(*prefix, "/"));
293 : 0 : r = strv_extendf(&dirs, "%s%s%s", *prefix, name,
294 [ # # ]: 0 : is_collection ? "" : ".d");
295 [ # # ]: 0 : if (r < 0)
296 [ # # ]: 0 : return log_error_errno(r, "Failed to build directory list: %m");
297 : : }
298 : :
299 : 0 : r = conf_files_list_strv(&files, extension, root, 0, (const char* const*) dirs);
300 [ # # ]: 0 : if (r < 0)
301 [ # # ]: 0 : return log_error_errno(r, "Failed to query file list: %m");
302 : :
303 [ # # ]: 0 : if (!is_collection) {
304 : 0 : path = path_join(root, "/etc", name);
305 [ # # ]: 0 : if (!path)
306 : 0 : return log_oom();
307 : : }
308 : :
309 [ # # ]: 0 : if (DEBUG_LOGGING) {
310 [ # # ]: 0 : log_debug("Looking for configuration in:");
311 [ # # ]: 0 : if (path)
312 [ # # ]: 0 : log_debug(" %s", path);
313 [ # # # # ]: 0 : STRV_FOREACH(t, dirs)
314 [ # # ]: 0 : log_debug(" %s/*%s", *t, extension);
315 : : }
316 : :
317 : : /* show */
318 : 0 : return cat_files(path, files, CAT_FLAGS_MAIN_FILE_OPTIONAL);
319 : : }
|