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'> <varlistentry id='colors'>
<term><varname>$SYSTEMD_COLORS</varname></term> <term><varname>$SYSTEMD_COLORS</varname></term>
<listitem><para>The value must be a boolean. Controls whether colorized output should be <listitem><para>Takes a boolean argument. When true, <command>systemd</command> and related utilities
generated. This can be specified to override the decision that <command>systemd</command> makes based will use colors in their output, otherwise the output will be monochrome. Additionally, the variable can
on <varname>$TERM</varname> and what the console is connected to.</para> take one of the following special values: <literal>16</literal>, <literal>256</literal> to restrict the use
</listitem> 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> </varlistentry>
<!-- This is not documented on purpose, because it is not clear if $NO_COLOR will become supported <!-- 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) { if (show_location) {
const char *lon = "", *loff = ""; const char *lon = "", *loff = "";
if (log_get_show_color()) { if (log_get_show_color()) {
lon = ANSI_HIGHLIGHT_YELLOW4; lon = ansi_highlight_yellow4();
loff = ANSI_NORMAL; loff = ANSI_NORMAL;
} }

View File

@ -47,7 +47,7 @@ static volatile unsigned cached_columns = 0;
static volatile unsigned cached_lines = 0; static volatile unsigned cached_lines = 0;
static volatile int cached_on_tty = -1; 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; static volatile int cached_underline_enabled = -1;
int chvt(int vt) { int chvt(int vt) {
@ -164,8 +164,7 @@ int ask_char(char *ret, const char *replies, const char *fmt, ...) {
char c; char c;
bool need_nl = true; bool need_nl = true;
if (colors_enabled()) fputs(ansi_highlight(), stdout);
fputs(ANSI_HIGHLIGHT, stdout);
putchar('\r'); putchar('\r');
@ -173,8 +172,7 @@ int ask_char(char *ret, const char *replies, const char *fmt, ...) {
vprintf(fmt, ap); vprintf(fmt, ap);
va_end(ap); va_end(ap);
if (colors_enabled()) fputs(ansi_normal(), stdout);
fputs(ANSI_NORMAL, stdout);
fflush(stdout); fflush(stdout);
@ -213,15 +211,13 @@ int ask_string(char **ret, const char *text, ...) {
assert(ret); assert(ret);
assert(text); assert(text);
if (colors_enabled()) fputs(ansi_highlight(), stdout);
fputs(ANSI_HIGHLIGHT, stdout);
va_start(ap, text); va_start(ap, text);
vprintf(text, ap); vprintf(text, ap);
va_end(ap); va_end(ap);
if (colors_enabled()) fputs(ansi_normal(), stdout);
fputs(ANSI_NORMAL, stdout);
fflush(stdout); fflush(stdout);
@ -867,7 +863,7 @@ void reset_terminal_feature_caches(void) {
cached_columns = 0; cached_columns = 0;
cached_lines = 0; cached_lines = 0;
cached_colors_enabled = -1; cached_color_mode = _COLOR_INVALID;
cached_underline_enabled = -1; cached_underline_enabled = -1;
cached_on_tty = -1; cached_on_tty = -1;
} }
@ -1206,38 +1202,57 @@ bool terminal_is_dumb(void) {
return getenv_terminal_is_dumb(); 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 e = getenv("SYSTEMD_COLORS");
* (which is the explicit way to turn colors on/off). If that didn't work we turn colors off unless we are on a if (!e)
* 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 return _COLOR_INVALID;
* we are PID 1 then we do not check whether we are connected to a TTY, because we don't keep /dev/console open if (streq(e, "16"))
* continuously due to fear of SAK, and hence things are a bit weird. */ 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) { ColorMode get_color_mode(void) {
int val;
val = getenv_bool("SYSTEMD_COLORS"); /* Returns the mode used to choose output colors. The possible modes are COLOR_OFF for no colors,
if (val >= 0) * COLOR_16 for only the base 16 ANSI colors, COLOR_256 for more colors and COLOR_ON for unrestricted
cached_colors_enabled = val; * 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")) else if (getenv("NO_COLOR"))
/* We only check for the presence of the variable; value is ignored. */ /* 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) else if (getpid_cached() == 1)
/* PID1 outputs to the console without holding it open all the time */ /* PID1 outputs to the console without holding it open all the time.
cached_colors_enabled = !getenv_terminal_is_dumb(); * Also note the Linux console can only handle 16 colors.
*/
cached_color_mode = getenv_terminal_is_dumb() ? COLOR_OFF : COLOR_16;
else 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) { bool dev_console_colors_enabled(void) {
_cleanup_free_ char *s = NULL; _cleanup_free_ char *s = NULL;
int b; ColorMode m;
/* Returns true if we assume that color is supported on /dev/console. /* 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 * line. If we find $TERM set we assume color if it's not set to "dumb", similarly to how regular
* colors_enabled() operates. */ * colors_enabled() operates. */
b = getenv_bool("SYSTEMD_COLORS"); m = parse_systemd_colors();
if (b >= 0) if (m >= 0)
return b; return m;
if (getenv("NO_COLOR")) if (getenv("NO_COLOR"))
return false; 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 (priority <= LOG_ERR) {
if (on) if (on)
*on = ANSI_HIGHLIGHT_RED; *on = ansi_highlight_red();
if (off) if (off)
*off = ANSI_NORMAL; *off = ANSI_NORMAL;
if (highlight) 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) { } else if (priority <= LOG_WARNING) {
if (on) if (on)
*on = ANSI_HIGHLIGHT_YELLOW; *on = ansi_highlight_yellow();
if (off) if (off)
*off = ANSI_NORMAL; *off = ANSI_NORMAL;
if (highlight) if (highlight)
@ -1373,14 +1388,14 @@ void get_log_colors(int priority, const char **on, const char **off, const char
if (off) if (off)
*off = ANSI_NORMAL; *off = ANSI_NORMAL;
if (highlight) if (highlight)
*highlight = ANSI_HIGHLIGHT_RED; *highlight = ansi_highlight_red();
} else if (priority >= LOG_DEBUG) { } else if (priority >= LOG_DEBUG) {
if (on) if (on)
*on = ANSI_GREY; *on = ansi_grey();
if (off) if (off)
*off = ANSI_NORMAL; *off = ANSI_NORMAL;
if (highlight) 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 "\x1B[0;1;39m"
#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m" #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 */ /* Reset/clear ANSI styles */
#define ANSI_NORMAL "\x1B[0m" #define ANSI_NORMAL "\x1B[0m"
@ -93,6 +97,23 @@ typedef enum AcquireTerminalFlags {
ACQUIRE_TERMINAL_PERMISSIVE = 1 << 2, ACQUIRE_TERMINAL_PERMISSIVE = 1 << 2,
} AcquireTerminalFlags; } 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 acquire_terminal(const char *name, AcquireTerminalFlags flags, usec_t timeout);
int release_terminal(void); int release_terminal(void);
@ -127,19 +148,44 @@ void reset_terminal_feature_caches(void);
bool on_tty(void); bool on_tty(void);
bool terminal_is_dumb(void); bool terminal_is_dumb(void);
bool colors_enabled(void); ColorMode get_color_mode(void);
bool underline_enabled(void); bool underline_enabled(void);
bool dev_console_colors_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) \ #define DEFINE_ANSI_FUNC(name, NAME) \
static inline const char *ansi_##name(void) { \ static inline const char *ansi_##name(void) { \
return colors_enabled() ? ANSI_##NAME : ""; \ return colors_enabled() ? ANSI_##NAME : ""; \
} }
#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME, REPLACEMENT) \ #define DEFINE_ANSI_FUNC_256(name, NAME, FALLBACK) \
static inline const char *ansi_##name(void) { \ static inline const char *ansi_##name(void) { \
return underline_enabled() ? ANSI_##NAME : \ switch (get_color_mode()) { \
colors_enabled() ? ANSI_##REPLACEMENT : ""; \ 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); DEFINE_ANSI_FUNC(normal, NORMAL);
@ -152,7 +198,7 @@ DEFINE_ANSI_FUNC(blue, BLUE);
DEFINE_ANSI_FUNC(magenta, MAGENTA); DEFINE_ANSI_FUNC(magenta, MAGENTA);
DEFINE_ANSI_FUNC(cyan, CYAN); DEFINE_ANSI_FUNC(cyan, CYAN);
DEFINE_ANSI_FUNC(white, WHITE); 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_black, BRIGHT_BLACK);
DEFINE_ANSI_FUNC(bright_red, BRIGHT_RED); 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_cyan, BRIGHT_CYAN);
DEFINE_ANSI_FUNC(bright_white, BRIGHT_WHITE); DEFINE_ANSI_FUNC(bright_white, BRIGHT_WHITE);
DEFINE_ANSI_FUNC(highlight_black, HIGHLIGHT_BLACK); DEFINE_ANSI_FUNC(highlight_black, HIGHLIGHT_BLACK);
DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED); DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN); DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW); DEFINE_ANSI_FUNC_256(highlight_yellow, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE); DEFINE_ANSI_FUNC_256(highlight_yellow4, HIGHLIGHT_YELLOW4, HIGHLIGHT_YELLOW_FALLBACK);
DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA); DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
DEFINE_ANSI_FUNC(highlight_cyan, HIGHLIGHT_CYAN); DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
DEFINE_ANSI_FUNC(highlight_grey, HIGHLIGHT_GREY); DEFINE_ANSI_FUNC(highlight_cyan, HIGHLIGHT_CYAN);
DEFINE_ANSI_FUNC(highlight_white, HIGHLIGHT_WHITE); 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) { static inline const char* _ansi_highlight_yellow(void) {
return colors_enabled() ? _ANSI_HIGHLIGHT_YELLOW : ""; return colors_enabled() ? _ANSI_HIGHLIGHT_YELLOW : "";
} }
DEFINE_ANSI_FUNC_UNDERLINE(underline, UNDERLINE, NORMAL); DEFINE_ANSI_FUNC_UNDERLINE(underline, NORMAL);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT_UNDERLINE, HIGHLIGHT); DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT);
DEFINE_ANSI_FUNC_UNDERLINE(grey_underline, GREY_UNDERLINE, GREY); DEFINE_ANSI_FUNC_UNDERLINE_256(grey_underline, GREY, BRIGHT_BLACK);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE, HIGHLIGHT_RED); DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE, HIGHLIGHT_GREEN); DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE, HIGHLIGHT_YELLOW); DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_yellow_underline, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE, HIGHLIGHT_BLUE); DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta_underline, HIGHLIGHT_MAGENTA_UNDERLINE, HIGHLIGHT_MAGENTA); DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta_underline, HIGHLIGHT_MAGENTA);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_grey_underline, HIGHLIGHT_GREY_UNDERLINE, HIGHLIGHT_GREY); 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_devnr(pid_t pid, dev_t *d);
int get_ctty(pid_t, dev_t *_devnr, char **r); 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); fputs("\r", stdout);
if (colors_enabled()) 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++) for (i = 0; i < j; i++)
fputs("\xe2\x96\x88", stdout); 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 (!(flags & ASK_PASSWORD_SILENT) && !(flags & ASK_PASSWORD_ECHO)) {
if (use_color) 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); (void) loop_write(ttyfd, PRESS_TAB, strlen(PRESS_TAB), false);
press_tab_visible = true; press_tab_visible = true;
} }

View File

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

View File

@ -67,7 +67,7 @@ static int print_catalog(FILE *f, sd_journal *j) {
prefix = "--"; prefix = "--";
if (colors_enabled()) 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 else
newline = strjoina("\n", prefix, " "); newline = strjoina("\n", prefix, " ");
@ -76,7 +76,7 @@ static int print_catalog(FILE *f, sd_journal *j) {
return log_oom(); return log_oom();
if (colors_enabled()) 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 else
fprintf(f, "%s ", prefix); fprintf(f, "%s ", prefix);