From 23298bfc88bc0216f2dab0c930724516f8ecbfc2 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Mar 2017 12:16:31 +0200 Subject: [PATCH] cli: move utils function from common.h to nm-meta-setting-desc.c These functions are only used by nm-meta-setting-desc.c. Make them internal. Unfortunately, they are part of "common.h" which cannot be used without the rest of nmcli. Still todo. --- Makefile.am | 5 + clients/cli/common.c | 423 +----------------------- clients/cli/common.h | 12 - clients/cli/connections.c | 2 + clients/cli/devices.c | 2 + clients/cli/general.c | 6 +- clients/cli/nmcli.c | 2 + clients/cli/settings.c | 5 +- clients/cli/utils.c | 218 +------------ clients/cli/utils.h | 31 -- clients/common/nm-client-utils.c | 239 ++++++++++++++ clients/common/nm-client-utils.h | 63 ++++ clients/common/nm-meta-setting-desc.c | 452 +++++++++++++++++++++++++- clients/common/nm-meta-setting-desc.h | 3 + libnm-core/nm-setting-user.c | 2 +- po/POTFILES.in | 1 + shared/nm-utils/nm-shared-utils.h | 2 + 17 files changed, 780 insertions(+), 688 deletions(-) create mode 100644 clients/common/nm-client-utils.c create mode 100644 clients/common/nm-client-utils.h diff --git a/Makefile.am b/Makefile.am index 6fdbe186f6..cfcc27c4dd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3117,10 +3117,15 @@ if BUILD_NMCLI bin_PROGRAMS += clients/cli/nmcli clients_cli_nmcli_SOURCES = \ + \ + shared/nm-utils/nm-shared-utils.c \ + shared/nm-utils/nm-shared-utils.h \ \ shared/nm-meta-setting.c \ shared/nm-meta-setting.h \ \ + clients/common/nm-client-utils.c \ + clients/common/nm-client-utils.h \ clients/common/nm-meta-setting-desc.c \ clients/common/nm-meta-setting-desc.h \ \ diff --git a/clients/cli/common.c b/clients/cli/common.c index e3f00482e2..61a0ef3559 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -30,6 +30,8 @@ #include #include "nm-vpn-helpers.h" +#include "nm-client-utils.h" + #include "common.h" #include "utils.h" @@ -346,193 +348,6 @@ print_dhcp6_config (NMDhcpConfig *dhcp6, return FALSE; } -/* - * Parse IP address from string to NMIPAddress stucture. - * ip_str is the IP address in the form address/prefix - */ -NMIPAddress * -nmc_parse_and_build_address (int family, const char *ip_str, GError **error) -{ - int max_prefix = (family == AF_INET) ? 32 : 128; - NMIPAddress *addr = NULL; - const char *ip; - char *tmp; - char *plen; - long int prefix; - GError *local = NULL; - - g_return_val_if_fail (ip_str != NULL, NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - tmp = g_strdup (ip_str); - plen = strchr (tmp, '/'); /* prefix delimiter */ - if (plen) - *plen++ = '\0'; - - ip = tmp; - - prefix = max_prefix; - if (plen) { - if (!nmc_string_to_int (plen, TRUE, 1, max_prefix, &prefix)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("invalid prefix '%s'; <1-%d> allowed"), plen, max_prefix); - goto finish; - } - } - - addr = nm_ip_address_new (family, ip, (guint32) prefix, &local); - if (!addr) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("invalid IP address: %s"), local->message); - g_clear_error (&local); - } - -finish: - g_free (tmp); - return addr; -} - -/* - * nmc_parse_and_build_route: - * @family: AF_INET or AF_INET6 - * @str: route string to be parsed - * @error: location to store GError - * - * Parse route from string and return an #NMIPRoute - * - * Returns: a new #NMIPRoute or %NULL on error - */ -NMIPRoute * -nmc_parse_and_build_route (int family, - const char *str, - GError **error) -{ - int max_prefix = (family == AF_INET) ? 32 : 128; - char *plen = NULL; - const char *next_hop = NULL; - const char *canon_dest; - long int prefix = max_prefix; - unsigned long int tmp_ulong; - NMIPRoute *route = NULL; - gboolean success = FALSE; - GError *local = NULL; - gint64 metric = -1; - guint i, len; - gs_strfreev char **routev = NULL; - gs_free char *value = NULL; - gs_free char *dest = NULL; - gs_unref_hashtable GHashTable *attrs = NULL; - GHashTable *tmp_attrs; - - g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE); - g_return_val_if_fail (str, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - value = g_strdup (str); - routev = nmc_strsplit_set (g_strstrip (value), " \t", 0); - len = g_strv_length (routev); - if (len < 1) { - g_set_error (error, 1, 0, _("'%s' is not valid (the format is: ip[/prefix] [next-hop] [metric] [attr=val] [attr=val])"), - str); - goto finish; - } - - dest = g_strdup (routev[0]); - plen = strchr (dest, '/'); /* prefix delimiter */ - if (plen) - *plen++ = '\0'; - - if (plen) { - if (!nmc_string_to_int (plen, TRUE, 1, max_prefix, &prefix)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("invalid prefix '%s'; <1-%d> allowed"), - plen, max_prefix); - goto finish; - } - } - - for (i = 1; i < len; i++) { - if (nm_utils_ipaddr_valid (family, routev[i])) { - if (metric != -1 || attrs) { - g_set_error (error, 1, 0, _("the next hop ('%s') must be first"), routev[i]); - goto finish; - } - next_hop = routev[i]; - } else if (nmc_string_to_uint (routev[i], TRUE, 0, G_MAXUINT32, &tmp_ulong)) { - if (attrs) { - g_set_error (error, 1, 0, _("the metric ('%s') must be before attributes"), routev[i]); - goto finish; - } - metric = tmp_ulong; - } else if (strchr (routev[i], '=')) { - GHashTableIter iter; - char *iter_key; - GVariant *iter_value; - - tmp_attrs = nm_utils_parse_variant_attributes (routev[i], ' ', '=', FALSE, - nm_ip_route_get_variant_attribute_spec(), - error); - if (!tmp_attrs) { - g_prefix_error (error, "invalid option '%s': ", routev[i]); - goto finish; - } - - if (!attrs) - attrs = g_hash_table_new (g_str_hash, g_str_equal); - - g_hash_table_iter_init (&iter, tmp_attrs); - while (g_hash_table_iter_next (&iter, (gpointer *) &iter_key, (gpointer *) &iter_value)) { - if (!nm_ip_route_attribute_validate (iter_key, iter_value, family, NULL, error)) { - g_prefix_error (error, "%s: ", iter_key); - g_hash_table_unref (tmp_attrs); - goto finish; - } - g_hash_table_insert (attrs, iter_key, iter_value); - g_hash_table_iter_steal (&iter); - } - g_hash_table_unref (tmp_attrs); - } else { - g_set_error (error, 1, 0, _("unrecognized option '%s'"), routev[i]); - goto finish; - } - } - - route = nm_ip_route_new (family, dest, prefix, next_hop, metric, &local); - if (!route) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("invalid route: %s"), local->message); - g_clear_error (&local); - goto finish; - } - - /* We don't accept default routes as NetworkManager handles it - * itself. But we have to check this after @route has normalized the - * dest string. - */ - canon_dest = nm_ip_route_get_dest (route); - if (!strcmp (canon_dest, "0.0.0.0") || !strcmp (canon_dest, "::")) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("default route cannot be added (NetworkManager handles it by itself)")); - g_clear_pointer (&route, nm_ip_route_unref); - goto finish; - } - - if (attrs) { - GHashTableIter iter; - char *name; - GVariant *variant; - - g_hash_table_iter_init (&iter, attrs); - while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant)) - nm_ip_route_set_attribute (route, name, variant); - } - - success = TRUE; - -finish: - return route; -} - const char * nmc_device_state_to_string (NMDeviceState state) { @@ -782,240 +597,6 @@ nmc_device_reason_to_string (NMDeviceStateReason reason) } } - -/* Max priority values from libnm-core/nm-setting-vlan.c */ -#define MAX_SKB_PRIO G_MAXUINT32 -#define MAX_8021P_PRIO 7 /* Max 802.1p priority */ - -/* - * Parse VLAN priority mappings from the following format: 2:1,3:4,7:3 - * and verify if the priority numbers are valid - * - * Return: string array with split maps, or NULL on error - * Caller is responsible for freeing the array. - */ -char ** -nmc_vlan_parse_priority_maps (const char *priority_map, - NMVlanPriorityMap map_type, - GError **error) -{ - char **mapping = NULL, **iter; - unsigned long from, to, from_max, to_max; - - g_return_val_if_fail (priority_map != NULL, NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - if (map_type == NM_VLAN_INGRESS_MAP) { - from_max = MAX_8021P_PRIO; - to_max = MAX_SKB_PRIO; - } else { - from_max = MAX_SKB_PRIO; - to_max = MAX_8021P_PRIO; - } - - mapping = g_strsplit (priority_map, ",", 0); - for (iter = mapping; iter && *iter; iter++) { - char *left, *right; - - left = g_strstrip (*iter); - right = strchr (left, ':'); - if (!right) { - g_set_error (error, 1, 0, _("invalid priority map '%s'"), *iter); - g_strfreev (mapping); - return NULL; - } - *right++ = '\0'; - - if (!nmc_string_to_uint (left, TRUE, 0, from_max, &from)) { - g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"), - left, from_max); - g_strfreev (mapping); - return NULL; - } - if (!nmc_string_to_uint (right, TRUE, 0, to_max, &to)) { - g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"), - right, to_max); - g_strfreev (mapping); - return NULL; - } - *(right-1) = ':'; /* Put back ':' */ - } - return mapping; -} - -const char * -nmc_bond_validate_mode (const char *mode, GError **error) -{ - unsigned long mode_int; - static const char *valid_modes[] = { "balance-rr", - "active-backup", - "balance-xor", - "broadcast", - "802.3ad", - "balance-tlb", - "balance-alb", - NULL }; - if (nmc_string_to_uint (mode, TRUE, 0, 6, &mode_int)) { - /* Translate bonding mode numbers to mode names: - * https://www.kernel.org/doc/Documentation/networking/bonding.txt - */ - return valid_modes[mode_int]; - } else - return nmc_string_is_valid (mode, valid_modes, error); -} - -/* - * nmc_team_check_config: - * @config: file name with team config, or raw team JSON config data - * @out_config: raw team JSON config data - * The value must be freed with g_free(). - * @error: location to store error, or %NUL - * - * Check team config from @config parameter and return the checked - * config in @out_config. - * - * Returns: %TRUE if the config is valid, %FALSE if it is invalid - */ -gboolean -nmc_team_check_config (const char *config, char **out_config, GError **error) -{ - enum { - _TEAM_CONFIG_TYPE_GUESS, - _TEAM_CONFIG_TYPE_FILE, - _TEAM_CONFIG_TYPE_JSON, - } desired_type = _TEAM_CONFIG_TYPE_GUESS; - const char *filename = NULL; - size_t c_len = 0; - gs_free char *config_clone = NULL; - - *out_config = NULL; - - if (!config || !config[0]) - return TRUE; - - if (g_str_has_prefix (config, "file://")) { - config += NM_STRLEN ("file://"); - desired_type = _TEAM_CONFIG_TYPE_FILE; - } else if (g_str_has_prefix (config, "json://")) { - config += NM_STRLEN ("json://"); - desired_type = _TEAM_CONFIG_TYPE_JSON; - } - - if (NM_IN_SET (desired_type, _TEAM_CONFIG_TYPE_FILE, _TEAM_CONFIG_TYPE_GUESS)) { - gs_free char *contents = NULL; - - if (!g_file_get_contents (config, &contents, &c_len, NULL)) { - if (desired_type == _TEAM_CONFIG_TYPE_FILE) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("cannot read team config from file '%s'"), - config); - return FALSE; - } - } else { - if (c_len != strlen (contents)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("team config file '%s' contains non-valid utf-8"), - config); - return FALSE; - } - filename = config; - config = config_clone = g_steal_pointer (&contents); - } - } - - if (!nm_utils_is_json_object (config, NULL)) { - if (filename) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("'%s' does not contain a valid team configuration"), filename); - } else { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("team configuration must be a JSON object")); - } - return FALSE; - } - - *out_config = (config == config_clone) - ? g_steal_pointer (&config_clone) - : g_strdup (config); - return TRUE; -} - -/* - * nmc_proxy_check_script: - * @script: file name with PAC script, or raw PAC Script data - * @out_script: raw PAC Script (with removed new-line characters) - * @error: location to store error, or %NULL - * - * Check PAC Script from @script parameter and return the checked/sanitized - * config in @out_script. - * - * Returns: %TRUE if the script is valid, %FALSE if it is invalid - */ -gboolean -nmc_proxy_check_script (const char *script, char **out_script, GError **error) -{ - enum { - _PAC_SCRIPT_TYPE_GUESS, - _PAC_SCRIPT_TYPE_FILE, - _PAC_SCRIPT_TYPE_JSON, - } desired_type = _PAC_SCRIPT_TYPE_GUESS; - const char *filename = NULL; - size_t c_len = 0; - gs_free char *script_clone = NULL; - - *out_script = NULL; - - if (!script || !script[0]) - return TRUE; - - if (g_str_has_prefix (script, "file://")) { - script += NM_STRLEN ("file://"); - desired_type = _PAC_SCRIPT_TYPE_FILE; - } else if (g_str_has_prefix (script, "js://")) { - script += NM_STRLEN ("js://"); - desired_type = _PAC_SCRIPT_TYPE_JSON; - } - - if (NM_IN_SET (desired_type, _PAC_SCRIPT_TYPE_FILE, _PAC_SCRIPT_TYPE_GUESS)) { - gs_free char *contents = NULL; - - if (!g_file_get_contents (script, &contents, &c_len, NULL)) { - if (desired_type == _PAC_SCRIPT_TYPE_FILE) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("cannot read pac-script from file '%s'"), - script); - return FALSE; - } - } else { - if (c_len != strlen (contents)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("file '%s' contains non-valid utf-8"), - script); - return FALSE; - } - filename = script; - script = script_clone = g_steal_pointer (&contents); - } - } - - if ( !strstr (script, "FindProxyForURL") - || !g_utf8_validate (script, -1, NULL)) { - if (filename) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("'%s' does not contain a valid PAC Script"), filename); - } else { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Not a valid PAC Script")); - } - return FALSE; - } - - *out_script = (script == script_clone) - ? g_steal_pointer (&script_clone) - : g_strdup (script); - return TRUE; -} - /* * nmc_find_connection: * @connections: array of NMConnections to search in diff --git a/clients/cli/common.h b/clients/cli/common.h index d755879a53..f3b4e702db 100644 --- a/clients/cli/common.h +++ b/clients/cli/common.h @@ -30,22 +30,10 @@ gboolean print_ip6_config (NMIPConfig *cfg6, NmCli *nmc, const char *group_prefi gboolean print_dhcp4_config (NMDhcpConfig *dhcp4, NmCli *nmc, const char *group_prefix, const char *one_field); gboolean print_dhcp6_config (NMDhcpConfig *dhcp6, NmCli *nmc, const char *group_prefix, const char *one_field); -NMIPAddress *nmc_parse_and_build_address (int family, const char *ip_str, GError **error); -NMIPRoute *nmc_parse_and_build_route (int family, const char *str, GError **error); - const char * nmc_device_state_to_string (NMDeviceState state); const char * nmc_device_reason_to_string (NMDeviceStateReason reason); const char * nmc_device_metered_to_string (NMMetered value); -char ** -nmc_vlan_parse_priority_maps (const char *priority_map, - NMVlanPriorityMap map_type, - GError **error); - -const char *nmc_bond_validate_mode (const char *mode, GError **error); -gboolean nmc_team_check_config (const char *config, char **out_config, GError **error); -gboolean nmc_proxy_check_script (const char *script, char **out_script, GError **error); - NMConnection *nmc_find_connection (const GPtrArray *connections, const char *filter_type, const char *filter_val, diff --git a/clients/cli/connections.c b/clients/cli/connections.c index f381dfd73d..5caa143779 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -29,6 +29,8 @@ #include #include +#include "nm-client-utils.h" + #include "utils.h" #include "common.h" #include "settings.h" diff --git a/clients/cli/devices.c b/clients/cli/devices.c index ce8febcc61..f49eabba9e 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -26,6 +26,8 @@ #include #include "nm-secret-agent-simple.h" +#include "nm-client-utils.h" + #include "polkit-agent.h" #include "utils.h" #include "common.h" diff --git a/clients/cli/general.c b/clients/cli/general.c index 8e52dd29ca..7724f38021 100644 --- a/clients/cli/general.c +++ b/clients/cli/general.c @@ -22,13 +22,15 @@ #include #include +#include "nm-common-macros.h" + +#include "nm-client-utils.h" + #include "polkit-agent.h" #include "utils.h" #include "common.h" #include "general.h" #include "common.h" -#include "nm-common-macros.h" - #include "devices.h" #include "connections.h" diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index f7fc99899d..c0917e6d90 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -32,6 +32,8 @@ #include #include +#include "nm-client-utils.h" + #include "polkit-agent.h" #include "nmcli.h" #include "utils.h" diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 28f7ebfcd9..210c226c89 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -25,9 +25,12 @@ #include #include "nm-common-macros.h" + +#include "nm-client-utils.h" +#include "nm-vpn-helpers.h" + #include "utils.h" #include "common.h" -#include "nm-vpn-helpers.h" /*****************************************************************************/ diff --git a/clients/cli/utils.c b/clients/cli/utils.c index 93fe651047..5db9a08217 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -27,19 +27,12 @@ #include #include +#include "nm-client-utils.h" + #include "utils.h" #include "common.h" #include "settings.h" -gboolean -matches (const char *cmd, const char *pattern) -{ - size_t len = strlen (cmd); - if (!len || len > strlen (pattern)) - return FALSE; - return memcmp (pattern, cmd, len) == 0; -} - static gboolean parse_global_arg (NmCli *nmc, const char *arg) { @@ -426,137 +419,6 @@ nmc_filter_out_colors (const char *str) return filtered; } -/* - * Convert string to signed integer. - * If required, the resulting number is checked to be in the range. - */ -gboolean -nmc_string_to_int_base (const char *str, - int base, - gboolean range_check, - long int min, - long int max, - long int *value) -{ - char *end; - long int tmp; - - errno = 0; - tmp = strtol (str, &end, base); - if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) { - return FALSE; - } - *value = tmp; - return TRUE; -} - -/* - * Convert string to unsigned integer. - * If required, the resulting number is checked to be in the range. - */ -gboolean -nmc_string_to_uint_base (const char *str, - int base, - gboolean range_check, - unsigned long int min, - unsigned long int max, - unsigned long int *value) -{ - char *end; - unsigned long int tmp; - - errno = 0; - tmp = strtoul (str, &end, base); - if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) { - return FALSE; - } - *value = tmp; - return TRUE; -} - -gboolean -nmc_string_to_int (const char *str, - gboolean range_check, - long int min, - long int max, - long int *value) -{ - return nmc_string_to_int_base (str, 10, range_check, min, max, value); -} - -gboolean -nmc_string_to_uint (const char *str, - gboolean range_check, - unsigned long int min, - unsigned long int max, - unsigned long int *value) -{ - return nmc_string_to_uint_base (str, 10, range_check, min, max, value); -} - -gboolean -nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error) -{ - const char *s_true[] = { "true", "yes", "on", NULL }; - const char *s_false[] = { "false", "no", "off", NULL }; - - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - if (g_strcmp0 (str, "o") == 0) { - g_set_error (error, 1, 0, - /* Translators: the first %s is the partial value entered by - * the user, the second %s a list of compatible values. - */ - _("'%s' is ambiguous (%s)"), str, "on x off"); - return FALSE; - } - - if (nmc_string_is_valid (str, s_true, NULL)) - *val_bool = TRUE; - else if (nmc_string_is_valid (str, s_false, NULL)) - *val_bool = FALSE; - else { - g_set_error (error, 1, 0, - _("'%s' is not valid; use [%s] or [%s]"), - str, "true, yes, on", "false, no, off"); - return FALSE; - } - return TRUE; -} - -gboolean -nmc_string_to_tristate (const char *str, NMCTriStateValue *val, GError **error) -{ - const char *s_true[] = { "true", "yes", "on", NULL }; - const char *s_false[] = { "false", "no", "off", NULL }; - const char *s_unknown[] = { "unknown", NULL }; - - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - if (g_strcmp0 (str, "o") == 0) { - g_set_error (error, 1, 0, - /* Translators: the first %s is the partial value entered by - * the user, the second %s a list of compatible values. - */ - _("'%s' is ambiguous (%s)"), str, "on x off"); - return FALSE; - } - - if (nmc_string_is_valid (str, s_true, NULL)) - *val = NMC_TRI_STATE_YES; - else if (nmc_string_is_valid (str, s_false, NULL)) - *val = NMC_TRI_STATE_NO; - else if (nmc_string_is_valid (str, s_unknown, NULL)) - *val = NMC_TRI_STATE_UNKNOWN; - else { - g_set_error (error, 1, 0, - _("'%s' is not valid; use [%s], [%s] or [%s]"), - str, "true, yes, on", "false, no, off", "unknown"); - return FALSE; - } - return TRUE; -} - /* * Ask user for input and return the string. * The caller is responsible for freeing the returned string. @@ -619,57 +481,6 @@ nmc_string_to_arg_array (const char *line, const char *delim, gboolean unquote, return 0; } -/* - * Check whether 'input' is contained in 'allowed' array. It performs case - * insensitive comparison and supports shortcut strings if they are unique. - * Returns: a pointer to found string in allowed array on success or NULL. - * On failure: error->code : 0 - string not found; 1 - string is ambiguous - */ -const char * -nmc_string_is_valid (const char *input, const char **allowed, GError **error) -{ - const char **p; - size_t input_ln, p_len; - gboolean prev_match = FALSE; - const char *ret = NULL; - - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - if (!input || !*input) - goto finish; - - input_ln = strlen (input); - for (p = allowed; p && *p; p++) { - p_len = strlen (*p); - if (g_ascii_strncasecmp (input, *p, input_ln) == 0) { - if (input_ln == p_len) { - ret = *p; - break; - } - if (!prev_match) - ret = *p; - else { - g_set_error (error, 1, 1, _("'%s' is ambiguous (%s x %s)"), - input, ret, *p); - return NULL; - } - prev_match = TRUE; - } - } - -finish: - if (ret == NULL) { - char *valid_vals = g_strjoinv (", ", (char **) allowed); - if (!input || !*input) - g_set_error (error, 1, 0, _("missing name, try one of [%s]"), valid_vals); - else - g_set_error (error, 1, 0, _("'%s' not among [%s]"), input, valid_vals); - - g_free (valid_vals); - } - return ret; -} - /* * Convert string array (char **) to description string in the form of: * "[string1, string2, ]" @@ -697,31 +508,6 @@ nmc_util_strv_for_display (const char *const*strv, gboolean brackets) return g_string_free (result, FALSE); } -/* - * Wrapper function for g_strsplit_set() that removes empty strings - * from the vector as they are not useful in most cases. - */ -char ** -nmc_strsplit_set (const char *str, const char *delimiter, int max_tokens) -{ - char **result; - uint i; - uint j; - - result = g_strsplit_set (str, delimiter, max_tokens); - - /* remove empty strings */ - for (i = 0; result && result[i]; i++) { - if (*(result[i]) == '\0') { - g_free (result[i]); - for (j = i; result[j]; j++) - result[j] = result[j + 1]; - i--; - } - } - return result; -} - /* * Find out how many columns an UTF-8 string occupies on the screen. */ diff --git a/clients/cli/utils.h b/clients/cli/utils.h index d1dac09929..ae4ca739bb 100644 --- a/clients/cli/utils.h +++ b/clients/cli/utils.h @@ -32,43 +32,12 @@ typedef struct { gboolean found; } nmc_arg_t; -typedef enum { - NMC_TRI_STATE_NO, - NMC_TRI_STATE_YES, - NMC_TRI_STATE_UNKNOWN, -} NMCTriStateValue; - /* === Functions === */ -gboolean matches (const char *cmd, const char *pattern); int next_arg (NmCli *nmc, int *argc, char ***argv); gboolean nmc_arg_is_help (const char *arg); gboolean nmc_arg_is_option (const char *arg, const char *opt_name); gboolean nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error); char *ssid_to_hex (const char *str, gsize len); -gboolean nmc_string_to_int_base (const char *str, - int base, - gboolean range_check, - long int min, - long int max, - long int *value); -gboolean nmc_string_to_uint_base (const char *str, - int base, - gboolean range_check, - unsigned long int min, - unsigned long int max, - unsigned long int *value); -gboolean nmc_string_to_int (const char *str, - gboolean range_check, - long int min, - long int max, - long int *value); -gboolean nmc_string_to_uint (const char *str, - gboolean range_check, - unsigned long int min, - unsigned long int max, - unsigned long int *value); -gboolean nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error); -gboolean nmc_string_to_tristate (const char *str, NMCTriStateValue *val, GError **error); void nmc_terminal_erase_line (void); void nmc_terminal_show_progress (const char *str); const char *nmc_term_color_sequence (NmcTermColor color); diff --git a/clients/common/nm-client-utils.c b/clients/common/nm-client-utils.c new file mode 100644 index 0000000000..6df143861a --- /dev/null +++ b/clients/common/nm-client-utils.c @@ -0,0 +1,239 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-client-utils.h" + +/* + * Convert string to signed integer. + * If required, the resulting number is checked to be in the range. + */ +gboolean +nmc_string_to_int_base (const char *str, + int base, + gboolean range_check, + long int min, + long int max, + long int *value) +{ + char *end; + long int tmp; + + errno = 0; + tmp = strtol (str, &end, base); + if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) { + return FALSE; + } + *value = tmp; + return TRUE; +} + +/* + * Convert string to unsigned integer. + * If required, the resulting number is checked to be in the range. + */ +gboolean +nmc_string_to_uint_base (const char *str, + int base, + gboolean range_check, + unsigned long int min, + unsigned long int max, + unsigned long int *value) +{ + char *end; + unsigned long int tmp; + + errno = 0; + tmp = strtoul (str, &end, base); + if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) { + return FALSE; + } + *value = tmp; + return TRUE; +} + +gboolean +nmc_string_to_int (const char *str, + gboolean range_check, + long int min, + long int max, + long int *value) +{ + return nmc_string_to_int_base (str, 10, range_check, min, max, value); +} + +gboolean +nmc_string_to_uint (const char *str, + gboolean range_check, + unsigned long int min, + unsigned long int max, + unsigned long int *value) +{ + return nmc_string_to_uint_base (str, 10, range_check, min, max, value); +} + +gboolean +nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error) +{ + const char *s_true[] = { "true", "yes", "on", NULL }; + const char *s_false[] = { "false", "no", "off", NULL }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (g_strcmp0 (str, "o") == 0) { + g_set_error (error, 1, 0, + /* Translators: the first %s is the partial value entered by + * the user, the second %s a list of compatible values. + */ + _("'%s' is ambiguous (%s)"), str, "on x off"); + return FALSE; + } + + if (nmc_string_is_valid (str, s_true, NULL)) + *val_bool = TRUE; + else if (nmc_string_is_valid (str, s_false, NULL)) + *val_bool = FALSE; + else { + g_set_error (error, 1, 0, + _("'%s' is not valid; use [%s] or [%s]"), + str, "true, yes, on", "false, no, off"); + return FALSE; + } + return TRUE; +} + +gboolean +nmc_string_to_tristate (const char *str, NMCTriStateValue *val, GError **error) +{ + const char *s_true[] = { "true", "yes", "on", NULL }; + const char *s_false[] = { "false", "no", "off", NULL }; + const char *s_unknown[] = { "unknown", NULL }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (g_strcmp0 (str, "o") == 0) { + g_set_error (error, 1, 0, + /* Translators: the first %s is the partial value entered by + * the user, the second %s a list of compatible values. + */ + _("'%s' is ambiguous (%s)"), str, "on x off"); + return FALSE; + } + + if (nmc_string_is_valid (str, s_true, NULL)) + *val = NMC_TRI_STATE_YES; + else if (nmc_string_is_valid (str, s_false, NULL)) + *val = NMC_TRI_STATE_NO; + else if (nmc_string_is_valid (str, s_unknown, NULL)) + *val = NMC_TRI_STATE_UNKNOWN; + else { + g_set_error (error, 1, 0, + _("'%s' is not valid; use [%s], [%s] or [%s]"), + str, "true, yes, on", "false, no, off", "unknown"); + return FALSE; + } + return TRUE; +} + +/* + * Check whether 'input' is contained in 'allowed' array. It performs case + * insensitive comparison and supports shortcut strings if they are unique. + * Returns: a pointer to found string in allowed array on success or NULL. + * On failure: error->code : 0 - string not found; 1 - string is ambiguous + */ +const char * +nmc_string_is_valid (const char *input, const char **allowed, GError **error) +{ + const char **p; + size_t input_ln, p_len; + gboolean prev_match = FALSE; + const char *ret = NULL; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (!input || !*input) + goto finish; + + input_ln = strlen (input); + for (p = allowed; p && *p; p++) { + p_len = strlen (*p); + if (g_ascii_strncasecmp (input, *p, input_ln) == 0) { + if (input_ln == p_len) { + ret = *p; + break; + } + if (!prev_match) + ret = *p; + else { + g_set_error (error, 1, 1, _("'%s' is ambiguous (%s x %s)"), + input, ret, *p); + return NULL; + } + prev_match = TRUE; + } + } + +finish: + if (ret == NULL) { + char *valid_vals = g_strjoinv (", ", (char **) allowed); + if (!input || !*input) + g_set_error (error, 1, 0, _("missing name, try one of [%s]"), valid_vals); + else + g_set_error (error, 1, 0, _("'%s' not among [%s]"), input, valid_vals); + + g_free (valid_vals); + } + return ret; +} + +/* + * Wrapper function for g_strsplit_set() that removes empty strings + * from the vector as they are not useful in most cases. + */ +char ** +nmc_strsplit_set (const char *str, const char *delimiter, int max_tokens) +{ + char **result; + uint i; + uint j; + + result = g_strsplit_set (str, delimiter, max_tokens); + + /* remove empty strings */ + for (i = 0; result && result[i]; i++) { + if (*(result[i]) == '\0') { + g_free (result[i]); + for (j = i; result[j]; j++) + result[j] = result[j + 1]; + i--; + } + } + return result; +} + +gboolean +matches (const char *cmd, const char *pattern) +{ + size_t len = strlen (cmd); + if (!len || len > strlen (pattern)) + return FALSE; + return memcmp (pattern, cmd, len) == 0; +} + diff --git a/clients/common/nm-client-utils.h b/clients/common/nm-client-utils.h new file mode 100644 index 0000000000..7f700f930f --- /dev/null +++ b/clients/common/nm-client-utils.h @@ -0,0 +1,63 @@ +/* nmcli - command-line tool to control NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2017 Red Hat, Inc. + */ + +#ifndef __NM_CLIENT_UTILS_H__ +#define __NM_CLIENT_UTILS_H__ + +#include "nm-meta-setting.h" + + +typedef enum { + NMC_TRI_STATE_NO, + NMC_TRI_STATE_YES, + NMC_TRI_STATE_UNKNOWN, +} NMCTriStateValue; + +const char *nmc_string_is_valid (const char *input, const char **allowed, GError **error); + +char **nmc_strsplit_set (const char *str, const char *delimiter, int max_tokens); + +gboolean nmc_string_to_int_base (const char *str, + int base, + gboolean range_check, + long int min, + long int max, + long int *value); +gboolean nmc_string_to_uint_base (const char *str, + int base, + gboolean range_check, + unsigned long int min, + unsigned long int max, + unsigned long int *value); +gboolean nmc_string_to_int (const char *str, + gboolean range_check, + long int min, + long int max, + long int *value); +gboolean nmc_string_to_uint (const char *str, + gboolean range_check, + unsigned long int min, + unsigned long int max, + unsigned long int *value); +gboolean nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error); +gboolean nmc_string_to_tristate (const char *str, NMCTriStateValue *val, GError **error); + +gboolean matches (const char *cmd, const char *pattern); + +#endif /* __NM_CLIENT_UTILS_H__ */ diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 1b64188fe9..48c0773685 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -25,11 +25,27 @@ #include #include "nm-common-macros.h" -#include "nm-vpn-helpers.h" -/* FIXME: don't include headers from nmcli */ -#include "utils.h" -#include "common.h" +#include "nm-vpn-helpers.h" +#include "nm-client-utils.h" + +/*****************************************************************************/ + +/* FIXME: don't include headers from nmcli. And move all uses of NMClient out + * of there. */ + +/* FIXME: don't directly print any warnings. Instead, add a hook mechanism to notify + * the caller about warnings, error or whatever. + */ + +#include "nmcli.h" + +NMConnection * +nmc_find_connection (const GPtrArray *connections, + const char *filter_type, + const char *filter_val, + int *start, + gboolean complete); /*****************************************************************************/ @@ -46,6 +62,428 @@ static char *secret_flags_to_string (guint32 flags, NMMetaAccessorGetType get_ty /*****************************************************************************/ +/* + * Parse IP address from string to NMIPAddress stucture. + * ip_str is the IP address in the form address/prefix + */ +static NMIPAddress * +nmc_parse_and_build_address (int family, const char *ip_str, GError **error) +{ + int max_prefix = (family == AF_INET) ? 32 : 128; + NMIPAddress *addr = NULL; + const char *ip; + char *tmp; + char *plen; + long int prefix; + GError *local = NULL; + + g_return_val_if_fail (ip_str != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + tmp = g_strdup (ip_str); + plen = strchr (tmp, '/'); /* prefix delimiter */ + if (plen) + *plen++ = '\0'; + + ip = tmp; + + prefix = max_prefix; + if (plen) { + if (!nmc_string_to_int (plen, TRUE, 1, max_prefix, &prefix)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("invalid prefix '%s'; <1-%d> allowed"), plen, max_prefix); + goto finish; + } + } + + addr = nm_ip_address_new (family, ip, (guint32) prefix, &local); + if (!addr) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("invalid IP address: %s"), local->message); + g_clear_error (&local); + } + +finish: + g_free (tmp); + return addr; +} + +/* + * nmc_parse_and_build_route: + * @family: AF_INET or AF_INET6 + * @str: route string to be parsed + * @error: location to store GError + * + * Parse route from string and return an #NMIPRoute + * + * Returns: a new #NMIPRoute or %NULL on error + */ +static NMIPRoute * +nmc_parse_and_build_route (int family, + const char *str, + GError **error) +{ + int max_prefix = (family == AF_INET) ? 32 : 128; + char *plen = NULL; + const char *next_hop = NULL; + const char *canon_dest; + long int prefix = max_prefix; + unsigned long int tmp_ulong; + NMIPRoute *route = NULL; + gboolean success = FALSE; + GError *local = NULL; + gint64 metric = -1; + guint i, len; + gs_strfreev char **routev = NULL; + gs_free char *value = NULL; + gs_free char *dest = NULL; + gs_unref_hashtable GHashTable *attrs = NULL; + GHashTable *tmp_attrs; + + g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE); + g_return_val_if_fail (str, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + value = g_strdup (str); + routev = nmc_strsplit_set (g_strstrip (value), " \t", 0); + len = g_strv_length (routev); + if (len < 1) { + g_set_error (error, 1, 0, _("'%s' is not valid (the format is: ip[/prefix] [next-hop] [metric] [attr=val] [attr=val])"), + str); + goto finish; + } + + dest = g_strdup (routev[0]); + plen = strchr (dest, '/'); /* prefix delimiter */ + if (plen) + *plen++ = '\0'; + + if (plen) { + if (!nmc_string_to_int (plen, TRUE, 1, max_prefix, &prefix)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("invalid prefix '%s'; <1-%d> allowed"), + plen, max_prefix); + goto finish; + } + } + + for (i = 1; i < len; i++) { + if (nm_utils_ipaddr_valid (family, routev[i])) { + if (metric != -1 || attrs) { + g_set_error (error, 1, 0, _("the next hop ('%s') must be first"), routev[i]); + goto finish; + } + next_hop = routev[i]; + } else if (nmc_string_to_uint (routev[i], TRUE, 0, G_MAXUINT32, &tmp_ulong)) { + if (attrs) { + g_set_error (error, 1, 0, _("the metric ('%s') must be before attributes"), routev[i]); + goto finish; + } + metric = tmp_ulong; + } else if (strchr (routev[i], '=')) { + GHashTableIter iter; + char *iter_key; + GVariant *iter_value; + + tmp_attrs = nm_utils_parse_variant_attributes (routev[i], ' ', '=', FALSE, + nm_ip_route_get_variant_attribute_spec(), + error); + if (!tmp_attrs) { + g_prefix_error (error, "invalid option '%s': ", routev[i]); + goto finish; + } + + if (!attrs) + attrs = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_iter_init (&iter, tmp_attrs); + while (g_hash_table_iter_next (&iter, (gpointer *) &iter_key, (gpointer *) &iter_value)) { + if (!nm_ip_route_attribute_validate (iter_key, iter_value, family, NULL, error)) { + g_prefix_error (error, "%s: ", iter_key); + g_hash_table_unref (tmp_attrs); + goto finish; + } + g_hash_table_insert (attrs, iter_key, iter_value); + g_hash_table_iter_steal (&iter); + } + g_hash_table_unref (tmp_attrs); + } else { + g_set_error (error, 1, 0, _("unrecognized option '%s'"), routev[i]); + goto finish; + } + } + + route = nm_ip_route_new (family, dest, prefix, next_hop, metric, &local); + if (!route) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("invalid route: %s"), local->message); + g_clear_error (&local); + goto finish; + } + + /* We don't accept default routes as NetworkManager handles it + * itself. But we have to check this after @route has normalized the + * dest string. + */ + canon_dest = nm_ip_route_get_dest (route); + if (!strcmp (canon_dest, "0.0.0.0") || !strcmp (canon_dest, "::")) { + g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("default route cannot be added (NetworkManager handles it by itself)")); + g_clear_pointer (&route, nm_ip_route_unref); + goto finish; + } + + if (attrs) { + GHashTableIter iter; + char *name; + GVariant *variant; + + g_hash_table_iter_init (&iter, attrs); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant)) + nm_ip_route_set_attribute (route, name, variant); + } + + success = TRUE; + +finish: + return route; +} + +/* Max priority values from libnm-core/nm-setting-vlan.c */ +#define MAX_SKB_PRIO G_MAXUINT32 +#define MAX_8021P_PRIO 7 /* Max 802.1p priority */ + +/* + * Parse VLAN priority mappings from the following format: 2:1,3:4,7:3 + * and verify if the priority numbers are valid + * + * Return: string array with split maps, or NULL on error + * Caller is responsible for freeing the array. + */ +static char ** +nmc_vlan_parse_priority_maps (const char *priority_map, + NMVlanPriorityMap map_type, + GError **error) +{ + char **mapping = NULL, **iter; + unsigned long from, to, from_max, to_max; + + g_return_val_if_fail (priority_map != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (map_type == NM_VLAN_INGRESS_MAP) { + from_max = MAX_8021P_PRIO; + to_max = MAX_SKB_PRIO; + } else { + from_max = MAX_SKB_PRIO; + to_max = MAX_8021P_PRIO; + } + + mapping = g_strsplit (priority_map, ",", 0); + for (iter = mapping; iter && *iter; iter++) { + char *left, *right; + + left = g_strstrip (*iter); + right = strchr (left, ':'); + if (!right) { + g_set_error (error, 1, 0, _("invalid priority map '%s'"), *iter); + g_strfreev (mapping); + return NULL; + } + *right++ = '\0'; + + if (!nmc_string_to_uint (left, TRUE, 0, from_max, &from)) { + g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"), + left, from_max); + g_strfreev (mapping); + return NULL; + } + if (!nmc_string_to_uint (right, TRUE, 0, to_max, &to)) { + g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"), + right, to_max); + g_strfreev (mapping); + return NULL; + } + *(right-1) = ':'; /* Put back ':' */ + } + return mapping; +} + +/* + * nmc_proxy_check_script: + * @script: file name with PAC script, or raw PAC Script data + * @out_script: raw PAC Script (with removed new-line characters) + * @error: location to store error, or %NULL + * + * Check PAC Script from @script parameter and return the checked/sanitized + * config in @out_script. + * + * Returns: %TRUE if the script is valid, %FALSE if it is invalid + */ +static gboolean +nmc_proxy_check_script (const char *script, char **out_script, GError **error) +{ + enum { + _PAC_SCRIPT_TYPE_GUESS, + _PAC_SCRIPT_TYPE_FILE, + _PAC_SCRIPT_TYPE_JSON, + } desired_type = _PAC_SCRIPT_TYPE_GUESS; + const char *filename = NULL; + size_t c_len = 0; + gs_free char *script_clone = NULL; + + *out_script = NULL; + + if (!script || !script[0]) + return TRUE; + + if (g_str_has_prefix (script, "file://")) { + script += NM_STRLEN ("file://"); + desired_type = _PAC_SCRIPT_TYPE_FILE; + } else if (g_str_has_prefix (script, "js://")) { + script += NM_STRLEN ("js://"); + desired_type = _PAC_SCRIPT_TYPE_JSON; + } + + if (NM_IN_SET (desired_type, _PAC_SCRIPT_TYPE_FILE, _PAC_SCRIPT_TYPE_GUESS)) { + gs_free char *contents = NULL; + + if (!g_file_get_contents (script, &contents, &c_len, NULL)) { + if (desired_type == _PAC_SCRIPT_TYPE_FILE) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("cannot read pac-script from file '%s'"), + script); + return FALSE; + } + } else { + if (c_len != strlen (contents)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("file '%s' contains non-valid utf-8"), + script); + return FALSE; + } + filename = script; + script = script_clone = g_steal_pointer (&contents); + } + } + + if ( !strstr (script, "FindProxyForURL") + || !g_utf8_validate (script, -1, NULL)) { + if (filename) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' does not contain a valid PAC Script"), filename); + } else { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("Not a valid PAC Script")); + } + return FALSE; + } + + *out_script = (script == script_clone) + ? g_steal_pointer (&script_clone) + : g_strdup (script); + return TRUE; +} + +const char * +nmc_bond_validate_mode (const char *mode, GError **error) +{ + unsigned long mode_int; + static const char *valid_modes[] = { "balance-rr", + "active-backup", + "balance-xor", + "broadcast", + "802.3ad", + "balance-tlb", + "balance-alb", + NULL }; + if (nmc_string_to_uint (mode, TRUE, 0, 6, &mode_int)) { + /* Translate bonding mode numbers to mode names: + * https://www.kernel.org/doc/Documentation/networking/bonding.txt + */ + return valid_modes[mode_int]; + } else + return nmc_string_is_valid (mode, valid_modes, error); +} + +/* + * nmc_team_check_config: + * @config: file name with team config, or raw team JSON config data + * @out_config: raw team JSON config data + * The value must be freed with g_free(). + * @error: location to store error, or %NUL + * + * Check team config from @config parameter and return the checked + * config in @out_config. + * + * Returns: %TRUE if the config is valid, %FALSE if it is invalid + */ +static gboolean +nmc_team_check_config (const char *config, char **out_config, GError **error) +{ + enum { + _TEAM_CONFIG_TYPE_GUESS, + _TEAM_CONFIG_TYPE_FILE, + _TEAM_CONFIG_TYPE_JSON, + } desired_type = _TEAM_CONFIG_TYPE_GUESS; + const char *filename = NULL; + size_t c_len = 0; + gs_free char *config_clone = NULL; + + *out_config = NULL; + + if (!config || !config[0]) + return TRUE; + + if (g_str_has_prefix (config, "file://")) { + config += NM_STRLEN ("file://"); + desired_type = _TEAM_CONFIG_TYPE_FILE; + } else if (g_str_has_prefix (config, "json://")) { + config += NM_STRLEN ("json://"); + desired_type = _TEAM_CONFIG_TYPE_JSON; + } + + if (NM_IN_SET (desired_type, _TEAM_CONFIG_TYPE_FILE, _TEAM_CONFIG_TYPE_GUESS)) { + gs_free char *contents = NULL; + + if (!g_file_get_contents (config, &contents, &c_len, NULL)) { + if (desired_type == _TEAM_CONFIG_TYPE_FILE) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("cannot read team config from file '%s'"), + config); + return FALSE; + } + } else { + if (c_len != strlen (contents)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("team config file '%s' contains non-valid utf-8"), + config); + return FALSE; + } + filename = config; + config = config_clone = g_steal_pointer (&contents); + } + } + + if (!nm_utils_is_json_object (config, NULL)) { + if (filename) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' does not contain a valid team configuration"), filename); + } else { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + _("team configuration must be a JSON object")); + } + return FALSE; + } + + *out_config = (config == config_clone) + ? g_steal_pointer (&config_clone) + : g_strdup (config); + return TRUE; +} + +/*****************************************************************************/ + #define ARGS_DESCRIBE_FCN \ const NMMetaSettingInfoEditor *setting_info, const NMMetaPropertyInfo *property_info, char **out_to_free @@ -3933,6 +4371,11 @@ nmc_value_transform_char_string (const GValue *src_value, static void __attribute__((constructor)) register_nmcli_value_transforms (void) { + /* FIXME: we should not register a g-value transform function. Instead, our meta data + * should be able to access the values according to their type. + * + * Also, running code as a ((constructor)) is hightly unexpected and affects the + * entire binary. */ g_value_register_transform_func (G_TYPE_BOOLEAN, G_TYPE_STRING, nmc_value_transform_bool_string); g_value_register_transform_func (G_TYPE_CHAR, G_TYPE_STRING, nmc_value_transform_char_string); } @@ -6106,3 +6549,4 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[_NM_META_SETTING_TYPE .properties_num = G_N_ELEMENTS (property_infos_wireless_security), }, }; + diff --git a/clients/common/nm-meta-setting-desc.h b/clients/common/nm-meta-setting-desc.h index 58b20e409f..18e0e98e4f 100644 --- a/clients/common/nm-meta-setting-desc.h +++ b/clients/common/nm-meta-setting-desc.h @@ -115,4 +115,7 @@ struct _NMMetaSettingInfoEditor { extern const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[_NM_META_SETTING_TYPE_NUM]; +/* FIXME: don't expose this function on it's own, at least not from this file. */ +const char *nmc_bond_validate_mode (const char *mode, GError **error); + #endif /* __NM_META_SETTING_DESC_H__ */ diff --git a/libnm-core/nm-setting-user.c b/libnm-core/nm-setting-user.c index 0d0817ef1e..2ba2eb84a6 100644 --- a/libnm-core/nm-setting-user.c +++ b/libnm-core/nm-setting-user.c @@ -438,7 +438,7 @@ set_property (GObject *object, guint prop_id, nm_clear_g_free (&priv->keys); return; } - g_hash_table_iter_init (&iter, priv->data); + g_hash_table_iter_init (&iter, data); while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) { if (!nm_setting_user_check_key (key, NULL)) g_return_if_reached (); diff --git a/po/POTFILES.in b/po/POTFILES.in index d72566c93a..e2995e63c1 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -10,6 +10,7 @@ clients/cli/nmcli.c clients/cli/polkit-agent.c clients/cli/settings.c clients/cli/utils.c +clients/common/nm-client-utils.c clients/common/nm-meta-setting-desc.c clients/common/nm-polkit-listener.c clients/common/nm-secret-agent-simple.c diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index f1f9f51833..b559401c8b 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -62,10 +62,12 @@ gint _nm_utils_ascii_str_to_bool (const char *str, * error reason. Depending on the usage, this might indicate a bug because * usually the target object should stay alive as long as there are pending * operations. + * @NM_UTILS_ERROR_INVALID_ARGUMENT: invalid argument. */ typedef enum { NM_UTILS_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/ NM_UTILS_ERROR_CANCELLED_DISPOSING, /*< nick=CancelledDisposing >*/ + NM_UTILS_ERROR_INVALID_ARGUMENT, /*< nick=InvalidArgument >*/ } NMUtilsError; #define NM_UTILS_ERROR (nm_utils_error_quark ())