diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 22492f371f..301b4c2354 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -61,15 +61,16 @@ static NmcOutputField nmc_fields_con_show[] = { {"TIMESTAMP", N_("TIMESTAMP"), 12}, /* 3 */ {"TIMESTAMP-REAL", N_("TIMESTAMP-REAL"), 34}, /* 4 */ {"AUTOCONNECT", N_("AUTOCONNECT"), 13}, /* 5 */ - {"READONLY", N_("READONLY"), 10}, /* 6 */ - {"DBUS-PATH", N_("DBUS-PATH"), 42}, /* 7 */ - {"ACTIVE", N_("ACTIVE"), 10}, /* 8 */ - {"DEVICE", N_("DEVICE"), 10}, /* 9 */ - {"STATE", N_("STATE"), 12}, /* 10 */ - {"ACTIVE-PATH", N_("ACTIVE-PATH"), 51}, /* 11 */ + {"AUTOCONNECT-PRIORITY", N_("AUTOCONNECT-PRIORITY"), 10}, /* 6 */ + {"READONLY", N_("READONLY"), 10}, /* 7 */ + {"DBUS-PATH", N_("DBUS-PATH"), 42}, /* 8 */ + {"ACTIVE", N_("ACTIVE"), 10}, /* 9 */ + {"DEVICE", N_("DEVICE"), 10}, /* 10 */ + {"STATE", N_("STATE"), 12}, /* 11 */ + {"ACTIVE-PATH", N_("ACTIVE-PATH"), 51}, /* 12 */ {NULL, NULL, 0} }; -#define NMC_FIELDS_CON_SHOW_ALL "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,READONLY,DBUS-PATH,"\ +#define NMC_FIELDS_CON_SHOW_ALL "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,AUTOCONNECT-PRIORITY,READONLY,DBUS-PATH,"\ "ACTIVE,DEVICE,STATE,ACTIVE-PATH" #define NMC_FIELDS_CON_SHOW_COMMON "NAME,UUID,TYPE,DEVICE" @@ -745,6 +746,7 @@ fill_output_connection (gpointer data, gpointer user_data, gboolean active_only) time_t timestamp_real; char *timestamp_str; char *timestamp_real_str = ""; + char *prio_str; NmcOutputField *arr; NMActiveConnection *ac = NULL; const char *ac_path = NULL; @@ -772,6 +774,7 @@ fill_output_connection (gpointer data, gpointer user_data, gboolean active_only) timestamp_real_str = g_malloc0 (64); strftime (timestamp_real_str, 64, "%c", localtime (×tamp_real)); } + prio_str = g_strdup_printf ("%u", nm_setting_connection_get_autoconnect_priority (s_con)); arr = nmc_dup_fields_array (nmc_fields_con_show, sizeof (nmc_fields_con_show), @@ -782,12 +785,13 @@ fill_output_connection (gpointer data, gpointer user_data, gboolean active_only) set_val_str (arr, 3, timestamp_str); set_val_str (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never"))); set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no")); - set_val_strc (arr, 6, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no")); - set_val_strc (arr, 7, nm_connection_get_path (connection)); - set_val_strc (arr, 8, ac ? _("yes") : _("no")); - set_val_str (arr, 9, ac_dev); - set_val_strc (arr, 10, ac_state); - set_val_strc (arr, 11, ac_path); + set_val_str (arr, 6, prio_str); + set_val_strc (arr, 7, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no")); + set_val_strc (arr, 8, nm_connection_get_path (connection)); + set_val_strc (arr, 9, ac ? _("yes") : _("no")); + set_val_str (arr, 10, ac_dev); + set_val_strc (arr, 11, ac_state); + set_val_strc (arr, 12, ac_path); g_ptr_array_add (nmc->output_data, arr); } diff --git a/clients/cli/settings.c b/clients/cli/settings.c index b7137560de..6a78e5e1f7 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -43,14 +43,15 @@ NmcOutputField nmc_fields_setting_connection[] = { SETTING_FIELD (NM_SETTING_CONNECTION_INTERFACE_NAME, 20), /* 3 */ SETTING_FIELD (NM_SETTING_CONNECTION_TYPE, 17), /* 4 */ SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT, 13), /* 5 */ - SETTING_FIELD (NM_SETTING_CONNECTION_TIMESTAMP, 10), /* 6 */ - SETTING_FIELD (NM_SETTING_CONNECTION_READ_ONLY, 10), /* 7 */ - SETTING_FIELD (NM_SETTING_CONNECTION_PERMISSIONS, 30), /* 8 */ - SETTING_FIELD (NM_SETTING_CONNECTION_ZONE, 10), /* 9 */ - SETTING_FIELD (NM_SETTING_CONNECTION_MASTER, 20), /* 10 */ - SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE, 20), /* 11 */ - SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES, 40), /* 12 */ - SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, 30), /* 13 */ + SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, 10), /* 6 */ + SETTING_FIELD (NM_SETTING_CONNECTION_TIMESTAMP, 10), /* 7 */ + SETTING_FIELD (NM_SETTING_CONNECTION_READ_ONLY, 10), /* 8 */ + SETTING_FIELD (NM_SETTING_CONNECTION_PERMISSIONS, 30), /* 9 */ + SETTING_FIELD (NM_SETTING_CONNECTION_ZONE, 10), /* 10 */ + SETTING_FIELD (NM_SETTING_CONNECTION_MASTER, 20), /* 11 */ + SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE, 20), /* 12 */ + SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES, 40), /* 13 */ + SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, 30), /* 14 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_CONNECTION_ALL "name"","\ @@ -59,6 +60,7 @@ NmcOutputField nmc_fields_setting_connection[] = { NM_SETTING_CONNECTION_INTERFACE_NAME","\ NM_SETTING_CONNECTION_TYPE","\ NM_SETTING_CONNECTION_AUTOCONNECT","\ + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY","\ NM_SETTING_CONNECTION_TIMESTAMP","\ NM_SETTING_CONNECTION_READ_ONLY","\ NM_SETTING_CONNECTION_PERMISSIONS","\ @@ -1004,6 +1006,7 @@ DEFINE_GETTER (nmc_property_connection_get_uuid, NM_SETTING_CONNECTION_UUID) DEFINE_GETTER (nmc_property_connection_get_interface_name, NM_SETTING_CONNECTION_INTERFACE_NAME) DEFINE_GETTER (nmc_property_connection_get_type, NM_SETTING_CONNECTION_TYPE) DEFINE_GETTER (nmc_property_connection_get_autoconnect, NM_SETTING_CONNECTION_AUTOCONNECT) +DEFINE_GETTER (nmc_property_connection_get_autoconnect_priority, NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY) DEFINE_GETTER (nmc_property_connection_get_timestamp, NM_SETTING_CONNECTION_TIMESTAMP) DEFINE_GETTER (nmc_property_connection_get_read_only, NM_SETTING_CONNECTION_READ_ONLY) @@ -2155,7 +2158,27 @@ nmc_property_set_uint (NMSetting *setting, const char *prop, const char *val, GE if (!validate_uint (setting, prop, (guint) val_int, error)) return FALSE; - g_object_set (setting, prop, val_int, NULL); + g_object_set (setting, prop, (guint) val_int, NULL); + return TRUE; +} + +static gboolean +nmc_property_set_int (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + long int val_int; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!nmc_string_to_int (val, TRUE, G_MININT, G_MAXINT, &val_int)) { + g_set_error (error, 1, 0, _("'%s' is not a valid number (or out of range)"), val); + return FALSE; + } + + /* Validate the number according to the property spec */ + if (!validate_int (setting, prop, (gint) val_int, error)) + return FALSE; + + g_object_set (setting, prop, (gint) val_int, NULL); return TRUE; } @@ -5115,6 +5138,13 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, AUTOCONNECT_PRIORITY), + nmc_property_connection_get_autoconnect_priority, + nmc_property_set_int, + NULL, + NULL, + NULL, + NULL); nmc_add_prop_funcs (GLUE (CONNECTION, TIMESTAMP), nmc_property_connection_get_timestamp, NULL, /* read-only */ @@ -6482,14 +6512,15 @@ setting_connection_details (NMSetting *setting, NmCli *nmc, const char *one_prop set_val_str (arr, 3, nmc_property_connection_get_interface_name (setting)); set_val_str (arr, 4, nmc_property_connection_get_type (setting)); set_val_str (arr, 5, nmc_property_connection_get_autoconnect (setting)); - set_val_str (arr, 6, nmc_property_connection_get_timestamp (setting)); - set_val_str (arr, 7, nmc_property_connection_get_read_only (setting)); - set_val_str (arr, 8, nmc_property_connection_get_permissions (setting)); - set_val_str (arr, 9, nmc_property_connection_get_zone (setting)); - set_val_str (arr, 10, nmc_property_connection_get_master (setting)); - set_val_str (arr, 11, nmc_property_connection_get_slave_type (setting)); - set_val_str (arr, 12, nmc_property_connection_get_secondaries (setting)); - set_val_str (arr, 13, nmc_property_connection_get_gateway_ping_timeout (setting)); + set_val_str (arr, 6, nmc_property_connection_get_autoconnect_priority (setting)); + set_val_str (arr, 7, nmc_property_connection_get_timestamp (setting)); + set_val_str (arr, 8, nmc_property_connection_get_read_only (setting)); + set_val_str (arr, 9, nmc_property_connection_get_permissions (setting)); + set_val_str (arr, 10, nmc_property_connection_get_zone (setting)); + set_val_str (arr, 11, nmc_property_connection_get_master (setting)); + set_val_str (arr, 12, nmc_property_connection_get_slave_type (setting)); + set_val_str (arr, 13, nmc_property_connection_get_secondaries (setting)); + set_val_str (arr, 14, nmc_property_connection_get_gateway_ping_timeout (setting)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ diff --git a/include/nm-test-utils.h b/include/nm-test-utils.h index e296df044b..12e84b3393 100644 --- a/include/nm-test-utils.h +++ b/include/nm-test-utils.h @@ -871,9 +871,8 @@ nmtst_assert_connection_verifies_without_normalization (NMConnection *con) success = nm_connection_normalize (con, NULL, &was_modified, &error); g_assert_no_error (error); g_assert (success); - g_assert (!was_modified); - nmtst_assert_connection_equals (con, FALSE, clone, FALSE); + g_assert (!was_modified); } inline static void diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index e6b537122a..890f77d1cc 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -80,6 +80,7 @@ typedef struct { char *slave_type; GSList *permissions; /* list of Permission structs */ gboolean autoconnect; + gint autoconnect_priority; guint64 timestamp; gboolean read_only; char *zone; @@ -95,6 +96,7 @@ enum { PROP_TYPE, PROP_PERMISSIONS, PROP_AUTOCONNECT, + PROP_AUTOCONNECT_PRIORITY, PROP_TIMESTAMP, PROP_READ_ONLY, PROP_ZONE, @@ -497,6 +499,23 @@ nm_setting_connection_get_autoconnect (NMSettingConnection *setting) return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->autoconnect; } +/** + * nm_setting_connection_get_autoconnect_priority: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:autoconnect-priority property of the connection. + * The higher number, the higher priority. + * + * Returns: the connection's autoconnect priority + **/ +gint +nm_setting_connection_get_autoconnect_priority (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), 0); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->autoconnect_priority; +} + /** * nm_setting_connection_get_timestamp: * @setting: the #NMSettingConnection @@ -1094,6 +1113,9 @@ set_property (GObject *object, guint prop_id, case PROP_AUTOCONNECT: priv->autoconnect = g_value_get_boolean (value); break; + case PROP_AUTOCONNECT_PRIORITY: + priv->autoconnect_priority = g_value_get_int (value); + break; case PROP_TIMESTAMP: priv->timestamp = g_value_get_uint64 (value); break; @@ -1165,6 +1187,9 @@ get_property (GObject *object, guint prop_id, case PROP_AUTOCONNECT: g_value_set_boolean (value, nm_setting_connection_get_autoconnect (setting)); break; + case PROP_AUTOCONNECT_PRIORITY: + g_value_set_int (value, nm_setting_connection_get_autoconnect_priority (setting)); + break; case PROP_TIMESTAMP: g_value_set_uint64 (value, nm_setting_connection_get_timestamp (setting)); break; @@ -1331,6 +1356,24 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS)); + /** + * NMSettingConnection:autoconnect-priority: + * + * The autoconnect priority. If the connection is set to autoconnect, + * connections with higher priority will be preferred. Defaults to 0. + * The higher number means higher priority. + **/ + g_object_class_install_property + (object_class, PROP_AUTOCONNECT_PRIORITY, + g_param_spec_int (NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, "", "", + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN, + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX, + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + /** * NMSettingConnection:timestamp: * diff --git a/libnm-core/nm-setting-connection.h b/libnm-core/nm-setting-connection.h index 169e2c18ee..4b40401d32 100644 --- a/libnm-core/nm-setting-connection.h +++ b/libnm-core/nm-setting-connection.h @@ -72,11 +72,16 @@ typedef enum #define NM_SETTING_CONNECTION_ERROR nm_setting_connection_error_quark () GQuark nm_setting_connection_error_quark (void); +#define NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN -999 +#define NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX 999 +#define NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT 0 + #define NM_SETTING_CONNECTION_ID "id" #define NM_SETTING_CONNECTION_UUID "uuid" #define NM_SETTING_CONNECTION_INTERFACE_NAME "interface-name" #define NM_SETTING_CONNECTION_TYPE "type" #define NM_SETTING_CONNECTION_AUTOCONNECT "autoconnect" +#define NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY "autoconnect-priority" #define NM_SETTING_CONNECTION_TIMESTAMP "timestamp" #define NM_SETTING_CONNECTION_READ_ONLY "read-only" #define NM_SETTING_CONNECTION_PERMISSIONS "permissions" @@ -111,6 +116,7 @@ const char *nm_setting_connection_get_uuid (NMSettingConnection *set const char *nm_setting_connection_get_interface_name (NMSettingConnection *setting); const char *nm_setting_connection_get_connection_type (NMSettingConnection *setting); gboolean nm_setting_connection_get_autoconnect (NMSettingConnection *setting); +gint nm_setting_connection_get_autoconnect_priority (NMSettingConnection *setting); guint64 nm_setting_connection_get_timestamp (NMSettingConnection *setting); gboolean nm_setting_connection_get_read_only (NMSettingConnection *setting); diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index f1e592f535..913ba4b4df 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -552,7 +552,7 @@ _nm_utils_copy_slist_to_array (const GSList *list, array = g_ptr_array_new_with_free_func (unref_func); for (iter = list; iter; iter = iter->next) - g_ptr_array_add (array, copy_func (iter->data)); + g_ptr_array_add (array, copy_func ? copy_func (iter->data) : iter->data); return array; } diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 2cb6137866..63811102ca 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -1599,6 +1599,7 @@ test_connection_diff_a_only (void) { NM_SETTING_CONNECTION_TYPE, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_TIMESTAMP, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_AUTOCONNECT, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_READ_ONLY, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_PERMISSIONS, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_ZONE, NM_SETTING_DIFF_RESULT_IN_A }, diff --git a/libnm/libnm.ver b/libnm/libnm.ver index bcb3a97381..ca8283abb4 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -530,6 +530,7 @@ global: nm_setting_connection_error_get_type; nm_setting_connection_error_quark; nm_setting_connection_get_autoconnect; + nm_setting_connection_get_autoconnect_priority; nm_setting_connection_get_connection_type; nm_setting_connection_get_gateway_ping_timeout; nm_setting_connection_get_id; diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index dcc64270c1..bdf03557a1 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -1634,6 +1634,31 @@ nm_utils_match_connection (GSList *connections, return best_match; } +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; + + 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; + + 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; +} + /* nm_utils_ascii_str_to_int64: * * A wrapper for g_ascii_strtoll, that checks whether the whole string diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index d66a1ddd9e..88de6dd208 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -138,6 +138,8 @@ NMConnection *nm_utils_match_connection (GSList *connections, NMUtilsMatchFilterFunc match_filter_func, gpointer match_filter_data); +int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnection **b); + 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/devices/nm-device.c b/src/devices/nm-device.c index c2755ef5a2..d2edef95f8 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1640,6 +1640,34 @@ can_auto_connect (NMDevice *self, return nm_device_connection_is_available (self, connection, FALSE); } +/** + * nm_device_can_auto_connect: + * @self: an #NMDevice + * @connection: a #NMConnection + * @specific_object: (out) (transfer full): on output, the path of an + * object associated with the returned connection, to be passed to + * nm_manager_activate_connection(), or %NULL. + * + * Checks if @connection can be auto-activated on @self right now. + * This requires, at a minimum, that the connection be compatible with + * @self, and that it have the #NMSettingConnection:autoconnect property + * set. Some devices impose additional requirements. (Eg, a Wi-Fi connection + * can only be activated if its SSID was seen in the last scan.) + * + * Returns: %TRUE, if the @connection can be auto-activated. + **/ +gboolean +nm_device_can_auto_connect (NMDevice *self, + NMConnection *connection, + char **specific_object) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (specific_object && !*specific_object, FALSE); + + return NM_DEVICE_GET_CLASS (self)->can_auto_connect (self, connection, specific_object); +} + static gboolean device_has_config (NMDevice *self) { @@ -1796,46 +1824,6 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master) return connection; } -/** - * nm_device_get_best_auto_connection: - * @self: an #NMDevice - * @connections: (element-type #NMConnection): a list of connections - * @specific_object: (out) (transfer full): on output, the path of an - * object associated with the returned connection, to be passed to - * nm_manager_activate_connection(), or %NULL. - * - * Looks through @connections to see if there is a connection that can - * be auto-activated on @self right now. This requires, at a minimum, - * that the connection be compatible with @self, and that it have the - * #NMSettingConnection:autoconnect property set. Some devices impose - * additional requirements. (Eg, a Wi-Fi connection can only be - * activated if its SSID was seen in the last scan.) - * - * Returns: an auto-activatable #NMConnection, or %NULL if none are - * available. - */ - -NMConnection * -nm_device_get_best_auto_connection (NMDevice *self, - GSList *connections, - char **specific_object) -{ - GSList *iter; - - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - g_return_val_if_fail (specific_object != NULL, NULL); - g_return_val_if_fail (*specific_object == NULL, NULL); - - for (iter = connections; iter; iter = iter->next) { - NMConnection *connection = NM_CONNECTION (iter->data); - - if (NM_DEVICE_GET_CLASS (self)->can_auto_connect (self, connection, specific_object)) - return connection; - } - - return NULL; -} - gboolean nm_device_complete_connection (NMDevice *self, NMConnection *connection, diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 28a2ff0be9..00da022905 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -269,9 +269,9 @@ gboolean nm_device_master_update_slave_connection (NMDevice *master, NMConnection *connection, GError **error); -NMConnection * nm_device_get_best_auto_connection (NMDevice *dev, - GSList *connections, - char **specific_object); +gboolean nm_device_can_auto_connect (NMDevice *self, + NMConnection *connection, + char **specific_object); gboolean nm_device_complete_connection (NMDevice *device, NMConnection *connection, diff --git a/src/nm-policy.c b/src/nm-policy.c index f6ef476bf9..120afd65c6 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -42,6 +42,7 @@ #include "nm-firewall-manager.h" #include "nm-dispatcher.h" #include "nm-utils.h" +#include "nm-core-internal.h" #include "nm-glib-compat.h" #include "nm-manager.h" #include "nm-settings.h" @@ -990,7 +991,9 @@ auto_activate_device (gpointer user_data) NMPolicyPrivate *priv; NMConnection *best_connection; char *specific_object = NULL; - GSList *connections, *iter; + GPtrArray *connections; + GSList *connection_list; + guint i; g_assert (data); policy = data->policy; @@ -1005,20 +1008,31 @@ auto_activate_device (gpointer user_data) if (nm_device_get_act_request (data->device)) goto out; - iter = connections = nm_manager_get_activatable_connections (priv->manager); + connection_list = nm_manager_get_activatable_connections (priv->manager); + if (!connection_list) + goto out; - /* Remove connections that shouldn't be auto-activated */ - while (iter) { - NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (iter->data); + connections = _nm_utils_copy_slist_to_array (connection_list, NULL, NULL); + g_slist_free (connection_list); - /* Grab next item before we possibly delete the current item */ - iter = g_slist_next (iter); + /* 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); + + /* 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]); if (!nm_settings_connection_can_autoconnect (candidate)) - connections = g_slist_remove (connections, candidate); + continue; + if (nm_device_can_auto_connect (data->device, (NMConnection *) candidate, &specific_object)) { + best_connection = (NMConnection *) candidate; + break; + } } + g_ptr_array_free (connections, TRUE); - best_connection = nm_device_get_best_auto_connection (data->device, connections, &specific_object); if (best_connection) { GError *error = NULL; NMAuthSubject *subject; @@ -1041,8 +1055,6 @@ auto_activate_device (gpointer user_data) g_object_unref (subject); } - g_slist_free (connections); - out: activate_data_free (data); return G_SOURCE_REMOVE; diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 096608ce05..5a7bf4159e 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -326,17 +326,17 @@ connection_sort (gconstpointer pa, gconstpointer pb) 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); - if (nm_setting_connection_get_autoconnect (con_a) != nm_setting_connection_get_autoconnect (con_b)) { - if (nm_setting_connection_get_autoconnect (con_a)) - return -1; - return 1; - } + 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); diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index 5cb96a0349..1c6aa70cb2 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -169,8 +169,14 @@ make_connection_setting (const char *file, } /* Missing ONBOOT is treated as "ONBOOT=true" by the old network service */ - g_object_set (s_con, NM_SETTING_CONNECTION_AUTOCONNECT, + g_object_set (s_con, + NM_SETTING_CONNECTION_AUTOCONNECT, svTrueValue (ifcfg, "ONBOOT", TRUE), + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, + (gint) svGetValueInt64 (ifcfg, "AUTOCONNECT_PRIORITY", 10, + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN, + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX, + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT), NULL); value = svGetValue (ifcfg, "USERS", FALSE); diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index 4e8e29ce59..b4c911fb6d 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -34,6 +34,11 @@ #include "shvar.h" +#include "NetworkManagerUtils.h" +#include "nm-logging.h" + +#define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, " " msg) + /* Open the file , returning a shvarFile on success and NULL on failure. * Add a wrinkle to let the caller specify whether or not to create the file * (actually, return a structure anyway) if it doesn't exist. @@ -306,6 +311,38 @@ svTrueValue (shvarFile *s, const char *key, gboolean def) return returnValue; } +/* svGetValueInt64: + * @s: fhe file + * @key: the name of the key to read + * @base: the numeric base (usually 10). Setting to 0 means "auto". Usually you want 10. + * @min: the minimum for range-check + * @max: the maximum for range-check + * @fallback: the fallback value in any error case + * + * Reads a value @key and converts it to an integer using nm_utils_ascii_str_to_int64(). + * In case of error, @errno will be set and @fallback returned. */ +gint64 +svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback) +{ + char *tmp; + gint64 result; + int errsv; + + tmp = svGetValue (s, key, FALSE); + if (!tmp) { + errno = 0; + return fallback; + } + + result = nm_utils_ascii_str_to_int64 (tmp, base, min, max, fallback); + errsv = errno; + if (errsv != 0) + PARSE_WARNING ("Error reading '%s' value '%s' as integer (%d)", key, tmp, errsv); + + g_free (tmp); + + return result; +} /* Set the variable equal to the value . * If does not exist, and the pointer is set, append diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index 4cbf1a31a7..b2a2f2636e 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -63,6 +63,8 @@ char *svGetValue (shvarFile *s, const char *key, gboolean verbatim); */ gboolean svTrueValue (shvarFile *s, const char *key, gboolean def); +gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback); + /* Set the variable equal to the value . * If does not exist, and the pointer is set, append * the key=value pair after that line. Otherwise, prepend the pair diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open index d4bb8f75b0..48db45b25d 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open @@ -3,6 +3,7 @@ DEVICE=eth2 HWADDR=00:16:41:11:22:33 NM_CONTROLLED=yes BOOTPROTO=dhcp +AUTOCONNECT_PRIORITY=-1 ESSID=blahblah CHANNEL=1 MODE=Managed diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index e72e7a3681..03caff5f81 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -2910,6 +2910,8 @@ test_read_wifi_open (void) NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_AUTOCONNECT); + g_assert_cmpint (nm_setting_connection_get_autoconnect_priority (s_con), ==, -1); + /* ===== WIRELESS SETTING ===== */ s_wireless = nm_connection_get_setting_wireless (connection); diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index 86f317cc19..70c0e1b024 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -1664,6 +1664,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) GString *str; const char *master; char *tmp; + gint i_int; svSetValue (ifcfg, "NAME", nm_setting_connection_get_id (s_con), FALSE); svSetValue (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con), FALSE); @@ -1672,6 +1673,12 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) nm_setting_connection_get_autoconnect (s_con) ? "yes" : "no", FALSE); + i_int = nm_setting_connection_get_autoconnect_priority (s_con); + tmp = i_int != NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT + ? g_strdup_printf ("%d", i_int) : NULL; + svSetValue (ifcfg, "AUTOCONNECT_PRIORITY", tmp, FALSE); + g_free (tmp); + /* Permissions */ svSetValue (ifcfg, "USERS", NULL, FALSE); n = nm_setting_connection_get_num_permissions (s_con); diff --git a/src/tests/test-general.c b/src/tests/test-general.c index c1970b897c..ec771bc55b 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-general.c @@ -581,6 +581,104 @@ test_connection_no_match_ip4_addr (void) g_object_unref (copy); } +static NMConnection * +_create_connection_autoconnect (const char *id, gboolean autoconnect, int autoconnect_priority) +{ + NMConnection *c; + NMSettingConnection *s_con; + + c = nmtst_create_minimal_connection (id, NULL, NM_SETTING_WIRED_SETTING_NAME, &s_con); + g_object_set (s_con, + NM_SETTING_CONNECTION_AUTOCONNECT, autoconnect, + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, autoconnect_priority, + NULL); + nmtst_connection_normalize (c); + return c; +} + +static void +_test_connection_sort_autoconnect_priority_one (NMConnection **list, gboolean shuffle) +{ + int i, j; + int count = 0; + gs_unref_ptrarray GPtrArray *connections = g_ptr_array_new (); + + while (list[count]) + count++; + g_assert (count > 1); + + /* copy the list of connections over to @connections and shuffle. */ + for (i = 0; i < count; i++) + g_ptr_array_add (connections, list[i]); + if (shuffle) { + for (i = count - 1; i > 0; i--) { + j = g_rand_int (nmtst_get_rand ()) % (i + 1); + NMTST_SWAP (connections->pdata[i], connections->pdata[j]); + } + } + + /* sort it... */ + g_ptr_array_sort (connections, (GCompareFunc) nm_utils_cmp_connection_by_autoconnect_priority); + + 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) + continue; + g_message ("After sorting, the order of connections is not as expected!! Offending index: %d", i); + for (j = 0; j < count; j++) + g_message (" %3d: %p/%-20s - %p/%-20s", j, list[j], nm_connection_get_id (list[j]), connections->pdata[j], nm_connection_get_id (connections->pdata[j])); + g_assert_not_reached (); + } +} + +static void +_test_connection_sort_autoconnect_priority_free (NMConnection **list) +{ + while (*list) { + g_object_unref (*list); + *list = NULL; + } +} + +static void +test_connection_sort_autoconnect_priority (void) +{ + NMConnection *c1[] = { + _create_connection_autoconnect ("AC/100", TRUE, 100), + _create_connection_autoconnect ("AC/100", TRUE, 100), + _create_connection_autoconnect ("AC/99", TRUE, 99), + _create_connection_autoconnect ("AC/0", TRUE, 0), + _create_connection_autoconnect ("AC/0", TRUE, 0), + _create_connection_autoconnect ("AC/-1", TRUE, -1), + _create_connection_autoconnect ("AC/-3", TRUE, -3), + _create_connection_autoconnect ("ac/0", FALSE, 0), + _create_connection_autoconnect ("ac/0", FALSE, 0), + _create_connection_autoconnect ("ac/1", FALSE, 1), + _create_connection_autoconnect ("ac/-1", FALSE, -1), + _create_connection_autoconnect ("ac/1", FALSE, 1), + _create_connection_autoconnect ("ac/0", FALSE, 0), + NULL, + }; + NMConnection *c2[] = { + _create_connection_autoconnect ("AC/100", TRUE, 100), + _create_connection_autoconnect ("AC/99", TRUE, 99), + _create_connection_autoconnect ("AC/0", TRUE, 0), + _create_connection_autoconnect ("AC/-1", TRUE, -1), + _create_connection_autoconnect ("AC/-3", TRUE, -3), + _create_connection_autoconnect ("ac/0", FALSE, 0), + NULL, + }; + + _test_connection_sort_autoconnect_priority_one (c1, FALSE); + _test_connection_sort_autoconnect_priority_one (c2, FALSE); + _test_connection_sort_autoconnect_priority_one (c1, TRUE); + _test_connection_sort_autoconnect_priority_one (c2, TRUE); + + _test_connection_sort_autoconnect_priority_free (c1); + _test_connection_sort_autoconnect_priority_free (c2); +} + /*******************************************/ NMTST_DEFINE (); @@ -602,6 +700,8 @@ main (int argc, char **argv) g_test_add_func ("/general/connection-match/wired", test_connection_match_wired); g_test_add_func ("/general/connection-match/no-match-ip4-addr", test_connection_no_match_ip4_addr); + g_test_add_func ("/general/connection-sort/autoconnect-priority", test_connection_sort_autoconnect_priority); + return g_test_run (); }