/* SPDX-License-Identifier: LGPL-2.1+ */ #include #include #include #include "alloc-util.h" #include "conf-files.h" #include "def.h" #include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "pager.h" #include "path-util.h" #include "pretty-print.h" #include "string-util.h" #include "strv.h" #include "terminal-util.h" #include "util.h" static bool urlify_enabled(void) { static int cached_urlify_enabled = -1; /* Unfortunately 'less' doesn't support links like this yet 😭, hence let's disable this as long as there's a * pager in effect. Let's drop this check as soon as less got fixed a and enough time passed so that it's safe * to assume that a link-enabled 'less' version has hit most installations. */ if (cached_urlify_enabled < 0) { int val; val = getenv_bool("SYSTEMD_URLIFY"); if (val >= 0) cached_urlify_enabled = val; else cached_urlify_enabled = colors_enabled() && !pager_have(); } return cached_urlify_enabled; } int terminal_urlify(const char *url, const char *text, char **ret) { char *n; assert(url); /* Takes an URL and a pretty string and formats it as clickable link for the terminal. See * https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda for details. */ if (isempty(text)) text = url; if (urlify_enabled()) n = strjoin("\x1B]8;;", url, "\a", text, "\x1B]8;;\a"); else n = strdup(text); if (!n) return -ENOMEM; *ret = n; return 0; } int file_url_from_path(const char *path, char **ret) { _cleanup_free_ char *absolute = NULL; struct utsname u; char *url = NULL; int r; if (uname(&u) < 0) return -errno; if (!path_is_absolute(path)) { r = path_make_absolute_cwd(path, &absolute); if (r < 0) return r; path = absolute; } /* As suggested by https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda, let's include the local * hostname here. Note that we don't use gethostname_malloc() or gethostname_strict() since we are interested * in the raw string the kernel has set, whatever it may be, under the assumption that terminals are not overly * careful with validating the strings either. */ url = strjoin("file://", u.nodename, path); if (!url) return -ENOMEM; *ret = url; return 0; } int terminal_urlify_path(const char *path, const char *text, char **ret) { _cleanup_free_ char *url = NULL; int r; assert(path); /* Much like terminal_urlify() above, but takes a file system path as input * and turns it into a proper file:// URL first. */ if (isempty(path)) return -EINVAL; if (isempty(text)) text = path; if (!urlify_enabled()) { char *n; n = strdup(text); if (!n) return -ENOMEM; *ret = n; return 0; } r = file_url_from_path(path, &url); if (r < 0) return r; return terminal_urlify(url, text, ret); } int terminal_urlify_man(const char *page, const char *section, char **ret) { const char *url, *text; url = strjoina("man:", page, "(", section, ")"); text = strjoina(page, "(", section, ") man page"); return terminal_urlify(url, text, ret); } static int cat_file(const char *filename, bool newline) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *urlified = NULL; int r; f = fopen(filename, "re"); if (!f) return -errno; r = terminal_urlify_path(filename, NULL, &urlified); if (r < 0) return r; printf("%s%s# %s%s\n", newline ? "\n" : "", ansi_highlight_blue(), urlified, ansi_normal()); fflush(stdout); for (;;) { _cleanup_free_ char *line = NULL; r = read_line(f, LONG_LINE_MAX, &line); if (r < 0) return log_error_errno(r, "Failed to read \"%s\": %m", filename); if (r == 0) break; puts(line); } return 0; } int cat_files(const char *file, char **dropins, CatFlags flags) { char **path; int r; if (file) { r = cat_file(file, false); if (r == -ENOENT && (flags & CAT_FLAGS_MAIN_FILE_OPTIONAL)) printf("%s# config file %s not found%s\n", ansi_highlight_magenta(), file, ansi_normal()); else if (r < 0) return log_warning_errno(r, "Failed to cat %s: %m", file); } STRV_FOREACH(path, dropins) { r = cat_file(*path, file || path != dropins); if (r < 0) return log_warning_errno(r, "Failed to cat %s: %m", *path); } return 0; } void print_separator(void) { /* Outputs a separator line that resolves to whitespace when copied from the terminal. We do that by outputting * one line filled with spaces with ANSI underline set, followed by a second (empty) line. */ if (underline_enabled()) { size_t i, c; c = columns(); flockfile(stdout); fputs_unlocked(ANSI_UNDERLINE, stdout); for (i = 0; i < c; i++) fputc_unlocked(' ', stdout); fputs_unlocked(ANSI_NORMAL "\n\n", stdout); funlockfile(stdout); } else fputs("\n\n", stdout); } int conf_files_cat(const char *root, const char *name) { _cleanup_strv_free_ char **dirs = NULL, **files = NULL; _cleanup_free_ char *path = NULL; const char *dir; char **t; int r; NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) { assert(endswith(dir, "/")); r = strv_extendf(&dirs, "%s%s.d", dir, name); if (r < 0) return log_error_errno(r, "Failed to build directory list: %m"); } r = conf_files_list_strv(&files, ".conf", root, 0, (const char* const*) dirs); if (r < 0) return log_error_errno(r, "Failed to query file list: %m"); path = path_join(root, "/etc", name); if (!path) return log_oom(); if (DEBUG_LOGGING) { log_debug("Looking for configuration in:"); log_debug(" %s", path); STRV_FOREACH(t, dirs) log_debug(" %s/*.conf", *t); } /* show */ return cat_files(path, files, CAT_FLAGS_MAIN_FILE_OPTIONAL); }