Merge pull request #8767 from poettering/urlify-all-things

try to generate clickable links in our output if we can
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-04-19 18:56:46 +02:00 committed by GitHub
commit 65a2718af5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 170 additions and 17 deletions

View file

@ -855,6 +855,15 @@
</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>

View file

@ -8,21 +8,22 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.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 <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 <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);
}

View 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);

View file

@ -20,6 +20,7 @@
#include "bus-util.h"
#include "hostname-util.h"
#include "spawn-polkit-agent.h"
#include "terminal-util.h"
#include "util.h"
#include "verbs.h"
@ -44,6 +45,7 @@ typedef struct StatusInfo {
const char *os_cpe_name;
const char *virtualization;
const char *architecture;
const char *home_url;
} StatusInfo;
static void print_status_info(StatusInfo *i) {
@ -87,8 +89,17 @@ static void print_status_info(StatusInfo *i) {
if (!isempty(i->virtualization))
printf(" Virtualization: %s\n", i->virtualization);
if (!isempty(i->os_pretty_name))
printf(" Operating System: %s\n", i->os_pretty_name);
if (!isempty(i->os_pretty_name)) {
_cleanup_free_ char *formatted = NULL;
const char *t = i->os_pretty_name;
if (i->home_url) {
if (terminal_urlify(i->home_url, i->os_pretty_name, &formatted) >= 0)
t = formatted;
}
printf(" Operating System: %s\n", t);
}
if (!isempty(i->os_cpe_name))
printf(" CPE OS Name: %s\n", i->os_cpe_name);
@ -141,6 +152,7 @@ static int show_all_names(sd_bus *bus, sd_bus_error *error) {
{ "KernelRelease", "s", NULL, offsetof(StatusInfo, kernel_release) },
{ "OperatingSystemPrettyName", "s", NULL, offsetof(StatusInfo, os_pretty_name) },
{ "OperatingSystemCPEName", "s", NULL, offsetof(StatusInfo, os_cpe_name) },
{ "HomeURL", "s", NULL, offsetof(StatusInfo, home_url) },
{}
};

View file

@ -39,6 +39,7 @@ enum {
PROP_KERNEL_VERSION,
PROP_OS_PRETTY_NAME,
PROP_OS_CPE_NAME,
PROP_HOME_URL,
_PROP_MAX
};
@ -100,6 +101,7 @@ static int context_read_data(Context *c) {
r = parse_env_file("/etc/os-release", NEWLINE,
"PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
"CPE_NAME", &c->data[PROP_OS_CPE_NAME],
"HOME_URL", &c->data[PROP_HOME_URL],
NULL);
if (r == -ENOENT)
r = parse_env_file("/usr/lib/os-release", NEWLINE,
@ -640,6 +642,7 @@ static const sd_bus_vtable hostname_vtable[] = {
SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HomeURL", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED),

View file

@ -3954,11 +3954,11 @@ static void print_status_info(
UnitStatusInfo *i,
bool *ellipsized) {
ExecStatusInfo *p;
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1, since2[FORMAT_TIMESTAMP_MAX], *s2;
const char *active_on, *active_off, *on, *off, *ss;
_cleanup_free_ char *formatted_path = NULL;
ExecStatusInfo *p;
usec_t timestamp;
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
char since2[FORMAT_TIMESTAMP_MAX], *s2;
const char *path;
char **t, **t2;
int r;
@ -3993,7 +3993,9 @@ static void print_status_info(
} else
on = off = "";
path = i->source_path ? i->source_path : i->fragment_path;
path = i->source_path ?: i->fragment_path;
if (path && terminal_urlify_path(path, NULL, &formatted_path) >= 0)
path = formatted_path;
if (i->load_error != 0)
printf(" Loaded: %s%s%s (Reason: %s)\n",
@ -4021,6 +4023,9 @@ static void print_status_info(
char ** dropin;
STRV_FOREACH(dropin, i->dropin_paths) {
_cleanup_free_ char *dropin_formatted = NULL;
const char *df;
if (!dir || last) {
printf(dir ? " " :
" Drop-In: ");
@ -4040,7 +4045,12 @@ static void print_status_info(
last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir));
printf("%s%s", basename(*dropin), last ? "\n" : ", ");
if (terminal_urlify_path(*dropin, basename(*dropin), &dropin_formatted) >= 0)
df = dropin_formatted;
else
df = *dropin;
printf("%s%s", df, last ? "\n" : ", ");
}
}
@ -4143,8 +4153,17 @@ static void print_status_info(
if (i->what)
printf(" What: %s\n", i->what);
STRV_FOREACH(t, i->documentation)
printf(" %*s %s\n", 9, t == i->documentation ? "Docs:" : "", *t);
STRV_FOREACH(t, i->documentation) {
_cleanup_free_ char *formatted = NULL;
const char *q;
if (terminal_urlify(*t, NULL, &formatted) >= 0)
q = formatted;
else
q = *t;
printf(" %*s %s\n", 9, t == i->documentation ? "Docs:" : "", q);
}
STRV_FOREACH_PAIR(t, t2, i->listen)
printf(" %*s %s (%s)\n", 9, t == i->listen ? "Listen:" : "", *t2, *t);

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