]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ansi-color: fix SYSTEMD_COLORS=true regression when output is piped
authorNandakumar Raghavan <naraghavan@microsoft.com>
Mon, 16 Mar 2026 10:42:08 +0000 (10:42 +0000)
committerMike Yuan <me@yhndnzj.com>
Wed, 18 Mar 2026 11:03:38 +0000 (12:03 +0100)
The SYSTEMD_COLORS=true/1/yes no longer forced colors
when stdout was not a TTY (e.g. piped), because the COLOR_TRUE bypass
of the terminal_is_dumb() check was accidentally dropped.

Restore the old behavior by guarding the TTY check with
`m != COLOR_TRUE`, so an explicit boolean "true" value continues to
unconditionally force color output regardless of whether stdout is a TTY
or whether $NO_COLOR is set.

src/basic/ansi-color.c
src/test/test-terminal-util.c

index e5c78c93458f670c958ab44476bffbb466a6fd74..36cdca727c93db3eb39aa5060ab3b34560f49d35 100644 (file)
@@ -58,16 +58,20 @@ static ColorMode get_color_mode_impl(void) {
         if (m >= 0 && m < _COLOR_MODE_FIXED_MAX)
                 return m;
 
-        /* Next, check for the presence of $NO_COLOR; value is ignored. */
-        if (m != COLOR_TRUE && getenv("NO_COLOR"))
-                return COLOR_OFF;
-
-        /* If the above didn't work, we turn colors off unless we are on a TTY. And if we are on a TTY we
-         * turn it off if $TERM is set to "dumb". There's one special tweak though: if we are PID 1 then we
-         * do not check whether we are connected to a TTY, because we don't keep /dev/console open
-         * continuously due to fear of SAK, and hence things are a bit weird. */
-        if (getpid_cached() == 1 ? getenv_terminal_is_dumb() : terminal_is_dumb())
-                return COLOR_OFF;
+        /* If SYSTEMD_COLORS=true was set explicitly, skip the environment checks below — the user
+         * explicitly requested colors, so honor it even when stdout is piped or $NO_COLOR is set. */
+        if (m != COLOR_TRUE) {
+                /* Check for the presence of $NO_COLOR; value is ignored. */
+                if (getenv("NO_COLOR"))
+                        return COLOR_OFF;
+
+                /* Turn colors off unless we are on a TTY. And if we are on a TTY we turn it off if $TERM
+                 * is set to "dumb". There's one special tweak though: if we are PID 1 then we do not check
+                 * whether we are connected to a TTY, because we don't keep /dev/console open continuously
+                 * due to fear of SAK, and hence things are a bit weird. */
+                if (getpid_cached() == 1 ? getenv_terminal_is_dumb() : terminal_is_dumb())
+                        return COLOR_OFF;
+        }
 
         /* We failed to figure out any reason to *disable* colors. Let's see how many colors we shall use. */
         if (m == COLOR_AUTO_16)
index 4e2b751ad262776509e046647ed3a5c8a3157448..efaed72b10b9b507ee4b64e54d111386cb2ba3f6 100644 (file)
@@ -338,13 +338,17 @@ TEST(get_color_mode) {
         test_get_color_mode_with_env("SYSTEMD_COLORS", "auto-256",   terminal_is_dumb() ? COLOR_OFF : COLOR_256);
         test_get_color_mode_with_env("SYSTEMD_COLORS", "auto-24bit", terminal_is_dumb() ? COLOR_OFF : COLOR_24BIT);
         ASSERT_OK_ERRNO(setenv("COLORTERM", "truecolor", true));
-        test_get_color_mode_with_env("SYSTEMD_COLORS", "1",          terminal_is_dumb() ? COLOR_OFF : COLOR_24BIT);
-        test_get_color_mode_with_env("SYSTEMD_COLORS", "yes",        terminal_is_dumb() ? COLOR_OFF : COLOR_24BIT);
+        /* SYSTEMD_COLORS=1/yes/true all map to COLOR_TRUE and must force colors on
+         * even when stdout is not a TTY (piped). With COLORTERM=truecolor, we get 24bit. */
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "1",          COLOR_24BIT);
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "yes",        COLOR_24BIT);
         ASSERT_OK_ERRNO(unsetenv("COLORTERM"));
-        test_get_color_mode_with_env("SYSTEMD_COLORS", "true",       terminal_is_dumb() ? COLOR_OFF : COLOR_256);
+        /* Without COLORTERM, COLOR_TRUE still bypasses the TTY check but autodetects depth. */
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "true",       COLOR_256);
 
         ASSERT_OK_ERRNO(setenv("NO_COLOR", "1", true));
-        test_get_color_mode_with_env("SYSTEMD_COLORS", "true",       terminal_is_dumb() ? COLOR_OFF : COLOR_256);
+        /* COLOR_TRUE also bypasses NO_COLOR. */
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "true",       COLOR_256);
         test_get_color_mode_with_env("SYSTEMD_COLORS", "auto-16",    COLOR_OFF);
         test_get_color_mode_with_env("SYSTEMD_COLORS", "auto-256",   COLOR_OFF);
         test_get_color_mode_with_env("SYSTEMD_COLORS", "auto-24bit", COLOR_OFF);