]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
terminal: add internal API to format URLs for display in capable terminals
authorLennart Poettering <lennart@poettering.net>
Thu, 19 Apr 2018 15:48:53 +0000 (17:48 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 19 Apr 2018 16:04:26 +0000 (18:04 +0200)
Newer terminals (in particular gnome-terminal) understand special escape
sequence for formatting clickable links. Let's support that to make our
tool output more clickable where that's appropriate.

For details see this:

https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda

The one big issue is that 'less' currently doesn't grok this, and
doesn't ignore sequence like regular terminal implementations do if they
don't support it. Hence for now, let's disable URL output if a pager is
used. We should revisit that though as soon as less added support for it
and enough time passed for it to enter various distributions.

man/systemd.xml
src/basic/terminal-util.c
src/basic/terminal-util.h
src/test/test-terminal-util.c

index 679851fc90949b6772d8c280865a34f61785bf84..84aa43723cdeb471157e5a9116c729e25b27da59 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>$SYSTEMD_URLIFY</varname></term>
+
+        <listitem><para>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
+        <command>systemd</command> makes based on <varname>$TERM</varname> and other conditions.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>$LISTEN_PID</varname></term>
         <term><varname>$LISTEN_FDS</varname></term>
index f0405e3c3ac02a71debb5550380b0fc426081f6b..3166145b332b4c7dfd44a20c385e16743337aea8 100644 (file)
@@ -8,21 +8,22 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <linux/kd.h>
+#include <linux/tiocl.h>
+#include <linux/vt.h>
+#include <poll.h>
+#include <signal.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/inotify.h>
+#include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/sysmacros.h>
 #include <sys/time.h>
-#include <linux/kd.h>
-#include <linux/tiocl.h>
-#include <linux/vt.h>
-#include <poll.h>
-#include <signal.h>
-#include <sys/ioctl.h>
 #include <sys/types.h>
+#include <sys/utsname.h>
 #include <termios.h>
 #include <unistd.h>
 
@@ -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);
+}
index d60956455b9655161007ee468c8339e46d24cf34..ad6ee338edff17886370288fb9495284e27e693e 100644 (file)
@@ -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);
index 0a240280ea0a60628aa145a71a2c8aeda9926f3a..c83dfa9dcd54e637dc45d8ebb781fd0849b3a6ca 100644 (file)
@@ -9,6 +9,7 @@
 #include <stdbool.h>
 #include <stdio.h>
 
+#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;
 }