Merge pull request #17702 from rnhmjoj/master

Extend $SYSTEMD_COLORS to switch colors mode
This commit is contained in:
Lennart Poettering 2020-12-16 19:26:40 +01:00 committed by GitHub
commit e4dde4e87d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 137 additions and 72 deletions

View File

@ -94,10 +94,11 @@
<varlistentry id='colors'>
<term><varname>$SYSTEMD_COLORS</varname></term>
<listitem><para>The value must be a boolean. Controls whether colorized output should be
generated. This can be specified to override the decision that <command>systemd</command> makes based
on <varname>$TERM</varname> and what the console is connected to.</para>
</listitem>
<listitem><para>Takes a boolean argument. When true, <command>systemd</command> and related utilities
will use colors in their output, otherwise the output will be monochrome. Additionally, the variable can
take one of the following special values: <literal>16</literal>, <literal>256</literal> to restrict the use
of colors to the base 16 or 256 ANSI colors, respectively. This can be specified to override the automatic
decision based on <varname>$TERM</varname> and what the console is connected to.</para></listitem>
</varlistentry>
<!-- This is not documented on purpose, because it is not clear if $NO_COLOR will become supported

View File

@ -404,7 +404,7 @@ static int write_to_console(
if (show_location) {
const char *lon = "", *loff = "";
if (log_get_show_color()) {
lon = ANSI_HIGHLIGHT_YELLOW4;
lon = ansi_highlight_yellow4();
loff = ANSI_NORMAL;
}

View File

@ -47,7 +47,7 @@ static volatile unsigned cached_columns = 0;
static volatile unsigned cached_lines = 0;
static volatile int cached_on_tty = -1;
static volatile int cached_colors_enabled = -1;
static volatile int cached_color_mode = _COLOR_INVALID;
static volatile int cached_underline_enabled = -1;
int chvt(int vt) {
@ -164,8 +164,7 @@ int ask_char(char *ret, const char *replies, const char *fmt, ...) {
char c;
bool need_nl = true;
if (colors_enabled())
fputs(ANSI_HIGHLIGHT, stdout);
fputs(ansi_highlight(), stdout);
putchar('\r');
@ -173,8 +172,7 @@ int ask_char(char *ret, const char *replies, const char *fmt, ...) {
vprintf(fmt, ap);
va_end(ap);
if (colors_enabled())
fputs(ANSI_NORMAL, stdout);
fputs(ansi_normal(), stdout);
fflush(stdout);
@ -213,15 +211,13 @@ int ask_string(char **ret, const char *text, ...) {
assert(ret);
assert(text);
if (colors_enabled())
fputs(ANSI_HIGHLIGHT, stdout);
fputs(ansi_highlight(), stdout);
va_start(ap, text);
vprintf(text, ap);
va_end(ap);
if (colors_enabled())
fputs(ANSI_NORMAL, stdout);
fputs(ansi_normal(), stdout);
fflush(stdout);
@ -867,7 +863,7 @@ void reset_terminal_feature_caches(void) {
cached_columns = 0;
cached_lines = 0;
cached_colors_enabled = -1;
cached_color_mode = _COLOR_INVALID;
cached_underline_enabled = -1;
cached_on_tty = -1;
}
@ -1206,38 +1202,57 @@ bool terminal_is_dumb(void) {
return getenv_terminal_is_dumb();
}
bool colors_enabled(void) {
static ColorMode parse_systemd_colors(void) {
const char *e;
int r;
/* Returns true if colors are considered supported on our stdout. For that we check $SYSTEMD_COLORS first
* (which is the explicit way to turn colors on/off). If that 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. */
e = getenv("SYSTEMD_COLORS");
if (!e)
return _COLOR_INVALID;
if (streq(e, "16"))
return COLOR_16;
if (streq(e, "256"))
return COLOR_256;
r = parse_boolean(e);
if (r >= 0)
return r > 0 ? COLOR_ON : COLOR_OFF;
return _COLOR_INVALID;
}
if (cached_colors_enabled < 0) {
int val;
ColorMode get_color_mode(void) {
val = getenv_bool("SYSTEMD_COLORS");
if (val >= 0)
cached_colors_enabled = val;
/* Returns the mode used to choose output colors. The possible modes are COLOR_OFF for no colors,
* COLOR_16 for only the base 16 ANSI colors, COLOR_256 for more colors and COLOR_ON for unrestricted
* color output. For that we check $SYSTEMD_COLORS first (which is the explicit way to
* change the mode). If that 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. */
ColorMode m;
if (cached_color_mode < 0) {
m = parse_systemd_colors();
if (m >= 0)
cached_color_mode = m;
else if (getenv("NO_COLOR"))
/* We only check for the presence of the variable; value is ignored. */
cached_colors_enabled = false;
cached_color_mode = COLOR_OFF;
else if (getpid_cached() == 1)
/* PID1 outputs to the console without holding it open all the time */
cached_colors_enabled = !getenv_terminal_is_dumb();
/* PID1 outputs to the console without holding it open all the time.
* Also note the Linux console can only handle 16 colors.
*/
cached_color_mode = getenv_terminal_is_dumb() ? COLOR_OFF : COLOR_16;
else
cached_colors_enabled = !terminal_is_dumb();
cached_color_mode = terminal_is_dumb() ? COLOR_OFF : COLOR_256;
}
return cached_colors_enabled;
return cached_color_mode;
}
bool dev_console_colors_enabled(void) {
_cleanup_free_ char *s = NULL;
int b;
ColorMode m;
/* Returns true if we assume that color is supported on /dev/console.
*
@ -1246,9 +1261,9 @@ bool dev_console_colors_enabled(void) {
* line. If we find $TERM set we assume color if it's not set to "dumb", similarly to how regular
* colors_enabled() operates. */
b = getenv_bool("SYSTEMD_COLORS");
if (b >= 0)
return b;
m = parse_systemd_colors();
if (m >= 0)
return m;
if (getenv("NO_COLOR"))
return false;
@ -1353,7 +1368,7 @@ void get_log_colors(int priority, const char **on, const char **off, const char
if (priority <= LOG_ERR) {
if (on)
*on = ANSI_HIGHLIGHT_RED;
*on = ansi_highlight_red();
if (off)
*off = ANSI_NORMAL;
if (highlight)
@ -1361,7 +1376,7 @@ void get_log_colors(int priority, const char **on, const char **off, const char
} else if (priority <= LOG_WARNING) {
if (on)
*on = ANSI_HIGHLIGHT_YELLOW;
*on = ansi_highlight_yellow();
if (off)
*off = ANSI_NORMAL;
if (highlight)
@ -1373,14 +1388,14 @@ void get_log_colors(int priority, const char **on, const char **off, const char
if (off)
*off = ANSI_NORMAL;
if (highlight)
*highlight = ANSI_HIGHLIGHT_RED;
*highlight = ansi_highlight_red();
} else if (priority >= LOG_DEBUG) {
if (on)
*on = ANSI_GREY;
*on = ansi_grey();
if (off)
*off = ANSI_NORMAL;
if (highlight)
*highlight = ANSI_HIGHLIGHT_RED;
*highlight = ansi_highlight_red();
}
}

View File

@ -61,6 +61,10 @@
#define ANSI_HIGHLIGHT "\x1B[0;1;39m"
#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m"
/* Fallback colors: 256 -> 16 */
#define ANSI_HIGHLIGHT_GREY_FALLBACK "\x1B[0;1;90m"
#define ANSI_HIGHLIGHT_YELLOW_FALLBACK "\x1B[0;1;33m"
/* Reset/clear ANSI styles */
#define ANSI_NORMAL "\x1B[0m"
@ -93,6 +97,23 @@ typedef enum AcquireTerminalFlags {
ACQUIRE_TERMINAL_PERMISSIVE = 1 << 2,
} AcquireTerminalFlags;
/* Limits the use of ANSI colors to a subset. */
typedef enum ColorMode {
/* No colors, monochrome output. */
COLOR_OFF = 0,
/* All colors, no restrictions. */
COLOR_ON = 1,
/* Only the base 16 colors. */
COLOR_16 = 16,
/* Only 256 colors. */
COLOR_256 = 256,
_COLOR_INVALID = -1,
} ColorMode;
int acquire_terminal(const char *name, AcquireTerminalFlags flags, usec_t timeout);
int release_terminal(void);
@ -127,19 +148,44 @@ void reset_terminal_feature_caches(void);
bool on_tty(void);
bool terminal_is_dumb(void);
bool colors_enabled(void);
ColorMode get_color_mode(void);
bool underline_enabled(void);
bool dev_console_colors_enabled(void);
static inline bool colors_enabled(void) {
/* Returns true if colors are considered supported on our stdout. */
return get_color_mode() != COLOR_OFF;
}
#define DEFINE_ANSI_FUNC(name, NAME) \
static inline const char *ansi_##name(void) { \
return colors_enabled() ? ANSI_##NAME : ""; \
}
#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME, REPLACEMENT) \
static inline const char *ansi_##name(void) { \
return underline_enabled() ? ANSI_##NAME : \
colors_enabled() ? ANSI_##REPLACEMENT : ""; \
#define DEFINE_ANSI_FUNC_256(name, NAME, FALLBACK) \
static inline const char *ansi_##name(void) { \
switch (get_color_mode()) { \
case COLOR_OFF: return ""; \
case COLOR_16: return ANSI_##FALLBACK; \
default : return ANSI_##NAME; \
} \
}
#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME) \
static inline const char *ansi_##name(void) { \
return underline_enabled() ? ANSI_##NAME ANSI_UNDERLINE : \
colors_enabled() ? ANSI_##NAME : ""; \
}
#define DEFINE_ANSI_FUNC_UNDERLINE_256(name, NAME, FALLBACK) \
static inline const char *ansi_##name(void) { \
switch (get_color_mode()) { \
case COLOR_OFF: return ""; \
case COLOR_16: return underline_enabled() ? ANSI_##FALLBACK ANSI_UNDERLINE : ANSI_##FALLBACK; \
default : return underline_enabled() ? ANSI_##NAME ANSI_UNDERLINE: ANSI_##NAME; \
} \
}
DEFINE_ANSI_FUNC(normal, NORMAL);
@ -152,7 +198,7 @@ DEFINE_ANSI_FUNC(blue, BLUE);
DEFINE_ANSI_FUNC(magenta, MAGENTA);
DEFINE_ANSI_FUNC(cyan, CYAN);
DEFINE_ANSI_FUNC(white, WHITE);
DEFINE_ANSI_FUNC(grey, GREY);
DEFINE_ANSI_FUNC_256(grey, GREY, BRIGHT_BLACK);
DEFINE_ANSI_FUNC(bright_black, BRIGHT_BLACK);
DEFINE_ANSI_FUNC(bright_red, BRIGHT_RED);
@ -163,29 +209,30 @@ DEFINE_ANSI_FUNC(bright_magenta, BRIGHT_MAGENTA);
DEFINE_ANSI_FUNC(bright_cyan, BRIGHT_CYAN);
DEFINE_ANSI_FUNC(bright_white, BRIGHT_WHITE);
DEFINE_ANSI_FUNC(highlight_black, HIGHLIGHT_BLACK);
DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW);
DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
DEFINE_ANSI_FUNC(highlight_cyan, HIGHLIGHT_CYAN);
DEFINE_ANSI_FUNC(highlight_grey, HIGHLIGHT_GREY);
DEFINE_ANSI_FUNC(highlight_white, HIGHLIGHT_WHITE);
DEFINE_ANSI_FUNC(highlight_black, HIGHLIGHT_BLACK);
DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
DEFINE_ANSI_FUNC_256(highlight_yellow, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
DEFINE_ANSI_FUNC_256(highlight_yellow4, HIGHLIGHT_YELLOW4, HIGHLIGHT_YELLOW_FALLBACK);
DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
DEFINE_ANSI_FUNC(highlight_cyan, HIGHLIGHT_CYAN);
DEFINE_ANSI_FUNC_256(highlight_grey, HIGHLIGHT_GREY, HIGHLIGHT_GREY_FALLBACK);
DEFINE_ANSI_FUNC(highlight_white, HIGHLIGHT_WHITE);
static inline const char* _ansi_highlight_yellow(void) {
return colors_enabled() ? _ANSI_HIGHLIGHT_YELLOW : "";
}
DEFINE_ANSI_FUNC_UNDERLINE(underline, UNDERLINE, NORMAL);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT_UNDERLINE, HIGHLIGHT);
DEFINE_ANSI_FUNC_UNDERLINE(grey_underline, GREY_UNDERLINE, GREY);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE, HIGHLIGHT_RED);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE, HIGHLIGHT_GREEN);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE, HIGHLIGHT_YELLOW);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE, HIGHLIGHT_BLUE);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta_underline, HIGHLIGHT_MAGENTA_UNDERLINE, HIGHLIGHT_MAGENTA);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_grey_underline, HIGHLIGHT_GREY_UNDERLINE, HIGHLIGHT_GREY);
DEFINE_ANSI_FUNC_UNDERLINE(underline, NORMAL);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT);
DEFINE_ANSI_FUNC_UNDERLINE_256(grey_underline, GREY, BRIGHT_BLACK);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN);
DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_yellow_underline, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta_underline, HIGHLIGHT_MAGENTA);
DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_grey_underline, HIGHLIGHT_GREY, HIGHLIGHT_GREY_FALLBACK);
int get_ctty_devnr(pid_t pid, dev_t *d);
int get_ctty(pid_t, dev_t *_devnr, char **r);

