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;
}