diff --git a/man/systemd.xml b/man/systemd.xml index 679851fc90..84aa43723c 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -855,6 +855,15 @@ + + $SYSTEMD_URLIFY + + The value must be a boolean. Controls whether clickable links should be generated in the output + for terminal emulators supporting this. This can be specified to override the decision that + systemd makes based on $TERM and other conditions. + + + $LISTEN_PID $LISTEN_FDS diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index f0405e3c3a..3166145b33 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -8,21 +8,22 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include #include #include +#include +#include +#include +#include +#include #include +#include +#include +#include #include +#include #include #include @@ -34,6 +35,7 @@ #include "io-util.h" #include "log.h" #include "macro.h" +#include "pager.h" #include "parse-util.h" #include "path-util.h" #include "proc-cmdline.h" @@ -1269,3 +1271,94 @@ int vt_reset_keyboard(int fd) { return 0; } + +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 terminal_urlify_path(const char *path, const char *text, char **ret) { + _cleanup_free_ char *absolute = NULL; + struct utsname u; + const char *url; + int r; + + assert(path); + + /* Much like terminal_urlify() above, but takes a file system path as input, and turns it into a properl + * 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; + } + + 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 = strjoina("file://", u.nodename, path); + + return terminal_urlify(url, text, ret); +} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index d60956455b..ad6ee338ed 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -156,3 +156,6 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode); int vt_default_utf8(void); int vt_reset_keyboard(int fd); + +int terminal_urlify(const char *url, const char *text, char **ret); +int terminal_urlify_path(const char *path, const char *text, char **ret); diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index 0a240280ea..c83dfa9dcd 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -9,6 +9,7 @@ #include #include +#include "alloc-util.h" #include "fd-util.h" #include "fileio.h" #include "log.h" @@ -63,12 +64,25 @@ static void test_read_one_char(void) { unlink(name); } +static void test_terminal_urlify(void) { + _cleanup_free_ char *formatted = NULL; + + assert_se(terminal_urlify("https://www.freedesktop.org/wiki/Software/systemd/", "systemd homepage", &formatted) >= 0); + printf("Hey, considere visiting the %s right now! It is very good!\n", formatted); + + formatted = mfree(formatted); + + assert_se(terminal_urlify_path("/etc/fstab", "this link to your /etc/fstab", &formatted) >= 0); + printf("Or click on %s to have a look at it!\n", formatted); +} + int main(int argc, char *argv[]) { log_parse_environment(); log_open(); test_default_term_for_tty(); test_read_one_char(); + test_terminal_urlify(); return 0; }