]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
run/uid0: tint the terminal background color (and add new --background= switch)
authorLennart Poettering <lennart@poettering.net>
Wed, 20 Dec 2023 11:09:27 +0000 (12:09 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 21 Dec 2023 18:15:01 +0000 (19:15 +0100)
This adds a new --background= switch that allows specifiying a
background color for the terminal while the tool runs.

It also teaches the tool when invoked as uid0 to tint the terminal in a
reddish hue when operating as root, and in a yellowish hue when
operating as any other user.

This should highlight nicely when the user is operating with elevated
privileges, or changed privileges.

man/systemd-run.xml
man/uid0.xml
src/run/run.c

index a334ab94d547e9416657cf3a1b0a6807036f34b6..7b508a462c568586adde56d16c221712202a294d 100644 (file)
         <xi:include href="version-info.xml" xpointer="v256"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--background=<replaceable>COLOR</replaceable></option></term>
+
+        <listitem><para>Change the terminal background color to the specified ANSI color as long as the
+        session lasts. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such as
+        <literal>40</literal>, <literal>41</literal>, …, <literal>47</literal>, <literal>48;2;…</literal>,
+        <literal>48;5;…</literal>. See <ulink
+        url="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">ANSI
+        Escape Code (Wikipedia)</ulink> for details.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
       <xi:include href="user-system-options.xml" xpointer="user" />
       <xi:include href="user-system-options.xml" xpointer="system" />
       <xi:include href="user-system-options.xml" xpointer="host" />
index 6ef868a8e53d1bfeb8d6ea7a37c1318541c48314..0d36ca77a4f925ff6e8b68677e0085b4feddf1a5 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--background=<replaceable>COLOR</replaceable></option></term>
+
+        <listitem><para>Change the terminal background color to the specified ANSI color as long as the
+        session lasts. If not specified, the background will be tinted in a reddish tone when operating as
+        root, and in a yellowish tone when operating under another UID, as reminder of the changed
+        privileges. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such as
+        <literal>40</literal>, <literal>41</literal>, …, <literal>47</literal>, <literal>48;2;…</literal>,
+        <literal>48;5;…</literal>. See <ulink
+        url="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">ANSI
+        Escape Code (Wikipedia)</ulink> for details. Set to an empty string to disable.</para>
+
+        <para>Example: <literal>--background=44</literal> for a blue background.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
       <xi:include href="user-system-options.xml" xpointer="machine" />
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
index d90dbbe411c0b2a8bf2d17b5eeddbb4f23c0e918..920e38af0c9b99df099e6c36ca3e7883333e7df5 100644 (file)
@@ -17,6 +17,7 @@
 #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"
@@ -75,6 +76,7 @@ static bool arg_shell = false;
 static char **arg_cmdline = NULL;
 static char *arg_exec_path = NULL;
 static bool arg_ignore_failure = false;
+static char *arg_background = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
@@ -85,6 +87,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_timer_property, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
 
 static int help(void) {
         _cleanup_free_ char *link = NULL;
@@ -127,6 +130,7 @@ static int help(void) {
                "  -G --collect                    Unload unit after it ran, even when failed\n"
                "  -S --shell                      Invoke a $SHELL interactively\n"
                "     --ignore-failure             Ignore the exit status of the invoked process\n"
+               "     --background=COLOR           Set ANSI color for background\n"
                "\n%3$sPath options:%4$s\n"
                "     --path-property=NAME=VALUE   Set path unit property\n"
                "\n%3$sSocket options:%4$s\n"
@@ -174,6 +178,7 @@ static int help_sudo_mode(void) {
                "     --nice=NICE                  Nice level\n"
                "  -D --chdir=PATH                 Set working directory\n"
                "     --setenv=NAME[=VALUE]        Set environment variable\n"
+               "     --background=COLOR           Set ANSI color for background\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                ansi_highlight(),
@@ -244,6 +249,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_WORKING_DIRECTORY,
                 ARG_SHELL,
                 ARG_IGNORE_FAILURE,
+                ARG_BACKGROUND,
         };
 
         static const struct option options[] = {
@@ -290,6 +296,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "same-dir",           no_argument,       NULL, 'd'                    },
                 { "shell",              no_argument,       NULL, 'S'                    },
                 { "ignore-failure",     no_argument,       NULL, ARG_IGNORE_FAILURE     },
+                { "background",         no_argument,       NULL, ARG_BACKGROUND         },
                 {},
         };
 
@@ -333,7 +340,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_DESCRIPTION:
-                        r = free_and_strdup(&arg_description, optarg);
+                        r = free_and_strdup_warn(&arg_description, optarg);
                         if (r < 0)
                                 return r;
                         break;
@@ -579,6 +586,12 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_ignore_failure = true;
                         break;
 
+                case ARG_BACKGROUND:
+                        r = free_and_strdup_warn(&arg_background, optarg);
+                        if (r < 0)
+                                return r;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -719,6 +732,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
                 ARG_SLICE_INHERIT,
                 ARG_NICE,
                 ARG_SETENV,
+                ARG_BACKGROUND,
         };
 
         /* If invoked as "uid0" binary, let's expose a more sudo-like interface. We add various extensions
@@ -739,6 +753,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
                 { "nice",               required_argument, NULL, ARG_NICE               },
                 { "chdir",              required_argument, NULL, 'D'                    },
                 { "setenv",             required_argument, NULL, ARG_SETENV             },
+                { "background",         required_argument, NULL, ARG_BACKGROUND         },
                 {},
         };
 
@@ -823,6 +838,13 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_BACKGROUND:
+                        r = free_and_strdup_warn(&arg_background, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -916,6 +938,37 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
         if (strv_extend(&arg_property, "PAMName=systemd-uid0") < 0)
                 return log_oom();
 
+        if (!arg_background && arg_stdio == ARG_STDIO_PTY) {
+                double red, green, blue;
+
+                r = get_default_background_color(&red, &green, &blue);
+                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;
 }
 
@@ -1701,6 +1754,9 @@ static int start_transient_service(sd_bus *bus) {
 
                         /* Make sure to process any TTY events before we process bus events */
                         (void) pty_forward_set_priority(c.forward, SD_EVENT_PRIORITY_IMPORTANT);
+
+                        if (!isempty(arg_background))
+                                (void) pty_forward_set_background_color(c.forward, arg_background);
                 }
 
                 path = unit_dbus_path_from_name(service);