ansi-color: fix SYSTEMD_COLORS=true regression when output is piped

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.
This commit is contained in:
Nandakumar Raghavan
2026-03-18 12:03:38 +01:00
committed by Mike Yuan
parent e9396ef165
commit a4aae32478
2 changed files with 21 additions and 13 deletions
+13 -9
View 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 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;
/* 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;
/* 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)
+8 -4
View 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);