diff --git a/src/settings/Makefile.am b/src/settings/Makefile.am index 8024d54142..e6d66db5c0 100644 --- a/src/settings/Makefile.am +++ b/src/settings/Makefile.am @@ -48,7 +48,9 @@ libsettings_la_SOURCES = \ nm-secret-agent.c \ nm-secret-agent.h \ nm-settings-utils.h \ - nm-settings-utils.c + nm-settings-utils.c \ + nm-connection-provider.h \ + nm-connection-provider.c libsettings_la_CPPFLAGS = \ $(DBUS_CFLAGS) \ diff --git a/src/settings/nm-connection-provider.c b/src/settings/nm-connection-provider.c new file mode 100644 index 0000000000..5765151a4a --- /dev/null +++ b/src/settings/nm-connection-provider.c @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2012 Red Hat, Inc. + */ + +#include "nm-connection-provider.h" + +GSList * +nm_connection_provider_get_best_connections (NMConnectionProvider *self, + guint max_requested, + const char *ctype1, + const char *ctype2, + NMConnectionFilterFunc func, + gpointer func_data) +{ + g_return_val_if_fail (NM_IS_CONNECTION_PROVIDER (self), NULL); + + if (NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_best_connections) + return NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_best_connections (self, max_requested, ctype1, ctype2, func, func_data); + return NULL; +} + +/*****************************************************************************/ + +static void +nm_connection_provider_init (gpointer g_iface) +{ +} + +GType +nm_connection_provider_get_type (void) +{ + static GType cp_type = 0; + + if (!G_UNLIKELY (cp_type)) { + const GTypeInfo cp_info = { + sizeof (NMConnectionProvider), /* class_size */ + nm_connection_provider_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + cp_type = g_type_register_static (G_TYPE_INTERFACE, "NMConnectionProvider", &cp_info, 0); + g_type_interface_add_prerequisite (cp_type, G_TYPE_OBJECT); + } + + return cp_type; +} diff --git a/src/settings/nm-connection-provider.h b/src/settings/nm-connection-provider.h new file mode 100644 index 0000000000..0fa995b6b8 --- /dev/null +++ b/src/settings/nm-connection-provider.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2012 Red Hat, Inc. + */ + +#ifndef NM_CONNECTION_PROVIDER_H +#define NM_CONNECTION_PROVIDER_H + +#include +#include + +#define NM_TYPE_CONNECTION_PROVIDER (nm_connection_provider_get_type ()) +#define NM_CONNECTION_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONNECTION_PROVIDER, NMConnectionProvider)) +#define NM_IS_CONNECTION_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CONNECTION_PROVIDER)) +#define NM_CONNECTION_PROVIDER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_CONNECTION_PROVIDER, NMConnectionProvider)) + +typedef struct _NMConnectionProvider NMConnectionProvider; + +/** + * NMConnectionFilterFunc: + * @provider: The provider requesting the filtering + * @connection: the connection to be filtered + * @func_data: the caller-provided data pointer + * + * Returns: %TRUE to allow the connection, %FALSE to ignore it + */ +typedef gboolean (*NMConnectionFilterFunc) (NMConnectionProvider *provider, + NMConnection *connection, + gpointer func_data); + + +struct _NMConnectionProvider { + GTypeInterface g_iface; + + /* Methods */ + GSList * (*get_best_connections) (NMConnectionProvider *self, + guint max_requested, + const char *ctype1, + const char *ctype2, + NMConnectionFilterFunc func, + gpointer func_data); +}; + +GType nm_connection_provider_get_type (void); + +/** + * nm_connection_provider_get_best_connections: + * @self: the #NMConnectionProvider + * @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_connection_provider_get_best_connections (NMConnectionProvider *self, + guint max_requested, + const char *ctype1, + const char *ctype2, + NMConnectionFilterFunc func, + gpointer func_data); + +#endif /* NM_CONNECTION_PROVIDER_H */ diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 1ead4f8b7b..731a919584 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -68,6 +68,7 @@ #include "plugins/keyfile/plugin.h" #include "nm-agent-manager.h" #include "nm-settings-utils.h" +#include "nm-connection-provider.h" #define CONFIG_KEY_NO_AUTO_DEFAULT "no-auto-default" @@ -110,6 +111,12 @@ static void impl_settings_save_hostname (NMSettings *self, static void unmanaged_specs_changed (NMSystemConfigInterface *config, gpointer user_data); +static void connection_provider_init (NMConnectionProvider *cp_class); + +G_DEFINE_TYPE_EXTENDED (NMSettings, nm_settings, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION_PROVIDER, connection_provider_init)) + + typedef struct { NMDBusManager *dbus_mgr; DBusGConnection *bus; @@ -127,8 +134,6 @@ typedef struct { GSList *unmanaged_specs; } NMSettingsPrivate; -G_DEFINE_TYPE (NMSettings, nm_settings, G_TYPE_OBJECT) - #define NM_SETTINGS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTINGS, NMSettingsPrivate)) enum { @@ -1621,6 +1626,82 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device) /***************************************************************/ +static gint +best_connection_sort (gconstpointer a, gconstpointer b, gpointer user_data) +{ + NMSettingsConnection *ac = (NMSettingsConnection *) a; + NMSettingsConnection *bc = (NMSettingsConnection *) b; + guint64 ats, bts; + + if (!ac && bc) + return -1; + else if (ac && !bc) + return 1; + else if (!ac && !bc) + return 0; + + g_assert (ac && bc); + + /* In the future we may use connection priorities in addition to timestamps */ + ats = nm_settings_connection_get_timestamp (ac); + bts = nm_settings_connection_get_timestamp (bc); + + if (ats < bts) + return -1; + else if (ats > bts) + return 1; + return 0; +} + +static GSList * +get_best_connections (NMConnectionProvider *provider, + guint max_requested, + const char *ctype1, + const char *ctype2, + NMConnectionFilterFunc func, + gpointer func_data) +{ + NMSettings *self = NM_SETTINGS (provider); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + GSList *sorted = NULL; + GHashTableIter iter; + NMSettingsConnection *connection; + guint added = 0; + guint64 oldest = 0; + + g_hash_table_iter_init (&iter, priv->connections); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) { + 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 (provider, 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) <= oldest) + continue; + + /* List is sorted with oldest first */ + sorted = g_slist_insert_sorted_with_data (sorted, connection, best_connection_sort, NULL); + added++; + + if (max_requested && added > max_requested) { + /* Over the limit, remove the oldest one */ + sorted = g_slist_delete_link (sorted, sorted); + added--; + } + + oldest = nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (sorted->data)); + } + + return g_slist_reverse (sorted); +} + +/***************************************************************/ + NMSettings * nm_settings_new (const char *config_file, const char **plugins, @@ -1659,6 +1740,12 @@ nm_settings_new (const char *config_file, return self; } +static void +connection_provider_init (NMConnectionProvider *cp_class) +{ + cp_class->get_best_connections = get_best_connections; +} + static void nm_settings_init (NMSettings *self) {