From ce640b2637130d045f79bec564479fbf41b0cb65 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 15 Nov 2013 11:56:57 +0100 Subject: [PATCH 1/1] fixup! bond: add proper properties to NMSettingBond, deprecate "options" [WIP] Signed-off-by: Thomas Haller --- libnm-util/nm-setting-bond.c | 385 +++++++++++++++++++++++++++++++------------ 1 file changed, 280 insertions(+), 105 deletions(-) diff --git a/libnm-util/nm-setting-bond.c b/libnm-util/nm-setting-bond.c index 1177451..e1160ac 100644 --- a/libnm-util/nm-setting-bond.c +++ b/libnm-util/nm-setting-bond.c @@ -63,100 +63,103 @@ nm_setting_bond_error_quark (void) return quark; } G_DEFINE_TYPE_WITH_CODE (NMSettingBond, nm_setting_bond, NM_TYPE_SETTING, _nm_register_setting (NM_SETTING_BOND_SETTING_NAME, g_define_type_id, 1, NM_SETTING_BOND_ERROR)) NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BOND) #define NM_SETTING_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BOND, NMSettingBondPrivate)) typedef struct { char *interface_name; + char *mode; - guint miimon; - guint downdelay; - guint updelay; - guint arp_interval; + int miimon; + int downdelay; + int updelay; + int arp_interval; char **arp_ip_target; char *arp_validate; char *primary; char *primary_reselect; char *fail_over_mac; + int use_carrier; char *ad_select; char *xmit_hash_policy; - gboolean use_carrier; - guint resend_igmp; + int resend_igmp; GHashTable *options; } NMSettingBondPrivate; enum { PROP_0, PROP_INTERFACE_NAME, PROP_MODE, PROP_MIIMON, PROP_DOWNDELAY, PROP_UPDELAY, PROP_ARP_INTERVAL, PROP_ARP_IP_TARGET, PROP_ARP_VALIDATE, PROP_PRIMARY, PROP_PRIMARY_RESELECT, PROP_FAIL_OVER_MAC, PROP_USE_CARRIER, PROP_AD_SELECT, PROP_XMIT_HASH_POLICY, PROP_RESEND_IGMP, PROP_OPTIONS, LAST_PROP }; #define _FIRST_LEGACY_PROP PROP_MODE #define _LAST_LEGACY_PROP PROP_RESEND_IGMP enum { + TYPE_NONE, /* must be 0, so that it is the default in props if not explicitly set. */ TYPE_INT, TYPE_STR, TYPE_BOTH, TYPE_IP, TYPE_IFNAME, }; typedef struct { guint opt_type; const char *legacy_name; - const char *list[10]; + const char *list[7]; + const char *list_sentinel[1]; /* dummy, to NULL terminate the previous 'list' array */ GParamSpec *pspec; char *defval; } BondProperty; -static BondProperty props[ LAST_PROP + 1 ] = { +static BondProperty props[LAST_PROP] = { [PROP_MODE] = { TYPE_BOTH, NM_SETTING_BOND_OPTION_MODE, { "balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb" } }, [PROP_MIIMON] = { TYPE_INT, NM_SETTING_BOND_OPTION_MIIMON }, [PROP_DOWNDELAY] = { TYPE_INT, NM_SETTING_BOND_OPTION_DOWNDELAY }, [PROP_UPDELAY] = { TYPE_INT, NM_SETTING_BOND_OPTION_UPDELAY }, [PROP_ARP_INTERVAL] = { TYPE_INT, NM_SETTING_BOND_OPTION_ARP_INTERVAL }, [PROP_ARP_IP_TARGET] = { TYPE_IP, NM_SETTING_BOND_OPTION_ARP_IP_TARGET }, [PROP_ARP_VALIDATE] = { TYPE_BOTH, NULL, { "none", "active", "backup", "all" } }, - [PROP_PRIMARY] = { TYPE_IFNAME, NULL }, + [PROP_PRIMARY] = { TYPE_IFNAME }, [PROP_PRIMARY_RESELECT] = { TYPE_BOTH, NULL, { "always", "better", "failure" } }, [PROP_FAIL_OVER_MAC] = { TYPE_BOTH, NULL, { "none", "active", "follow" } }, [PROP_USE_CARRIER] = { TYPE_INT }, [PROP_AD_SELECT] = { TYPE_BOTH, NULL, { "stable", "bandwidth", "count" } }, [PROP_XMIT_HASH_POLICY] = { TYPE_STR, NULL, { "layer2", "layer2+3", "layer3+4", "encap2+3", "encap3+4" } }, [PROP_RESEND_IGMP] = { TYPE_INT }, }; /** * nm_setting_bond_new: * @@ -402,92 +405,106 @@ nm_setting_bond_get_xmit_hash_policy (NMSettingBond *setting) * Returns: the #NMSettingBond:resend-igmp property of the setting * * Since: 0.9.10 **/ guint nm_setting_bond_get_resend_igmp (NMSettingBond *setting) { g_return_val_if_fail (NM_IS_SETTING_BOND (setting), 0); return NM_SETTING_BOND_GET_PRIVATE (setting)->resend_igmp; } /*****************************************************************************/ static BondProperty * -find_property (const char *name, const char **out_new_name, guint *out_idx) +find_property_by_pspec (const GParamSpec *pspec, guint *out_idx, gboolean legacy_only) { - guint i; + guint i = legacy_only ? _FIRST_LEGACY_PROP : PROP_0 + 1; + guint end = legacy_only ? _LAST_LEGACY_PROP + 1 : LAST_PROP; + + g_return_val_if_fail (pspec != NULL, NULL); + + for (; i < end; i++) { + if (props[i].pspec == pspec) { + if (out_idx) + *out_idx = i; + return &props[i]; + } + } + if (out_idx) + *out_idx = LAST_PROP; + return NULL; +} + +/* Depending on legacy_only, this only finds properties that are exposed as legacy options. */ +static BondProperty * +find_property_by_name (const char *name, guint *out_idx, gboolean legacy_only) +{ + guint i = legacy_only ? _FIRST_LEGACY_PROP : PROP_0 + 1; + guint end = legacy_only ? _LAST_LEGACY_PROP + 1 : LAST_PROP; g_return_val_if_fail (name != NULL, NULL); - for (i = _FIRST_LEGACY_PROP; i <= _LAST_LEGACY_PROP; i++) { + for (; i < end; i++) { const char *new_name = g_param_spec_get_name (props[i].pspec); if (strcmp (name, new_name) == 0 || g_strcmp0 (name, props[i].legacy_name) == 0) { - if (out_new_name) - *out_new_name = new_name; if (out_idx) *out_idx = i; return &props[i]; } } + if (out_idx) + *out_idx = LAST_PROP; return NULL; } /* For a new property or legacy option name, returns the new property name */ static const char * -get_property_name (const char *name, const BondProperty **out_prop) +get_property_name (const BondProperty *prop) { - const char *new_name = NULL; - - *out_prop = find_property (name, &new_name, NULL); - return *out_prop ? new_name : NULL; + return prop ? g_param_spec_get_name (prop->pspec) : NULL; } /* For a new property or legacy option name, returns the legacy option name */ static const char * -get_legacy_name (GParamSpec *pspec) +get_legacy_name (const BondProperty *prop) { - const BondProperty *prop; - const char *new_name = NULL; - - prop = find_property (g_param_spec_get_name (pspec), &new_name, NULL); - if (prop) - return prop->legacy_name ? prop->legacy_name : new_name; - - return NULL; + if (!prop) + return NULL; + return prop->legacy_name ? prop->legacy_name : g_param_spec_get_name (prop->pspec); } /** * nm_setting_bond_get_num_options: * @setting: the #NMSettingBond * * Returns the number of options that are set in the legacy * #NMSettingBond:options property. This does not include other bond * properties which are not included in #NMSettingBond:options. * * Returns: the number of legacy bonding options * * Deprecated: use the option-specific getters instead. **/ guint32 nm_setting_bond_get_num_options (NMSettingBond *setting) { g_return_val_if_fail (NM_IS_SETTING_BOND (setting), 0); - return _LAST_LEGACY_PROP - _FIRST_LEGACY_PROP; + return _LAST_LEGACY_PROP - _FIRST_LEGACY_PROP + 1; } /** * nm_setting_bond_get_option: * @setting: the #NMSettingBond * @idx: index of the desired option, from 0 to * nm_setting_bond_get_num_options() - 1 * @out_name: (out): on return, the name of the bonding option; this * value is owned by the setting and should not be modified * @out_value: (out): on return, the value of the name of the bonding * option; this value is owned by the setting and should not be modified * * Given an index, return the value of the bonding option at that index. Indexes * are *not* guaranteed to be static across modifications to options done by * nm_setting_bond_add_option() and nm_setting_bond_remove_option(), @@ -495,118 +512,119 @@ nm_setting_bond_get_num_options (NMSettingBond *setting) * such as during option iteration. * * Returns: %TRUE on success if the index was valid and an option was found, * %FALSE if the index was invalid (ie, greater than the number of options * currently held by the setting) * * Deprecated: use the option-specific getters instead. **/ gboolean nm_setting_bond_get_option (NMSettingBond *setting, guint32 idx, const char **out_name, const char **out_value) { NMSettingBondPrivate *priv; - const char *legacy_name, *value; + const char *legacy_name = NULL, *value = NULL; + gboolean result = FALSE; g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); priv = NM_SETTING_BOND_GET_PRIVATE (setting); + if (idx >= _LAST_LEGACY_PROP - _FIRST_LEGACY_PROP) + goto out; idx += _FIRST_LEGACY_PROP; - g_return_val_if_fail (idx <= _LAST_LEGACY_PROP, FALSE); - legacy_name = get_legacy_name (props[idx].pspec); + legacy_name = get_legacy_name (&props[idx]); g_assert (legacy_name); - value = g_hash_table_lookup (priv->options, legacy_name); - if (!value) - return FALSE; + result = g_hash_table_lookup_extended (priv->options, legacy_name, NULL, (void **) &value); + g_assert (result); +out: if (out_name) *out_name = legacy_name; if (out_value) *out_value = value; - return TRUE; + return result; } static gboolean int_from_string (const char *s, glong *out_num) { - guint i; - - for (i = 0; i < strlen (s); i++) { - if (!g_ascii_isdigit (s[i]) && s[i] != '-') - return FALSE; - } + long int n; + if (!s) + return FALSE; errno = 0; - *out_num = strtol (s, NULL, 10); - return errno ? FALSE : TRUE; + n = strtol (s, NULL, 10); + if (out_num) + *out_num = n; + return !!errno; } static gboolean -validate_int (const BondProperty *prop, const char *value) +validate_int (const BondProperty *prop, const char *value, int *out_num) { GParamSpecInt *ispec; glong num = 0; + gboolean success = FALSE; - if (!G_IS_PARAM_SPEC_INT (prop->pspec)) - return FALSE; - if (!value) - return FALSE; + g_assert (G_IS_PARAM_SPEC_INT (prop->pspec)); if (!int_from_string (value, &num)) - return FALSE; + goto out; ispec = G_PARAM_SPEC_INT (prop->pspec); - return (num >= ispec->minimum && num <= ispec->maximum); + success = (num >= ispec->minimum && num <= ispec->maximum); +out: + if (out_num) + *out_num = success ? num : 0; + return success; } static gboolean validate_list (const BondProperty *prop, const char *value) { - guint i; - - if (!value) - return FALSE; + const char *const*ptr; - for (i = 0; i < G_N_ELEMENTS (prop->list) && prop->list[i]; i++) { - if (g_strcmp0 (prop->list[i], value) == 0) - return TRUE; + if (value) { + for (ptr = prop->list; *ptr; ptr++) { + if (strcmp (*ptr, value) == 0) + return TRUE; + } } - - /* empty validation list means all values pass */ - return prop->list[0] == NULL ? TRUE : FALSE; + return FALSE; } +/* by making it a macro, we don't have to worry about using glong as type of idx (otherwise, we would have to check for integer overflow too. */ +#define IS_VALID_LIST_INDEX(prop, idx) ( ((idx) >= 0) && ((idx) < g_strv_length ((char **) (prop)->list)) ) + static gboolean validate_both (const BondProperty *prop, const char *value) { glong num = -1; - g_assert (prop->list); - if (!value) return FALSE; if (validate_list (prop, value)) return TRUE; if (!int_from_string (value, &num)) return FALSE; /* Ensure number is within bounds of string list */ - return num >= 0 && num < G_N_ELEMENTS (prop->list); + return IS_VALID_LIST_INDEX (prop, num); } static char ** parse_ip (const char *value, gboolean warn_on_error) { char **ips, **iter; struct in_addr addr; if (!value || !value[0]) { /* missing value is valid, we just NULL instead of an empty array. */ return NULL; } ips = g_strsplit_set (value, ",", 0); for (iter = ips; *iter; iter++) { @@ -651,253 +669,280 @@ validate_ifname (const char *value) } return nm_utils_iface_valid_name (value); } /* Checks whether @value is is a valid value for @prop. * * Returns: TRUE, if the @value is valid for the given name. * If @value is NULL, false will be returned. **/ static gboolean validate_property (const BondProperty *prop, const char *value) { switch (prop->opt_type) { case TYPE_INT: - return validate_int (prop, value); + return validate_int (prop, value, NULL); case TYPE_STR: return validate_list (prop, value); case TYPE_BOTH: return validate_both (prop, value); case TYPE_IP: return validate_ip (value); case TYPE_IFNAME: return validate_ifname (value); + case TYPE_NONE: default: g_assert_not_reached(); } return FALSE; } /** * nm_setting_bond_get_option_by_name: * @setting: the #NMSettingBond * @name: the option name for which to retrieve the value * * Returns the value associated with the bonding option specified by * @name, if it exists. * * Returns: the value, or %NULL if the key/value pair was never added to the * setting; the value is owned by the setting and must not be modified * * Deprecated: use the option-specific getters instead. **/ const char * nm_setting_bond_get_option_by_name (NMSettingBond *setting, const char *name) { + NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting); + const char *value; + gboolean result; + BondProperty *prop; + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); - return g_hash_table_lookup (NM_SETTING_BOND_GET_PRIVATE (setting)->options, name); + result = g_hash_table_lookup_extended (priv->options, name, NULL, (void **) &value); + if (result) + return value; + + /* Try to lookup by alias name (new name vs. legacy name)... */ + prop = find_property_by_name (name, NULL, TRUE); + if (!prop) + return NULL; + + /* 'name' is a valid property name, but we did not find it in the options hash. + * Since every element *must* be in the options hash, this can only mean, that + * the user tried to lookup by new_name for items that have a different + * legacy_name. Support lookup by this alias. */ + result = g_hash_table_lookup_extended (priv->options, get_legacy_name (prop), NULL, (void **) &value); + g_assert (result); + + return value; } /** * nm_setting_bond_add_option: * @setting: the #NMSettingBond * @name: name for the option * @value: value for the option * * Add an option to the table. The option is compared to an internal list * of allowed options. Option names may contain only alphanumeric characters - * (ie [a-zA-Z0-9]). Adding a new name replaces any existing name/value pair + * (ie [a-zA-Z0-9_]). Adding a new name replaces any existing name/value pair * that may already exist. * * The order of how to set several options is relevant because there are options * that conflict with each other. * * Returns: %TRUE if the option was valid and was added to the internal option * list, %FALSE if it was not. * * Deprecated: use the option-specific properties instead. **/ gboolean nm_setting_bond_add_option (NMSettingBond *setting, const char *name, const char *value) { NMSettingBondPrivate *priv; GObject *object = G_OBJECT (setting); const BondProperty *prop = NULL; const char *prop_name; glong num = 0; g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); priv = NM_SETTING_BOND_GET_PRIVATE (setting); - prop_name = get_property_name (name, &prop); + prop = find_property_by_name (name, NULL, TRUE); + prop_name = get_property_name (prop); if (!prop_name) return FALSE; if (!validate_property (prop, value)) return FALSE; g_object_freeze_notify (object); switch (prop->opt_type) { case TYPE_INT: - if (!int_from_string (value, &num)) - return FALSE; + if (int_from_string (value, &num)) + g_assert_not_reached (); g_object_set (object, prop_name, (gint) num, NULL); break; case TYPE_BOTH: { const char *str_value = value; /* Might be an integer-as-string; find the string */ - if (!validate_list (prop, value)) { - if (!int_from_string (value, &num)) - return FALSE; - /* Be paranoid although it's already been checked by validate_property() */ - g_assert (num >= 0 && num < G_N_ELEMENTS (prop->list)); + if (int_from_string (value, &num)) { + /* FIXME: do we really want to coerce the value? verify() currently accepts numeric values + * for the TYPE_BOTH items. NMDeviceBond has to cope with the ambiguity of the options + * names anyway. It might be better, to support the same names as the kernel does, + * including numeric values. Also, when reading the value from sysfs, we will also + * encounter numeric values (so, either we ~always~ coerce -> set_property), or not at all. + **/ str_value = prop->list[num]; } g_object_set (object, prop_name, str_value, NULL); break; } case TYPE_IFNAME: case TYPE_STR: g_object_set (object, prop_name, value, NULL); break; case TYPE_IP: { char **ip = parse_ip (value, TRUE); g_object_set (object, prop_name, ip, NULL); g_strfreev (ip); break; } + case TYPE_NONE: default: g_assert_not_reached (); break; } g_object_thaw_notify (object); return TRUE; } /** * nm_setting_bond_remove_option: * @setting: the #NMSettingBond * @name: name of the option to remove * * Remove the bonding option referenced by @name from the internal option - * list. + * list. As the option list is deprected, you can no longer actually remove + * an item from the option hash. Removing it is now equivalent to resetting + * the default value. * * Returns: %TRUE if the option was found and removed from the internal option * list, %FALSE if it was not. * * Deprecated: use the option-specific properties instead. **/ gboolean nm_setting_bond_remove_option (NMSettingBond *setting, const char *name) { GObject *object = G_OBJECT (setting); NMSettingBondPrivate *priv; const BondProperty *prop; const char *prop_name; GValue defval = G_VALUE_INIT; g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); priv = NM_SETTING_BOND_GET_PRIVATE (setting); - prop_name = get_property_name (name, &prop); + prop = find_property_by_name (name, NULL, TRUE); + prop_name = get_property_name (prop); if (!prop_name) return FALSE; g_object_freeze_notify (object); - /* we don't really remove the property, instead we reset the default value. */ + /* We don't really remove the property, instead we reset the default value. + * Thus, the number of options is always constant (all of them) and the option + * hash always contains every (legacy) option. */ g_value_init (&defval, prop->pspec->value_type); g_param_value_set_default (prop->pspec, &defval); g_object_set_property (object, prop_name, &defval); g_value_unset (&defval); g_object_thaw_notify (object); return TRUE; } /** * nm_setting_bond_get_valid_options: * @setting: the #NMSettingBond * * Returns a list of valid bond options. * * Returns: (transfer none): a %NULL-terminated array of strings of valid bond options. * * Deprecated: the valid options are defined by the #NMSettingBond * properties. **/ const char ** nm_setting_bond_get_valid_options (NMSettingBond *setting) { - static const char *array[LAST_PROP + 1] = { NULL }; + static const char *array[_LAST_LEGACY_PROP - _FIRST_LEGACY_PROP + 1 + 1] = { NULL }; /* initialize the array once */ if (G_UNLIKELY (array[0] == NULL)) { guint prop, i; - for (prop = _FIRST_LEGACY_PROP, i = 0; prop <= _LAST_LEGACY_PROP; prop++, i++) { - array[i] = props[prop].legacy_name ? - props[prop].legacy_name : g_param_spec_get_name (props[prop].pspec); - } + for (prop = _FIRST_LEGACY_PROP, i = 0; prop <= _LAST_LEGACY_PROP; prop++, i++) + array[i] = get_legacy_name (&props[prop]); } return array; } /** * nm_setting_bond_get_option_default: * @setting: the #NMSettingBond * @name: the name of the option * * Returns: the value of the bond option if not overridden by an entry in * the #NMSettingBond:options property. * * Deprecated: Use the default values of the option-specific properties. **/ const char * nm_setting_bond_get_option_default (NMSettingBond *setting, const char *name) { BondProperty *prop; GValue defval = G_VALUE_INIT; guint idx = 0; g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); - prop = find_property (name, NULL, &idx); + prop = find_property_by_name (name, &idx, TRUE); + if (!prop) return NULL; if (G_UNLIKELY (prop->defval == NULL)) { - if (idx == PROP_ARP_IP_TARGET) - prop->defval = ""; - else { - g_value_init (&defval, prop->pspec->value_type); - g_param_value_set_default (prop->pspec, &defval); - prop->defval = g_strdup_value_contents (&defval); - g_value_unset (&defval); - } + g_value_init (&defval, prop->pspec->value_type); + g_param_value_set_default (prop->pspec, &defval); + prop->defval = g_strdup_value_contents (&defval); + g_value_unset (&defval); + g_return_val_if_fail (prop->defval, NULL); } return prop->defval; } /*****************************************************************/ static void set_properties_from_hash (NMSettingBond *self, GHashTable *options) { const char *value; guint i; g_object_freeze_notify (G_OBJECT (self)); /* Set each property to the value given by @options, or if not present @@ -968,69 +1013,69 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) NM_SETTING_BOND_MODE); g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_MODE); return FALSE; } if (!validate_property (&props[PROP_MODE], priv->mode)) { g_set_error (error, NM_SETTING_BOND_ERROR, NM_SETTING_BOND_ERROR_INVALID_PROPERTY, _("'%s' is not a valid value for '%s'"), priv->mode, NM_SETTING_BOND_MODE); g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_MODE); return FALSE; } /* Make sure mode is compatible with other settings */ - if ( strcmp (priv->mode, "balance-alb") == 0 - || strcmp (priv->mode, "balance-tlb") == 0) { + if (__NM_SETTING_BOND_MODE_IS_balance_alb (priv->mode) || + __NM_SETTING_BOND_MODE_IS_balance_tlb (priv->mode)) { if (priv->arp_interval > 0) { g_set_error (error, NM_SETTING_BOND_ERROR, NM_SETTING_BOND_ERROR_INVALID_PROPERTY, _("'%s=%s' is incompatible with '%s > 0'"), NM_SETTING_BOND_OPTION_MODE, priv->mode, NM_SETTING_BOND_OPTION_ARP_INTERVAL); g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_ARP_INTERVAL); return FALSE; } } - if (strcmp (priv->mode, "active-backup") == 0) { + if (__NM_SETTING_BOND_MODE_IS_active_backup (priv->mode)) { if (priv->primary && !nm_utils_iface_valid_name (priv->primary)) { g_set_error (error, NM_SETTING_BOND_ERROR, NM_SETTING_BOND_ERROR_INVALID_PROPERTY, _("'%s' is not a valid interface name"), priv->primary); g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_PRIMARY); return FALSE; } } else { if (priv->primary) { g_set_error (error, NM_SETTING_BOND_ERROR, NM_SETTING_BOND_ERROR_INVALID_PROPERTY, _("'%s' is only valid for '%s=%s'"), NM_SETTING_BOND_PRIMARY, NM_SETTING_BOND_MODE, "active-backup"); g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_PRIMARY); return FALSE; } } if (nm_setting_find_in_list (all_settings, NM_SETTING_INFINIBAND_SETTING_NAME)) { - if (strcmp (priv->mode, "active-backup") != 0) { + if (__NM_SETTING_BOND_MODE_IS_active_backup (priv->mode)) { g_set_error (error, NM_SETTING_BOND_ERROR, NM_SETTING_BOND_ERROR_INVALID_PROPERTY, _("'%s=%s' is not a valid configuration for '%s'"), NM_SETTING_BOND_OPTION_MODE, priv->mode, NM_SETTING_INFINIBAND_SETTING_NAME); g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_MODE); return FALSE; } } if (priv->miimon == 0) { /* updelay and downdelay can only be used with miimon */ if (priv->updelay > 0) { g_set_error (error, @@ -1156,61 +1201,189 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_XMIT_HASH_POLICY); return FALSE; } return TRUE; } static const char * get_virtual_iface_name (NMSetting *setting) { NMSettingBond *self = NM_SETTING_BOND (setting); return nm_setting_bond_get_interface_name (self); } +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + BondProperty *prop = find_property_by_pspec (prop_spec, NULL, TRUE); + gboolean result = FALSE; + + if (!prop) + goto CHAIN; + + switch (prop->opt_type) { + case TYPE_BOTH: { + char *a0, *b0; + const char *a, *b; + + g_object_get (setting, prop_spec->name, &a0, NULL); + g_object_get (setting, prop_spec->name, &b0, NULL); + + a = a0; + b = b0; + if (!a || !b) + result = (a == b); + else if (strcmp (a, b) == 0) + result = TRUE; + else { + int idx; + + if (validate_int (prop, a, &idx)) { + if (!IS_VALID_LIST_INDEX (prop, idx)) + goto BOTH_FINISHED; + a = prop->list[idx]; + } + if (validate_int (prop, b, &idx)) { + if (!IS_VALID_LIST_INDEX (prop, idx)) + goto BOTH_FINISHED; + b = prop->list[idx]; + } + result = (strcmp (a, b) == 0); + } + +BOTH_FINISHED: + g_free (a0); + g_free (b0); + return result; + } + case TYPE_IP: { + char **a, **b; + struct in_addr *addr_a = NULL, *addr_b = NULL; + + g_object_get (setting, prop_spec->name, &a, NULL); + g_object_get (setting, prop_spec->name, &b, NULL); + + if (!a || !a[0] || !b || !b[0]) + result = ((a ? a[0] : NULL) == (b ? b[0] : NULL)); + else { + /* both arrays are not empty. We compare them by + * converting every item into a struct in_addr and looking + * whether we find it in the other array (and vice versa). + * If one of the addresses cannot be converted, the result + * is always false (because nothing compares to an invalid property). + **/ + + guint i, j; + guint alen = g_strv_length (a); + guint blen = g_strv_length (b); + + /* convert all strings to struct in_addr */ + addr_a = g_new (struct in_addr, alen); + for (i = 0; i < alen; i++) { + if (!inet_aton (a[i], &addr_a[i])) + goto IP_FINISHED; + } + + addr_b = g_new (struct in_addr, blen); + for (i = 0; i < blen; i++) { + if (!inet_aton (b[i], &addr_b[i])) + goto IP_FINISHED; + } + + /* ensure that we find every address in the other array too */ + for (i = 0; i < alen; i++) { + for (j = 0; j < blen; j++) { + if (addr_a[i].s_addr == addr_b[j].s_addr) + break; + } + if (j >= blen) + goto IP_FINISHED; + } + + for (i = 0; i < blen; i++) { + for (j = 0; j < alen; j++) { + if (addr_b[i].s_addr == addr_a[j].s_addr) + break; + } + if (j >= alen) + goto IP_FINISHED; + } + result = TRUE; + } +IP_FINISHED: + g_free (addr_a); + g_free (addr_b); + g_strfreev (a); + g_strfreev (b); + return result; + } + default: + /* other types shall be compared by the default implementation. */ + goto CHAIN; + } + +CHAIN: + return NM_SETTING_CLASS (nm_setting_bond_parent_class)->compare_property (setting, other, prop_spec, flags); +} + + static void nm_setting_bond_init (NMSettingBond *setting) { NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting); g_object_set (setting, NM_SETTING_NAME, NM_SETTING_BOND_SETTING_NAME, NULL); - priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); +} + +static void +constructed (GObject *object) +{ + G_OBJECT_CLASS (nm_setting_bond_parent_class)->constructed (object); + + g_assert (g_hash_table_size (NM_SETTING_BOND_GET_PRIVATE (object)->options) == (_LAST_LEGACY_PROP - _FIRST_LEGACY_PROP + 1)); } static void finalize (GObject *object) { NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); + g_assert (g_hash_table_size (priv->options) == (_LAST_LEGACY_PROP - _FIRST_LEGACY_PROP + 1)); + g_free (priv->interface_name); g_free (priv->mode); g_free (priv->primary); g_strfreev (priv->arp_ip_target); g_hash_table_destroy (priv->options); G_OBJECT_CLASS (nm_setting_bond_parent_class)->finalize (object); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMSettingBond *setting = NM_SETTING_BOND (object); NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); - const char *legacy_name = get_legacy_name (pspec); + BondProperty *prop = find_property_by_pspec (pspec, NULL, TRUE); char *legacy_value = NULL; switch (prop_id) { case PROP_INTERFACE_NAME: g_free (priv->interface_name); priv->interface_name = g_value_dup_string (value); break; case PROP_MODE: g_free (priv->mode); priv->mode = g_value_dup_string (value); legacy_value = g_value_dup_string (value); break; case PROP_MIIMON: priv->miimon = g_value_get_int (value); legacy_value = g_strdup_printf ("%u", g_value_get_int (value)); @@ -1266,32 +1439,32 @@ set_property (GObject *object, guint prop_id, priv->xmit_hash_policy = g_value_dup_string (value); legacy_value = g_value_dup_string (value); break; case PROP_RESEND_IGMP: priv->resend_igmp = g_value_get_int (value); legacy_value = g_strdup_printf ("%u", g_value_get_int (value)); break; case PROP_OPTIONS: set_properties_from_hash (setting, g_value_get_boxed (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } - if (legacy_value) { - g_hash_table_insert (priv->options, g_strdup (legacy_name), legacy_value); + if (prop) { + g_hash_table_insert (priv->options, (void *)get_legacy_name (prop), legacy_value); g_object_notify (object, NM_SETTING_BOND_OPTIONS); } } static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMSettingBond *setting = NM_SETTING_BOND (object); NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); switch (prop_id) { case PROP_INTERFACE_NAME: g_value_set_string (value, nm_setting_bond_get_interface_name (setting)); break; @@ -1344,34 +1517,36 @@ get_property (GObject *object, guint prop_id, G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void nm_setting_bond_class_init (NMSettingBondClass *setting_class) { GObjectClass *object_class = G_OBJECT_CLASS (setting_class); NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); guint i; g_type_class_add_private (setting_class, sizeof (NMSettingBondPrivate)); /* virtual methods */ - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->finalize = finalize; - parent_class->verify = verify; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->constructed = constructed; + object_class->finalize = finalize; + parent_class->verify = verify; + parent_class->compare_property = compare_property; parent_class->get_virtual_iface_name = get_virtual_iface_name; /* Properties */ /** * NMSettingBond:interface-name: * * The name of the virtual in-kernel bonding network interface **/ props[PROP_INTERFACE_NAME].pspec = g_param_spec_string (NM_SETTING_BOND_INTERFACE_NAME, "InterfaceName", "The name of the virtual in-kernel bonding network interface", NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE); @@ -1454,31 +1629,31 @@ nm_setting_bond_class_init (NMSettingBondClass *setting_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); /** * NMSettingBond:arp-ip-target: * * An array of IPv4 addresses to ping when using ARP-based link monitoring. * This only has an effect when #NMSettingBond:arp-interval is also set. * * Since: 0.9.10 **/ props[PROP_ARP_IP_TARGET].pspec = g_param_spec_boxed (NM_SETTING_BOND_ARP_IP_TARGET, "ARP IP target", "ARP monitoring target IP addresses", G_TYPE_STRV, - G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); /** * NMSettingBond:arp-validate: * * Specifies whether or not ARP probes and replies should be * validated in the active-backup mode. One of * 'none', 'active', 'backup', 'all'. * * Since: 0.9.10 **/ props[PROP_ARP_VALIDATE].pspec = g_param_spec_string (NM_SETTING_BOND_ARP_VALIDATE, "arp-validate", "Specifies whether or not ARP probes and replies should " "be validate in the active-backup mode", -- 1.8.4.1.559.gdb9bdfb