View File

@ -41,7 +41,9 @@ static void draw_progress(uint64_t p, usec_t *last_usec) {
fputs("\r", stdout);
if (colors_enabled())
fputs("\x1B[?25l" ANSI_HIGHLIGHT_GREEN, stdout);
fputs("\x1B[?25l", stdout);
fputs(ansi_highlight_green(), stdout);
for (i = 0; i < j; i++)
fputs("\xe2\x96\x88", stdout);

View File

@ -481,7 +481,7 @@ int ask_password_tty(
if (!(flags & ASK_PASSWORD_SILENT) && !(flags & ASK_PASSWORD_ECHO)) {
if (use_color)
(void) loop_write(ttyfd, ANSI_GREY, STRLEN(ANSI_GREY), false);
(void) loop_write(ttyfd, ansi_grey(), strlen(ansi_grey()), false);
(void) loop_write(ttyfd, PRESS_TAB, strlen(PRESS_TAB), false);
press_tab_visible = true;
}

View File

@ -1495,7 +1495,7 @@ static void json_format_string(FILE *f, const char *q, JsonFormatFlags flags) {
fputc('"', f);
if (flags & JSON_FORMAT_COLOR)
fputs(ANSI_GREEN, f);
fputs(ansi_green(), f);
for (; *q; q++)
switch (*q) {
@ -1557,7 +1557,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
return -errno;
if (flags & JSON_FORMAT_COLOR)
fputs(ANSI_HIGHLIGHT_BLUE, f);
fputs(ansi_highlight_blue(), f);
fprintf(f, "%.*Le", DECIMAL_DIG, json_variant_real(v));
@ -1570,7 +1570,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
case JSON_VARIANT_INTEGER:
if (flags & JSON_FORMAT_COLOR)
fputs(ANSI_HIGHLIGHT_BLUE, f);
fputs(ansi_highlight_blue(), f);
fprintf(f, "%" PRIdMAX, json_variant_integer(v));
@ -1580,7 +1580,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
case JSON_VARIANT_UNSIGNED:
if (flags & JSON_FORMAT_COLOR)
fputs(ANSI_HIGHLIGHT_BLUE, f);
fputs(ansi_highlight_blue(), f);
fprintf(f, "%" PRIuMAX, json_variant_unsigned(v));

View File

@ -67,7 +67,7 @@ static int print_catalog(FILE *f, sd_journal *j) {
prefix = "--";
if (colors_enabled())
newline = strjoina(ANSI_NORMAL "\n" ANSI_GREY, prefix, ANSI_NORMAL " " ANSI_GREEN);
newline = strjoina(ANSI_NORMAL "\n", ansi_grey(), prefix, ANSI_NORMAL " ", ansi_green());
else
newline = strjoina("\n", prefix, " ");
@ -76,7 +76,7 @@ static int print_catalog(FILE *f, sd_journal *j) {
return log_oom();
if (colors_enabled())
fprintf(f, ANSI_GREY "%s" ANSI_NORMAL " " ANSI_GREEN, prefix);
fprintf(f, "%s%s %s%s", ansi_grey(), prefix, ANSI_NORMAL, ansi_green());
else
fprintf(f, "%s ", prefix);