diff options
author | Thomas Haller <thaller@redhat.com> | 2014-02-26 09:51:09 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2014-10-07 10:28:46 +0200 |
commit | d1cd8967153f749abb101dff2a6022f7afe14f86 (patch) | |
tree | f8b58a1bf0f60cf9a088679b660eef8ff87830ef | |
parent | 22bbd869afc65ad5801497dba77194cc51969e43 (diff) | |
download | NetworkManager-d1cd8967153f749abb101dff2a6022f7afe14f86.tar.gz |
core: add nm_utils_log_connection_diff
Signed-off-by: Thomas Haller <thaller@redhat.com>
-rw-r--r-- | src/NetworkManagerUtils.c | 248 | ||||
-rw-r--r-- | src/NetworkManagerUtils.h | 2 | ||||
-rw-r--r-- | src/tests/test-general.c | 55 |
3 files changed, 305 insertions, 0 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index f4c3df9f5e..0a36f61982 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -1656,6 +1656,254 @@ nm_utils_get_monotonic_timestamp_s (void) return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec); } +typedef struct +{ + const char *name; + NMSetting *setting; + NMSetting *diff_base_setting; + GHashTable *setting_diff; +} LogConnectionSettingData; + +typedef struct +{ + const char *item_name; + NMSettingDiffResult diff_result; +} LogConnectionSettingItem; + +static gint +_log_connection_sort_hashes_fcn (gconstpointer a, gconstpointer b) +{ + const LogConnectionSettingData *v1 = a; + const LogConnectionSettingData *v2 = b; + guint32 p1, p2; + NMSetting *s1, *s2; + + s1 = v1->setting ? v1->setting : v1->diff_base_setting; + s2 = v2->setting ? v2->setting : v2->diff_base_setting; + + g_assert (s1 && s2); + + p1 = _nm_setting_get_setting_priority (s1); + p2 = _nm_setting_get_setting_priority (s2); + + if (p1 != p2) + return p1 > p2 ? 1 : -1; + + return strcmp (v1->name, v2->name); +} + +static GArray * +_log_connection_sort_hashes (NMConnection *connection, NMConnection *diff_base, GHashTable *connection_diff) +{ + GHashTableIter iter; + GArray *sorted_hashes; + LogConnectionSettingData setting_data; + + sorted_hashes = g_array_sized_new (TRUE, FALSE, sizeof (LogConnectionSettingData), g_hash_table_size (connection_diff)); + + g_hash_table_iter_init (&iter, connection_diff); + while (g_hash_table_iter_next (&iter, (gpointer) &setting_data.name, (gpointer) &setting_data.setting_diff)) { + setting_data.setting = nm_connection_get_setting_by_name (connection, setting_data.name); + setting_data.diff_base_setting = diff_base ? nm_connection_get_setting_by_name (diff_base, setting_data.name) : NULL; + g_assert (setting_data.setting || setting_data.diff_base_setting); + g_array_append_val (sorted_hashes, setting_data); + } + + g_array_sort (sorted_hashes, _log_connection_sort_hashes_fcn); + return sorted_hashes; +} + +static gint +_log_connection_sort_names_fcn (gconstpointer a, gconstpointer b) +{ + const LogConnectionSettingItem *v1 = a; + const LogConnectionSettingItem *v2 = b; + + /* we want to first show the items, that disappeared, then the one that changed and + * then the ones that were added. */ + + if ((v1->diff_result & NM_SETTING_DIFF_RESULT_IN_A) != (v1->diff_result & NM_SETTING_DIFF_RESULT_IN_A)) + return (v1->diff_result & NM_SETTING_DIFF_RESULT_IN_A) ? -1 : 1; + if ((v1->diff_result & NM_SETTING_DIFF_RESULT_IN_B) != (v1->diff_result & NM_SETTING_DIFF_RESULT_IN_B)) + return (v1->diff_result & NM_SETTING_DIFF_RESULT_IN_B) ? 1 : -1; + return strcmp (v1->item_name, v2->item_name); +} + +static char * +_log_connection_get_property (NMSetting *setting, const char *name) +{ + GValue val = G_VALUE_INIT; + char *s; + + g_return_val_if_fail (setting, NULL); + + if ( !NM_IS_SETTING_VPN (setting) + && nm_setting_get_secret_flags (setting, name, NULL, NULL)) + return g_strdup ("****"); + + if (!_nm_setting_get_property (setting, name, &val)) + g_return_val_if_reached (FALSE); + + if (G_VALUE_HOLDS_STRING (&val)) { + const char *val_s; + + val_s = g_value_get_string (&val); + if (!val_s) { + /* for NULL, we want to return the unquoted string "NULL". */ + s = g_strdup ("NULL"); + } else { + char *escaped = g_strescape (val_s, "'"); + + s = g_strdup_printf ("'%s'", escaped); + g_free (escaped); + } + } else { + s = g_strdup_value_contents (&val); + if (s == NULL) + s = g_strdup ("NULL"); + else { + char *escaped = g_strescape (s, "'"); + + g_free (s); + s = escaped; + } + } + g_value_unset(&val); + return s; +} + +static void +_log_connection_sort_names (LogConnectionSettingData *setting_data, GArray *sorted_names) +{ + GHashTableIter iter; + LogConnectionSettingItem item; + gpointer p; + + g_array_set_size (sorted_names, 0); + + g_hash_table_iter_init (&iter, setting_data->setting_diff); + while (g_hash_table_iter_next (&iter, (gpointer) &item.item_name, &p)) { + item.diff_result = GPOINTER_TO_UINT (p); + g_array_append_val (sorted_names, item); + } + + g_array_sort (sorted_names, _log_connection_sort_names_fcn); +} + +void +nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix) +{ + GHashTable *connection_diff = NULL; + GArray *sorted_hashes; + GArray *sorted_names = NULL; + int i, j; + gboolean connection_diff_are_same; + gboolean print_header = TRUE; + gboolean print_setting_header; + GString *str1; + + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (!diff_base || (NM_IS_CONNECTION (diff_base) && diff_base != connection)); + + /* For VPN setting types, this is broken, because we cannot (generically) print the content of data/secrets. Bummer... */ + + if (!nm_logging_enabled (level, domain)) + return; + + if (!prefix) + prefix = ""; + if (!name) + name = ""; + + connection_diff_are_same = nm_connection_diff (connection, diff_base, NM_SETTING_COMPARE_FLAG_EXACT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT, &connection_diff); + if (connection_diff_are_same) { + if (diff_base) + nm_log (level, domain, "%sconnection '%s' (%p and %p): no difference", prefix, name, connection, diff_base); + else + nm_log (level, domain, "%sconnection '%s' (%p): no properties set", prefix, name, connection); + g_assert (!connection_diff); + return; + } + + /* FIXME: it doesn't nicely show the content of NMSettingVpn, becuase nm_connection_diff() does not + * expand the hash values. */ + + sorted_hashes = _log_connection_sort_hashes (connection, diff_base, connection_diff); + if (sorted_hashes->len <= 0) + goto out; + + sorted_names = g_array_new (FALSE, FALSE, sizeof (LogConnectionSettingItem)); + str1 = g_string_new (NULL); + + for (i = 0; i < sorted_hashes->len; i++) { + LogConnectionSettingData *setting_data = &g_array_index (sorted_hashes, LogConnectionSettingData, i); + + _log_connection_sort_names (setting_data, sorted_names); + print_setting_header = TRUE; + for (j = 0; j < sorted_names->len; j++) { + char *str_conn, *str_diff; + LogConnectionSettingItem *item = &g_array_index (sorted_names, LogConnectionSettingItem, j); + + str_conn = (item->diff_result & NM_SETTING_DIFF_RESULT_IN_A) + ? _log_connection_get_property (setting_data->setting, item->item_name) + : NULL; + str_diff = (item->diff_result & NM_SETTING_DIFF_RESULT_IN_B) + ? _log_connection_get_property (setting_data->diff_base_setting, item->item_name) + : NULL; + + if (print_header) { + GError *err_verify = NULL; + + if (diff_base) + nm_log (level, domain, "%sconnection '%s' (%p < %p):", prefix, name, connection, diff_base); + else + nm_log (level, domain, "%sconnection '%s' (%p):", prefix, name, connection); + print_header = FALSE; + + if (!nm_connection_verify (connection, &err_verify)) { + nm_log (level, domain, "%sconnection %p does not verify: %s", prefix, connection, err_verify->message); + g_clear_error (&err_verify); + } + } +#define _NM_LOG_ALIGN "-25" + if (print_setting_header) { + if (diff_base) { + if (setting_data->setting && setting_data->diff_base_setting) + g_string_printf (str1, "%p < %p", setting_data->setting, setting_data->diff_base_setting); + else if (setting_data->diff_base_setting) + g_string_printf (str1, "*missing* < %p", setting_data->diff_base_setting); + else + g_string_printf (str1, "%p < *missing*", setting_data->setting); + nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s [ %s ]", prefix, setting_data->name, str1->str); + } else + nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s [ %p ]", prefix, setting_data->name, setting_data->setting); + print_setting_header = FALSE; + } + g_string_printf (str1, "%s.%s", setting_data->name, item->item_name); + switch (item->diff_result & (NM_SETTING_DIFF_RESULT_IN_A | NM_SETTING_DIFF_RESULT_IN_B)) { + case NM_SETTING_DIFF_RESULT_IN_B: + nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s < %s", prefix, str1->str, str_diff ? str_diff : "NULL"); + break; + case NM_SETTING_DIFF_RESULT_IN_A: + nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s = %s", prefix, str1->str, str_conn ? str_conn : "NULL"); + break; + default: + nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s = %s < %s", prefix, str1->str, str_conn ? str_conn : "NULL", str_diff ? str_diff : "NULL"); + break; +#undef _NM_LOG_ALIGN + } + g_free (str_conn); + g_free (str_diff); + } + } + + g_array_free (sorted_names, TRUE); + g_string_free (str1, TRUE); +out: + g_hash_table_destroy (connection_diff); + g_array_free (sorted_hashes, TRUE); +} + /** * nm_utils_ip6_property_path: diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index eead08aae3..7f6a53440d 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -132,6 +132,8 @@ NMConnection *nm_utils_match_connection (GSList *connections, NMUtilsMatchFilterFunc match_filter_func, gpointer match_filter_data); +void nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix); + gint64 nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback); #define NM_UTILS_NS_PER_SECOND ((gint64) 1000000000) diff --git a/src/tests/test-general.c b/src/tests/test-general.c index c1970b897c..12768645b2 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-general.c @@ -24,6 +24,7 @@ #include "NetworkManagerUtils.h" #include "nm-logging.h" +#include "nm-utils.h" #include "nm-test-utils.h" @@ -226,6 +227,59 @@ test_nm_utils_ip6_address_clear_host_address (void) g_rand_free (r); } + +static void +test_nm_utils_log_connection_diff() +{ + NMConnection *connection; + NMConnection *connection2; + + /* if logging is disabled (the default), nm_utils_log_connection_diff() returns + * early without doing anything. Hence, in the normal testing, this test does nothing. + * It only gets interesting, when run verbosely with NMTST_DEBUG=debug ... */ + + nm_log (LOGL_DEBUG, LOGD_CORE, "START TEST test_nm_utils_log_connection_diff..."); + + connection = nm_simple_connection_new (); + nm_connection_add_setting (connection, nm_setting_connection_new ()); + nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test1", ">>> "); + + nm_connection_add_setting (connection, nm_setting_wired_new ()); + nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test2", ">>> "); + + connection2 = nm_simple_connection_new_clone (connection); + nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test3", ">>> "); + + g_object_set (nm_connection_get_setting_connection (connection), + NM_SETTING_CONNECTION_ID, "id", + NM_SETTING_CONNECTION_UUID, "uuid", + NULL); + g_object_set (nm_connection_get_setting_connection (connection2), + NM_SETTING_CONNECTION_ID, "id2", + NM_SETTING_CONNECTION_MASTER, "master2", + NULL); + nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test4", ">>> "); + + nm_connection_add_setting (connection, nm_setting_802_1x_new ()); + nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test5", ">>> "); + + g_object_set (nm_connection_get_setting_802_1x (connection), + NM_SETTING_802_1X_PASSWORD, "id2", + NM_SETTING_802_1X_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED, + NULL); + nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test6", ">>> "); + nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test7", ">>> "); + nm_utils_log_connection_diff (connection2, connection, LOGL_DEBUG, LOGD_CORE, "test8", ">>> "); + + g_clear_object (&connection); + g_clear_object (&connection2); + + connection = nmtst_create_minimal_connection ("id-vpn-1", NULL, NM_SETTING_VPN_SETTING_NAME, NULL); + nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test-vpn-1", ">>> "); + + g_clear_object (&connection); +} + /*******************************************/ static NMConnection * @@ -592,6 +646,7 @@ main (int argc, char **argv) g_test_add_func ("/general/nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64); g_test_add_func ("/general/nm_utils_ip6_address_clear_host_address", test_nm_utils_ip6_address_clear_host_address); + g_test_add_func ("/general/nm_utils_log_connection_diff", test_nm_utils_log_connection_diff); g_test_add_func ("/general/connection-match/basic", test_connection_match_basic); g_test_add_func ("/general/connection-match/ip6-method", test_connection_match_ip6_method); |