diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | clients/cli/connections.c | 2 | ||||
-rw-r--r-- | clients/cli/meson.build | 1 | ||||
-rw-r--r-- | clients/cli/nmcli.c | 275 | ||||
-rw-r--r-- | clients/cli/nmcli.h | 2 | ||||
-rw-r--r-- | man/nmcli.xml | 272 |
6 files changed, 545 insertions, 8 deletions
diff --git a/Makefile.am b/Makefile.am index 26a6b8863e..808ed91160 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3514,6 +3514,7 @@ clients_cli_nmcli_CPPFLAGS = \ -I$(srcdir)/clients/cli \ $(clients_cppflags) \ $(SANITIZER_EXEC_CFLAGS) \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ -DG_LOG_DOMAIN=\""nmcli"\" \ -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_CLIENT \ -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 8c4995845f..5fb78d467d 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -7538,7 +7538,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t } else nmc->nmc_config_mutable.show_secrets = bb; } else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color")) { - g_debug ("Ignoring erroneous --prompt-color argument.\n"); + g_debug ("Ignoring erroneous --prompt-color argument. Use terminal-colors.d(5) to set the prompt color.\n"); } else if (!cmd_arg_p) { g_print (_("Current nmcli configuration:\n")); g_print ("status-line: %s\n" diff --git a/clients/cli/meson.build b/clients/cli/meson.build index 65317a0ac9..b7f5e19360 100644 --- a/clients/cli/meson.build +++ b/clients/cli/meson.build @@ -27,6 +27,7 @@ deps = [ ] cflags = clients_cflags + [ + '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir), '-DG_LOG_DOMAIN="@0@"'.format(name), '-DNMCLI_LOCALEDIR="@0@"'.format(nm_localedir) ] diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index 89d474c39b..24fa37c638 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -329,22 +329,276 @@ matches_arg (NmCli *nmc, int *argc, char ***argv, const char *pattern, char **ar return TRUE; } +/*************************************************************************************/ + typedef enum { NMC_USE_COLOR_AUTO, NMC_USE_COLOR_YES, NMC_USE_COLOR_NO, } NmcColorOption; +/* Checks whether a particular terminal-colors.d(5) file (.enabled, .disabled or .schem) + * exists. If contents is non-NULL, it returns the content. */ +static gboolean +check_colors_file (NmCli *nmc, NmcColorOption *color_option, + const char *base_dir, const char *name, const char *term, const char *type, + char **contents) +{ + char *filename; + gboolean exists; + + filename = g_strdup_printf ("%s/terminal-colors.d/%s%s%s%s%s", + base_dir, + name ? name : "", + term ? "@" : "", term ? term : "", + (name || term) ? "." : "", + type); + if (contents) + exists = g_file_get_contents (filename, contents, NULL, NULL); + else + exists = g_file_test (filename, G_FILE_TEST_EXISTS); + g_free (filename); + + return exists; +} + static void -set_colors (NmCli *nmc, NmcColorOption *color_option) +check_colors_files_for_term (NmCli *nmc, NmcColorOption *color_option, + const char *base_dir, const char *name, const char *term) { - if (*color_option == NMC_USE_COLOR_AUTO) { + if ( *color_option == NMC_USE_COLOR_AUTO + && check_colors_file (nmc, color_option, base_dir, name, term, "enable", NULL)) { + *color_option = NMC_USE_COLOR_YES; + } + + if ( *color_option == NMC_USE_COLOR_AUTO + && check_colors_file (nmc, color_option, base_dir, name, term, "disable", NULL)) { + *color_option = NMC_USE_COLOR_NO; + } + + if (*color_option == NMC_USE_COLOR_NO) { + /* No need to bother any further. */ + return; + } + + if (nmc->palette_buffer == NULL) + check_colors_file (nmc, color_option, base_dir, name, term, "schem", &nmc->palette_buffer); +} + +static void +check_colors_files_for_name (NmCli *nmc, NmcColorOption *color_option, + const char *base_dir, const char *name) +{ + const gchar *term; + + /* Take a shortcut if the directory is not there. */ + if (!g_file_test (base_dir, G_FILE_TEST_EXISTS)) + return; + + term = g_getenv ("TERM"); + if (term) + check_colors_files_for_term (nmc, color_option, base_dir, name, term); + check_colors_files_for_term (nmc, color_option, base_dir, name, NULL); +} + +static void +check_colors_files_for_base_dir (NmCli *nmc, NmcColorOption *color_option, + const char *base_dir) +{ + check_colors_files_for_name (nmc, color_option, base_dir, "nmcli"); + check_colors_files_for_name (nmc, color_option, base_dir, NULL); +} + +static const char * +resolve_color_alias (const char *color) +{ + static const struct { + const char *name; + const char *alias; + } aliases[] = { + { "reset", "0" }, + { "bold", "1" }, + { "white", "1;37" }, + { "halfbright", "2" }, + { "underscore", "4" }, + { "blink", "5" }, + { "reverse", "7" }, + { "black", "30" }, + { "red", "31" }, + { "green", "32" }, + { "brown", "33" }, + { "yellow", "33" }, /* well, yellow */ + { "blue", "34" }, + { "magenta", "35" }, + { "cyan", "36" }, + { "gray", "37" }, + { "darkgray", "90" }, + { "lightred", "91" }, + { "lightgreen", "92" }, + { "lightblue", "94" }, + { "lightmagenta", "95" }, + { "lightcyan", "96" }, + { "lightgray", "97" }, + }; + int i; + + /* Shortcut literal sequences. */ + if (g_ascii_isdigit (*color)) + return color; + + for (i = 0; i < G_N_ELEMENTS (aliases); i++) { + if (strcmp (color, aliases[i].name) == 0) + return aliases[i].alias; + } + + return color; +} + +static gboolean +parse_color_scheme (NmCli *nmc, GError **error) +{ + char *p = nmc->palette_buffer; + const char *name; + const char *color; + const char *map[_NM_META_COLOR_NUM] = { + [NM_META_COLOR_NONE] = NULL, + [NM_META_COLOR_CONNECTION_ACTIVATED] = "connection-activated", + [NM_META_COLOR_CONNECTION_ACTIVATING] = "connection-activating", + [NM_META_COLOR_CONNECTION_DISCONNECTING] = "connection-disconnecting", + [NM_META_COLOR_CONNECTION_INVISIBLE] = "connection-invisible", + [NM_META_COLOR_CONNECTION_UNKNOWN] = "connection-unknown", + [NM_META_COLOR_CONNECTIVITY_FULL] = "connectivity-full", + [NM_META_COLOR_CONNECTIVITY_LIMITED] = "connectivity-limited", + [NM_META_COLOR_CONNECTIVITY_NONE] = "connectivity-none", + [NM_META_COLOR_CONNECTIVITY_PORTAL] = "connectivity-portal", + [NM_META_COLOR_CONNECTIVITY_UNKNOWN] = "connectivity-unknown", + [NM_META_COLOR_DEVICE_ACTIVATED] = "device-activated", + [NM_META_COLOR_DEVICE_ACTIVATING] = "device-activating", + [NM_META_COLOR_DEVICE_DISCONNECTED] = "device-disconnected", + [NM_META_COLOR_DEVICE_FIRMWARE_MISSING] = "device-firmware-missing", + [NM_META_COLOR_DEVICE_PLUGIN_MISSING] = "device-plugin-missing", + [NM_META_COLOR_DEVICE_UNAVAILABLE] = "device-unavailable", + [NM_META_COLOR_DEVICE_UNKNOWN] = "device-unknown", + [NM_META_COLOR_MANAGER_RUNNING] = "manager-running", + [NM_META_COLOR_MANAGER_STARTING] = "manager-starting", + [NM_META_COLOR_MANAGER_STOPPED] = "manager-stopped", + [NM_META_COLOR_PERMISSION_AUTH] = "permission-auth", + [NM_META_COLOR_PERMISSION_NO] = "permission-no", + [NM_META_COLOR_PERMISSION_UNKNOWN] = "permission-unknown", + [NM_META_COLOR_PERMISSION_YES] = "permission-yes", + [NM_META_COLOR_PROMPT] = "prompt", + [NM_META_COLOR_STATE_ASLEEP] = "state-asleep", + [NM_META_COLOR_STATE_CONNECTED_GLOBAL] = "state-connected-global", + [NM_META_COLOR_STATE_CONNECTED_LOCAL] = "state-connected-local", + [NM_META_COLOR_STATE_CONNECTED_SITE] = "state-connected-site", + [NM_META_COLOR_STATE_CONNECTING] = "state-connecting", + [NM_META_COLOR_STATE_DISCONNECTED] = "state-disconnected", + [NM_META_COLOR_STATE_DISCONNECTING] = "state-disconnecting", + [NM_META_COLOR_STATE_UNKNOWN] = "state-unknown", + [NM_META_COLOR_WIFI_SIGNAL_EXCELLENT] = "wifi-signal-excellent", + [NM_META_COLOR_WIFI_SIGNAL_FAIR] = "wifi-signal-fair", + [NM_META_COLOR_WIFI_SIGNAL_GOOD] = "wifi-signal-good", + [NM_META_COLOR_WIFI_SIGNAL_POOR] = "wifi-signal-poor", + [NM_META_COLOR_WIFI_SIGNAL_UNKNOWN] = "wifi-signal-unknown", + [NM_META_COLOR_DISABLED] = "disabled", + [NM_META_COLOR_ENABLED] = "enabled", + }; + int i; + + /* This reads through the raw color scheme file contents, identifying the + * color names and sequences, putting in terminating NULs in place, so that + * pointers into the buffer can readily be used as strings in the palette. */ + while (1) { + /* Leading whitespace. */ + while (nm_utils_is_separator (*p) || *p == '\n') + p++; + + if (*p == '\0') + break; + + /* Comments. */ + if (*p == '#') { + while (*p != '\n' && *p != '\0') + p++; + continue; + } + + /* Color name. */ + name = p; + while (g_ascii_isgraph (*p)) + p++; + if (*p == '\0') { + g_set_error (error, NMCLI_ERROR, 0, + _("Unexpected end of file following '%s'\n"), name); + return FALSE; + } + + /* Separating whitespace. */ + if (!nm_utils_is_separator (*p)) { + *p = '\0'; + g_set_error (error, NMCLI_ERROR, 0, + _("Expected whitespace following '%s'\n"), name); + return FALSE; + } + while (nm_utils_is_separator (*p)) { + *p = '\0'; + p++; + } + + /* Color sequence. */ + color = p; + if (!g_ascii_isgraph (*p)) { + g_set_error (error, NMCLI_ERROR, 0, + _("Expected a value for '%s'\n"), name); + return FALSE; + } + while (g_ascii_isgraph (*p)) + p++; + + /* Trailing whitespace. */ + while (nm_utils_is_separator (*p)) { + *p = '\0'; + p++; + } + if (*p != '\0') { + if (*p != '\n') { + g_set_error (error, NMCLI_ERROR, 0, + _("Expected a line break following '%s'\n"), color); + return FALSE; + } + *p = '\0'; + p++; + } + + /* All good, set the palette entry. */ + for (i = NM_META_COLOR_NONE + 1; i < _NM_META_COLOR_NUM; i++) { + if (strcmp (map[i], name) == 0) { + nmc->nmc_config_mutable.palette[i] = resolve_color_alias (color); + break; + } + } + if (i == _NM_META_COLOR_NUM) + g_debug ("Ignoring an unrecognized color: '%s'\n", name); + } + + return TRUE; +} + +static void +set_colors (NmCli *nmc, NmcColorOption color_option) +{ + GError *error = NULL; + + if (color_option == NMC_USE_COLOR_AUTO) { if ( g_strcmp0 (g_getenv ("TERM"), "dumb") == 0 || !isatty (STDOUT_FILENO)) - *color_option = NMC_USE_COLOR_NO; + color_option = NMC_USE_COLOR_NO; } - switch (*color_option) { + check_colors_files_for_base_dir (nmc, &color_option, g_get_user_config_dir ()); + check_colors_files_for_base_dir (nmc, &color_option, SYSCONFDIR); + + switch (color_option) { case NMC_USE_COLOR_YES: case NMC_USE_COLOR_AUTO: nmc->nmc_config_mutable.use_colors = TRUE; @@ -353,8 +607,17 @@ set_colors (NmCli *nmc, NmcColorOption *color_option) nmc->nmc_config_mutable.use_colors = FALSE; break; } + + if (nmc->nmc_config_mutable.use_colors && nmc->palette_buffer) { + if (!parse_color_scheme (nmc, &error)) { + g_debug ("Error parsing color scheme: %s", error->message); + g_error_free (error); + } + } } +/*************************************************************************************/ + static gboolean process_command_line (NmCli *nmc, int argc, char **argv) { @@ -508,7 +771,7 @@ process_command_line (NmCli *nmc, int argc, char **argv) if (nmc->required_fields) nmc->nmc_config_mutable.overview = FALSE; - set_colors (nmc, &colors); + set_colors (nmc, colors); /* Now run the requested command */ nmc_do_cmd (nmc, nmcli_cmds, *argv, argc, argv); @@ -674,6 +937,8 @@ nmc_cleanup (NmCli *nmc) nmc->pager_pid = 0; } + nm_clear_g_free (&nmc->palette_buffer); + nmc_polkit_agent_fini (nmc); } diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h index 55ca1d30ab..1b176a66a9 100644 --- a/clients/cli/nmcli.h +++ b/clients/cli/nmcli.h @@ -137,6 +137,8 @@ typedef struct _NmCli { gboolean complete; /* Autocomplete the command line */ gboolean editor_status_line; /* Whether to display status line in connection editor */ gboolean editor_save_confirmation; /* Whether to ask for confirmation on saving connections with 'autoconnect=yes' */ + + char *palette_buffer; /* Buffer with sequences for terminal-colors.d(5)-based coloring. */ } NmCli; extern NmCli nm_cli; diff --git a/man/nmcli.xml b/man/nmcli.xml index 2619234e11..0787fd8baa 100644 --- a/man/nmcli.xml +++ b/man/nmcli.xml @@ -9,7 +9,7 @@ <!-- nmcli(1) manual page - Copyright 2010 - 2016 Red Hat, Inc. + Copyright 2010 - 2018 Red Hat, Inc. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 @@ -183,6 +183,10 @@ <literal>yes</literal> enables colors, <literal>no</literal> disables them, <literal>auto</literal> only produces colors when standard output is directed to a terminal. The default value is <literal>auto</literal>.</para> + <para>The actual colors used are configured as described in + <citerefentry><refentrytitle>terminal-colors.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>. + Please refer to the <link linkend='colors' endterm='colors.title' /> section for a + list of color names supported by <command>nmcli</command>.</para> </listitem> </varlistentry> @@ -2035,6 +2039,269 @@ It's equivalent to the <literal>+bond.options 'option=value'</literal> syntax.</ </refsect1> + <refsect1 id='colors'><title id='colors.title'>Colors</title> + <para>Implicit coloring can be disabled by an empty file + <filename>/etc/terminal-colors.d/nmcli.disable</filename>.</para> + + <para>See <citerefentry><refentrytitle>terminal-colors.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> + for more details about colorization configuration. + The logical color names supported by <command>nmcli</command> are:</para> + + <variablelist> + <varlistentry> + <term><option>connection-activated</option></term> + <listitem> + <para>A connection that is active.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>connection-activating</option></term> + <listitem> + <para>Connection that is being activated.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>connection-disconnecting</option></term> + <listitem> + <para>Connection that is being disconnected.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>connection-invisible</option></term> + <listitem> + <para>Connection whose details is the user not permitted to see.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>connectivity-full</option></term> + <listitem> + <para>Conectivity state when Internet is reachable.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>connectivity-limited</option></term> + <listitem> + <para>Conectivity state when only a local network reachable.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>connectivity-none</option></term> + <listitem> + <para>Conectivity state when the network is disconnected.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>connectivity-portal</option></term> + <listitem> + <para>Conectivity state when a captive portal hijacked the connection.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>connectivity-unknown</option></term> + <listitem> + <para>Conectivity state when a connectivity check didn't run.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>device-activated</option></term> + <listitem> + <para>Device that is connected.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>device-activating</option></term> + <listitem> + <para>Device that is being configured.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>device-disconnected</option></term> + <listitem> + <para>Device that is not connected.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>device-firmware-missing</option></term> + <listitem> + <para>Warning of a missing device firmware.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>device-plugin-missing</option></term> + <listitem> + <para>Warning of a missing device plugin.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>device-unavailable</option></term> + <listitem> + <para>Device that is not available for activation.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>manager-running</option></term> + <listitem> + <para>Notice that the NetworkManager daemon is available.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>manager-starting</option></term> + <listitem> + <para>Notice that the NetworkManager daemon is being initially connected.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>manager-stopped</option></term> + <listitem> + <para>Notice that the NetworkManager daemon is not available.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>permission-auth</option></term> + <listitem> + <para>An action that requires user authentication to get permission.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>permission-no</option></term> + <listitem> + <para>An action that is not permitted.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>permission-yes</option></term> + <listitem> + <para>An action that is permitted.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>prompt</option></term> + <listitem> + <para>Prompt in interactive mode.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>state-asleep</option></term> + <listitem> + <para>Indication that NetworkManager in suspended state.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>state-connected-global</option></term> + <listitem> + <para>Indication that NetworkManager in connected to Internet.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>state-connected-local</option></term> + <listitem> + <para>Indication that NetworkManager in local network.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>state-connected-site</option></term> + <listitem> + <para>Indication that NetworkManager in connected to networks other than Internet.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>state-connecting</option></term> + <listitem> + <para>Indication that NetworkManager is establishing a network connection.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>state-disconnected</option></term> + <listitem> + <para>Indication that NetworkManager is disconnected from a network.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>state-disconnecting</option></term> + <listitem> + <para>Indication that NetworkManager is being disconnected from a network.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>wifi-signal-excellent</option></term> + <listitem> + <para>Wi-Fi network with an excellent signal level.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>wifi-signal-fair</option></term> + <listitem> + <para>Wi-Fi network with a fair signal level.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>wifi-signal-good</option></term> + <listitem> + <para>Wi-Fi network with a good signal level.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>wifi-signal-poor</option></term> + <listitem> + <para>Wi-Fi network with a poor signal level.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>wifi-signal-unknown</option></term> + <listitem> + <para>Wi-Fi network that hasn't been actually seen (a hidden AP).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>disabled</option></term> + <listitem> + <para>A property that is turned off.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>enabled</option></term> + <listitem> + <para>A property that is turned on.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + <refsect1 id='environment_variables'><title>Environment Variables</title> <para><command>nmcli</command>'s behavior is affected by the following @@ -2465,7 +2732,8 @@ It's equivalent to the <literal>+bond.options 'option=value'</literal> syntax.</ <link linkend='NetworkManager.conf'><citerefentry><refentrytitle>NetworkManager.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></link>, <link linkend='nm-settings'><citerefentry><refentrytitle>nm-settings</refentrytitle><manvolnum>5</manvolnum></citerefentry></link>, <citerefentry><refentrytitle>nm-applet</refentrytitle><manvolnum>1</manvolnum></citerefentry>, - <citerefentry><refentrytitle>nm-connection-editor</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para> + <citerefentry><refentrytitle>nm-connection-editor</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>terminal-colors.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> </refsect1> </refentry> |