/* 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-meta-setting-desc.h" #include #include #include "nm-common-macros.h" #include "nm-utils/nm-enum-utils.h" #include "NetworkManager.h" #include "nm-vpn-helpers.h" #include "nm-client-utils.h" #include "nm-meta-setting-access.h" /*****************************************************************************/ static gboolean validate_int (NMSetting *setting, const char* prop, gint val, GError **error); static gboolean validate_uint (NMSetting *setting, const char* prop, guint val, GError **error); static gboolean validate_int64 (NMSetting *setting, const char* prop, gint64 val, GError **error); static char *secret_flags_to_string (guint32 flags, NMMetaAccessorGetType get_type); #define ALL_SECRET_FLAGS \ (NM_SETTING_SECRET_FLAG_NONE | \ NM_SETTING_SECRET_FLAG_AGENT_OWNED | \ NM_SETTING_SECRET_FLAG_NOT_SAVED | \ NM_SETTING_SECRET_FLAG_NOT_REQUIRED) /*****************************************************************************/ static GType _gobject_property_get_gtype (GObject *gobject, const char *property_name) { GParamSpec *param_spec; param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (gobject), property_name); if (param_spec) return param_spec->value_type; g_return_val_if_reached (G_TYPE_INVALID); } static GType _gtype_property_get_gtype (GType gtype, const char *property_name) { /* given @gtype, a type for a GObject, lookup the property @property_name * and return its value_type. */ if (G_TYPE_IS_CLASSED (gtype)) { GParamSpec *param_spec; nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype); if (G_IS_OBJECT_CLASS (gtypeclass)) { param_spec = g_object_class_find_property (G_OBJECT_CLASS (gtypeclass), property_name); if (param_spec) return param_spec->value_type; } } g_return_val_if_reached (G_TYPE_INVALID); } /*****************************************************************************/ /* * 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; } /* * 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; } static const char * _get_text_hidden (NMMetaAccessorGetType get_type) { if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) return _(NM_META_TEXT_HIDDEN); return NM_META_TEXT_HIDDEN; } /*****************************************************************************/ G_GNUC_PRINTF (4, 5) static void _env_warn_fcn (const NMMetaEnvironment *environment, gpointer environment_user_data, NMMetaEnvWarnLevel warn_level, const char *fmt_l10n, ...) { va_list ap; if (!environment || !environment->warn_fcn) return; va_start (ap, fmt_l10n); environment->warn_fcn (environment, environment_user_data, warn_level, fmt_l10n, ap); va_end (ap); } /*****************************************************************************/ #define ARGS_DESCRIBE_FCN \ const NMMetaPropertyInfo *property_info, char **out_to_free #define ARGS_GET_FCN \ const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, NMSetting *setting, NMMetaAccessorGetType get_type, NMMetaAccessorGetFlags get_flags, NMMetaAccessorGetOutFlags *out_flags, gpointer *out_to_free #define ARGS_SET_FCN \ const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, NMSetting *setting, const char *value, GError **error #define ARGS_REMOVE_FCN \ const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, NMSetting *setting, const char *value, guint32 idx, GError **error #define ARGS_COMPLETE_FCN \ const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, const NMMetaOperationContext *operation_context, const char *text, char ***out_to_free #define ARGS_VALUES_FCN \ const NMMetaPropertyInfo *property_info, char ***out_to_free #define ARGS_SETTING_INIT_FCN \ const NMMetaSettingInfoEditor *setting_info, NMSetting *setting, NMMetaAccessorSettingInitType init_type #define RETURN_UNSUPPORTED_GET_TYPE() \ G_STMT_START { \ if (!NM_IN_SET (get_type, \ NM_META_ACCESSOR_GET_TYPE_PARSABLE, \ NM_META_ACCESSOR_GET_TYPE_PRETTY)) { \ nm_assert_not_reached (); \ return NULL; \ } \ } G_STMT_END; #define RETURN_STR_TO_FREE(val) \ G_STMT_START { \ char *_val = (val); \ return ((*(out_to_free)) = _val); \ } G_STMT_END static gconstpointer _get_fcn_nmc_with_default (ARGS_GET_FCN) { const char *s; char *s_full; GValue val = G_VALUE_INIT; RETURN_UNSUPPORTED_GET_TYPE (); if (property_info->property_typ_data->subtype.get_with_default.fcn (setting)) { if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) return _("(default)"); return ""; } g_value_init (&val, G_TYPE_STRING); g_object_get_property (G_OBJECT (setting), property_info->property_name, &val); s = g_value_get_string (&val); if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) s_full = s ? g_strdup_printf ("\"%s\"", s) : g_strdup (""); else s_full = g_strdup (s && *s ? s : " "); g_value_unset (&val); RETURN_STR_TO_FREE (s_full); } static gconstpointer _get_fcn_gobject_impl (const NMMetaPropertyInfo *property_info, NMSetting *setting, NMMetaAccessorGetType get_type, gpointer *out_to_free) { char *s; const char *s_c; GType gtype_prop; nm_auto_unset_gvalue GValue val = G_VALUE_INIT; RETURN_UNSUPPORTED_GET_TYPE (); gtype_prop = _gobject_property_get_gtype (G_OBJECT (setting), property_info->property_name); if (gtype_prop == G_TYPE_BOOLEAN) { gboolean b; g_value_init (&val, gtype_prop); g_object_get_property (G_OBJECT (setting), property_info->property_name, &val); b = g_value_get_boolean (&val); if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) s_c = b ? _("yes") : _("no"); else s_c = b ? "yes" : "no"; return s_c; } else { g_value_init (&val, G_TYPE_STRING); g_object_get_property (G_OBJECT (setting), property_info->property_name, &val); s = g_value_dup_string (&val); RETURN_STR_TO_FREE (s); } } static gconstpointer _get_fcn_gobject (ARGS_GET_FCN) { return _get_fcn_gobject_impl (property_info, setting, get_type, out_to_free); } static gconstpointer _get_fcn_gobject_mtu (ARGS_GET_FCN) { guint32 mtu; RETURN_UNSUPPORTED_GET_TYPE (); if ( !property_info->property_typ_data || !property_info->property_typ_data->subtype.mtu.get_fcn) return _get_fcn_gobject_impl (property_info, setting, get_type, out_to_free); mtu = property_info->property_typ_data->subtype.mtu.get_fcn (setting); if (mtu == 0) { if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) return _("auto"); return "auto"; } RETURN_STR_TO_FREE (g_strdup_printf ("%u", (unsigned) mtu)); } static gconstpointer _get_fcn_gobject_secret_flags (ARGS_GET_FCN) { guint v; GValue val = G_VALUE_INIT; RETURN_UNSUPPORTED_GET_TYPE (); g_value_init (&val, G_TYPE_UINT); g_object_get_property (G_OBJECT (setting), property_info->property_name, &val); v = g_value_get_uint (&val); g_value_unset (&val); RETURN_STR_TO_FREE (secret_flags_to_string (v, get_type)); } static gconstpointer _get_fcn_gobject_enum (ARGS_GET_FCN) { GType gtype = 0; GType gtype_prop; nm_auto_unref_gtypeclass GTypeClass *gtype_class = NULL; nm_auto_unref_gtypeclass GTypeClass *gtype_prop_class = NULL; gboolean has_gtype = FALSE; nm_auto_unset_gvalue GValue gval = G_VALUE_INIT; gint64 v; gboolean format_numeric = FALSE; gboolean format_numeric_hex = FALSE; gboolean format_numeric_hex_unknown = FALSE; gboolean format_text = FALSE; gboolean format_text_l10n = FALSE; gs_free char *s = NULL; char s_numeric[64]; RETURN_UNSUPPORTED_GET_TYPE (); if (property_info->property_typ_data) { if (property_info->property_typ_data->subtype.gobject_enum.get_gtype) { gtype = property_info->property_typ_data->subtype.gobject_enum.get_gtype (); has_gtype = TRUE; } } if ( property_info->property_typ_data && get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY && NM_FLAGS_ANY (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC_HEX | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT_L10N)) { format_numeric_hex = NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC_HEX); format_numeric = format_numeric_hex || NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC); format_text_l10n = NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT_L10N); format_text = format_text_l10n || NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT); } else if ( property_info->property_typ_data && get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY && NM_FLAGS_ANY (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC_HEX | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT)) { format_numeric_hex = NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC_HEX); format_numeric = format_numeric && NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC); format_text = NM_FLAGS_HAS (property_info->property_typ_data->typ_flags, NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT); } else if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) { /* by default, output in format "%u (%s)" (with hex for flags and l10n). */ format_numeric = TRUE; format_numeric_hex_unknown = TRUE; format_text = TRUE; format_text_l10n = TRUE; } else { /* by default, output only numeric (with hex for flags). */ format_numeric = TRUE; format_numeric_hex_unknown = TRUE; } nm_assert (format_text || format_numeric); gtype_prop = _gobject_property_get_gtype (G_OBJECT (setting), property_info->property_name); g_value_init (&gval, gtype_prop); g_object_get_property (G_OBJECT (setting), property_info->property_name, &gval); if ( gtype_prop == G_TYPE_INT || ( G_TYPE_IS_CLASSED (gtype_prop) && G_IS_ENUM_CLASS ((gtype_prop_class ?: (gtype_prop_class = g_type_class_ref (gtype_prop)))))) { if (gtype_prop == G_TYPE_INT) { if (!has_gtype) g_return_val_if_reached (NULL); v = g_value_get_int (&gval); } else v = g_value_get_enum (&gval); } else if ( gtype_prop == G_TYPE_UINT || ( G_TYPE_IS_CLASSED (gtype_prop) && G_IS_FLAGS_CLASS ((gtype_prop_class ?: (gtype_prop_class = g_type_class_ref (gtype_prop)))))) { if (gtype_prop == G_TYPE_UINT) { if (!has_gtype) g_return_val_if_reached (NULL); v = g_value_get_uint (&gval); } else v = g_value_get_flags (&gval); } else g_return_val_if_reached (NULL); if (!has_gtype) { gtype = gtype_prop; gtype_class = g_steal_pointer (>ype_prop_class); } nm_assert (({ nm_auto_unref_gtypeclass GTypeClass *t = NULL; ( G_TYPE_IS_CLASSED (gtype) && (t = g_type_class_ref (gtype)) && (G_IS_ENUM_CLASS (t) || G_IS_FLAGS_CLASS (t))); })); if (format_numeric && !format_text) { s = format_numeric_hex || ( format_numeric_hex_unknown && !G_IS_ENUM_CLASS (gtype_class ?: (gtype_class = g_type_class_ref (gtype)))) ? g_strdup_printf ("0x%"G_GINT64_FORMAT, v) : g_strdup_printf ("%"G_GINT64_FORMAT, v); RETURN_STR_TO_FREE (s); } /* the gobject_enum.value_infos are currently ignored for the getter. They * only declare additional aliases for the setter. */ s = nm_utils_enum_to_str (gtype, (int) v); if (!format_numeric) RETURN_STR_TO_FREE (g_steal_pointer (&s)); if ( format_numeric_hex || ( format_numeric_hex_unknown && !G_IS_ENUM_CLASS (gtype_class ?: (gtype_class = g_type_class_ref (gtype))))) nm_sprintf_buf (s_numeric, "0x%"G_GINT64_FORMAT, v); else nm_sprintf_buf (s_numeric, "%"G_GINT64_FORMAT, v); if (nm_streq0 (s, s_numeric)) RETURN_STR_TO_FREE (g_steal_pointer (&s)); if (format_text_l10n) RETURN_STR_TO_FREE (g_strdup_printf (_("%s (%s)"), s_numeric, s)); else RETURN_STR_TO_FREE (g_strdup_printf ("%s (%s)", s_numeric, s)); } /*****************************************************************************/ static gboolean _set_fcn_gobject_string (ARGS_SET_FCN) { if ( property_info->property_typ_data && property_info->property_typ_data->values_static) { value = nmc_string_is_valid (value, (const char **) property_info->property_typ_data->values_static, error); if (!value) return FALSE; } g_object_set (setting, property_info->property_name, value, NULL); return TRUE; } static gboolean _set_fcn_gobject_bool (ARGS_SET_FCN) { gboolean val_bool; if (!nmc_string_to_bool (value, &val_bool, error)) return FALSE; g_object_set (setting, property_info->property_name, val_bool, NULL); return TRUE; } static gboolean _set_fcn_gobject_trilean (ARGS_SET_FCN) { long int val_int; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (!nmc_string_to_int (value, TRUE, -1, 1, &val_int)) { g_set_error (error, 1, 0, _("'%s' is not a valid value; use -1, 0 or 1"), value); return FALSE; } g_object_set (setting, property_info->property_name, val_int, NULL); return TRUE; } static gboolean _set_fcn_gobject_int (ARGS_SET_FCN) { long int val_int; if (!nmc_string_to_int (value, TRUE, G_MININT, G_MAXINT, &val_int)) { g_set_error (error, 1, 0, _("'%s' is not a valid number (or out of range)"), value); return FALSE; } /* Validate the number according to the property spec */ if (!validate_int (setting, property_info->property_name, (gint) val_int, error)) return FALSE; g_object_set (setting, property_info->property_name, (gint) val_int, NULL); return TRUE; } static gboolean _set_fcn_gobject_int64 (ARGS_SET_FCN) { long val_int; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (!nmc_string_to_int (value, FALSE, 0, 0, &val_int)) { g_set_error (error, 1, 0, _("'%s' is not a valid number (or out of range)"), value); return FALSE; } /* Validate the number according to the property spec */ if (!validate_int64 (setting, property_info->property_name, (gint64) val_int, error)) return FALSE; g_object_set (setting, property_info->property_name, (gint64) val_int, NULL); return TRUE; } static gboolean _set_fcn_gobject_uint_impl (const NMMetaPropertyInfo *property_info, NMSetting *setting, const char *value, GError **error) { unsigned long val_int; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (!nmc_string_to_uint (value, TRUE, 0, G_MAXUINT, &val_int)) { g_set_error (error, 1, 0, _("'%s' is not a valid number (or out of range)"), value); return FALSE; } /* Validate the number according to the property spec */ if (!validate_uint (setting, property_info->property_name, (guint) val_int, error)) return FALSE; g_object_set (setting, property_info->property_name, (guint) val_int, NULL); return TRUE; } static gboolean _set_fcn_gobject_uint (ARGS_SET_FCN) { return _set_fcn_gobject_uint_impl (property_info, setting, value, error); } static gboolean _set_fcn_gobject_mtu (ARGS_SET_FCN) { if (nm_streq0 (value, "auto")) value = "0"; return _set_fcn_gobject_uint_impl (property_info, setting, value, error); } static gboolean _set_fcn_gobject_mac (ARGS_SET_FCN) { NMMetaPropertyTypeMacMode mode; gboolean valid; if (property_info->property_typ_data) mode = property_info->property_typ_data->subtype.mac.mode; else mode = NM_META_PROPERTY_TYPE_MAC_MODE_DEFAULT; if (mode == NM_META_PROPERTY_TYPE_MAC_MODE_INFINIBAND) valid = nm_utils_hwaddr_valid (value, INFINIBAND_ALEN); else { valid = nm_utils_hwaddr_valid (value, ETH_ALEN) || ( mode == NM_META_PROPERTY_TYPE_MAC_MODE_CLONED && NM_CLONED_MAC_IS_SPECIAL (value)); } if (!valid) { g_set_error (error, 1, 0, _("'%s' is not a valid Ethernet MAC"), value); return FALSE; } g_object_set (setting, property_info->property_name, value, NULL); return TRUE; } static gboolean _set_fcn_gobject_secret_flags (ARGS_SET_FCN) { char **strv = NULL, **iter; unsigned long flags = 0, val_int; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); strv = nmc_strsplit_set (value, " \t,", 0); for (iter = strv; iter && *iter; iter++) { if (!nmc_string_to_uint (*iter, TRUE, 0, ALL_SECRET_FLAGS, &val_int)) { g_set_error (error, 1, 0, _("'%s' is not a valid flag number; use <0-%d>"), *iter, ALL_SECRET_FLAGS); g_strfreev (strv); return FALSE; } flags += val_int; } g_strfreev (strv); /* Validate the flags number */ if (flags > ALL_SECRET_FLAGS) { flags = ALL_SECRET_FLAGS; _env_warn_fcn (environment, environment_user_data, NM_META_ENV_WARN_LEVEL_WARN, N_("'%s' sum is higher than all flags => all flags set"), value); } g_object_set (setting, property_info->property_name, (guint) flags, NULL); return TRUE; } static gboolean _set_fcn_gobject_enum (ARGS_SET_FCN) { GType gtype = 0; GType gtype_prop; gboolean has_gtype = FALSE; nm_auto_unset_gvalue GValue gval = G_VALUE_INIT; nm_auto_unref_gtypeclass GTypeClass *gtype_class = NULL; int v; if (property_info->property_typ_data) { if (property_info->property_typ_data->subtype.gobject_enum.get_gtype) { gtype = property_info->property_typ_data->subtype.gobject_enum.get_gtype (); has_gtype = TRUE; } } gtype_prop = _gobject_property_get_gtype (G_OBJECT (setting), property_info->property_name); if ( gtype_prop == G_TYPE_INT || gtype_prop == G_TYPE_UINT) { if (!has_gtype) g_return_val_if_reached (FALSE); } else if (G_TYPE_IS_CLASSED (gtype_prop)) { gtype_class = g_type_class_ref (gtype_prop); if ( !G_IS_ENUM_CLASS (gtype_class) && !G_IS_FLAGS_CLASS (gtype_class)) g_return_val_if_reached (FALSE); } else g_return_val_if_reached (FALSE); if (!has_gtype) gtype = gtype_prop; if (!_nm_utils_enum_from_str_full (gtype, value, &v, NULL, property_info->property_typ_data ? property_info->property_typ_data->subtype.gobject_enum.value_infos : NULL)) goto fail; g_value_init (&gval, gtype_prop); if (gtype_prop == G_TYPE_INT) g_value_set_int (&gval, v); else if (gtype_prop == G_TYPE_UINT) g_value_set_uint (&gval, v); else if (G_IS_ENUM_CLASS (gtype_class)) g_value_set_enum (&gval, v); else if (G_IS_FLAGS_CLASS (gtype_class)) g_value_set_flags (&gval, v); else g_return_val_if_reached (FALSE); if (!nm_g_object_set_property (G_OBJECT (setting), property_info->property_name, &gval, NULL)) goto fail; return TRUE; fail: if (error) { gs_free const char **valid_all = NULL; gs_free const char *valid_str = NULL; int min = G_MININT; int max = G_MAXINT; if (property_info->property_typ_data) { if ( property_info->property_typ_data->subtype.gobject_enum.min || property_info->property_typ_data->subtype.gobject_enum.max) { min = property_info->property_typ_data->subtype.gobject_enum.min; max = property_info->property_typ_data->subtype.gobject_enum.max; } } valid_all = nm_utils_enum_get_values (gtype, min, max); valid_str = g_strjoinv (",", (char **) valid_all); g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, _("invalid option '%s', use one of [%s]"), value, valid_str); } return FALSE; } /*****************************************************************************/ static const char *const* _values_fcn_gobject_enum (ARGS_VALUES_FCN) { GType gtype = 0; gboolean has_gtype = FALSE; int min = G_MININT; int max = G_MAXINT; char **v, **w; if (property_info->property_typ_data) { if ( property_info->property_typ_data->subtype.gobject_enum.min || property_info->property_typ_data->subtype.gobject_enum.max) { min = property_info->property_typ_data->subtype.gobject_enum.min; max = property_info->property_typ_data->subtype.gobject_enum.max; } if (property_info->property_typ_data->subtype.gobject_enum.get_gtype) { gtype = property_info->property_typ_data->subtype.gobject_enum.get_gtype (); has_gtype = TRUE; } } if (!has_gtype) { gtype = _gtype_property_get_gtype (property_info->setting_info->general->get_setting_gtype (), property_info->property_name); } /* the gobject_enum.value_infos are currently ignored for the list of * values. They only declare additional (hidden) aliases for the setter. */ v = (char **) nm_utils_enum_get_values (gtype, min, max); if (v) { for (w = v; *w; w++) *w = g_strdup (*w); } return (const char *const*) (*out_to_free = v); } /*****************************************************************************/ static const char *const* _complete_fcn_gobject_bool (ARGS_COMPLETE_FCN) { static const char *const v[] = { "true", "false", "on", "off", "1", "0", "yes", "no", NULL, }; if (!text || !text[0]) return &v[6]; return v; } static const char *const* _complete_fcn_gobject_devices (ARGS_COMPLETE_FCN) { NMDevice *const*devices = NULL; guint i, j; guint len = 0; char **ifnames; if ( environment && environment->get_nm_devices) { devices = environment->get_nm_devices (environment, environment_user_data, &len); } if (len == 0) return NULL; ifnames = g_new (char *, len + 1); for (i = 0, j = 0; i < len; i++) { const char *ifname; nm_assert (NM_IS_DEVICE (devices[i])); ifname = nm_device_get_iface (devices[i]); if (ifname) ifnames[j++] = g_strdup (ifname); } ifnames[j++] = NULL; *out_to_free = ifnames; return (const char *const*) ifnames; } /*****************************************************************************/ static char * wep_key_type_to_string (NMWepKeyType type) { switch (type) { case NM_WEP_KEY_TYPE_KEY: return g_strdup_printf (_("%d (key)"), type); case NM_WEP_KEY_TYPE_PASSPHRASE: return g_strdup_printf (_("%d (passphrase)"), type); case NM_WEP_KEY_TYPE_UNKNOWN: default: return g_strdup_printf (_("%d (unknown)"), type); } } static char * bytes_to_string (GBytes *bytes) { const guint8 *data; gsize len; GString *cert = NULL; int i; if (!bytes) return NULL; data = g_bytes_get_data (bytes, &len); cert = g_string_new (NULL); for (i = 0; i < len; i++) g_string_append_printf (cert, "%02X", data[i]); return g_string_free (cert, FALSE); } static char * vlan_flags_to_string (guint32 flags, NMMetaAccessorGetType get_type) { GString *flag_str; if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY) return g_strdup_printf ("%u", flags); if (flags == 0) return g_strdup (_("0 (NONE)")); flag_str = g_string_new (NULL); g_string_printf (flag_str, "%d (", flags); if (flags & NM_VLAN_FLAG_REORDER_HEADERS) g_string_append (flag_str, _("REORDER_HEADERS, ")); if (flags & NM_VLAN_FLAG_GVRP) g_string_append (flag_str, _("GVRP, ")); if (flags & NM_VLAN_FLAG_LOOSE_BINDING) g_string_append (flag_str, _("LOOSE_BINDING, ")); if (flags & NM_VLAN_FLAG_MVRP) g_string_append (flag_str, _("MVRP, ")); if (flag_str->str[flag_str->len-1] == '(') g_string_append (flag_str, _("unknown")); else g_string_truncate (flag_str, flag_str->len-2); /* chop off trailing ', ' */ g_string_append_c (flag_str, ')'); return g_string_free (flag_str, FALSE); } static char * vlan_priorities_to_string (NMSettingVlan *s_vlan, NMVlanPriorityMap map) { GString *priorities; int i; priorities = g_string_new (NULL); for (i = 0; i < nm_setting_vlan_get_num_priorities (s_vlan, map); i++) { guint32 from, to; if (nm_setting_vlan_get_priority (s_vlan, map, i, &from, &to)) g_string_append_printf (priorities, "%d:%d,", from, to); } if (priorities->len) g_string_truncate (priorities, priorities->len-1); /* chop off trailing ',' */ return g_string_free (priorities, FALSE); } static char * ip6_privacy_to_string (NMSettingIP6ConfigPrivacy ip6_privacy, NMMetaAccessorGetType get_type) { if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY) return g_strdup_printf ("%d", ip6_privacy); switch (ip6_privacy) { case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: return g_strdup_printf (_("%d (disabled)"), ip6_privacy); case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR: return g_strdup_printf (_("%d (enabled, prefer public IP)"), ip6_privacy); case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR: return g_strdup_printf (_("%d (enabled, prefer temporary IP)"), ip6_privacy); default: return g_strdup_printf (_("%d (unknown)"), ip6_privacy); } } static char * autoconnect_slaves_to_string (NMSettingConnectionAutoconnectSlaves autoconnect_slaves, NMMetaAccessorGetType get_type) { if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY) return g_strdup_printf ("%d", autoconnect_slaves); switch (autoconnect_slaves) { case NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO: return g_strdup_printf (_("%d (no)"), autoconnect_slaves); case NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES: return g_strdup_printf (_("%d (yes)"), autoconnect_slaves); case NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT: default: return g_strdup_printf (_("%d (default)"), autoconnect_slaves); } } static char * secret_flags_to_string (guint32 flags, NMMetaAccessorGetType get_type) { GString *flag_str; if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY) return g_strdup_printf ("%u", flags); if (flags == 0) return g_strdup (_("0 (none)")); flag_str = g_string_new (NULL); g_string_printf (flag_str, "%u (", flags); if (flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED) g_string_append (flag_str, _("agent-owned, ")); if (flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) g_string_append (flag_str, _("not saved, ")); if (flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) g_string_append (flag_str, _("not required, ")); if (flag_str->str[flag_str->len-1] == '(') g_string_append (flag_str, _("unknown")); else g_string_truncate (flag_str, flag_str->len-2); /* chop off trailing ', ' */ g_string_append_c (flag_str, ')'); return g_string_free (flag_str, FALSE); } static void vpn_data_item (const char *key, const char *value, gpointer user_data) { GString *ret_str = (GString *) user_data; if (ret_str->len != 0) g_string_append (ret_str, ", "); g_string_append_printf (ret_str, "%s = %s", key, value); } #define DEFINE_SETTER_STR_LIST_MULTI(def_func, s_macro, set_func) \ static gboolean \ def_func (NMSetting *setting, \ const char *prop, \ const char *value, \ const char **valid_strv, \ GError **error) \ { \ char **strv = NULL, **iter; \ const char *item; \ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \ strv = nmc_strsplit_set (value, " \t,", 0); \ for (iter = strv; iter && *iter; iter++) { \ if (!(item = nmc_string_is_valid (g_strstrip (*iter), valid_strv, error))) { \ g_strfreev (strv); \ return FALSE; \ } \ set_func (s_macro (setting), item); \ } \ g_strfreev (strv); \ return TRUE; \ } #define DEFINE_SETTER_OPTIONS(def_func, s_macro, s_type, add_func, valid_func1, valid_func2) \ static gboolean \ def_func (ARGS_SET_FCN) \ { \ char **strv = NULL, **iter; \ const char **(*valid_func1_p) (s_type *) = valid_func1; \ const char * (*valid_func2_p) (const char *, const char *, GError **) = valid_func2; \ const char *opt_name, *opt_val; \ \ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); \ \ strv = nmc_strsplit_set (value, ",", 0); \ for (iter = strv; iter && *iter; iter++) { \ char *left = g_strstrip (*iter); \ char *right = strchr (left, '='); \ if (!right) { \ g_set_error (error, 1, 0, _("'%s' is not valid; use