From 3d8ba7b83f254ec9e137210630a602001674e4d0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Jan 2024 23:41:01 +0100 Subject: [PATCH 1/4] nspawn: optionally tint the background color of a container --- man/systemd-nspawn.xml | 15 +++++++++++++++ shell-completion/bash/systemd-nspawn | 2 +- src/nspawn/nspawn.c | 22 +++++++++++++++++++--- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 1a9fb09aae..59b2c1a2d0 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -1711,6 +1711,21 @@ After=sys-subsystem-net-devices-ens1.device + + + + + Change the terminal background color to the specified ANSI color as long as the + container runs. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such + as 40, 41, …, 47, 48;2;…, + 48;5;…. See ANSI + Escape Code (Wikipedia) for details. Assign an empty string to disable any coloring. + + + + + diff --git a/shell-completion/bash/systemd-nspawn b/shell-completion/bash/systemd-nspawn index cc3d2f6598..b12711be80 100644 --- a/shell-completion/bash/systemd-nspawn +++ b/shell-completion/bash/systemd-nspawn @@ -73,7 +73,7 @@ _systemd_nspawn() { --pivot-root --property --private-users --private-users-ownership --network-namespace-path --network-ipvlan --network-veth-extra --network-zone -p --port --system-call-filter --overlay --overlay-ro --settings --rlimit --hostname --no-new-privileges --oom-score-adjust --cpu-affinity - --resolv-conf --timezone --root-hash-sig' + --resolv-conf --timezone --root-hash-sig --background' ) _init_completion || return diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 89ef0e4daa..5820be8d13 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -236,6 +236,7 @@ static bool arg_suppress_sync = false; static char *arg_settings_filename = NULL; static Architecture arg_architecture = _ARCHITECTURE_INVALID; static ImagePolicy *arg_image_policy = NULL; +static char *arg_background = NULL; STATIC_DESTRUCTOR_REGISTER(arg_directory, freep); STATIC_DESTRUCTOR_REGISTER(arg_template, freep); @@ -272,6 +273,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_sysctl, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_settings_filename, freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); +STATIC_DESTRUCTOR_REGISTER(arg_background, freep); static int handle_arg_console(const char *arg) { if (streq(arg, "help")) { @@ -450,6 +452,7 @@ static int help(void) { " --console=MODE Select how stdin/stdout/stderr and /dev/console are\n" " set up for the container.\n" " -P --pipe Equivalent to --console=pipe\n\n" + " --background=COLOR Set ANSI color for background\n" "%3$sCredentials:%4$s\n" " --set-credential=ID:VALUE\n" " Pass a credential with literal value to container.\n" @@ -745,6 +748,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_BIND_USER, ARG_SUPPRESS_SYNC, ARG_IMAGE_POLICY, + ARG_BACKGROUND, }; static const struct option options[] = { @@ -819,6 +823,7 @@ static int parse_argv(int argc, char *argv[]) { { "bind-user", required_argument, NULL, ARG_BIND_USER }, { "suppress-sync", required_argument, NULL, ARG_SUPPRESS_SYNC }, { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, + { "background", required_argument, NULL, ARG_BACKGROUND }, {} }; @@ -1608,6 +1613,12 @@ static int parse_argv(int argc, char *argv[]) { return r; break; + case ARG_BACKGROUND: + r = free_and_strdup_warn(&arg_background, optarg); + if (r < 0) + return r; + break; + case '?': return -EINVAL; @@ -5339,9 +5350,14 @@ static int run_container( return log_error_errno(r, "Failed to create PTY forwarder: %m"); if (arg_console_width != UINT_MAX || arg_console_height != UINT_MAX) - (void) pty_forward_set_width_height(forward, - arg_console_width, - arg_console_height); + (void) pty_forward_set_width_height( + forward, + arg_console_width, + arg_console_height); + + if (!isempty(arg_background)) + (void) pty_forward_set_background_color(forward, arg_background); + break; default: From f9bc96f65795038db84f00611c2831a7c4e58696 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Jan 2024 23:59:00 +0100 Subject: [PATCH 2/4] color-util: make return values of rgb_to_hsv() optional When we want to tint the bg color we don't care about the hue, we want to set it ourself after all, hence make the arguments optional, so that we don't even have to ask for it. --- src/shared/color-util.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/shared/color-util.c b/src/shared/color-util.c index 776445ecfc..9d714c0fff 100644 --- a/src/shared/color-util.c +++ b/src/shared/color-util.c @@ -11,35 +11,38 @@ void rgb_to_hsv(double r, double g, double b, assert(r >= 0 && r <= 1); assert(g >= 0 && g <= 1); assert(b >= 0 && b <= 1); - assert(ret_h); - assert(ret_s); - assert(ret_v); double max_color = fmax(r, fmax(g, b)); double min_color = fmin(r, fmin(g, b)); double delta = max_color - min_color; - *ret_v = max_color * 100.0; + if (ret_v) + *ret_v = max_color * 100.0; - if (max_color > 0) - *ret_s = delta / max_color * 100.0; - else { - *ret_s = 0; - *ret_h = NAN; + if (max_color <= 0) { + if (ret_s) + *ret_s = 0; + if (ret_h) + *ret_h = NAN; return; } - if (delta > 0) { - if (r >= max_color) - *ret_h = 60 * fmod((g - b) / delta, 6); - else if (g >= max_color) - *ret_h = 60 * (((b - r) / delta) + 2); - else if (b >= max_color) - *ret_h = 60 * (((r - g) / delta) + 4); + if (ret_s) + *ret_s = delta / max_color * 100.0; - *ret_h = fmod(*ret_h, 360); - } else - *ret_h = NAN; + if (ret_h) { + if (delta > 0) { + if (r >= max_color) + *ret_h = 60 * fmod((g - b) / delta, 6); + else if (g >= max_color) + *ret_h = 60 * (((b - r) / delta) + 2); + else if (b >= max_color) + *ret_h = 60 * (((r - g) / delta) + 4); + + *ret_h = fmod(*ret_h, 360); + } else + *ret_h = NAN; + } } void hsv_to_rgb(double h, double s, double v, From 3ef072ee268f817730f81fe8f81a3072fa990b4f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Jan 2024 23:59:54 +0100 Subject: [PATCH 3/4] pretty-print: split out color tinting into a helper of its own --- src/run/run.c | 33 +++++++-------------------------- src/shared/pretty-print.c | 30 ++++++++++++++++++++++++++++++ src/shared/pretty-print.h | 2 ++ 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/run/run.c b/src/run/run.c index c3313c6c52..a695fea0f0 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -17,7 +17,6 @@ #include "bus-unit-util.h" #include "bus-wait-for-jobs.h" #include "calendarspec.h" -#include "color-util.h" #include "env-util.h" #include "escape.h" #include "exit-status.h" @@ -940,34 +939,16 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { return log_oom(); if (!arg_background && arg_stdio == ARG_STDIO_PTY) { - double red, green, blue; + double hue; - r = get_default_background_color(&red, &green, &blue); + if (!arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0")) + hue = 0; /* red */ + else + hue = 60 /* yellow */; + + r = terminal_tint_color(hue, &arg_background); if (r < 0) log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m"); - else { - double h, s, v; - - rgb_to_hsv(red, green, blue, &h, &s, &v); - - if (!arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0")) - h = 0; /* red */ - else - h = 60 /* yellow */; - - if (v > 50) /* If the background is bright, then pull down saturation */ - s = 25; - else /* otherwise pump it up */ - s = 75; - - v = MAX(30, v); /* Make sure we don't hide the color in black */ - - uint8_t r8, g8, b8; - hsv_to_rgb(h, s, v, &r8, &g8, &b8); - - if (asprintf(&arg_background, "48;2;%u;%u;%u", r8, g8, b8) < 0) - return log_oom(); - } } return 1; diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c index cb1ac2bb76..7dd31a099f 100644 --- a/src/shared/pretty-print.c +++ b/src/shared/pretty-print.c @@ -5,6 +5,7 @@ #include #include "alloc-util.h" +#include "color-util.h" #include "conf-files.h" #include "constants.h" #include "env-util.h" @@ -441,3 +442,32 @@ int conf_files_cat(const char *root, const char *name, CatFlags flags) { return cat_files(path, files, flags); } + +int terminal_tint_color(double hue, char **ret) { + double red, green, blue; + int r; + + assert(ret); + + r = get_default_background_color(&red, &green, &blue); + if (r < 0) + return log_debug_errno(r, "Unable to get terminal background color: %m"); + + double s, v; + rgb_to_hsv(red, green, blue, /* h= */ NULL, &s, &v); + + if (v > 50) /* If the background is bright, then pull down saturation */ + s = 25; + else /* otherwise pump it up */ + s = 75; + + v = MAX(30, v); /* Make sure we don't hide the color in black */ + + uint8_t r8, g8, b8; + hsv_to_rgb(hue, s, v, &r8, &g8, &b8); + + if (asprintf(ret, "48;2;%u;%u;%u", r8, g8, b8) < 0) + return -ENOMEM; + + return 0; +} diff --git a/src/shared/pretty-print.h b/src/shared/pretty-print.h index c17e976580..6069318be7 100644 --- a/src/shared/pretty-print.h +++ b/src/shared/pretty-print.h @@ -47,3 +47,5 @@ static inline const char *green_check_mark_internal(char buffer[static GREEN_CHE #define GREEN_CHECK_MARK() green_check_mark_internal((char[GREEN_CHECK_MARK_MAX]) {}) #define COLOR_MARK_BOOL(b) ((b) ? GREEN_CHECK_MARK() : RED_CROSS_MARK()) + +int terminal_tint_color(double hue, char **ret); From 8bf3891e8e322f16f55665a99af26ec9a843657d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 20 Jan 2024 00:00:20 +0100 Subject: [PATCH 4/4] nspawn: tint the terminal bg blue if we are in a container Let's give people a hint that they are not operating on the host. --- src/nspawn/nspawn.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 5820be8d13..50bb0565ca 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -5355,7 +5355,15 @@ static int run_container( arg_console_width, arg_console_height); - if (!isempty(arg_background)) + if (!arg_background) { + _cleanup_free_ char *bg = NULL; + + r = terminal_tint_color(220 /* blue */, &bg); + if (r < 0) + log_debug_errno(r, "Failed to determine terminal background color, not tinting."); + else + (void) pty_forward_set_background_color(forward, bg); + } else if (!isempty(arg_background)) (void) pty_forward_set_background_color(forward, arg_background); break;