1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include <sys/utsname.h>
7 #include "alloc-util.h"
8 #include "conf-files.h"
14 #include "path-util.h"
15 #include "pretty-print.h"
16 #include "string-util.h"
18 #include "terminal-util.h"
21 static bool urlify_enabled(void) {
22 static int cached_urlify_enabled
= -1;
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. */
28 if (cached_urlify_enabled
< 0) {
31 val
= getenv_bool("SYSTEMD_URLIFY");
33 cached_urlify_enabled
= val
;
35 cached_urlify_enabled
= colors_enabled() && !pager_have();
38 return cached_urlify_enabled
;
41 int terminal_urlify(const char *url
, const char *text
, char **ret
) {
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. */
53 n
= strjoin("\x1B]8;;", url
, "\a", text
, "\x1B]8;;\a");
63 int file_url_from_path(const char *path
, char **ret
) {
64 _cleanup_free_
char *absolute
= NULL
;
72 if (!path_is_absolute(path
)) {
73 r
= path_make_absolute_cwd(path
, &absolute
);
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. */
85 url
= strjoin("file://", u
.nodename
, path
);
93 int terminal_urlify_path(const char *path
, const char *text
, char **ret
) {
94 _cleanup_free_
char *url
= NULL
;
99 /* Much like terminal_urlify() above, but takes a file system path as input
100 * and turns it into a proper file:// URL first. */
108 if (!urlify_enabled()) {
119 r
= file_url_from_path(path
, &url
);
123 return terminal_urlify(url
, text
, ret
);
126 int terminal_urlify_man(const char *page
, const char *section
, char **ret
) {
127 const char *url
, *text
;
129 url
= strjoina("man:", page
, "(", section
, ")");
130 text
= strjoina(page
, "(", section
, ") man page");
132 return terminal_urlify(url
, text
, ret
);
135 static int cat_file(const char *filename
, bool newline
) {
136 _cleanup_fclose_
FILE *f
= NULL
;
137 _cleanup_free_
char *urlified
= NULL
;
140 f
= fopen(filename
, "re");
144 r
= terminal_urlify_path(filename
, NULL
, &urlified
);
148 printf("%s%s# %s%s\n",
150 ansi_highlight_blue(),
156 _cleanup_free_
char *line
= NULL
;
158 r
= read_line(f
, LONG_LINE_MAX
, &line
);
160 return log_error_errno(r
, "Failed to read \"%s\": %m", filename
);
170 int cat_files(const char *file
, char **dropins
, CatFlags flags
) {
175 r
= cat_file(file
, false);
176 if (r
== -ENOENT
&& (flags
& CAT_FLAGS_MAIN_FILE_OPTIONAL
))
177 printf("%s# config file %s not found%s\n",
178 ansi_highlight_magenta(),
182 return log_warning_errno(r
, "Failed to cat %s: %m", file
);
185 STRV_FOREACH(path
, dropins
) {
186 r
= cat_file(*path
, file
|| path
!= dropins
);
188 return log_warning_errno(r
, "Failed to cat %s: %m", *path
);
194 void print_separator(void) {
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. */
199 if (underline_enabled()) {
205 fputs_unlocked(ANSI_UNDERLINE
, stdout
);
207 for (i
= 0; i
< c
; i
++)
208 fputc_unlocked(' ', stdout
);
210 fputs_unlocked(ANSI_NORMAL
"\n\n", stdout
);
213 fputs("\n\n", stdout
);
216 static int guess_type(const char **name
, bool *is_usr
, 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. */
220 _cleanup_free_
char *n
= NULL
;
221 bool usr
= false, coll
= false;
222 const char *ext
= ".conf";
224 if (path_equal(*name
, "environment.d"))
225 /* Special case: we need to include /etc/environment in the search path, even
226 * though the whole concept is called environment.d. */
227 *name
= "environment";
233 delete_trailing_chars(n
, "/");
235 if (endswith(n
, ".d"))
238 if (path_equal(n
, "environment"))
241 if (path_equal(n
, "udev/hwdb.d"))
244 if (path_equal(n
, "udev/rules.d"))
247 if (path_equal(n
, "kernel/install.d"))
250 if (PATH_IN_SET(n
, "systemd/system-preset", "systemd/user-preset")) {
255 if (path_equal(n
, "systemd/user-preset"))
259 *is_collection
= coll
;
264 int conf_files_cat(const char *root
, const char *name
) {
265 _cleanup_strv_free_
char **dirs
= NULL
, **files
= NULL
;
266 _cleanup_free_
char *path
= NULL
;
268 bool is_usr
, is_collection
;
269 const char *extension
;
273 r
= guess_type(&name
, &is_usr
, &is_collection
, &extension
);
277 STRV_FOREACH(dir
, is_usr
? CONF_PATHS_USR_STRV("") : CONF_PATHS_STRV("")) {
278 assert(endswith(*dir
, "/"));
279 r
= strv_extendf(&dirs
, "%s%s%s", *dir
, name
,
280 is_collection
? "" : ".d");
282 return log_error_errno(r
, "Failed to build directory list: %m");
285 r
= conf_files_list_strv(&files
, extension
, root
, 0, (const char* const*) dirs
);
287 return log_error_errno(r
, "Failed to query file list: %m");
289 if (!is_collection
) {
290 path
= path_join(root
, "/etc", name
);
296 log_debug("Looking for configuration in:");
298 log_debug(" %s", path
);
299 STRV_FOREACH(t
, dirs
)
300 log_debug(" %s/*%s", *t
, extension
);
304 return cat_files(path
, files
, CAT_FLAGS_MAIN_FILE_OPTIONAL
);