From eacaeb839b0c11a9c867d53f9a9261ae1507554a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 2 Mar 2009 12:58:45 -0500 Subject: [PATCH] system-settings: make default wired connections less confusing The "Auto ethX" connection that the system settings service creates for each wired device that does not have an existing backing connection provided by one of the system settings plugins is now read/write when at least one plugin has the MODIFY capability. When the user updates the "Auto ethX" connection, the system settings service will try to move that connection to a plugin, thereby preserving the user's changes. It will also then save that device's MAC address and never create an "Auto ethX" connection for it again. --- marshallers/nm-marshal.list | 1 + po/POTFILES.in | 1 + system-settings/src/Makefile.am | 2 + system-settings/src/dbus-settings.c | 125 +++--- system-settings/src/dbus-settings.h | 17 +- system-settings/src/main.c | 289 +++++++++++--- .../src/nm-default-wired-connection.c | 355 ++++++++++++++++++ .../src/nm-default-wired-connection.h | 57 +++ 8 files changed, 733 insertions(+), 114 deletions(-) create mode 100644 system-settings/src/nm-default-wired-connection.c create mode 100644 system-settings/src/nm-default-wired-connection.h diff --git a/marshallers/nm-marshal.list b/marshallers/nm-marshal.list index ab1cf1266d..ec3cf45cee 100644 --- a/marshallers/nm-marshal.list +++ b/marshallers/nm-marshal.list @@ -17,3 +17,4 @@ VOID:STRING,INT VOID:STRING,UINT VOID:OBJECT,OBJECT,ENUM VOID:POINTER,STRING +POINTER:POINTER diff --git a/po/POTFILES.in b/po/POTFILES.in index a7f5030f46..5638eabab1 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -9,4 +9,5 @@ src/NetworkManager.c src/dhcp-manager/nm-dhcp-dhclient.c src/named-manager/nm-named-manager.c system-settings/src/main.c +system-settings/src/nm-default-wired-connection.c diff --git a/system-settings/src/Makefile.am b/system-settings/src/Makefile.am index 8a66fd1543..b444e9c616 100644 --- a/system-settings/src/Makefile.am +++ b/system-settings/src/Makefile.am @@ -25,6 +25,8 @@ nm_system_settings_SOURCES = \ nm-system-config-hal-manager.h \ nm-sysconfig-connection.c \ nm-sysconfig-connection.h \ + nm-default-wired-connection.c \ + nm-default-wired-connection.h \ sha1.c \ sha1.h diff --git a/system-settings/src/dbus-settings.c b/system-settings/src/dbus-settings.c index d5b7c26aa1..d098652919 100644 --- a/system-settings/src/dbus-settings.c +++ b/system-settings/src/dbus-settings.c @@ -101,7 +101,7 @@ load_connections (NMSysconfigSettings *self) // priority plugin. for (elt = plugin_connections; elt; elt = g_slist_next (elt)) - nm_sysconfig_settings_add_connection (self, NM_EXPORTED_CONNECTION (elt->data)); + nm_sysconfig_settings_add_connection (self, NM_EXPORTED_CONNECTION (elt->data), TRUE); g_slist_free (plugin_connections); } @@ -223,9 +223,9 @@ get_unmanaged_devices (NMSysconfigSettings *self) return devices; } -static NMSystemConfigInterface * -get_first_plugin_by_capability (NMSysconfigSettings *self, - guint32 capability) +NMSystemConfigInterface * +nm_sysconfig_settings_get_plugin (NMSysconfigSettings *self, + guint32 capability) { NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); GSList *iter; @@ -285,7 +285,7 @@ get_property (GObject *object, guint prop_id, g_value_set_static_string (value, ""); break; case PROP_CAN_MODIFY: - g_value_set_boolean (value, !!get_first_plugin_by_capability (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)); + g_value_set_boolean (value, !!nm_sysconfig_settings_get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -391,10 +391,10 @@ nm_sysconfig_settings_new (DBusGConnection *g_conn, NMSystemConfigHalManager *ha static void plugin_connection_added (NMSystemConfigInterface *config, - NMExportedConnection *connection, - gpointer user_data) + NMExportedConnection *connection, + gpointer user_data) { - nm_sysconfig_settings_add_connection (NM_SYSCONFIG_SETTINGS (user_data), connection); + nm_sysconfig_settings_add_connection (NM_SYSCONFIG_SETTINGS (user_data), connection, TRUE); } static void @@ -477,7 +477,8 @@ connection_removed (NMExportedConnection *connection, void nm_sysconfig_settings_add_connection (NMSysconfigSettings *self, - NMExportedConnection *connection) + NMExportedConnection *connection, + gboolean do_export) { NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); @@ -491,13 +492,16 @@ nm_sysconfig_settings_add_connection (NMSysconfigSettings *self, g_hash_table_insert (priv->connections, g_object_ref (connection), GINT_TO_POINTER (1)); g_signal_connect (connection, "removed", G_CALLBACK (connection_removed), self); - nm_exported_connection_register_object (connection, NM_CONNECTION_SCOPE_SYSTEM, priv->g_connection); - nm_settings_signal_new_connection (NM_SETTINGS (self), connection); + if (do_export) { + nm_exported_connection_register_object (connection, NM_CONNECTION_SCOPE_SYSTEM, priv->g_connection); + nm_settings_signal_new_connection (NM_SETTINGS (self), connection); + } } void nm_sysconfig_settings_remove_connection (NMSysconfigSettings *self, - NMExportedConnection *connection) + NMExportedConnection *connection, + gboolean do_signal) { NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); @@ -527,19 +531,67 @@ nm_sysconfig_settings_is_device_managed (NMSysconfigSettings *self, return TRUE; } -static gboolean -impl_settings_add_connection (NMSysconfigSettings *self, - GHashTable *hash, - DBusGMethodInvocation *context) +gboolean +nm_sysconfig_settings_add_new_connection (NMSysconfigSettings *self, + GHashTable *hash, + GError **error) { NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); NMConnection *connection; + GError *tmp_error = NULL, *last_error = NULL; GSList *iter; - GError *err = NULL, *cnfh_error = NULL; gboolean success = FALSE; + connection = nm_connection_new_from_hash (hash, &tmp_error); + if (!connection) { + /* Invalid connection hash */ + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid connection: '%s' / '%s' invalid: %d", + tmp_error ? g_type_name (nm_connection_lookup_setting_type_by_quark (tmp_error->domain)) : "(unknown)", + tmp_error ? tmp_error->message : "(unknown)", tmp_error ? tmp_error->code : -1); + g_clear_error (&tmp_error); + return FALSE; + } + + /* Here's how it works: + 1) plugin writes a connection. + 2) plugin notices that a new connection is available for reading. + 3) plugin reads the new connection (the one it wrote in 1) and emits 'connection-added' signal. + 4) NMSysconfigSettings receives the signal and adds it to it's connection list. + */ + + for (iter = priv->plugins; iter && !success; iter = iter->next) { + success = nm_system_config_interface_add_connection (NM_SYSTEM_CONFIG_INTERFACE (iter->data), + connection, &tmp_error); + g_clear_error (&last_error); + if (!success) + last_error = tmp_error; + } + + g_object_unref (connection); + + if (!success) { + g_set_error (error, NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_ADD_FAILED, + "Saving connection failed: (%d) %s", + last_error ? last_error->code : -1, + last_error && last_error->message ? last_error->message : "(unknown)"); + g_clear_error (&last_error); + } + + return success; +} + +static gboolean +impl_settings_add_connection (NMSysconfigSettings *self, + GHashTable *hash, + DBusGMethodInvocation *context) +{ + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + GError *err = NULL; + /* Do any of the plugins support adding? */ - if (!get_first_plugin_by_capability (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) { + if (!nm_sysconfig_settings_get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) { err = g_error_new (NM_SYSCONFIG_SETTINGS_ERROR, NM_SYSCONFIG_SETTINGS_ERROR_ADD_NOT_SUPPORTED, "%s", "None of the registered plugins support add."); @@ -549,40 +601,7 @@ impl_settings_add_connection (NMSysconfigSettings *self, if (!check_polkit_privileges (priv->g_connection, priv->pol_ctx, context, &err)) goto out; - connection = nm_connection_new_from_hash (hash, &cnfh_error); - if (connection) { - GError *add_error = NULL; - - /* Here's how it works: - 1) plugin writes a connection. - 2) plugin notices that a new connection is available for reading. - 3) plugin reads the new connection (the one it wrote in 1) and emits 'connection-added' signal. - 4) NMSysconfigSettings receives the signal and adds it to it's connection list. - */ - - success = FALSE; - for (iter = priv->plugins; iter && success == FALSE; iter = iter->next) { - success = nm_system_config_interface_add_connection (NM_SYSTEM_CONFIG_INTERFACE (iter->data), - connection, &add_error); - if (!success && add_error) - g_error_free (add_error); - } - - g_object_unref (connection); - - if (!success) { - err = g_error_new (NM_SYSCONFIG_SETTINGS_ERROR, - NM_SYSCONFIG_SETTINGS_ERROR_ADD_FAILED, - "%s", "Saving connection failed."); - } - } else { - /* Invalid connection hash */ - err = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid connection: '%s' / '%s' invalid: %d", - g_type_name (nm_connection_lookup_setting_type_by_quark (cnfh_error->domain)), - cnfh_error->message, cnfh_error->code); - g_error_free (cnfh_error); - } + nm_sysconfig_settings_add_new_connection (self, hash, &err); out: if (err) { @@ -606,7 +625,7 @@ impl_settings_save_hostname (NMSysconfigSettings *self, gboolean success = FALSE; /* Do any of the plugins support setting the hostname? */ - if (!get_first_plugin_by_capability (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) { + if (!nm_sysconfig_settings_get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) { err = g_error_new (NM_SYSCONFIG_SETTINGS_ERROR, NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_NOT_SUPPORTED, "%s", "None of the registered plugins support setting the hostname."); diff --git a/system-settings/src/dbus-settings.h b/system-settings/src/dbus-settings.h index 23101ab159..2e253ab80f 100644 --- a/system-settings/src/dbus-settings.h +++ b/system-settings/src/dbus-settings.h @@ -67,11 +67,14 @@ NMSysconfigSettings *nm_sysconfig_settings_new (DBusGConnection *g_conn, void nm_sysconfig_settings_add_plugin (NMSysconfigSettings *settings, NMSystemConfigInterface *plugin); +/* Registers an exising connection with the settings service */ void nm_sysconfig_settings_add_connection (NMSysconfigSettings *settings, - NMExportedConnection *connection); + NMExportedConnection *connection, + gboolean do_export); void nm_sysconfig_settings_remove_connection (NMSysconfigSettings *settings, - NMExportedConnection *connection); + NMExportedConnection *connection, + gboolean do_signal); void nm_sysconfig_settings_update_unamanged_devices (NMSysconfigSettings *settings, GSList *new_list); @@ -79,4 +82,14 @@ void nm_sysconfig_settings_update_unamanged_devices (NMSysconfigSettings *settin gboolean nm_sysconfig_settings_is_device_managed (NMSysconfigSettings *settings, const char *udi); +NMSystemConfigInterface *nm_sysconfig_settings_get_plugin (NMSysconfigSettings *self, + guint32 capability); + +/* Adds a new connection from a hash of that connection's settings, + * potentially saving the new connection to persistent storage. + */ +gboolean nm_sysconfig_settings_add_new_connection (NMSysconfigSettings *self, + GHashTable *hash, + GError **error); + #endif /* __DBUS_SETTINGS_H__ */ diff --git a/system-settings/src/main.c b/system-settings/src/main.c index 105da6e9ad..2c4734d8cd 100644 --- a/system-settings/src/main.c +++ b/system-settings/src/main.c @@ -49,6 +49,9 @@ #include "dbus-settings.h" #include "nm-system-config-hal-manager.h" #include "nm-system-config-interface.h" +#include "nm-default-wired-connection.h" + +#define CONFIG_KEY_NO_AUTO_DEFAULT "no-auto-default" static GMainLoop *loop = NULL; static gboolean debug = FALSE; @@ -63,6 +66,8 @@ typedef struct { NMSysconfigSettings *settings; GHashTable *wired_devices; + + const char *config; } Application; @@ -207,11 +212,11 @@ load_stuff (gpointer user_data) typedef struct { Application *app; - NMExportedConnection *connection; + NMDefaultWiredConnection *connection; guint add_id; + guint updated_id; + guint deleted_id; char *udi; - GByteArray *mac; - char *iface; } WiredDeviceInfo; static void @@ -219,13 +224,16 @@ wired_device_info_destroy (gpointer user_data) { WiredDeviceInfo *info = (WiredDeviceInfo *) user_data; - g_free (info->iface); - if (info->mac) - g_byte_array_free (info->mac, TRUE); if (info->add_id) g_source_remove (info->add_id); + if (info->updated_id) + g_source_remove (info->updated_id); + if (info->deleted_id) + g_source_remove (info->deleted_id); if (info->connection) { - nm_sysconfig_settings_remove_connection (info->app->settings, info->connection); + nm_sysconfig_settings_remove_connection (info->app->settings, + NM_EXPORTED_CONNECTION (info->connection), + TRUE); g_object_unref (info->connection); } g_free (info); @@ -297,9 +305,7 @@ have_connection_for_device (Application *app, GByteArray *mac) g_return_val_if_fail (app != NULL, FALSE); g_return_val_if_fail (mac != NULL, FALSE); - /* If the device doesn't have a connection advertised by any of the - * plugins, create a new default DHCP-enabled connection for it. - */ + /* Find a wired connection locked to the given MAC address, if any */ list = nm_settings_list_connections (NM_SETTINGS (app->settings)); for (iter = list; iter; iter = g_slist_next (iter)) { NMExportedConnection *exported = NM_EXPORTED_CONNECTION (iter->data); @@ -345,76 +351,240 @@ have_connection_for_device (Application *app, GByteArray *mac) return ret; } +/* Search through the list of blacklisted MAC addresses in the config file. */ static gboolean -add_default_dhcp_connection (gpointer user_data) +is_mac_auto_wired_blacklisted (const GByteArray *mac, const char *filename) +{ + GKeyFile *config; + char **list, **iter; + gboolean found = FALSE; + + g_return_val_if_fail (mac != NULL, FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + + config = g_key_file_new (); + if (!config) { + g_warning ("%s: not enough memory to load config file.", __func__); + return FALSE; + } + + g_key_file_set_list_separator (config, ','); + if (!g_key_file_load_from_file (config, filename, G_KEY_FILE_NONE, NULL)) + goto out; + + list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, NULL, NULL); + for (iter = list; iter && *iter; iter++) { + struct ether_addr *candidate; + + candidate = ether_aton (*iter); + if (candidate && !memcmp (mac->data, candidate->ether_addr_octet, ETH_ALEN)) { + found = TRUE; + break; + } + } + + if (list) + g_strfreev (list); + +out: + g_key_file_free (config); + return found; +} + +static void +default_wired_deleted (NMDefaultWiredConnection *wired, + const GByteArray *mac, + WiredDeviceInfo *info) +{ + NMConnection *wrapped; + NMSettingConnection *s_con; + char *tmp; + GKeyFile *config; + char **list, **iter, **updated; + gboolean found = FALSE; + gsize len = 0; + char *data; + + /* If there was no config file specified, there's nothing to do */ + if (!info->app->config) + goto cleanup; + + /* When the default wired connection is removed (either deleted or saved + * to a new persistent connection by a plugin), write the MAC address of + * the wired device to the config file and don't create a new default wired + * connection for that device again. + */ + + wrapped = nm_exported_connection_get_connection (NM_EXPORTED_CONNECTION (wired)); + g_assert (wrapped); + s_con = (NMSettingConnection *) nm_connection_get_setting (wrapped, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + + /* Ignore removals of read-only connections, since they couldn't have + * been removed by the user. + */ + if (nm_setting_connection_get_read_only (s_con)) + goto cleanup; + + config = g_key_file_new (); + if (!config) + goto cleanup; + + g_key_file_set_list_separator (config, ','); + g_key_file_load_from_file (config, info->app->config, G_KEY_FILE_KEEP_COMMENTS, NULL); + + list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, &len, NULL); + /* Traverse entire list to get count of # items */ + for (iter = list; iter && *iter; iter++) { + struct ether_addr *candidate; + + candidate = ether_aton (*iter); + if (candidate && !memcmp (mac->data, candidate->ether_addr_octet, ETH_ALEN)) + found = TRUE; + } + + /* Add this device's MAC to the list */ + if (!found) { + tmp = g_strdup_printf ("%02x:%02x:%02x:%02x:%02x:%02x", + mac->data[0], mac->data[1], mac->data[2], + mac->data[3], mac->data[4], mac->data[5]); + + updated = g_malloc0 (sizeof (char*) * (len + 2)); + if (list && len) + memcpy (updated, list, len); + updated[len] = tmp; + + g_key_file_set_string_list (config, + "main", CONFIG_KEY_NO_AUTO_DEFAULT, + (const char **) updated, + len + 1); + /* g_free() not g_strfreev() since 'updated' isn't a deep-copy */ + g_free (updated); + g_free (tmp); + + data = g_key_file_to_data (config, &len, NULL); + if (data) { + g_file_set_contents (info->app->config, data, len, NULL); + g_free (data); + } + } + + if (list) + g_strfreev (list); + g_key_file_free (config); + +cleanup: + /* Clear the connection first so that a 'removed' signal doesn't get emitted + * during wired_device_info_destroy(), becuase this connection removal + * is expected and already handled. + */ + g_object_unref (wired); + info->connection = NULL; + + g_hash_table_remove (info->app->wired_devices, info->udi); +} + +static GError * +default_wired_try_update (NMDefaultWiredConnection *wired, + GHashTable *new_settings, + WiredDeviceInfo *info) +{ + GError *error = NULL; + NMConnection *wrapped; + NMSettingConnection *s_con; + const char *id; + + /* Try to move this default wired conneciton to a plugin so that it has + * persistent storage. + */ + + wrapped = nm_exported_connection_get_connection (NM_EXPORTED_CONNECTION (wired)); + g_assert (wrapped); + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (wrapped, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + id = nm_setting_connection_get_id (s_con); + g_assert (id); + + nm_sysconfig_settings_remove_connection (info->app->settings, NM_EXPORTED_CONNECTION (wired), FALSE); + if (nm_sysconfig_settings_add_new_connection (info->app->settings, new_settings, &error)) { + g_message ("Saved default wired connection '%s' to persistent storage", id); + return NULL; + } + + g_warning ("%s: couldn't save default wired connection '%s': %d / %s", + __func__, id, error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); + + /* If there was an error, don't destroy the default wired connection, + * but add it back to the system settings service. Connection is already + * exported on the bus, don't export it again, thus do_export == FALSE. + */ + nm_sysconfig_settings_add_connection (info->app->settings, NM_EXPORTED_CONNECTION (wired), FALSE); + + return error; +} + +static gboolean +add_default_wired_connection (gpointer user_data) { WiredDeviceInfo *info = (WiredDeviceInfo *) user_data; + GByteArray *mac = NULL; + struct ether_addr tmp; + char *iface = NULL; NMSettingConnection *s_con; - NMSettingWired *s_wired; NMConnection *wrapped; - GByteArray *setting_mac; - char *id; - char *uuid; + gboolean read_only = TRUE; + const char *id; - if (info->add_id) - info->add_id = 0; + info->add_id = 0; + g_assert (info->connection == NULL); /* If the device isn't managed, ignore it */ if (!nm_sysconfig_settings_is_device_managed (info->app->settings, info->udi)) goto ignore; - if (!info->iface) { - struct ether_addr mac; - - info->iface = get_details_for_udi (info->app, info->udi, &mac); - if (!info->iface) - goto ignore; - info->mac = g_byte_array_sized_new (ETH_ALEN); - g_byte_array_append (info->mac, mac.ether_addr_octet, ETH_ALEN); - } - - if (have_connection_for_device (info->app, info->mac)) + iface = get_details_for_udi (info->app, info->udi, &tmp); + if (!iface) goto ignore; - wrapped = nm_connection_new (); - info->connection = nm_exported_connection_new (wrapped); - g_object_unref (wrapped); + mac = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (mac, tmp.ether_addr_octet, ETH_ALEN); - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + if (have_connection_for_device (info->app, mac)) + goto ignore; - id = g_strdup_printf (_("Auto %s"), info->iface); - uuid = nm_utils_uuid_generate (); + if (info->app->config && is_mac_auto_wired_blacklisted (mac, info->app->config)) + goto ignore; - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, id, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_READ_ONLY, TRUE, - NULL); + if (nm_sysconfig_settings_get_plugin (info->app->settings, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) + read_only = FALSE; - nm_connection_add_setting (wrapped, NM_SETTING (s_con)); + info->connection = nm_default_wired_connection_new (mac, iface, read_only); + if (!info->connection) + goto ignore; - g_message ("Adding default connection '%s' for %s", id, info->udi); - - g_free (id); - g_free (uuid); + wrapped = nm_exported_connection_get_connection (NM_EXPORTED_CONNECTION (info->connection)); + g_assert (wrapped); + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (wrapped, NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + id = nm_setting_connection_get_id (s_con); + g_assert (id); - /* Lock the connection to this device */ - s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); - - setting_mac = g_byte_array_sized_new (ETH_ALEN); - g_byte_array_append (setting_mac, info->mac->data, ETH_ALEN); - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, setting_mac, NULL); - g_byte_array_free (setting_mac, TRUE); - - nm_connection_add_setting (wrapped, NM_SETTING (s_wired)); - - nm_sysconfig_settings_add_connection (info->app->settings, info->connection); + g_message ("Added default wired connection '%s' for %s", id, info->udi); + info->updated_id = g_signal_connect (info->connection, "try-update", + (GCallback) default_wired_try_update, info); + info->deleted_id = g_signal_connect (info->connection, "deleted", + (GCallback) default_wired_deleted, info); + nm_sysconfig_settings_add_connection (info->app->settings, + NM_EXPORTED_CONNECTION (info->connection), + TRUE); return FALSE; ignore: + if (mac) + g_byte_array_free (mac, TRUE); + g_free (iface); g_hash_table_remove (info->app->wired_devices, info->udi); return FALSE; } @@ -431,7 +601,7 @@ device_added_cb (DBusGProxy *proxy, const char *udi, NMDeviceType devtype, gpoin /* Wait for a plugin to figure out if the device should be managed or not */ info = g_malloc0 (sizeof (WiredDeviceInfo)); info->app = app; - info->add_id = g_timeout_add_seconds (4, add_default_dhcp_connection, info); + info->add_id = g_timeout_add_seconds (4, add_default_wired_connection, info); info->udi = g_strdup (udi); g_hash_table_insert (app->wired_devices, info->udi, info); } @@ -663,7 +833,8 @@ main (int argc, char **argv) g_option_context_free (opt_ctx); if (config) { - if (!parse_config_file (config, &plugins, &error)) { + app->config = config; + if (!parse_config_file (app->config, &plugins, &error)) { g_warning ("Invalid config file: %s.", error->message); return 1; } diff --git a/system-settings/src/nm-default-wired-connection.c b/system-settings/src/nm-default-wired-connection.c new file mode 100644 index 0000000000..c8a6daab72 --- /dev/null +++ b/system-settings/src/nm-default-wired-connection.c @@ -0,0 +1,355 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 Novell, Inc. + * (C) Copyright 2009 Red Hat, Inc. + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include "nm-dbus-glib-types.h" +#include "nm-marshal.h" +#include "nm-default-wired-connection.h" + +G_DEFINE_TYPE (NMDefaultWiredConnection, nm_default_wired_connection, NM_TYPE_SYSCONFIG_CONNECTION) + +#define NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionPrivate)) + +typedef struct { + char *iface; + GByteArray *mac; + gboolean read_only; +} NMDefaultWiredConnectionPrivate; + +enum { + PROP_0, + PROP_MAC, + PROP_IFACE, + PROP_READ_ONLY, + LAST_PROP +}; + +enum { + TRY_UPDATE, + DELETED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +NMDefaultWiredConnection * +nm_default_wired_connection_new (const GByteArray *mac, + const char *iface, + gboolean read_only) +{ + + g_return_val_if_fail (mac != NULL, NULL); + g_return_val_if_fail (mac->len == ETH_ALEN, NULL); + g_return_val_if_fail (iface != NULL, NULL); + + return g_object_new (NM_TYPE_DEFAULT_WIRED_CONNECTION, + NM_DEFAULT_WIRED_CONNECTION_MAC, mac, + NM_DEFAULT_WIRED_CONNECTION_IFACE, iface, + NM_DEFAULT_WIRED_CONNECTION_READ_ONLY, read_only, + NULL); +} + +static GByteArray * +dup_wired_mac (NMExportedConnection *exported) +{ + NMConnection *wrapped; + NMSettingWired *s_wired; + const GByteArray *mac; + GByteArray *dup; + + wrapped = nm_exported_connection_get_connection (exported); + if (!wrapped) + return NULL; + + s_wired = (NMSettingWired *) nm_connection_get_setting (wrapped, NM_TYPE_SETTING_WIRED); + if (!s_wired) + return NULL; + + mac = nm_setting_wired_get_mac_address (s_wired); + if (!mac || (mac->len != ETH_ALEN)) + return NULL; + + dup = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (dup, mac->data, ETH_ALEN); + return dup; +} + +static gboolean +update (NMExportedConnection *exported, + GHashTable *new_settings, + GError **error) +{ + NMDefaultWiredConnection *connection = NM_DEFAULT_WIRED_CONNECTION (exported); + gboolean success; + GByteArray *mac; + + /* Ensure object stays alive across signal emission */ + g_object_ref (exported); + + /* Save a copy of the current MAC address just in case the user + * changed it when updating the connection. + */ + mac = dup_wired_mac (exported); + + /* Let NMSysconfigConnection check permissions */ + success = NM_EXPORTED_CONNECTION_CLASS (nm_default_wired_connection_parent_class)->update (exported, new_settings, error); + if (success) { + g_signal_emit_by_name (connection, "try-update", new_settings, error); + success = *error ? FALSE : TRUE; + + if (success) + g_signal_emit (connection, signals[DELETED], 0, mac); + } + + g_byte_array_free (mac, TRUE); + g_object_unref (exported); + return success; +} + +static gboolean +do_delete (NMExportedConnection *exported, GError **error) +{ + gboolean success; + GByteArray *mac; + + g_object_ref (exported); + mac = dup_wired_mac (exported); + + success = NM_EXPORTED_CONNECTION_CLASS (nm_default_wired_connection_parent_class)->do_delete (exported, error); + if (success) + g_signal_emit (exported, signals[DELETED], 0, mac); + + g_byte_array_free (mac, TRUE); + g_object_unref (exported); + return success; +} + +static void +nm_default_wired_connection_init (NMDefaultWiredConnection *self) +{ +} + +static GObject * +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMDefaultWiredConnectionPrivate *priv; + NMConnection *wrapped; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + char *id, *uuid; + + object = G_OBJECT_CLASS (nm_default_wired_connection_parent_class)->constructor (type, n_construct_params, construct_params); + if (!object) + return NULL; + + priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object); + + wrapped = nm_connection_new (); + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + + id = g_strdup_printf (_("Auto %s"), priv->iface); + uuid = nm_utils_uuid_generate (); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, id, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_READ_ONLY, priv->read_only, + NULL); + + g_free (id); + g_free (uuid); + + nm_connection_add_setting (wrapped, NM_SETTING (s_con)); + + /* Lock the connection to the specific device */ + s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, priv->mac, NULL); + nm_connection_add_setting (wrapped, NM_SETTING (s_wired)); + + g_object_set (object, NM_EXPORTED_CONNECTION_CONNECTION, wrapped, NULL); + g_object_unref (wrapped); + + return object; +} + +static void +finalize (GObject *object) +{ + NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object); + + g_free (priv->iface); + g_byte_array_free (priv->mac, TRUE); + + G_OBJECT_CLASS (nm_default_wired_connection_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MAC: + g_value_set_pointer (value, priv->mac); + break; + case PROP_IFACE: + g_value_set_string (value, priv->iface); + break; + case PROP_READ_ONLY: + g_value_set_boolean (value, priv->read_only); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object); + GByteArray *array; + + switch (prop_id) { + case PROP_MAC: + /* Construct only */ + array = g_value_get_pointer (value); + if (priv->mac) { + g_byte_array_free (priv->mac, TRUE); + priv->mac = NULL; + } + if (array) { + g_return_if_fail (array->len == ETH_ALEN); + priv->mac = g_byte_array_sized_new (array->len); + g_byte_array_append (priv->mac, array->data, ETH_ALEN); + } + break; + case PROP_IFACE: + g_free (priv->iface); + priv->iface = g_value_dup_string (value); + break; + case PROP_READ_ONLY: + priv->read_only = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +try_update_signal_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer data) +{ + gpointer new_ptr = g_value_get_pointer (handler_return); + + g_value_set_pointer (return_accu, new_ptr); + + /* Continue if no error was returned from the handler */ + return new_ptr ? FALSE : TRUE; +} + +static void +nm_default_wired_connection_class_init (NMDefaultWiredConnectionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMExportedConnectionClass *exported_class = NM_EXPORTED_CONNECTION_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMDefaultWiredConnectionPrivate)); + + /* Virtual methods */ + object_class->constructor = constructor; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + exported_class->update = update; + exported_class->do_delete = do_delete; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_MAC, + g_param_spec_pointer (NM_DEFAULT_WIRED_CONNECTION_MAC, + "MAC", + "MAC Address", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_IFACE, + g_param_spec_string (NM_DEFAULT_WIRED_CONNECTION_IFACE, + "Iface", + "Interface", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_READ_ONLY, + g_param_spec_boolean (NM_DEFAULT_WIRED_CONNECTION_READ_ONLY, + "ReadOnly", + "Read Only", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /* Signals */ + signals[TRY_UPDATE] = + g_signal_new ("try-update", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, try_update_signal_accumulator, NULL, + _nm_marshal_POINTER__POINTER, + G_TYPE_POINTER, 1, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT); + + /* The 'deleted' signal is used to signal intentional deletions (like + * updating or user-requested deletion) rather than using the + * NMExportedConnection superclass' 'removed' signal, since that signal + * doesn't have the semantics we want; it gets emitted as a side-effect + * of various operations and is meant more for D-Bus clients instead + * of in-service uses. + */ + signals[DELETED] = + g_signal_new ("deleted", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} diff --git a/system-settings/src/nm-default-wired-connection.h b/system-settings/src/nm-default-wired-connection.h new file mode 100644 index 0000000000..50c8b182e1 --- /dev/null +++ b/system-settings/src/nm-default-wired-connection.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager system settings service + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2008 Novell, Inc. + * (C) Copyright 2009 Red Hat, Inc. + */ + +#ifndef NM_DEFAULT_WIRED_CONNECTION_H +#define NM_DEFAULT_WIRED_CONNECTION_H + +#include +#include "nm-sysconfig-connection.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEFAULT_WIRED_CONNECTION (nm_default_wired_connection_get_type ()) +#define NM_DEFAULT_WIRED_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnection)) +#define NM_DEFAULT_WIRED_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionClass)) +#define NM_IS_DEFAULT_WIRED_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION)) +#define NM_IS_DEFAULT_WIRED_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION)) +#define NM_DEFAULT_WIRED_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionClass)) + +#define NM_DEFAULT_WIRED_CONNECTION_MAC "mac" +#define NM_DEFAULT_WIRED_CONNECTION_IFACE "iface" +#define NM_DEFAULT_WIRED_CONNECTION_READ_ONLY "read-only" + +typedef struct { + NMSysconfigConnection parent; +} NMDefaultWiredConnection; + +typedef struct { + NMSysconfigConnectionClass parent; +} NMDefaultWiredConnectionClass; + +GType nm_default_wired_connection_get_type (void); + +NMDefaultWiredConnection *nm_default_wired_connection_new (const GByteArray *mac, + const char *iface, + gboolean read_only); + +G_END_DECLS + +#endif /* NM_DEFAULT_WIRED_CONNECTION_H */