basic/term-util: extend $SYSTEMD_COLORS

This commit extends $SYSTEMD_COLORS to an enum variable (compared to
a simple boolean) which specifies the "colors mode". This means that, in
addition to disabling colors altogether, it's now possible to restrict
the console output to 16 or 256 colors only.
This commit is contained in:
rnhmjoj 2020-11-22 02:37:27 +01:00
parent 239952e890
commit c4fea19abb
No known key found for this signature in database
GPG Key ID: BFBAF4C975F76450
3 changed files with 119 additions and 46 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

@ -48,6 +48,7 @@ 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) {
@ -868,6 +869,7 @@ void reset_terminal_feature_caches(void) {
cached_lines = 0;
cached_colors_enabled = -1;
cached_color_mode = _COLOR_INVALID;
cached_underline_enabled = -1;
cached_on_tty = -1;
}
@ -1206,38 +1208,67 @@ 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_color_mode;
}
bool colors_enabled(void) {
/* Returns true if colors are considered supported on our stdout. */
if (cached_colors_enabled < 0)
cached_colors_enabled = get_color_mode() != COLOR_OFF;
return cached_colors_enabled;
}
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 +1277,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;

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);
@ -128,6 +149,7 @@ 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);
@ -136,10 +158,29 @@ bool dev_console_colors_enabled(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 +193,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 +204,29 @@ 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(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);