diff --git a/ChangeLog b/ChangeLog index b4302ca9b..6ed91c71e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2007-10-23 Dan Williams + + * libnm-util/nm-setting.c + libnm-util/nm-setting.c + - (nm_setting_compare): implement + - (default_setting_compare_fn, do_one_compare, compare_gvalue_hash, + compare_one_hash_gvalue): compare the contents of a setting + + * libnm-util/nm-connection.c + - (nm_connection_compare): implement + 2007-10-23 Dan Williams * src/nm-activation-request.c diff --git a/libnm-util/nm-connection.c b/libnm-util/nm-connection.c index 8354fa883..2d4fb43d4 100644 --- a/libnm-util/nm-connection.c +++ b/libnm-util/nm-connection.c @@ -155,18 +155,59 @@ nm_connection_replace_settings (NMConnection *connection, return TRUE; } +typedef struct { + NMConnection *other; + gboolean failed; +} CompareConnectionInfo; + +static void +compare_one_setting (gpointer key, gpointer value, gpointer user_data) +{ + NMSetting *setting = (NMSetting *) value; + CompareConnectionInfo *info = (CompareConnectionInfo *) user_data; + NMSetting *other_setting; + NMConnectionPrivate *other_priv; + + if (info->failed) + return; + + other_priv = NM_CONNECTION_GET_PRIVATE (info->other); + other_setting = g_hash_table_lookup (other_priv->settings, setting->name); + if (!other_setting) + goto failed; + + info->failed = nm_setting_compare (setting, other_setting, FALSE) ? FALSE : TRUE; + return; + +failed: + info->failed = TRUE; +} + gboolean nm_connection_compare (NMConnection *connection, NMConnection *other) { + NMConnectionPrivate *priv; + CompareConnectionInfo info = { other, FALSE }; + if (!connection && !other) return TRUE; if (!connection || !other) return FALSE; - /* FIXME: Implement */ + priv = NM_CONNECTION_GET_PRIVATE (connection); + g_hash_table_foreach (priv->settings, compare_one_setting, &info); + if (info.failed == FALSE) { + /* compare A to B, then if that is the same compare B to A to ensure + * that keys that are in B but not A will make the comparison fail. + */ + info.failed = FALSE; + info.other = connection; + priv = NM_CONNECTION_GET_PRIVATE (other); + g_hash_table_foreach (priv->settings, compare_one_setting, &info); + } - return FALSE; + return info.failed ? FALSE : TRUE; } gboolean diff --git a/libnm-util/nm-setting.c b/libnm-util/nm-setting.c index 5e550be02..c27a00779 100644 --- a/libnm-util/nm-setting.c +++ b/libnm-util/nm-setting.c @@ -12,6 +12,7 @@ static GHashTable * nm_setting_hash (NMSetting *setting); static gboolean nm_setting_populate_from_hash_default (NMSetting *setting, GHashTable *table); static void default_setting_clear_secrets (NMSetting *setting); +static gboolean default_setting_compare_fn (NMSetting *setting, NMSetting *other, gboolean two_way); gboolean nm_setting_populate_from_hash (NMSetting *setting, GHashTable *hash) @@ -155,6 +156,22 @@ nm_setting_enumerate_values (NMSetting *setting, }; } +gboolean +nm_setting_compare (NMSetting *setting, NMSetting *other, gboolean two_way) +{ + g_return_val_if_fail (setting != NULL, FALSE); + g_return_val_if_fail (other != NULL, FALSE); + + g_return_val_if_fail (setting->name != NULL, FALSE); + g_return_val_if_fail (other->name != NULL, FALSE); + + g_return_val_if_fail (strcmp (setting->name, other->name) == 0, FALSE); + + if (!setting->compare_fn) + return default_setting_compare_fn (setting, other, two_way); + return setting->compare_fn (setting, other, two_way); +} + /***********************************************************************/ /* Helper functions for converting NMSetting to hash table. */ @@ -570,6 +587,219 @@ next: } } +typedef struct { + GHashTable *other; + gboolean failed; +} GValueHashCompareInfo; + +static void +compare_one_hash_gvalue (gpointer key, gpointer value, gpointer user_data) +{ + GValueHashCompareInfo *info = (GValueHashCompareInfo *) user_data; + GValue *val = (GValue *) value; + GValue *other_val; + + if (info->failed) + return; + + other_val = g_hash_table_lookup (info->other, key); + if (!other_val) + goto failed; + + if (G_VALUE_TYPE (val) != G_VALUE_TYPE (other_val)) + goto failed; + + switch (G_VALUE_TYPE (val)) { + case G_TYPE_STRING: { + const char *a_str = g_value_get_string (val); + const char *b_str = g_value_get_string (other_val); + if ((!a_str && b_str) || (a_str && !b_str)) + goto failed; + if (!a_str && !b_str) + break; + if (strcmp (a_str, b_str) != 0) + goto failed; + break; + } + default: + g_warning ("%s: unhandled GValue type %s", __func__, G_VALUE_TYPE_NAME (val)); + goto failed; + break; + } + return; + +failed: + info->failed = TRUE; +} + +static gboolean +compare_gvalue_hash (GHashTable *a, GHashTable *b) +{ + GValueHashCompareInfo info = { b, FALSE }; + + g_return_val_if_fail (a != NULL, FALSE); + g_return_val_if_fail (b != NULL, FALSE); + + g_hash_table_foreach (a, compare_one_hash_gvalue, &info); + return info.failed ? FALSE : TRUE; +} + +static gboolean +do_one_compare (NMSetting *a, NMSetting *b) +{ + SettingMember *m; + + g_return_if_fail (a != NULL); + g_return_if_fail (b != NULL); + + m = a->_members; + while (m->key) { + /* Ignore secrets since they aren't always supposed to be in the setting */ + if (m->secret == FALSE) + goto next; + + switch (m->type) { + case NM_S_TYPE_STRING: { + char **a_val = (char **) G_STRUCT_MEMBER_P (a, m->offset); + char **b_val = (char **) G_STRUCT_MEMBER_P (b, m->offset); + if ((*a_val && !*b_val) || (!*a_val && *b_val)) + return FALSE; + if (*a_val && *b_val && strcmp (*a_val, *b_val)) + return FALSE; + break; + } + case NM_S_TYPE_BOOL: { + gboolean *a_val = (gboolean *) G_STRUCT_MEMBER_P (a, m->offset); + gboolean *b_val = (gboolean *) G_STRUCT_MEMBER_P (b, m->offset); + if (*a_val != *b_val) + return FALSE; + break; + } + case NM_S_TYPE_UINT32: { + guint32 *a_val = (guint32 *) G_STRUCT_MEMBER_P (a, m->offset); + guint32 *b_val = (guint32 *) G_STRUCT_MEMBER_P (b, m->offset); + if (*a_val != *b_val) + return FALSE; + break; + } + case NM_S_TYPE_UINT64: { + guint64 *a_val = (guint64 *) G_STRUCT_MEMBER_P (a, m->offset); + guint64 *b_val = (guint64 *) G_STRUCT_MEMBER_P (b, m->offset); + if (*a_val != *b_val) + return FALSE; + break; + } + case NM_S_TYPE_BYTE_ARRAY: { + GByteArray **a_val = (GByteArray **) G_STRUCT_MEMBER_P (a, m->offset); + GByteArray **b_val = (GByteArray **) G_STRUCT_MEMBER_P (b, m->offset); + if ((*a_val && !*b_val) || (!*a_val && *b_val)) + return FALSE; + if (*a_val && *b_val) { + if ((*a_val)->len != (*b_val)->len) + return FALSE; + if (memcmp ((*a_val)->data, (*b_val)->data, (*a_val)->len)) + return FALSE; + } + break; + } + case NM_S_TYPE_UINT_ARRAY: { + GArray **a_val = (GArray **) G_STRUCT_MEMBER_P (a, m->offset); + GArray **b_val = (GArray **) G_STRUCT_MEMBER_P (b, m->offset); + if ((*a_val && !*b_val) || (!*a_val && *b_val)) + return FALSE; + if (*a_val && *b_val) { + if ((*a_val)->len != (*b_val)->len) + return FALSE; + if (memcmp ((*a_val)->data, (*b_val)->data, (*a_val)->len)) + return FALSE; + } + break; + } + case NM_S_TYPE_STRING_ARRAY: { + GSList **a_val = (GSList **) G_STRUCT_MEMBER_P (a, m->offset); + GSList **b_val = (GSList **) G_STRUCT_MEMBER_P (b, m->offset); + GSList *a_iter; + + for (a_iter = *a_val; a_iter; a_iter = g_slist_next (a_iter)) { + const char *a_str = a_iter->data; + GSList *b_iter; + gboolean found = FALSE; + + for (b_iter = *b_val; b_iter; b_iter = g_slist_next (b_iter)) { + const char *b_str = b_iter->data; + if (!a_str && !b_str) { + found = TRUE; + break; + } + if (a_str && b_str && !strcmp (a_str, b_str)) { + found = TRUE; + break; + } + } + if (!found) + return FALSE; + } + break; + } + case NM_S_TYPE_IP4_ADDRESSES: { + GSList **a_val = (GSList **) G_STRUCT_MEMBER_P (a, m->offset); + GSList **b_val = (GSList **) G_STRUCT_MEMBER_P (b, m->offset); + GSList *a_iter; + + for (a_iter = *a_val; a_iter; a_iter = g_slist_next (a_iter)) { + NMSettingIP4Address *a_addr = (NMSettingIP4Address *) a_iter->data; + GSList *b_iter; + gboolean found = FALSE; + + g_assert (a_addr); + for (b_iter = *b_val; b_iter; b_iter = g_slist_next (b_iter)) { + NMSettingIP4Address *b_addr = (NMSettingIP4Address *) b_iter->data; + + g_assert (b_addr); + if ( (a_addr->address == b_addr->address) + && (a_addr->netmask == b_addr->netmask) + && (a_addr->gateway == b_addr->gateway)) { + found = TRUE; + break; + } + } + if (!found) + return FALSE; + } + break; + } + case NM_S_TYPE_GVALUE_HASH: { + GHashTable **a_val = (GHashTable **) G_STRUCT_MEMBER_P (a, m->offset); + GHashTable **b_val = (GHashTable **) G_STRUCT_MEMBER_P (b, m->offset); + if (!compare_gvalue_hash (*a_val, *b_val)) + return FALSE; + break; + } + + default: + break; + } + +next: + m++; + } +} + +static gboolean +default_setting_compare_fn (NMSetting *setting, NMSetting *other, gboolean two_way) +{ + gboolean same; + + same = do_one_compare (setting, other); + + /* compare A to B, then if that is the same compare B to A to ensure + * that keys that are in B but not A will make the comparison fail. + */ + if (two_way && (same == TRUE)) + return do_one_compare (other, setting); + return same; +} + /* Connection */ diff --git a/libnm-util/nm-setting.h b/libnm-util/nm-setting.h index ad0eb1d1d..cfab40b1a 100644 --- a/libnm-util/nm-setting.h +++ b/libnm-util/nm-setting.h @@ -24,6 +24,10 @@ typedef GPtrArray *(*NMSettingNeedSecretsFn) (NMSetting *setting); typedef void (*NMSettingClearSecretsFn) (NMSetting *setting); +typedef gboolean (*NMSettingCompareFn) (NMSetting *setting, + NMSetting *other, + gboolean two_way); + typedef void (*NMSettingDestroyFn) (NMSetting *setting); typedef void (*NMSettingValueIterFn) (NMSetting *setting, @@ -63,6 +67,7 @@ struct _NMSetting { NMSettingUpdateSecretsFn update_secrets_fn; NMSettingNeedSecretsFn need_secrets_fn; NMSettingClearSecretsFn clear_secrets_fn; + NMSettingCompareFn compare_fn; NMSettingDestroyFn destroy_fn; }; @@ -70,6 +75,7 @@ gboolean nm_settings_verify_all (GHashTable *all_settings); gboolean nm_setting_populate_from_hash (NMSetting *setting, GHashTable *hash); gboolean nm_setting_verify (NMSetting *setting); +gboolean nm_setting_compare (NMSetting *setting, NMSetting *other, gboolean two_way); GHashTable *nm_setting_to_hash (NMSetting *setting); gboolean nm_setting_update_secrets (NMSetting *setting, GHashTable *secrets); GPtrArray * nm_setting_need_secrets (NMSetting *setting);