From eb5ceedbba87e5ea4d2b32ecc3e84f930c3d9835 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Feb 2017 15:38:58 +0100 Subject: [PATCH 1/9] core: add nm_utils_cmp_connection_by_autoconnect_priority_p_with_data() function Have a proper cmp() function and a wrapper *_p_with_data() that can be used for g_qsort_with_data(). Thus, establish a naming scheme (*_p_with_data()) for these compare wrappers that we need all over the place. Note, we also have nm_strcmp_p_with_data() for the same reason and later more such functions will follow. --- src/nm-core-utils.c | 15 ++++++++++++--- src/nm-core-utils.h | 3 ++- src/nm-policy.c | 2 +- src/tests/test-general.c | 4 ++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 352bde8a54..29c26ba817 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -1976,14 +1976,14 @@ nm_utils_read_resolv_conf_dns_options (const char *rc_contents) } int -nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnection **b) +nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection *b) { NMSettingConnection *a_s_con, *b_s_con; gboolean a_ac, b_ac; gint a_ap, b_ap; - a_s_con = nm_connection_get_setting_connection (*a); - b_s_con = nm_connection_get_setting_connection (*b); + a_s_con = nm_connection_get_setting_connection (a); + b_s_con = nm_connection_get_setting_connection (b); a_ac = !!nm_setting_connection_get_autoconnect (a_s_con); b_ac = !!nm_setting_connection_get_autoconnect (b_s_con); @@ -2000,6 +2000,15 @@ nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnection return 0; } +int +nm_utils_cmp_connection_by_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data) +{ + nm_assert (pa); + nm_assert (pb); + return nm_utils_cmp_connection_by_autoconnect_priority (*((NMConnection **) pa), + *((NMConnection **) pb)); +} + /*****************************************************************************/ static gint64 monotonic_timestamp_offset_sec; diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index e039760e09..82e10132b3 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -272,7 +272,8 @@ const char *nm_utils_new_infiniband_name (char *name, const char *parent_name, i GPtrArray *nm_utils_read_resolv_conf_nameservers (const char *rc_contents); GPtrArray *nm_utils_read_resolv_conf_dns_options (const char *rc_contents); -int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnection **b); +int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection *b); +int nm_utils_cmp_connection_by_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); void nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix); diff --git a/src/nm-policy.c b/src/nm-policy.c index a2ff2945b2..aebd843e3e 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -985,7 +985,7 @@ auto_activate_device (NMPolicy *self, /* sort is stable (which is important at this point) so that connections * with same priority are still sorted by last-connected-timestamp. */ - g_ptr_array_sort (connections, (GCompareFunc) nm_utils_cmp_connection_by_autoconnect_priority); + g_ptr_array_sort_with_data (connections, nm_utils_cmp_connection_by_autoconnect_priority_p_with_data, NULL); /* Find the first connection that should be auto-activated */ best_connection = NULL; diff --git a/src/tests/test-general.c b/src/tests/test-general.c index 81d8e05c1e..f8f8fa2606 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-general.c @@ -892,12 +892,12 @@ _test_connection_sort_autoconnect_priority_one (NMConnection **list, gboolean sh } /* sort it... */ - g_ptr_array_sort (connections, (GCompareFunc) nm_utils_cmp_connection_by_autoconnect_priority); + g_ptr_array_sort_with_data (connections, nm_utils_cmp_connection_by_autoconnect_priority_p_with_data, NULL); for (i = 0; i < count; i++) { if (list[i] == connections->pdata[i]) continue; - if (shuffle && nm_utils_cmp_connection_by_autoconnect_priority (&list[i], (NMConnection **) &connections->pdata[i]) == 0) + if (shuffle && nm_utils_cmp_connection_by_autoconnect_priority (list[i], connections->pdata[i]) == 0) continue; g_message ("After sorting, the order of connections is not as expected!! Offending index: %d", i); for (j = 0; j < count; j++) From 5c5845c649d3d6ac814f43c4500b7ae4cea68f5a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Feb 2017 15:45:06 +0100 Subject: [PATCH 2/9] core: add and use nm_settings_connection_cmp_timestamp*() Only move the function, no change in behavior. --- src/nm-manager.c | 2 +- src/settings/nm-settings-connection.c | 37 +++++++++++++++++++++++++++ src/settings/nm-settings-connection.h | 3 +++ src/settings/nm-settings.c | 32 +---------------------- src/settings/nm-settings.h | 2 -- 5 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/nm-manager.c b/src/nm-manager.c index 1bad6925b2..9893f7c475 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1738,7 +1738,7 @@ get_existing_connection (NMManager *self, NMDevice *device, gboolean *out_genera * When no configured connection matches the generated connection, we keep * the generated connection instead. */ - connections = g_slist_reverse (g_slist_sort (connections, nm_settings_sort_connections)); + connections = g_slist_reverse (g_slist_sort (connections, (GCompareFunc) nm_settings_connection_cmp_timestamp)); matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections, connection, nm_device_has_carrier (device), diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 81dcb69898..66069e740f 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -2162,6 +2162,43 @@ nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConn /*****************************************************************************/ +/* sorting for "best" connections. + * The function sorts connections in ascending timestamp order. + * That means an older connection (lower timestamp) goes before + * a newer one. + */ +int +nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *bc) +{ + guint64 ats = 0, bts = 0; + + if (ac == bc) + return 0; + if (!ac) + return -1; + if (!bc) + return 1; + + /* In the future we may use connection priorities in addition to timestamps */ + nm_settings_connection_get_timestamp (ac, &ats); + nm_settings_connection_get_timestamp (bc, &bts); + + if (ats < bts) + return -1; + else if (ats > bts) + return 1; + return 0; +} + +int +nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data) +{ + return nm_settings_connection_cmp_timestamp (*((NMSettingsConnection **) pa), + *((NMSettingsConnection **) pb)); +} + +/*****************************************************************************/ + /** * nm_settings_connection_get_timestamp: * @self: the #NMSettingsConnection diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index c5ddd8173e..fa7e4b5592 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -184,6 +184,9 @@ NMSettingsConnectionFlags nm_settings_connection_get_flags (NMSettingsConnection NMSettingsConnectionFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set); NMSettingsConnectionFlags nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConnectionFlags flags); +int nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *ab); +int nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); + gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self, guint64 *out_timestamp); diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 50b3b77222..dba9332d26 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -2085,36 +2085,6 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin /*****************************************************************************/ -/* GCompareFunc helper for sorting "best" connections. - * The function sorts connections in ascending timestamp order. - * That means an older connection (lower timestamp) goes before - * a newer one. - */ -gint -nm_settings_sort_connections (gconstpointer a, gconstpointer b) -{ - NMSettingsConnection *ac = (NMSettingsConnection *) a; - NMSettingsConnection *bc = (NMSettingsConnection *) b; - guint64 ats = 0, bts = 0; - - if (ac == bc) - return 0; - if (!ac) - return -1; - if (!bc) - return 1; - - /* In the future we may use connection priorities in addition to timestamps */ - nm_settings_connection_get_timestamp (ac, &ats); - nm_settings_connection_get_timestamp (bc, &bts); - - if (ats < bts) - return -1; - else if (ats > bts) - return 1; - return 0; -} - /** * nm_settings_get_best_connections: * @self: the #NMSetting @@ -2169,7 +2139,7 @@ nm_settings_get_best_connections (NMSettings *self, } /* List is sorted with oldest first */ - sorted = g_slist_insert_sorted (sorted, connection, nm_settings_sort_connections); + sorted = g_slist_insert_sorted (sorted, connection, (GCompareFunc) nm_settings_connection_cmp_timestamp); added++; if (max_requested && added > max_requested) { diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index c8a7ebae0c..3e56bf388e 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -126,8 +126,6 @@ void nm_settings_device_added (NMSettings *self, NMDevice *device); void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting); -gint nm_settings_sort_connections (gconstpointer a, gconstpointer b); - gboolean nm_settings_get_startup_complete (NMSettings *self); void nm_settings_set_transient_hostname (NMSettings *self, From 4a2572fcc1e52cd3f4cf029ce88db6af86653d7d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Feb 2017 16:00:33 +0100 Subject: [PATCH 3/9] core: add and use nm_settings_connection_cmp_default*() Only move the function, no change in behavior. --- src/settings/nm-settings-connection.c | 32 +++++++++++++++++++++++++++ src/settings/nm-settings-connection.h | 2 ++ src/settings/nm-settings.c | 31 +------------------------- 3 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 66069e740f..453b3c5b3e 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -2197,6 +2197,38 @@ nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointe *((NMSettingsConnection **) pb)); } +int +nm_settings_connection_cmp_default (NMSettingsConnection *a, NMSettingsConnection *b) +{ + NMSettingConnection *con_a; + NMSettingConnection *con_b; + guint64 ts_a = 0, ts_b = 0; + gboolean can_ac_a, can_ac_b; + + con_a = nm_connection_get_setting_connection (NM_CONNECTION (a)); + con_b = nm_connection_get_setting_connection (NM_CONNECTION (b)); + + can_ac_a = !!nm_setting_connection_get_autoconnect (con_a); + can_ac_b = !!nm_setting_connection_get_autoconnect (con_b); + if (can_ac_a != can_ac_b) + return can_ac_a ? -1 : 1; + + nm_settings_connection_get_timestamp (a, &ts_a); + nm_settings_connection_get_timestamp (b, &ts_b); + if (ts_a > ts_b) + return -1; + else if (ts_a == ts_b) + return 0; + return 1; +} + +int +nm_settings_connection_cmp_default_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data) +{ + return nm_settings_connection_cmp_default (*((NMSettingsConnection **) pa), + *((NMSettingsConnection **) pb)); +} + /*****************************************************************************/ /** diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index fa7e4b5592..8fe9533a86 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -186,6 +186,8 @@ NMSettingsConnectionFlags nm_settings_connection_set_flags_all (NMSettingsConnec int nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *ab); int nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); +int nm_settings_connection_cmp_default (NMSettingsConnection *a, NMSettingsConnection *b); +int nm_settings_connection_cmp_default_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self, guint64 *out_timestamp); diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index dba9332d26..0189d842b2 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -393,35 +393,6 @@ error: g_clear_object (&subject); } -static int -connection_sort (gconstpointer pa, gconstpointer pb) -{ - NMConnection *a = NM_CONNECTION (pa); - NMSettingConnection *con_a; - NMConnection *b = NM_CONNECTION (pb); - NMSettingConnection *con_b; - guint64 ts_a = 0, ts_b = 0; - gboolean can_ac_a, can_ac_b; - - con_a = nm_connection_get_setting_connection (a); - g_assert (con_a); - con_b = nm_connection_get_setting_connection (b); - g_assert (con_b); - - can_ac_a = !!nm_setting_connection_get_autoconnect (con_a); - can_ac_b = !!nm_setting_connection_get_autoconnect (con_b); - if (can_ac_a != can_ac_b) - return can_ac_a ? -1 : 1; - - nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pa), &ts_a); - nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pb), &ts_b); - if (ts_a > ts_b) - return -1; - else if (ts_a == ts_b) - return 0; - return 1; -} - /** * nm_settings_get_connections: * @self: the #NMSettings @@ -485,7 +456,7 @@ nm_settings_get_connections_sorted (NMSettings *self) g_hash_table_iter_init (&iter, NM_SETTINGS_GET_PRIVATE (self)->connections); while (g_hash_table_iter_next (&iter, NULL, &data)) - list = g_slist_insert_sorted (list, data, connection_sort); + list = g_slist_insert_sorted (list, data, (GCompareFunc) nm_settings_connection_cmp_default); return list; } From da072ff008feea85fc78d47b237341a3ce7dcda6 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Feb 2017 12:27:15 +0100 Subject: [PATCH 4/9] core: drop nm_settings_get_best_connections() for new nm_settings_get_connections_clone() nm_settings_get_best_connections() has only one caller: to create the hidden-SSID list. Instead of having a highly specialised function (that accepts 3 ways for filtering -- one of them broken, has one hard-coded way of sorting, and a @max_requested argument), add a more generic nm_settings_get_connections_clone() function. Also invert nm_settings_sort_connections(). The two callers want to sort descending, not ascending. --- src/devices/wifi/nm-device-wifi.c | 43 +++++---- src/nm-manager.c | 2 +- src/settings/nm-settings-connection.c | 12 +-- src/settings/nm-settings.c | 132 +++++++++++--------------- src/settings/nm-settings.h | 18 ++-- 5 files changed, 95 insertions(+), 112 deletions(-) diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index ce0c690e36..e70432ff64 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -1338,12 +1338,14 @@ check_scanning_allowed (NMDeviceWifi *self) static gboolean hidden_filter_func (NMSettings *settings, - NMConnection *connection, + NMSettingsConnection *connection, gpointer user_data) { NMSettingWireless *s_wifi; - s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection); + if (!nm_connection_is_type (NM_CONNECTION (connection), NM_SETTING_WIRELESS_SETTING_NAME)) + return FALSE; + s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (NM_CONNECTION (connection)); return s_wifi ? nm_setting_wireless_get_hidden (s_wifi) : FALSE; } @@ -1352,7 +1354,8 @@ build_hidden_probe_list (NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); guint max_scan_ssids = nm_supplicant_interface_get_max_scan_ssids (priv->sup_iface); - GSList *connections, *iter; + gs_free NMSettingsConnection **connections = NULL; + guint i, len; GPtrArray *ssids = NULL; static GByteArray *nullssid = NULL; @@ -1360,28 +1363,31 @@ build_hidden_probe_list (NMDeviceWifi *self) if (max_scan_ssids < 2) return NULL; - /* Static wildcard SSID used for every scan */ + connections = nm_settings_get_connections_clone (nm_device_get_settings ((NMDevice *) self), + &len, + hidden_filter_func, + NULL); + if (!connections[0]) + return NULL; + + g_qsort_with_data (connections, len, sizeof (NMSettingsConnection *), nm_settings_connection_cmp_timestamp_p_with_data, NULL); + + ssids = g_ptr_array_new_full (max_scan_ssids, (GDestroyNotify) g_byte_array_unref); + + /* Add wildcard SSID using a static wildcard SSID used for every scan */ if (G_UNLIKELY (nullssid == NULL)) nullssid = g_byte_array_new (); + g_ptr_array_add (ssids, g_byte_array_ref (nullssid)); - connections = nm_settings_get_best_connections (nm_device_get_settings ((NMDevice *) self), - max_scan_ssids - 1, - NM_SETTING_WIRELESS_SETTING_NAME, - NULL, - hidden_filter_func, - NULL); - if (connections && connections->data) { - ssids = g_ptr_array_new_full (max_scan_ssids - 1, (GDestroyNotify) g_byte_array_unref); - g_ptr_array_add (ssids, g_byte_array_ref (nullssid)); /* Add wildcard SSID */ - } - - for (iter = connections; iter; iter = g_slist_next (iter)) { - NMConnection *connection = iter->data; + for (i = 0; connections[i]; i++) { NMSettingWireless *s_wifi; GBytes *ssid; GByteArray *ssid_array; - s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection); + if (i >= max_scan_ssids - 1) + break; + + s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (NM_CONNECTION (connections[i])); g_assert (s_wifi); ssid = nm_setting_wireless_get_ssid (s_wifi); g_assert (ssid); @@ -1391,7 +1397,6 @@ build_hidden_probe_list (NMDeviceWifi *self) g_bytes_get_size (ssid)); g_ptr_array_add (ssids, ssid_array); } - g_slist_free (connections); return ssids; } diff --git a/src/nm-manager.c b/src/nm-manager.c index 9893f7c475..3cb158e082 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1738,7 +1738,7 @@ get_existing_connection (NMManager *self, NMDevice *device, gboolean *out_genera * When no configured connection matches the generated connection, we keep * the generated connection instead. */ - connections = g_slist_reverse (g_slist_sort (connections, (GCompareFunc) nm_settings_connection_cmp_timestamp)); + connections = g_slist_sort (connections, (GCompareFunc) nm_settings_connection_cmp_timestamp); matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections, connection, nm_device_has_carrier (device), diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 453b3c5b3e..345701a339 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -2163,8 +2163,8 @@ nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConn /*****************************************************************************/ /* sorting for "best" connections. - * The function sorts connections in ascending timestamp order. - * That means an older connection (lower timestamp) goes before + * The function sorts connections in descending timestamp order. + * That means an older connection (lower timestamp) goes after * a newer one. */ int @@ -2175,18 +2175,18 @@ nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnec if (ac == bc) return 0; if (!ac) - return -1; - if (!bc) return 1; + if (!bc) + return -1; /* In the future we may use connection priorities in addition to timestamps */ nm_settings_connection_get_timestamp (ac, &ats); nm_settings_connection_get_timestamp (bc, &bts); if (ats < bts) - return -1; - else if (ats > bts) return 1; + else if (ats > bts) + return -1; return 0; } diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 0189d842b2..057d341d4b 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -418,28 +418,79 @@ nm_settings_get_connections (NMSettings *self, guint *out_len) priv = NM_SETTINGS_GET_PRIVATE (self); - if (priv->connections_cached_list) { + if (G_LIKELY (priv->connections_cached_list)) { NM_SET_OUT (out_len, g_hash_table_size (priv->connections)); return priv->connections_cached_list; } l = g_hash_table_size (priv->connections); - v = g_new (NMSettingsConnection *, l + 1); + v = g_new (NMSettingsConnection *, (gsize) l + 1); i = 0; g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &con)) + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &con)) { + nm_assert (i < l); v[i++] = con; - v[i] = NULL; - + } nm_assert (i == l); + v[i] = NULL; NM_SET_OUT (out_len, l); priv->connections_cached_list = v; return v; } +/** + * nm_settings_get_connections_clone: + * @self: the #NMSetting + * @out_len: (allow-none): optional output argument + * @func: caller-supplied function for filtering connections + * @func_data: caller-supplied data passed to @func + * + * Returns: (transfer container) (element-type NMSettingsConnection): + * an NULL terminated array of #NMSettingsConnection objects that were + * filtered by @func (or all connections if no filter was specified). + * The order is arbitrary. + * Caller is responsible for freeing the returned array with free(), + * the contained values do not need to be unrefed. + */ +NMSettingsConnection ** +nm_settings_get_connections_clone (NMSettings *self, + guint *out_len, + NMSettingsConnectionFilterFunc func, + gpointer func_data) +{ + NMSettingsConnection *const*list_cached; + NMSettingsConnection **list; + guint len, i, j; + + g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); + + list_cached = nm_settings_get_connections (self, &len); + +#if NM_MORE_ASSERTS + nm_assert (list_cached); + for (i = 0; i < len; i++) + nm_assert (NM_IS_SETTINGS_CONNECTION (list_cached[i])); + nm_assert (!list_cached[i]); +#endif + + list = g_new (NMSettingsConnection *, ((gsize) len + 1)); + if (func) { + for (i = 0, j = 0; i < len; i++) { + if (func (self, list_cached[i], func_data)) + list[j++] = list_cached[i]; + } + list[j] = NULL; + len = j; + } else + memcpy (list, list_cached, sizeof (list[0]) * ((gsize) len + 1)); + + NM_SET_OUT (out_len, len); + return list; +} + /* Returns a list of NMSettingsConnections. * The list is sorted in the order suitable for auto-connecting, i.e. * first go connections with autoconnect=yes and most recent timestamp. @@ -2056,77 +2107,6 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin /*****************************************************************************/ -/** - * nm_settings_get_best_connections: - * @self: the #NMSetting - * @max_requested: if non-zero, the maximum number of connections to return - * @ctype1: an #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME) to - * filter connections against - * @ctype2: a second #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME) - * to filter connections against - * @func: caller-supplied function for filtering connections - * @func_data: caller-supplied data passed to @func - * - * Returns: a #GSList of #NMConnection objects in sorted order representing the - * "best" or highest-priority connections filtered by @ctype1 and/or @ctype2, - * and/or @func. Caller is responsible for freeing the returned #GSList, but - * the contained values do not need to be unreffed. - */ -GSList * -nm_settings_get_best_connections (NMSettings *self, - guint max_requested, - const char *ctype1, - const char *ctype2, - NMConnectionFilterFunc func, - gpointer func_data) -{ - NMSettingsPrivate *priv; - GSList *sorted = NULL; - GHashTableIter iter; - NMSettingsConnection *connection; - guint added = 0; - guint64 oldest = 0; - - g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); - - priv = NM_SETTINGS_GET_PRIVATE (self); - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) { - guint64 cur_ts = 0; - - if (ctype1 && !nm_connection_is_type (NM_CONNECTION (connection), ctype1)) - continue; - if (ctype2 && !nm_connection_is_type (NM_CONNECTION (connection), ctype2)) - continue; - if (func && !func (self, NM_CONNECTION (connection), func_data)) - continue; - - /* Don't bother with a connection that's older than the oldest one in the list */ - if (max_requested && added >= max_requested) { - nm_settings_connection_get_timestamp (connection, &cur_ts); - if (cur_ts <= oldest) - continue; - } - - /* List is sorted with oldest first */ - sorted = g_slist_insert_sorted (sorted, connection, (GCompareFunc) nm_settings_connection_cmp_timestamp); - added++; - - if (max_requested && added > max_requested) { - /* Over the limit, remove the oldest one */ - sorted = g_slist_delete_link (sorted, sorted); - added--; - } - - nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (sorted->data), &oldest); - } - - return g_slist_reverse (sorted); -} - -/*****************************************************************************/ - gboolean nm_settings_get_startup_complete (NMSettings *self) { diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index 3e56bf388e..09fe2d137e 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -57,9 +57,9 @@ * * Returns: %TRUE to allow the connection, %FALSE to ignore it */ -typedef gboolean (*NMConnectionFilterFunc) (NMSettings *settings, - NMConnection *connection, - gpointer func_data); +typedef gboolean (*NMSettingsConnectionFilterFunc) (NMSettings *settings, + NMSettingsConnection *connection, + gpointer func_data); typedef struct _NMSettingsClass NMSettingsClass; @@ -97,14 +97,12 @@ void nm_settings_add_connection_dbus (NMSettings *self, NMSettingsConnection *const* nm_settings_get_connections (NMSettings *settings, guint *out_len); -GSList *nm_settings_get_connections_sorted (NMSettings *settings); +NMSettingsConnection **nm_settings_get_connections_clone (NMSettings *self, + guint *out_len, + NMSettingsConnectionFilterFunc func, + gpointer func_data); -GSList *nm_settings_get_best_connections (NMSettings *self, - guint max_requested, - const char *ctype1, - const char *ctype2, - NMConnectionFilterFunc func, - gpointer func_data); +GSList *nm_settings_get_connections_sorted (NMSettings *settings); NMSettingsConnection *nm_settings_add_connection (NMSettings *settings, NMConnection *connection, From 0861f47a1c5d277188c31d3f5275755f830b0765 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Feb 2017 14:15:16 +0100 Subject: [PATCH 5/9] core: refactor nm_settings_get_connections_sorted() to return array instead of GSList We call these functions a lot. A GSList is just the wrong tool for the job. Refactor the code to use instead a sorted array everywhere. This means, we malloc() one array for all connections instead slice-allocate a GSList item for each. Also, sorting an array is faster then sorting a GSList. Technically, the GSList implementation had the same big-O runtime complexity, but using an array is still faster. That is, sorting an array and a GSList is both O(n*log(n)). Actually, nm_settings_get_connections_sorted() used g_slist_insert_sorted() instead of g_slist_sort(). That results in O(n^2). That could have been fixed to have O(n*log(n)), but instead refactor the code to use an array. --- src/nm-checkpoint.c | 10 ++--- src/nm-manager.c | 62 ++++++++++++++------------ src/nm-policy.c | 89 ++++++++++++++++++++------------------ src/settings/nm-settings.c | 21 ++++----- src/settings/nm-settings.h | 3 +- 5 files changed, 100 insertions(+), 85 deletions(-) diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c index 7d89e61ef8..08242ac60f 100644 --- a/src/nm-checkpoint.c +++ b/src/nm-checkpoint.c @@ -249,14 +249,14 @@ next_dev: if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) { NMSettingsConnection *con; - gs_free_slist GSList *list = NULL; - GSList *item; + gs_free NMSettingsConnection **list = NULL; + guint i; g_return_val_if_fail (priv->connection_uuids, NULL); - list = nm_settings_get_connections_sorted (nm_settings_get ()); + list = nm_settings_get_connections_sorted (nm_settings_get (), NULL); - for (item = list; item; item = g_slist_next (item)) { - con = item->data; + for (i = 0; list[i]; i++) { + con = list[i]; if (!g_hash_table_contains (priv->connection_uuids, nm_settings_connection_get_uuid (con))) { _LOGD ("rollback: deleting new connection %s (%s)", diff --git a/src/nm-manager.c b/src/nm-manager.c index 3cb158e082..fef1f6b2d9 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -433,18 +433,17 @@ GSList * nm_manager_get_activatable_connections (NMManager *manager) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - GSList *all_connections = nm_settings_get_connections_sorted (priv->settings); - GSList *connections = NULL, *iter; + gs_free NMSettingsConnection **all_connections = nm_settings_get_connections_sorted (priv->settings, NULL); + GSList *connections = NULL; NMSettingsConnection *connection; + guint i; - for (iter = all_connections; iter; iter = iter->next) { - connection = iter->data; - + for (i = 0; all_connections[i]; i++) { + connection = all_connections[i]; if (!find_ac_for_connection (manager, NM_CONNECTION (connection))) connections = g_slist_prepend (connections, connection); } - g_slist_free (all_connections); return g_slist_reverse (connections); } @@ -1205,7 +1204,8 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMDeviceFactory *factory; - gs_free_slist GSList *connections = NULL; + gs_free NMSettingsConnection **connections = NULL; + guint i; GSList *iter; gs_free char *iface = NULL; NMDevice *device = NULL, *parent = NULL; @@ -1276,9 +1276,9 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) } /* Create backing resources if the device has any autoconnect connections */ - connections = nm_settings_get_connections_sorted (priv->settings); - for (iter = connections; iter; iter = g_slist_next (iter)) { - NMConnection *candidate = iter->data; + connections = nm_settings_get_connections_sorted (priv->settings, NULL); + for (i = 0; connections[i]; i++) { + NMConnection *candidate = NM_CONNECTION (connections[i]); NMSettingConnection *s_con; if (!nm_device_check_connection_compatible (device, candidate)) @@ -1307,13 +1307,14 @@ static void retry_connections_for_parent_device (NMManager *self, NMDevice *device) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *connections, *iter; + gs_free NMSettingsConnection **connections = NULL; + guint i; g_return_if_fail (device); - connections = nm_settings_get_connections_sorted (priv->settings); - for (iter = connections; iter; iter = g_slist_next (iter)) { - NMConnection *candidate = iter->data; + connections = nm_settings_get_connections_sorted (priv->settings, NULL); + for (i = 0; connections[i]; i++) { + NMConnection *candidate = NM_CONNECTION (connections[i]); gs_free_error GError *error = NULL; gs_free char *ifname = NULL; NMDevice *parent; @@ -1328,8 +1329,6 @@ retry_connections_for_parent_device (NMManager *self, NMDevice *device) } } } - - g_slist_free (connections); } static void @@ -2775,7 +2774,8 @@ find_slaves (NMManager *manager, NMDevice *device) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - GSList *all_connections, *iter; + gs_free NMSettingsConnection **all_connections = NULL; + guint i; GSList *slaves = NULL; NMSettingConnection *s_con; @@ -2786,11 +2786,11 @@ find_slaves (NMManager *manager, * even if a slave was already active, it might be deactivated during * master reactivation. */ - all_connections = nm_settings_get_connections_sorted (priv->settings); - for (iter = all_connections; iter; iter = iter->next) { + all_connections = nm_settings_get_connections_sorted (priv->settings, NULL); + for (i = 0; all_connections[i]; i++) { NMSettingsConnection *master_connection = NULL; NMDevice *master_device = NULL; - NMConnection *candidate = iter->data; + NMConnection *candidate = NM_CONNECTION (all_connections[i]); find_master (manager, candidate, NULL, &master_connection, &master_device, NULL, NULL); if ( (master_connection && master_connection == connection) @@ -2798,7 +2798,6 @@ find_slaves (NMManager *manager, slaves = g_slist_prepend (slaves, candidate); } } - g_slist_free (all_connections); return g_slist_reverse (slaves); } @@ -3813,7 +3812,17 @@ impl_manager_add_and_activate_connection (NMManager *self, if (!subject) goto error; - all_connections = nm_settings_get_connections_sorted (priv->settings); + { + gs_free NMSettingsConnection **connections = NULL; + guint i, len; + + connections = nm_settings_get_connections_sorted (priv->settings, &len); + all_connections = NULL; + for (i = len; i > 0; ) { + i--; + all_connections = g_slist_prepend (all_connections, connections[i]); + } + } if (vpn) { /* Try to fill the VPN's connection setting and name at least */ if (!nm_connection_get_setting_vpn (connection)) { @@ -4770,7 +4779,7 @@ gboolean nm_manager_start (NMManager *self, GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter, *connections; + gs_free NMSettingsConnection **connections = NULL; guint i; if (!nm_settings_start (priv->settings, error)) @@ -4822,10 +4831,9 @@ nm_manager_start (NMManager *self, GError **error) * connection-added signals thus devices have to be created manually. */ _LOGD (LOGD_CORE, "creating virtual devices..."); - connections = nm_settings_get_connections_sorted (priv->settings); - for (iter = connections; iter; iter = iter->next) - connection_changed (self, NM_CONNECTION (iter->data)); - g_slist_free (connections); + connections = nm_settings_get_connections_sorted (priv->settings, NULL); + for (i = 0; connections[i]; i++) + connection_changed (self, NM_CONNECTION (connections[i])); priv->devices_inited = TRUE; diff --git a/src/nm-policy.c b/src/nm-policy.c index aebd843e3e..92de1e38ec 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -1149,7 +1149,8 @@ static void reset_autoconnect_all (NMPolicy *self, NMDevice *device) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - GSList *connections, *iter; + gs_free NMSettingsConnection **connections = NULL; + guint i; if (device) { _LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections on %s", @@ -1157,41 +1158,43 @@ reset_autoconnect_all (NMPolicy *self, NMDevice *device) } else _LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections"); - connections = nm_settings_get_connections_sorted (priv->settings); - for (iter = connections; iter; iter = g_slist_next (iter)) { - if (!device || nm_device_check_connection_compatible (device, iter->data)) { - nm_settings_connection_reset_autoconnect_retries (iter->data); - nm_settings_connection_set_autoconnect_blocked_reason (iter->data, NM_DEVICE_STATE_REASON_NONE); + connections = nm_settings_get_connections_sorted (priv->settings, NULL); + for (i = 0; connections[i]; i++) { + NMSettingsConnection *connection = connections[i]; + + if (!device || nm_device_check_connection_compatible (device, NM_CONNECTION (connection))) { + nm_settings_connection_reset_autoconnect_retries (connection); + nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_DEVICE_STATE_REASON_NONE); } } - g_slist_free (connections); } static void reset_autoconnect_for_failed_secrets (NMPolicy *self) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - GSList *connections, *iter; + gs_free NMSettingsConnection **connections = NULL; + guint i; _LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections with failed secrets"); - connections = nm_settings_get_connections_sorted (priv->settings); - for (iter = connections; iter; iter = g_slist_next (iter)) { - NMSettingsConnection *connection = NM_SETTINGS_CONNECTION (iter->data); + connections = nm_settings_get_connections_sorted (priv->settings, NULL); + for (i = 0; connections[i]; i++) { + NMSettingsConnection *connection = connections[i]; if (nm_settings_connection_get_autoconnect_blocked_reason (connection) == NM_DEVICE_STATE_REASON_NO_SECRETS) { nm_settings_connection_reset_autoconnect_retries (connection); nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_DEVICE_STATE_REASON_NONE); } } - g_slist_free (connections); } static void block_autoconnect_for_device (NMPolicy *self, NMDevice *device) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - GSList *connections, *iter; + gs_free NMSettingsConnection **connections = NULL; + guint i; _LOGD (LOGD_DEVICE, "blocking autoconnect for all connections on %s", nm_device_get_iface (device)); @@ -1203,14 +1206,15 @@ block_autoconnect_for_device (NMPolicy *self, NMDevice *device) if (!nm_device_is_software (device)) return; - connections = nm_settings_get_connections_sorted (priv->settings); - for (iter = connections; iter; iter = g_slist_next (iter)) { - if (nm_device_check_connection_compatible (device, iter->data)) { - nm_settings_connection_set_autoconnect_blocked_reason (NM_SETTINGS_CONNECTION (iter->data), + connections = nm_settings_get_connections_sorted (priv->settings, NULL); + for (i = 0; connections[i]; i++) { + NMSettingsConnection *connection = connections[i]; + + if (nm_device_check_connection_compatible (device, NM_CONNECTION (connection))) { + nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_DEVICE_STATE_REASON_USER_REQUESTED); } } - g_slist_free (connections); } static void @@ -1278,7 +1282,8 @@ reset_connections_retries (gpointer user_data) { NMPolicy *self = (NMPolicy *) user_data; NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - GSList *connections, *iter; + gs_free NMSettingsConnection **connections = NULL; + guint i; gint32 con_stamp, min_stamp, now; gboolean changed = FALSE; @@ -1286,9 +1291,9 @@ reset_connections_retries (gpointer user_data) min_stamp = 0; now = nm_utils_get_monotonic_timestamp_s (); - connections = nm_settings_get_connections_sorted (priv->settings); - for (iter = connections; iter; iter = g_slist_next (iter)) { - NMSettingsConnection *connection = NM_SETTINGS_CONNECTION (iter->data); + connections = nm_settings_get_connections_sorted (priv->settings, NULL); + for (i = 0; connections[i]; i++) { + NMSettingsConnection *connection = connections[i]; con_stamp = nm_settings_connection_get_autoconnect_retry_time (connection); if (con_stamp == 0) @@ -1300,7 +1305,6 @@ reset_connections_retries (gpointer user_data) } else if (min_stamp == 0 || min_stamp > con_stamp) min_stamp = con_stamp; } - g_slist_free (connections); /* Schedule the handler again if there are some stamps left */ if (min_stamp != 0) @@ -1318,8 +1322,7 @@ activate_slave_connections (NMPolicy *self, NMDevice *device) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); const char *master_device, *master_uuid_settings = NULL, *master_uuid_applied = NULL; - gs_free_slist GSList *connections = NULL; - GSList *iter; + guint i; NMActRequest *req; gboolean internal_activation = FALSE; @@ -1345,27 +1348,29 @@ activate_slave_connections (NMPolicy *self, NMDevice *device) internal_activation = subject && nm_auth_subject_is_internal (subject); } - if (!internal_activation) - connections = nm_settings_get_connections_sorted (priv->settings); + if (!internal_activation) { + gs_free NMSettingsConnection **connections = NULL; - for (iter = connections; iter; iter = g_slist_next (iter)) { - NMConnection *slave; - NMSettingConnection *s_slave_con; - const char *slave_master; + connections = nm_settings_get_connections_sorted (priv->settings, NULL); - slave = NM_CONNECTION (iter->data); - g_assert (slave); + for (i = 0; connections[i]; i++) { + NMConnection *slave; + NMSettingConnection *s_slave_con; + const char *slave_master; - s_slave_con = nm_connection_get_setting_connection (slave); - g_assert (s_slave_con); - slave_master = nm_setting_connection_get_master (s_slave_con); - if (!slave_master) - continue; + slave = NM_CONNECTION (connections[i]); - if ( !g_strcmp0 (slave_master, master_device) - || !g_strcmp0 (slave_master, master_uuid_applied) - || !g_strcmp0 (slave_master, master_uuid_settings)) - nm_settings_connection_reset_autoconnect_retries (NM_SETTINGS_CONNECTION (slave)); + s_slave_con = nm_connection_get_setting_connection (slave); + g_assert (s_slave_con); + slave_master = nm_setting_connection_get_master (s_slave_con); + if (!slave_master) + continue; + + if ( !g_strcmp0 (slave_master, master_device) + || !g_strcmp0 (slave_master, master_uuid_applied) + || !g_strcmp0 (slave_master, master_uuid_settings)) + nm_settings_connection_reset_autoconnect_retries (NM_SETTINGS_CONNECTION (slave)); + } } schedule_activate_all (self); diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 057d341d4b..1739a32fef 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -494,21 +494,22 @@ nm_settings_get_connections_clone (NMSettings *self, /* Returns a list of NMSettingsConnections. * The list is sorted in the order suitable for auto-connecting, i.e. * first go connections with autoconnect=yes and most recent timestamp. - * Caller must free the list with g_slist_free(). + * Caller must free the list with g_free(), but not the list items. */ -GSList * -nm_settings_get_connections_sorted (NMSettings *self) +NMSettingsConnection ** +nm_settings_get_connections_sorted (NMSettings *self, guint *out_len) { - GHashTableIter iter; - gpointer data = NULL; - GSList *list = NULL; + NMSettingsConnection **connections; + guint len; g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); - g_hash_table_iter_init (&iter, NM_SETTINGS_GET_PRIVATE (self)->connections); - while (g_hash_table_iter_next (&iter, NULL, &data)) - list = g_slist_insert_sorted (list, data, (GCompareFunc) nm_settings_connection_cmp_default); - return list; + connections = nm_settings_get_connections_clone (self, &len, NULL, NULL); + if (len > 1) + g_qsort_with_data (connections, len, sizeof (NMSettingsConnection *), nm_settings_connection_cmp_default_p_with_data, NULL); + + NM_SET_OUT (out_len, len); + return connections; } NMSettingsConnection * diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index 09fe2d137e..0771edee13 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -102,7 +102,8 @@ NMSettingsConnection **nm_settings_get_connections_clone (NMSettings *self, NMSettingsConnectionFilterFunc func, gpointer func_data); -GSList *nm_settings_get_connections_sorted (NMSettings *settings); +NMSettingsConnection **nm_settings_get_connections_sorted (NMSettings *self, + guint *out_len); NMSettingsConnection *nm_settings_add_connection (NMSettings *settings, NMConnection *connection, From b3b1793f3dc4fcedb63fe51ecc9d3ed17aea6d35 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Feb 2017 15:13:03 +0100 Subject: [PATCH 6/9] core: refactor nm_manager_get_activatable_connections() to return an array ... instead of a GSList. --- src/nm-manager.c | 57 ++++++++++++++++++++++++++++++------------------ src/nm-manager.h | 5 ++++- src/nm-policy.c | 34 +++++++++++++++++------------ 3 files changed, 60 insertions(+), 36 deletions(-) diff --git a/src/nm-manager.c b/src/nm-manager.c index fef1f6b2d9..c06b5c4862 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -423,28 +423,34 @@ find_ac_for_connection (NMManager *manager, NMConnection *connection) return NULL; } +static gboolean +_get_activatable_connections_filter (NMSettings *settings, + NMSettingsConnection *connection, + gpointer user_data) +{ + return !find_ac_for_connection (user_data, NM_CONNECTION (connection)); +} + /* Filter out connections that are already active. * nm_settings_get_connections_sorted() returns sorted list. We need to preserve the * order so that we didn't change auto-activation order (recent timestamps * are first). * Caller is responsible for freeing the returned list with g_slist_free(). */ -GSList * -nm_manager_get_activatable_connections (NMManager *manager) +NMSettingsConnection ** +nm_manager_get_activatable_connections (NMManager *manager, guint *out_len, gboolean sort) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - gs_free NMSettingsConnection **all_connections = nm_settings_get_connections_sorted (priv->settings, NULL); - GSList *connections = NULL; - NMSettingsConnection *connection; - guint i; + NMSettingsConnection **connections; + guint len; - for (i = 0; all_connections[i]; i++) { - connection = all_connections[i]; - if (!find_ac_for_connection (manager, NM_CONNECTION (connection))) - connections = g_slist_prepend (connections, connection); - } - - return g_slist_reverse (connections); + connections = nm_settings_get_connections_clone (priv->settings, &len, + _get_activatable_connections_filter, + manager); + if (sort && len > 1) + g_qsort_with_data (connections, len, sizeof (connections[0]), nm_settings_connection_cmp_default_p_with_data, NULL); + NM_SET_OUT (out_len, len); + return connections; } static NMActiveConnection * @@ -1687,7 +1693,7 @@ static NMSettingsConnection * get_existing_connection (NMManager *self, NMDevice *device, gboolean *out_generated) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - gs_free_slist GSList *connections = nm_manager_get_activatable_connections (self); + gs_free_slist GSList *connections = NULL; NMConnection *connection = NULL; NMSettingsConnection *matched; NMSettingsConnection *added = NULL; @@ -1737,7 +1743,17 @@ get_existing_connection (NMManager *self, NMDevice *device, gboolean *out_genera * When no configured connection matches the generated connection, we keep * the generated connection instead. */ - connections = g_slist_sort (connections, (GCompareFunc) nm_settings_connection_cmp_timestamp); + { + gs_free NMSettingsConnection **cons = NULL; + guint i, len; + + /* XXX: this code will go away soon. Copy the array over to a GSList + * and don't bother for now. */ + cons = nm_manager_get_activatable_connections (self, &len, TRUE); + for (i = len; i > 0; ) + connections = g_slist_prepend (connections, cons[--i]); + connections = g_slist_sort (connections, (GCompareFunc) nm_settings_connection_cmp_timestamp); + } matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections, connection, nm_device_has_carrier (device), @@ -2674,14 +2690,15 @@ ensure_master_active_connection (NMManager *self, * activate it on the device. */ if (master_state == NM_DEVICE_STATE_DISCONNECTED || !nm_device_is_real (master_device)) { - GSList *connections; + gs_free NMSettingsConnection **connections = NULL; + guint i; g_assert (master_connection == NULL); /* Find a compatible connection and activate this device using it */ - connections = nm_manager_get_activatable_connections (self); - for (iter = connections; iter; iter = g_slist_next (iter)) { - NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (iter->data); + connections = nm_manager_get_activatable_connections (self, NULL, TRUE); + for (i = 0; connections[i]; i++) { + NMSettingsConnection *candidate = connections[i]; /* Ensure eg bond/team slave and the candidate master is a * bond/team master @@ -2697,11 +2714,9 @@ ensure_master_active_connection (NMManager *self, master_device, subject, error); - g_slist_free (connections); return master_ac; } } - g_slist_free (connections); g_set_error (error, NM_MANAGER_ERROR, diff --git a/src/nm-manager.h b/src/nm-manager.h index c69fc9e192..a7baf30986 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -84,7 +84,10 @@ gboolean nm_manager_start (NMManager *manager, void nm_manager_stop (NMManager *manager); NMState nm_manager_get_state (NMManager *manager); const GSList *nm_manager_get_active_connections (NMManager *manager); -GSList * nm_manager_get_activatable_connections (NMManager *manager); + +NMSettingsConnection **nm_manager_get_activatable_connections (NMManager *manager, + guint *out_len, + gboolean sort); void nm_manager_write_device_state (NMManager *manager); diff --git a/src/nm-policy.c b/src/nm-policy.c index 92de1e38ec..0df576ffe2 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -953,6 +953,19 @@ activate_data_free (ActivateData *data) g_slice_free (ActivateData, data); } +static int +_auto_activate_device_cmp (gconstpointer pa, gconstpointer pb, gpointer user_data) +{ + NMSettingsConnection *a = NM_SETTINGS_CONNECTION (*((NMSettingsConnection **) pa)); + NMSettingsConnection *b = NM_SETTINGS_CONNECTION (*((NMSettingsConnection **) pb)); + int i; + + i = nm_utils_cmp_connection_by_autoconnect_priority (NM_CONNECTION (a), NM_CONNECTION (b)); + if (i != 0) + return i; + return nm_settings_connection_cmp_default (a, b); +} + static void auto_activate_device (NMPolicy *self, NMDevice *device) @@ -960,9 +973,8 @@ auto_activate_device (NMPolicy *self, NMPolicyPrivate *priv; NMSettingsConnection *best_connection; gs_free char *specific_object = NULL; - GPtrArray *connections; - GSList *connection_list; - guint i; + gs_free NMSettingsConnection **connections = NULL; + guint i, len; nm_assert (NM_IS_POLICY (self)); nm_assert (NM_IS_DEVICE (device)); @@ -976,21 +988,16 @@ auto_activate_device (NMPolicy *self, if (nm_device_get_act_request (device)) return; - connection_list = nm_manager_get_activatable_connections (priv->manager); - if (!connection_list) + connections = nm_manager_get_activatable_connections (priv->manager, &len, FALSE); + if (!connections[0]) return; - connections = _nm_utils_copy_slist_to_array (connection_list, NULL, NULL); - g_slist_free (connection_list); - - /* sort is stable (which is important at this point) so that connections - * with same priority are still sorted by last-connected-timestamp. */ - g_ptr_array_sort_with_data (connections, nm_utils_cmp_connection_by_autoconnect_priority_p_with_data, NULL); + g_qsort_with_data (connections, len, sizeof (connections[0]), _auto_activate_device_cmp, NULL); /* Find the first connection that should be auto-activated */ best_connection = NULL; - for (i = 0; i < connections->len; i++) { - NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (connections->pdata[i]); + for (i = 0; i < len; i++) { + NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (connections[i]); if (!nm_settings_connection_can_autoconnect (candidate)) continue; @@ -999,7 +1006,6 @@ auto_activate_device (NMPolicy *self, break; } } - g_ptr_array_free (connections, TRUE); if (best_connection) { GError *error = NULL; From a8221323990c80fa4ad585388b213788f12917c9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 5 Feb 2017 19:14:28 +0100 Subject: [PATCH 7/9] core: make nm_utils_cmp_connection_by_autoconnect_priority() more robust Check for NULL and unexpected missing NMSettingConnection. Be more forgiving and accept whatever is there when comparing @a with @b. --- src/nm-core-utils.c | 56 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 29c26ba817..143fdfe8b9 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -1975,27 +1975,57 @@ nm_utils_read_resolv_conf_dns_options (const char *rc_contents) return options; } +/*****************************************************************************/ + +/** + * nm_utils_cmp_connection_by_autoconnect_priority: + * @a: + * @b: + * + * compare connections @a and @b for their autoconnect property + * (with sorting the connection that has autoconnect enabled before + * the other) + * If they both have autoconnect enabled, sort them depending on their + * autoconnect-priority (with the higher priority first). + * + * If their autoconnect/autoconnect-priority is the same, 0 is returned. + * That is, they compare equal. + * + * Returns: -1, 0, or 1 + */ int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection *b) { - NMSettingConnection *a_s_con, *b_s_con; - gboolean a_ac, b_ac; - gint a_ap, b_ap; + NMSettingConnection *a_s_con; + NMSettingConnection *b_s_con; + int a_ap, b_ap; + gboolean can_autoconnect; + + if (a == b) + return 0; + if (!a) + return 1; + if (!b) + return -1; a_s_con = nm_connection_get_setting_connection (a); b_s_con = nm_connection_get_setting_connection (b); - a_ac = !!nm_setting_connection_get_autoconnect (a_s_con); - b_ac = !!nm_setting_connection_get_autoconnect (b_s_con); - if (a_ac != b_ac) - return ((int) b_ac) - ((int) a_ac); - if (!a_ac) - return 0; + if (!a_s_con) + return !b_s_con ? 0 : 1; + if (!b_s_con) + return -1; - a_ap = nm_setting_connection_get_autoconnect_priority (a_s_con); - b_ap = nm_setting_connection_get_autoconnect_priority (b_s_con); - if (a_ap != b_ap) - return (a_ap > b_ap) ? -1 : 1; + can_autoconnect = !!nm_setting_connection_get_autoconnect (a_s_con); + if (can_autoconnect != (!!nm_setting_connection_get_autoconnect (b_s_con))) + return can_autoconnect ? -1 : 1; + + if (can_autoconnect) { + a_ap = nm_setting_connection_get_autoconnect_priority (a_s_con); + b_ap = nm_setting_connection_get_autoconnect_priority (b_s_con); + if (a_ap != b_ap) + return (a_ap > b_ap) ? -1 : 1; + } return 0; } From 93f7ab2c54096461bf03e3adf56955a681c95946 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 5 Feb 2017 16:24:27 +0100 Subject: [PATCH 8/9] core: consolidate sorting of connections by autoconnect/timestamp NMPolicy's auto_activate_device() wants to sort by autoconnect-priority, nm_utils_cmp_connection_by_autoconnect_priority() but fallback to the default nm_settings_connection_cmp_default(), which includes the timestamp. Extend nm_settings_connection_cmp_default() to consider the autoconnect-priority as well. Thus change behavior so that nm_settings_connection_cmp_default() is the sort order that auto_activate_device() wants. That makes sense, as nm_settings_connection_cmp_default() already considered the ability to autoconnect as first. Hence, it should also honor the autoconnect priority. When doing that, rename nm_settings_connection_cmp_default() to nm_settings_connection_cmp_autoconnect_priority(). --- src/nm-core-utils.c | 9 ----- src/nm-core-utils.h | 1 - src/nm-manager.c | 2 +- src/nm-policy.c | 17 +------- src/settings/nm-settings-connection.c | 57 ++++++++++++++------------- src/settings/nm-settings-connection.h | 4 +- src/settings/nm-settings.c | 2 +- src/tests/test-general.c | 8 +++- 8 files changed, 42 insertions(+), 58 deletions(-) diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 143fdfe8b9..75c44b7865 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -2030,15 +2030,6 @@ nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection * return 0; } -int -nm_utils_cmp_connection_by_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data) -{ - nm_assert (pa); - nm_assert (pb); - return nm_utils_cmp_connection_by_autoconnect_priority (*((NMConnection **) pa), - *((NMConnection **) pb)); -} - /*****************************************************************************/ static gint64 monotonic_timestamp_offset_sec; diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 82e10132b3..a1d9c2d462 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -273,7 +273,6 @@ GPtrArray *nm_utils_read_resolv_conf_nameservers (const char *rc_contents); GPtrArray *nm_utils_read_resolv_conf_dns_options (const char *rc_contents); int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection *b); -int nm_utils_cmp_connection_by_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); void nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix); diff --git a/src/nm-manager.c b/src/nm-manager.c index c06b5c4862..152b1701fd 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -448,7 +448,7 @@ nm_manager_get_activatable_connections (NMManager *manager, guint *out_len, gboo _get_activatable_connections_filter, manager); if (sort && len > 1) - g_qsort_with_data (connections, len, sizeof (connections[0]), nm_settings_connection_cmp_default_p_with_data, NULL); + g_qsort_with_data (connections, len, sizeof (connections[0]), nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL); NM_SET_OUT (out_len, len); return connections; } diff --git a/src/nm-policy.c b/src/nm-policy.c index 0df576ffe2..7050f93281 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -953,19 +953,6 @@ activate_data_free (ActivateData *data) g_slice_free (ActivateData, data); } -static int -_auto_activate_device_cmp (gconstpointer pa, gconstpointer pb, gpointer user_data) -{ - NMSettingsConnection *a = NM_SETTINGS_CONNECTION (*((NMSettingsConnection **) pa)); - NMSettingsConnection *b = NM_SETTINGS_CONNECTION (*((NMSettingsConnection **) pb)); - int i; - - i = nm_utils_cmp_connection_by_autoconnect_priority (NM_CONNECTION (a), NM_CONNECTION (b)); - if (i != 0) - return i; - return nm_settings_connection_cmp_default (a, b); -} - static void auto_activate_device (NMPolicy *self, NMDevice *device) @@ -988,12 +975,10 @@ auto_activate_device (NMPolicy *self, if (nm_device_get_act_request (device)) return; - connections = nm_manager_get_activatable_connections (priv->manager, &len, FALSE); + connections = nm_manager_get_activatable_connections (priv->manager, &len, TRUE); if (!connections[0]) return; - g_qsort_with_data (connections, len, sizeof (connections[0]), _auto_activate_device_cmp, NULL); - /* Find the first connection that should be auto-activated */ best_connection = NULL; for (i = 0; i < len; i++) { diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 345701a339..4d0c3a75ac 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -2179,14 +2179,11 @@ nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnec if (!bc) return -1; - /* In the future we may use connection priorities in addition to timestamps */ nm_settings_connection_get_timestamp (ac, &ats); nm_settings_connection_get_timestamp (bc, &bts); + if (ats != bts) + return (ats > bts) ? -1 : 1; - if (ats < bts) - return 1; - else if (ats > bts) - return -1; return 0; } @@ -2198,35 +2195,41 @@ nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointe } int -nm_settings_connection_cmp_default (NMSettingsConnection *a, NMSettingsConnection *b) +nm_settings_connection_cmp_autoconnect_priority (NMSettingsConnection *a, NMSettingsConnection *b) { - NMSettingConnection *con_a; - NMSettingConnection *con_b; - guint64 ts_a = 0, ts_b = 0; - gboolean can_ac_a, can_ac_b; + guint64 ats = 0, bts = 0; + int c; - con_a = nm_connection_get_setting_connection (NM_CONNECTION (a)); - con_b = nm_connection_get_setting_connection (NM_CONNECTION (b)); - - can_ac_a = !!nm_setting_connection_get_autoconnect (con_a); - can_ac_b = !!nm_setting_connection_get_autoconnect (con_b); - if (can_ac_a != can_ac_b) - return can_ac_a ? -1 : 1; - - nm_settings_connection_get_timestamp (a, &ts_a); - nm_settings_connection_get_timestamp (b, &ts_b); - if (ts_a > ts_b) - return -1; - else if (ts_a == ts_b) + if (a == b) return 0; - return 1; + + /* first we compare them by their autoconnect priority. */ + c = nm_utils_cmp_connection_by_autoconnect_priority (NM_CONNECTION (a), NM_CONNECTION (b)); + if (c) + return c; + + /* then by their last activation timestamp (with the more recently connected one first) */ + nm_settings_connection_get_timestamp (a, &ats); + nm_settings_connection_get_timestamp (b, &bts); + if (ats != bts) + return (ats > bts) ? -1 : 1; + + /* if they are still equal, sort them by their UUID to give them an arbitrary, but stable + * order. */ + c = g_strcmp0 (nm_connection_get_uuid (NM_CONNECTION (a)), + nm_connection_get_uuid (NM_CONNECTION (b))); + if (c) + return c; + + /* hm, still the same. Use their pointer value. */ + return (a > b) ? -1 : 1; } int -nm_settings_connection_cmp_default_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data) +nm_settings_connection_cmp_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data) { - return nm_settings_connection_cmp_default (*((NMSettingsConnection **) pa), - *((NMSettingsConnection **) pb)); + return nm_settings_connection_cmp_autoconnect_priority (*((NMSettingsConnection **) pa), + *((NMSettingsConnection **) pb)); } /*****************************************************************************/ diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index 8fe9533a86..a2d6125734 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -186,8 +186,8 @@ NMSettingsConnectionFlags nm_settings_connection_set_flags_all (NMSettingsConnec int nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *ab); int nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); -int nm_settings_connection_cmp_default (NMSettingsConnection *a, NMSettingsConnection *b); -int nm_settings_connection_cmp_default_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); +int nm_settings_connection_cmp_autoconnect_priority (NMSettingsConnection *a, NMSettingsConnection *b); +int nm_settings_connection_cmp_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self, guint64 *out_timestamp); diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 1739a32fef..cc03a2c4d9 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -506,7 +506,7 @@ nm_settings_get_connections_sorted (NMSettings *self, guint *out_len) connections = nm_settings_get_connections_clone (self, &len, NULL, NULL); if (len > 1) - g_qsort_with_data (connections, len, sizeof (NMSettingsConnection *), nm_settings_connection_cmp_default_p_with_data, NULL); + g_qsort_with_data (connections, len, sizeof (NMSettingsConnection *), nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL); NM_SET_OUT (out_len, len); return connections; diff --git a/src/tests/test-general.c b/src/tests/test-general.c index f8f8fa2606..ef1a4f2b6a 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-general.c @@ -870,6 +870,12 @@ _create_connection_autoconnect (const char *id, gboolean autoconnect, int autoco return c; } +static int +_cmp_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data) +{ + return nm_utils_cmp_connection_by_autoconnect_priority (*((NMConnection **) pa), *((NMConnection **) pb)); +} + static void _test_connection_sort_autoconnect_priority_one (NMConnection **list, gboolean shuffle) { @@ -892,7 +898,7 @@ _test_connection_sort_autoconnect_priority_one (NMConnection **list, gboolean sh } /* sort it... */ - g_ptr_array_sort_with_data (connections, nm_utils_cmp_connection_by_autoconnect_priority_p_with_data, NULL); + g_ptr_array_sort_with_data (connections, _cmp_autoconnect_priority_p_with_data, NULL); for (i = 0; i < count; i++) { if (list[i] == connections->pdata[i]) From ef6c393889551cdcf46c7db3a7d881817d0866e6 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 5 Feb 2017 20:05:57 +0100 Subject: [PATCH 9/9] core: define a full sort order for nm_settings_connection_cmp_timestamp() We want to have some guaranteed order when comparing different connections. So, in case of equal timestamps, proceed with comparing more properties. It makes sense to consider the autoconnect-priority next. This is what get_existing_connection() needs, thus we no longer need to pre-sort the list. --- src/nm-manager.c | 2 +- src/settings/nm-settings-connection.c | 80 ++++++++++++++++----------- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/src/nm-manager.c b/src/nm-manager.c index 152b1701fd..b80d2da4bd 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1749,7 +1749,7 @@ get_existing_connection (NMManager *self, NMDevice *device, gboolean *out_genera /* XXX: this code will go away soon. Copy the array over to a GSList * and don't bother for now. */ - cons = nm_manager_get_activatable_connections (self, &len, TRUE); + cons = nm_manager_get_activatable_connections (self, &len, FALSE); for (i = len; i > 0; ) connections = g_slist_prepend (connections, cons[--i]); connections = g_slist_sort (connections, (GCompareFunc) nm_settings_connection_cmp_timestamp); diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 4d0c3a75ac..9f70aaba26 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -2162,29 +2162,64 @@ nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConn /*****************************************************************************/ +static int +_cmp_timestamp (NMSettingsConnection *a, NMSettingsConnection *b) +{ + gboolean a_has_ts, b_has_ts; + guint64 ats = 0, bts = 0; + + nm_assert (NM_IS_SETTINGS_CONNECTION (a)); + nm_assert (NM_IS_SETTINGS_CONNECTION (b)); + + a_has_ts = !!nm_settings_connection_get_timestamp (a, &ats); + b_has_ts = !!nm_settings_connection_get_timestamp (b, &bts); + if (a_has_ts != b_has_ts) + return a_has_ts ? -1 : 1; + if (a_has_ts && ats != bts) + return (ats > bts) ? -1 : 1; + return 0; +} + +static int +_cmp_last_resort (NMSettingsConnection *a, NMSettingsConnection *b) +{ + int c; + + nm_assert (NM_IS_SETTINGS_CONNECTION (a)); + nm_assert (NM_IS_SETTINGS_CONNECTION (b)); + + c = g_strcmp0 (nm_connection_get_uuid (NM_CONNECTION (a)), + nm_connection_get_uuid (NM_CONNECTION (b))); + if (c) + return c; + + /* hm, same UUID. Use their pointer value to give them a stable + * order. */ + return (a > b) ? -1 : 1; +} + /* sorting for "best" connections. * The function sorts connections in descending timestamp order. * That means an older connection (lower timestamp) goes after * a newer one. */ int -nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *bc) +nm_settings_connection_cmp_timestamp (NMSettingsConnection *a, NMSettingsConnection *b) { - guint64 ats = 0, bts = 0; + int c; - if (ac == bc) + if (a == b) return 0; - if (!ac) + if (!a) return 1; - if (!bc) + if (!b) return -1; - nm_settings_connection_get_timestamp (ac, &ats); - nm_settings_connection_get_timestamp (bc, &bts); - if (ats != bts) - return (ats > bts) ? -1 : 1; - - return 0; + if ((c = _cmp_timestamp (a, b))) + return c; + if ((c = nm_utils_cmp_connection_by_autoconnect_priority (NM_CONNECTION (a), NM_CONNECTION (b)))) + return c; + return _cmp_last_resort (a, b); } int @@ -2197,32 +2232,15 @@ nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointe int nm_settings_connection_cmp_autoconnect_priority (NMSettingsConnection *a, NMSettingsConnection *b) { - guint64 ats = 0, bts = 0; int c; if (a == b) return 0; - - /* first we compare them by their autoconnect priority. */ - c = nm_utils_cmp_connection_by_autoconnect_priority (NM_CONNECTION (a), NM_CONNECTION (b)); - if (c) + if ((c = nm_utils_cmp_connection_by_autoconnect_priority (NM_CONNECTION (a), NM_CONNECTION (b)))) return c; - - /* then by their last activation timestamp (with the more recently connected one first) */ - nm_settings_connection_get_timestamp (a, &ats); - nm_settings_connection_get_timestamp (b, &bts); - if (ats != bts) - return (ats > bts) ? -1 : 1; - - /* if they are still equal, sort them by their UUID to give them an arbitrary, but stable - * order. */ - c = g_strcmp0 (nm_connection_get_uuid (NM_CONNECTION (a)), - nm_connection_get_uuid (NM_CONNECTION (b))); - if (c) + if ((c = _cmp_timestamp (a, b))) return c; - - /* hm, still the same. Use their pointer value. */ - return (a > b) ? -1 : 1; + return _cmp_last_resort (a, b); } int