/* 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 - 2015 Red Hat, Inc. */ #include "nm-default.h" #include "settings.h" #include #include #include "nm-common-macros.h" #include "utils.h" #include "common.h" #include "nm-vpn-helpers.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, NmcPropertyGetType 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) #define HIDDEN_TEXT "" /*****************************************************************************/ #define ARGS_DESCRIBE_FCN \ const NmcSettingInfo *setting_info, const NmcPropertyInfo *property_info, char **out_to_free #define ARGS_GET_FCN \ const NmcSettingInfo *setting_info, const NmcPropertyInfo *property_info, NMSetting *setting, NmcPropertyGetType get_type, gboolean show_secrets #define ARGS_SET_FCN \ const NmcSettingInfo *setting_info, const NmcPropertyInfo *property_info, NMSetting *setting, const char *value, GError **error #define ARGS_REMOVE_FCN \ const NmcSettingInfo *setting_info, const NmcPropertyInfo *property_info, NMSetting *setting, const char *value, guint32 idx, GError **error #define ARGS_VALUES_FCN \ const NmcSettingInfo *setting_info, const NmcPropertyInfo *property_info, char ***out_to_free static char * _get_fcn_name (ARGS_GET_FCN) { nm_assert (nm_streq0 (nm_setting_get_name (setting), setting_info->general->setting_name)); return g_strdup (setting_info->general->setting_name); } static char * _get_fcn_nmc_with_default (ARGS_GET_FCN) { const char *s; char *s_full; GValue val = G_VALUE_INIT; if (property_info->property_typ_data->subtype.get_with_default.fcn (setting)) { if (get_type == NMC_PROPERTY_GET_PARSABLE) return g_strdup (""); return g_strdup (_("(default)")); } 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 == NMC_PROPERTY_GET_PARSABLE) s_full = g_strdup (s && *s ? s : " "); else s_full = s ? g_strdup_printf ("\"%s\"", s) : g_strdup (""); g_value_unset (&val); return s_full; } static char * _get_fcn_gobject (ARGS_GET_FCN) { char *s; GValue val = G_VALUE_INIT; 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); g_value_unset (&val); return s; } static char * _get_fcn_gobject_mtu (ARGS_GET_FCN) { guint32 mtu; if ( !property_info->property_typ_data || !property_info->property_typ_data->subtype.mtu.get_fcn) return _get_fcn_gobject (setting_info, property_info, setting, get_type, show_secrets); mtu = property_info->property_typ_data->subtype.mtu.get_fcn (setting); if (mtu == 0) { if (get_type == NMC_PROPERTY_GET_PARSABLE) return g_strdup ("auto"); else return g_strdup (_("auto")); } return g_strdup_printf ("%u", (unsigned) mtu); } static char * _get_fcn_gobject_secret_flags (ARGS_GET_FCN) { guint v; GValue val = G_VALUE_INIT; 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 secret_flags_to_string (v, get_type); } /*****************************************************************************/ 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 (ARGS_SET_FCN) { 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_mtu (ARGS_SET_FCN) { if (nm_streq0 (value, "auto")) value = "0"; return _set_fcn_gobject_uint (setting_info, property_info, setting, value, error); } static gboolean _set_fcn_gobject_mac (ARGS_SET_FCN) { NmcPropertyTypeMacMode mode; gboolean valid; if (property_info->property_typ_data) mode = property_info->property_typ_data->subtype.mac.mode; else mode = NMC_PROPERTY_TYPE_MAC_MODE_DEFAULT; if (mode == NMC_PROPERTY_TYPE_MAC_MODE_INFINIBAND) valid = nm_utils_hwaddr_valid (value, INFINIBAND_ALEN); else { valid = nm_utils_hwaddr_valid (value, ETH_ALEN) || ( mode == NMC_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; g_print (_("Warning: '%s' sum is higher than all flags => all flags set\n"), value); } g_object_set (setting, property_info->property_name, (guint) flags, NULL); return TRUE; } /*****************************************************************************/ static const char *const* _values_fcn_gobject_enum (ARGS_VALUES_FCN) { char **v, **w; bool has_minmax = property_info->property_typ_data->subtype.gobject_enum.min || property_info->property_typ_data->subtype.gobject_enum.max; v = (char **) nm_utils_enum_get_values ( property_info->property_typ_data->subtype.gobject_enum.get_gtype (), has_minmax ? property_info->property_typ_data->subtype.gobject_enum.min : G_MININT, has_minmax ? property_info->property_typ_data->subtype.gobject_enum.max : G_MAXINT); for (w = v; w && *w; w++) *w = g_strdup (*w); return (const char *const*) (*out_to_free = v); } /*****************************************************************************/ static const NmcSettingInfo * _meta_find_setting_info_by_name (const char *setting_name) { const NMMetaSettingInfo *meta_setting_info; const NmcSettingInfo *setting_info; g_return_val_if_fail (setting_name, NULL); meta_setting_info = nm_meta_setting_infos_by_name (setting_name); if (!meta_setting_info) return NULL; g_return_val_if_fail (nm_streq0 (meta_setting_info->setting_name, setting_name), NULL); if (meta_setting_info->meta_type >= G_N_ELEMENTS (nmc_setting_infos)) return NULL; setting_info = &nmc_setting_infos[meta_setting_info->meta_type]; g_return_val_if_fail (setting_info->general == meta_setting_info, NULL); return setting_info; } static const NmcSettingInfo * _meta_find_setting_info_by_gtype (GType gtype) { const NMMetaSettingInfo *meta_setting_info; const NmcSettingInfo *setting_info; meta_setting_info = nm_meta_setting_infos_by_gtype (gtype); if (!meta_setting_info) return NULL; g_return_val_if_fail (meta_setting_info->get_setting_gtype, NULL); g_return_val_if_fail (meta_setting_info->get_setting_gtype () == gtype, NULL); if (meta_setting_info->meta_type >= G_N_ELEMENTS (nmc_setting_infos)) return NULL; setting_info = &nmc_setting_infos[meta_setting_info->meta_type]; g_return_val_if_fail (setting_info->general == meta_setting_info, NULL); return setting_info; } static const NmcSettingInfo * _meta_find_setting_info_by_setting (NMSetting *setting) { const NmcSettingInfo *setting_info; g_return_val_if_fail (NM_IS_SETTING (setting), NULL); setting_info = _meta_find_setting_info_by_gtype (G_OBJECT_TYPE (setting)); if (!setting_info) return NULL; g_return_val_if_fail (setting_info == _meta_find_setting_info_by_name (nm_setting_get_name (setting)), NULL); return setting_info; } static const NmcPropertyInfo * _meta_setting_info_find_property_info (const NmcSettingInfo *setting_info, const char *property_name) { guint i; g_return_val_if_fail (setting_info, NULL); g_return_val_if_fail (property_name, NULL); for (i = 0; i < setting_info->properties_num; i++) { if (nm_streq (setting_info->properties[i].property_name, property_name)) return &setting_info->properties[i]; } return NULL; } static const NmcPropertyInfo * _meta_find_property_info_by_name (const char *setting_name, const char *property_name, const NmcSettingInfo **out_setting_info) { const NmcSettingInfo *setting_info; setting_info = _meta_find_setting_info_by_name (setting_name); NM_SET_OUT (out_setting_info, setting_info); if (!setting_info) return NULL; return _meta_setting_info_find_property_info (setting_info, property_name); } static const NmcPropertyInfo * _meta_find_property_info_by_setting (NMSetting *setting, const char *property_name, const NmcSettingInfo **out_setting_info) { const NmcSettingInfo *setting_info; const NmcPropertyInfo *property_info; setting_info = _meta_find_setting_info_by_setting (setting); NM_SET_OUT (out_setting_info, setting_info); if (!setting_info) return NULL; property_info = _meta_setting_info_find_property_info (setting_info, property_name); nm_assert (property_info == _meta_find_property_info_by_name (nm_setting_get_name (setting), property_name, NULL)); return property_info; } /*****************************************************************************/ static const NmcOutputField * _get_nmc_output_fields (const NmcSettingInfo *setting_info) { static NmcOutputField *fields[_NM_META_SETTING_TYPE_NUM + 1] = { }; NmcOutputField **field; guint i; g_return_val_if_fail (setting_info, NULL); g_return_val_if_fail (setting_info->general->meta_type < _NM_META_SETTING_TYPE_NUM, NULL); field = &fields[setting_info->general->meta_type]; if (G_UNLIKELY (!*field)) { *field = g_new0 (NmcOutputField, setting_info->properties_num + 1); for (i = 0; i < setting_info->properties_num; i++) { NmcOutputField *f = &(*field)[i]; f->name = setting_info->properties[i].property_name; f->name_l10n = setting_info->properties[i].property_name; } } return *field; } /*****************************************************************************/ 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, NmcPropertyGetType get_type) { GString *flag_str; if (get_type == NMC_PROPERTY_GET_PARSABLE) 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, NmcPropertyGetType get_type) { if (get_type == NMC_PROPERTY_GET_PARSABLE) 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, NmcPropertyGetType get_type) { if (get_type == NMC_PROPERTY_GET_PARSABLE) 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, NmcPropertyGetType get_type) { GString *flag_str; if (get_type == NMC_PROPERTY_GET_PARSABLE) 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