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 */