From 8b7eaeb07ec35f320663a19ace774bbd29886f27 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 14 May 2014 16:12:28 -0500 Subject: [PATCH 01/43] vpn: simplify service cleanup --- src/vpn-manager/nm-vpn-service.c | 39 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index 8ecd9a117..b389a5153 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2012 Red Hat, Inc. + * Copyright (C) 2005 - 2014 Red Hat, Inc. * Copyright (C) 2005 - 2008 Novell, Inc. */ @@ -38,9 +38,6 @@ G_DEFINE_TYPE (NMVPNService, nm_vpn_service, G_TYPE_OBJECT) typedef struct { - gboolean disposed; - - NMDBusManager *dbus_mgr; char *name; char *dbus_service; char *program; @@ -345,7 +342,7 @@ nm_vpn_service_activate (NMVPNService *service, priv->connections = g_slist_prepend (priv->connections, g_object_ref (vpn)); - if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, priv->dbus_service)) + if (nm_dbus_manager_name_has_owner (nm_dbus_manager_get (), priv->dbus_service)) nm_vpn_connection_activate (vpn); else if (priv->start_timeout == 0) { nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name); @@ -410,8 +407,7 @@ nm_vpn_service_init (NMVPNService *self) { NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); - priv->dbus_mgr = nm_dbus_manager_get (); - priv->name_owner_id = g_signal_connect (priv->dbus_mgr, + priv->name_owner_id = g_signal_connect (nm_dbus_manager_get (), NM_DBUS_MANAGER_NAME_OWNER_CHANGED, G_CALLBACK (nm_vpn_service_name_owner_changed), self); @@ -423,34 +419,42 @@ dispose (GObject *object) NMVPNService *self = NM_VPN_SERVICE (object); NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); - if (priv->disposed) - goto out; - priv->disposed = TRUE; - - if (priv->start_timeout) + if (priv->start_timeout) { g_source_remove (priv->start_timeout); + priv->start_timeout = 0; + } nm_vpn_service_connections_stop (NM_VPN_SERVICE (object), FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); - g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id); + if (priv->name_owner_id) { + g_signal_handler_disconnect (nm_dbus_manager_get (), priv->name_owner_id); + priv->name_owner_id = 0; + } - if (priv->child_watch) + if (priv->child_watch) { g_source_remove (priv->child_watch); + priv->child_watch = 0; + } clear_quit_timeout (self); service_quit (self); - priv->dbus_mgr = NULL; + G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (object); g_free (priv->name); g_free (priv->dbus_service); g_free (priv->program); g_free (priv->namefile); -out: - G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object); + G_OBJECT_CLASS (nm_vpn_service_parent_class)->finalize (object); } static void @@ -462,4 +466,5 @@ nm_vpn_service_class_init (NMVPNServiceClass *service_class) /* virtual methods */ object_class->dispose = dispose; + object_class->finalize = finalize; } From 4b57f6920e9a4dda7dcabaa46e0c6b693c9af83c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 14 May 2014 16:21:09 -0500 Subject: [PATCH 02/43] vpn: let VPN services quit themselves Instead of telling a VPN service to quit, leave that up to the service itself. Services based on libnm-glib-vpn already have a quit timeout of 20 seconds. We also eventually want to D-Bus activate the VPN services, and at that point we won't have a PID we can send signals to. --- src/vpn-manager/nm-vpn-service.c | 120 +++++++------------------------ 1 file changed, 26 insertions(+), 94 deletions(-) diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index b389a5153..c0e71e5fd 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -46,7 +46,6 @@ typedef struct { GPid pid; GSList *connections; guint start_timeout; - guint quit_timeout; guint child_watch; gulong name_owner_id; } NMVPNServicePrivate; @@ -117,36 +116,42 @@ nm_vpn_service_get_name_file (NMVPNService *service) return NM_VPN_SERVICE_GET_PRIVATE (service)->namefile; } +static void +connection_vpn_state_changed (NMVPNConnection *connection, + NMVPNConnectionState new_state, + NMVPNConnectionState old_state, + NMVPNConnectionStateReason reason, + gpointer user_data) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data); + + if (new_state == NM_VPN_CONNECTION_STATE_FAILED || + new_state == NM_VPN_CONNECTION_STATE_DISCONNECTED) { + /* Remove the connection from our list */ + priv->connections = g_slist_remove (priv->connections, connection); + g_object_unref (connection); + } +} + void nm_vpn_service_connections_stop (NMVPNService *service, gboolean fail, NMVPNConnectionStateReason reason) { NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); - GSList *iter, *copy; + GSList *iter; - /* Copy because stopping the connection may remove it from the list - * in the NMVPNService objects' VPN connection state handler. - */ - copy = g_slist_copy (priv->connections); - for (iter = copy; iter; iter = iter->next) { + for (iter = priv->connections; iter; iter = iter->next) { + NMVPNConnection *vpn = NM_VPN_CONNECTION (iter->data); + + g_signal_handlers_disconnect_by_func (vpn, G_CALLBACK (connection_vpn_state_changed), service); if (fail) - nm_vpn_connection_fail (NM_VPN_CONNECTION (iter->data), reason); + nm_vpn_connection_fail (vpn, reason); else - nm_vpn_connection_disconnect (NM_VPN_CONNECTION (iter->data), reason); - } - g_slist_free (copy); -} - -static void -clear_quit_timeout (NMVPNService *self) -{ - NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); - - if (priv->quit_timeout) { - g_source_remove (priv->quit_timeout); - priv->quit_timeout = 0; + nm_vpn_connection_disconnect (vpn, reason); + g_object_unref (vpn); } + g_clear_pointer (&priv->connections, g_slist_free); } /* @@ -195,7 +200,6 @@ vpn_service_watch_cb (GPid pid, gint status, gpointer user_data) priv->pid = 0; priv->child_watch = 0; - clear_quit_timeout (service); nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); } @@ -254,72 +258,6 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error) return success; } -static gboolean -ensure_killed (gpointer data) -{ - int pid = GPOINTER_TO_INT (data); - - if (kill (pid, 0) == 0) - kill (pid, SIGKILL); - - /* ensure the child is reaped */ - nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", pid); - waitpid (pid, NULL, 0); - nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", pid); - - return FALSE; -} - -static gboolean -service_quit (gpointer user_data) -{ - NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data); - - if (priv->pid) { - if (kill (priv->pid, SIGTERM) == 0) - g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid)); - else { - kill (priv->pid, SIGKILL); - - /* ensure the child is reaped */ - nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", priv->pid); - waitpid (priv->pid, NULL, 0); - nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", priv->pid); - } - priv->pid = 0; - } - priv->quit_timeout = 0; - - return FALSE; -} - -static void -connection_vpn_state_changed (NMVPNConnection *connection, - NMVPNConnectionState new_state, - NMVPNConnectionState old_state, - NMVPNConnectionStateReason reason, - gpointer user_data) -{ - NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data); - - switch (new_state) { - case NM_VPN_CONNECTION_STATE_FAILED: - case NM_VPN_CONNECTION_STATE_DISCONNECTED: - /* Remove the connection from our list */ - priv->connections = g_slist_remove (priv->connections, connection); - g_object_unref (connection); - - if (priv->connections == NULL) { - /* Tell the service to quit in a few seconds */ - if (!priv->quit_timeout) - priv->quit_timeout = g_timeout_add_seconds (5, service_quit, user_data); - } - break; - default: - break; - } -} - gboolean nm_vpn_service_activate (NMVPNService *service, NMVPNConnection *vpn, @@ -334,8 +272,6 @@ nm_vpn_service_activate (NMVPNService *service, priv = NM_VPN_SERVICE_GET_PRIVATE (service); - clear_quit_timeout (service); - g_signal_connect (vpn, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED, G_CALLBACK (connection_vpn_state_changed), service); @@ -389,7 +325,6 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr, if (!old_owner_good && new_owner_good) { /* service just appeared */ nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", priv->name); - clear_quit_timeout (service); for (iter = priv->connections; iter; iter = iter->next) nm_vpn_connection_activate (NM_VPN_CONNECTION (iter->data)); @@ -438,9 +373,6 @@ dispose (GObject *object) priv->child_watch = 0; } - clear_quit_timeout (self); - service_quit (self); - G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object); } From 5d1610b347ab30e7bc028eabf6cd197bbda6f20b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 14 May 2014 16:26:12 -0500 Subject: [PATCH 03/43] vpn: optimize un-needed strlen (trivial) --- src/vpn-manager/nm-vpn-service.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index c0e71e5fd..8e19c6ee9 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -298,11 +298,11 @@ nm_vpn_service_get_active_connections (NMVPNService *service) } static void -nm_vpn_service_name_owner_changed (NMDBusManager *mgr, - const char *name, - const char *old, - const char *new, - gpointer user_data) +_name_owner_changed (NMDBusManager *mgr, + const char *name, + const char *old, + const char *new, + gpointer user_data) { NMVPNService *service = NM_VPN_SERVICE (user_data); NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); @@ -319,8 +319,8 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr, priv->start_timeout = 0; } - old_owner_good = (old && (strlen (old) > 0)); - new_owner_good = (new && (strlen (new) > 0)); + old_owner_good = (old && old[0]); + new_owner_good = (new && new[0]); if (!old_owner_good && new_owner_good) { /* service just appeared */ @@ -344,7 +344,7 @@ nm_vpn_service_init (NMVPNService *self) priv->name_owner_id = g_signal_connect (nm_dbus_manager_get (), NM_DBUS_MANAGER_NAME_OWNER_CHANGED, - G_CALLBACK (nm_vpn_service_name_owner_changed), + G_CALLBACK (_name_owner_changed), self); } From ca7fd98bbe74569a17c374afaebc7b963a92729e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 14 May 2014 16:36:44 -0500 Subject: [PATCH 04/43] vpn: simplify creation of VPN service objects --- src/vpn-manager/nm-vpn-service.c | 49 ++++++++++++++------------------ 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index 8e19c6ee9..6de67aeb0 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -55,9 +55,9 @@ typedef struct { NMVPNService * nm_vpn_service_new (const char *namefile, GError **error) { - NMVPNService *self = NULL; + NMVPNService *self; + NMVPNServicePrivate *priv; GKeyFile *kf; - char *dbus_service = NULL, *program = NULL, *name = NULL; g_return_val_if_fail (namefile != NULL, NULL); g_return_val_if_fail (g_path_is_absolute (namefile), NULL); @@ -68,36 +68,29 @@ nm_vpn_service_new (const char *namefile, GError **error) return NULL; } - dbus_service = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", NULL); - if (!dbus_service) { - g_set_error (error, 0, 0, "VPN service file %s had no 'service' key", namefile); - goto out; - } - - program = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "program", NULL); - if (!program) { - g_set_error (error, 0, 0, "VPN service file %s had no 'program' key", namefile); - goto out; - } - - name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "name", NULL); - if (!name) { - g_set_error (error, 0, 0, "VPN service file %s had no 'name' key", namefile); - goto out; - } - self = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL); - NM_VPN_SERVICE_GET_PRIVATE (self)->name = g_strdup (name); - NM_VPN_SERVICE_GET_PRIVATE (self)->dbus_service = g_strdup (dbus_service); - NM_VPN_SERVICE_GET_PRIVATE (self)->program = g_strdup (program); - NM_VPN_SERVICE_GET_PRIVATE (self)->namefile = g_strdup (namefile); + priv = NM_VPN_SERVICE_GET_PRIVATE (self); + priv->namefile = g_strdup (namefile); + + priv->dbus_service = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", error); + if (!priv->dbus_service) + goto error; + + priv->program = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "program", error); + if (!priv->program) + goto error; + + priv->name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "name", error); + if (!priv->name) + goto error; - out: g_key_file_free (kf); - g_free (dbus_service); - g_free (program); - g_free (name); return self; + +error: + g_object_unref (self); + g_key_file_free (kf); + return NULL; } const char * From 063411df4278b704cd621ec83a63e0bf9d1ea9db Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 15 May 2014 14:40:03 -0500 Subject: [PATCH 05/43] vpn: remove useless 'disposed' member from NMVPNManager Just make dispose() able to be called again. --- src/vpn-manager/nm-vpn-manager.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index 6c63edc1e..65a07833b 100644 --- a/src/vpn-manager/nm-vpn-manager.c +++ b/src/vpn-manager/nm-vpn-manager.c @@ -38,8 +38,6 @@ G_DEFINE_TYPE (NMVPNManager, nm_vpn_manager, G_TYPE_OBJECT) #define NM_VPN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_MANAGER, NMVPNManagerPrivate)) typedef struct { - gboolean disposed; - GHashTable *services; GFileMonitor *monitor; guint monitor_id; @@ -343,17 +341,16 @@ dispose (GObject *object) { NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object); - if (!priv->disposed) { - priv->disposed = TRUE; - - if (priv->monitor) { - if (priv->monitor_id) - g_signal_handler_disconnect (priv->monitor, priv->monitor_id); - g_file_monitor_cancel (priv->monitor); - g_object_unref (priv->monitor); - } + if (priv->monitor) { + if (priv->monitor_id) + g_signal_handler_disconnect (priv->monitor, priv->monitor_id); + g_file_monitor_cancel (priv->monitor); + g_clear_object (&priv->monitor); + } + if (priv->services) { g_hash_table_destroy (priv->services); + priv->services = NULL; } G_OBJECT_CLASS (nm_vpn_manager_parent_class)->dispose (object); From c9d183f7fc8797bf7ec160220394a01a3bcc4ac8 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 15 May 2014 17:03:02 -0500 Subject: [PATCH 06/43] vpn: simplify duplicate service checking --- src/vpn-manager/nm-vpn-manager.c | 45 +++++++++----------------------- src/vpn-manager/nm-vpn-service.c | 2 ++ src/vpn-manager/nm-vpn-service.h | 2 -- 3 files changed, 14 insertions(+), 35 deletions(-) diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index 65a07833b..7fa753a39 100644 --- a/src/vpn-manager/nm-vpn-manager.c +++ b/src/vpn-manager/nm-vpn-manager.c @@ -184,58 +184,37 @@ nm_vpn_manager_deactivate_connection (NMVPNManager *self, return success; } -static char * -service_name_from_file (const char *path) -{ - GKeyFile *kf = NULL; - char *service_name = NULL; - - g_return_val_if_fail (g_path_is_absolute (path), NULL); - - if (!g_str_has_suffix (path, ".name")) - return NULL; - - kf = g_key_file_new (); - if (g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL)) - service_name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", NULL); - - g_key_file_free (kf); - return service_name; -} - static void try_add_service (NMVPNManager *self, const char *namefile) { NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self); NMVPNService *service = NULL; + GHashTableIter iter; GError *error = NULL; const char *service_name; - char *tmp; g_return_if_fail (g_path_is_absolute (namefile)); /* Make sure we don't add dupes */ - tmp = service_name_from_file (namefile); - if (tmp) - service = g_hash_table_lookup (priv->services, tmp); - g_free (tmp); - if (service) - return; + g_hash_table_iter_init (&iter, priv->services); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service)) { + if (g_strcmp0 (namefile, nm_vpn_service_get_name_file (service)) == 0) + return; + } - /* New service, add it */ + /* New service */ service = nm_vpn_service_new (namefile, &error); - if (!service) { + if (service) { + service_name = nm_vpn_service_get_dbus_service (service); + g_hash_table_insert (priv->services, (char *) service_name, service); + nm_log_info (LOGD_VPN, "VPN: loaded %s", service_name); + } else { nm_log_warn (LOGD_VPN, "failed to load VPN service file %s: (%d) %s", namefile, error ? error->code : -1, error && error->message ? error->message : "(unknown)"); g_clear_error (&error); - return; } - - service_name = nm_vpn_service_get_dbus_service (service); - g_hash_table_insert (priv->services, (char *) service_name, service); - nm_log_info (LOGD_VPN, "VPN: loaded %s", service_name); } static void diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index 6de67aeb0..43d9cf335 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -52,6 +52,8 @@ typedef struct { #define NM_VPN_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE, NMVPNServicePrivate)) +#define VPN_CONNECTION_GROUP "VPN Connection" + NMVPNService * nm_vpn_service_new (const char *namefile, GError **error) { diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h index 2ad79a0d6..cfb199730 100644 --- a/src/vpn-manager/nm-vpn-service.h +++ b/src/vpn-manager/nm-vpn-service.h @@ -34,8 +34,6 @@ #define NM_IS_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_VPN_SERVICE)) #define NM_VPN_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_SERVICE, NMVPNServiceClass)) -#define VPN_CONNECTION_GROUP "VPN Connection" - typedef struct { GObject parent; } NMVPNService; From 13a9f2781a42f76e3792775fc91198e5e59e293e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 16 May 2014 10:26:30 -0500 Subject: [PATCH 07/43] vpn: remove useless 'disposed' member from NMVPNConnection --- src/vpn-manager/nm-vpn-connection.c | 37 ++++++++++------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 17664ad1a..ae18a7cbf 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -63,8 +63,6 @@ typedef enum { } SecretsReq; typedef struct { - gboolean disposed; - NMConnection *connection; guint32 secrets_id; @@ -1795,38 +1793,26 @@ dispose (GObject *object) { NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object); - if (priv->disposed) { - G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object); - return; - } - priv->disposed = TRUE; - - if (priv->connect_hash) + if (priv->connect_hash) { g_hash_table_destroy (priv->connect_hash); + priv->connect_hash = NULL; + } - if (priv->ip6_internal_gw) - g_free (priv->ip6_internal_gw); - if (priv->ip6_external_gw) - g_free (priv->ip6_external_gw); - - if (priv->ip4_config) - g_object_unref (priv->ip4_config); - if (priv->ip6_config) - g_object_unref (priv->ip6_config); - - if (priv->connect_timeout) + if (priv->connect_timeout) { g_source_remove (priv->connect_timeout); - - if (priv->proxy) - g_object_unref (priv->proxy); + priv->connect_timeout = 0; + } if (priv->secrets_id) { nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection), priv->secrets_id); + priv->secrets_id = 0; } + g_clear_object (&priv->ip4_config); + g_clear_object (&priv->ip6_config); + g_clear_object (&priv->proxy); g_clear_object (&priv->connection); - g_free (priv->username); G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object); } @@ -1838,6 +1824,9 @@ finalize (GObject *object) g_free (priv->banner); g_free (priv->ip_iface); + g_free (priv->username); + g_free (priv->ip6_internal_gw); + g_free (priv->ip6_external_gw); G_OBJECT_CLASS (nm_vpn_connection_parent_class)->finalize (object); } From cf62986857a4b595235c825425d2bc2516443eec Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 16 May 2014 10:18:53 -0500 Subject: [PATCH 08/43] vpn: add DEACTIVATING state Since NM_VPN_CONNECTION_STATE_* are linear in number and defined API, we can't add a DEACTIVATING state in the right place. So create a NMVPNConnection internal VPN state and convert that to the old NM_VPN_CONNECTION_STATE_* numbers at exit points from the object. (This will also allow us to get rid of priv->secrets_req in the future and fold that into the VPN states) --- src/vpn-manager/nm-vpn-connection.c | 263 ++++++++++++++++------------ 1 file changed, 149 insertions(+), 114 deletions(-) diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index ae18a7cbf..770b064f3 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -62,6 +62,20 @@ typedef enum { SECRETS_REQ_LAST } SecretsReq; +/* Internal VPN states, private to NMVPNConnection */ +typedef enum { + STATE_UNKNOWN = 0, + STATE_WAITING, + STATE_PREPARE, + STATE_NEED_AUTH, + STATE_CONNECT, + STATE_IP_CONFIG_GET, + STATE_ACTIVATED, + STATE_DEACTIVATING, + STATE_DISCONNECTED, + STATE_FAILED, +} VpnState; + typedef struct { NMConnection *connection; @@ -69,7 +83,7 @@ typedef struct { SecretsReq secrets_idx; char *username; - NMVPNConnectionState vpn_state; + VpnState vpn_state; NMVPNConnectionStateReason failure_reason; DBusGProxy *proxy; GHashTable *connect_hash; @@ -119,20 +133,58 @@ static void plugin_interactive_secrets_required (DBusGProxy *proxy, const char **secrets, gpointer user_data); +/*********************************************************************/ + +static NMVPNConnectionState +_state_to_nm_vpn_state (VpnState state) +{ + switch (state) { + case STATE_WAITING: + case STATE_PREPARE: + return NM_VPN_CONNECTION_STATE_PREPARE; + case STATE_NEED_AUTH: + return NM_VPN_CONNECTION_STATE_NEED_AUTH; + case STATE_CONNECT: + return NM_VPN_CONNECTION_STATE_CONNECT; + case STATE_IP_CONFIG_GET: + return NM_VPN_CONNECTION_STATE_IP_CONFIG_GET; + case STATE_ACTIVATED: + return NM_VPN_CONNECTION_STATE_ACTIVATED; + case STATE_DEACTIVATING: { + /* Map DEACTIVATING to ACTIVATED to preserve external API behavior, + * since our API has no DEACTIVATING state of its own. Since this can + * take some time, and the VPN isn't actually disconnected until it + * hits the DISCONNECTED state, to clients it should still appear + * connected. + */ + return NM_VPN_CONNECTION_STATE_ACTIVATED; + } + case STATE_DISCONNECTED: + return NM_VPN_CONNECTION_STATE_DISCONNECTED; + case STATE_FAILED: + return NM_VPN_CONNECTION_STATE_FAILED; + default: + return STATE_UNKNOWN; + } +} + static NMActiveConnectionState -ac_state_from_vpn_state (NMVPNConnectionState vpn_state) +_state_to_ac_state (VpnState vpn_state) { /* Set the NMActiveConnection state based on VPN state */ switch (vpn_state) { - case NM_VPN_CONNECTION_STATE_PREPARE: - case NM_VPN_CONNECTION_STATE_NEED_AUTH: - case NM_VPN_CONNECTION_STATE_CONNECT: - case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: + case STATE_WAITING: + case STATE_PREPARE: + case STATE_NEED_AUTH: + case STATE_CONNECT: + case STATE_IP_CONFIG_GET: return NM_ACTIVE_CONNECTION_STATE_ACTIVATING; - case NM_VPN_CONNECTION_STATE_ACTIVATED: + case STATE_ACTIVATED: return NM_ACTIVE_CONNECTION_STATE_ACTIVATED; - case NM_VPN_CONNECTION_STATE_FAILED: - case NM_VPN_CONNECTION_STATE_DISCONNECTED: + case STATE_DEACTIVATING: + return NM_ACTIVE_CONNECTION_STATE_DEACTIVATING; + case STATE_DISCONNECTED: + case STATE_FAILED: return NM_ACTIVE_CONNECTION_STATE_DEACTIVATED; default: break; @@ -188,12 +240,13 @@ vpn_cleanup (NMVPNConnection *connection, NMDevice *parent_dev) } static void -nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, - NMVPNConnectionState vpn_state, - NMVPNConnectionStateReason reason) +_set_vpn_state (NMVPNConnection *connection, + VpnState vpn_state, + NMVPNConnectionStateReason reason) { NMVPNConnectionPrivate *priv; - NMVPNConnectionState old_vpn_state; + VpnState old_vpn_state; + NMVPNConnectionState new_external_state, old_external_state; NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection)); g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); @@ -214,7 +267,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, /* Update active connection base class state */ nm_active_connection_set_state (NM_ACTIVE_CONNECTION (connection), - ac_state_from_vpn_state (vpn_state)); + _state_to_ac_state (vpn_state)); /* Clear any in-progress secrets request */ if (priv->secrets_id) { @@ -228,18 +281,25 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, */ g_object_ref (connection); - g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, vpn_state, reason); - g_signal_emit (connection, signals[INTERNAL_STATE_CHANGED], 0, vpn_state, old_vpn_state, reason); - g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE); + old_external_state = _state_to_nm_vpn_state (old_vpn_state); + new_external_state = _state_to_nm_vpn_state (priv->vpn_state); + if (new_external_state != old_external_state) { + g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, new_external_state, reason); + g_signal_emit (connection, signals[INTERNAL_STATE_CHANGED], 0, + new_external_state, + old_external_state, + reason); + g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE); + } switch (vpn_state) { - case NM_VPN_CONNECTION_STATE_NEED_AUTH: + case STATE_NEED_AUTH: /* Do nothing; not part of 'default' because we don't want to touch * priv->secrets_req as NEED_AUTH is re-entered during interactive * secrets. */ break; - case NM_VPN_CONNECTION_STATE_ACTIVATED: + case STATE_ACTIVATED: /* Secrets no longer needed now that we're connected */ nm_connection_clear_secrets (priv->connection); @@ -253,9 +313,10 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, NULL, NULL); break; - case NM_VPN_CONNECTION_STATE_FAILED: - case NM_VPN_CONNECTION_STATE_DISCONNECTED: - if (old_vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED) { + case STATE_FAILED: + case STATE_DISCONNECTED: + if ( old_vpn_state >= STATE_ACTIVATED + && old_vpn_state <= STATE_DEACTIVATING) { /* Let dispatcher scripts know we're about to go down */ nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_DOWN, priv->connection, @@ -288,14 +349,18 @@ device_state_changed (NMActiveConnection *active, NMDeviceState old_state) { if (new_state <= NM_DEVICE_STATE_DISCONNECTED) { - nm_vpn_connection_set_vpn_state (NM_VPN_CONNECTION (active), - NM_VPN_CONNECTION_STATE_DISCONNECTED, - NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); + _set_vpn_state (NM_VPN_CONNECTION (active), + STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); } else if (new_state == NM_DEVICE_STATE_FAILED) { - nm_vpn_connection_set_vpn_state (NM_VPN_CONNECTION (active), - NM_VPN_CONNECTION_STATE_FAILED, - NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); + _set_vpn_state (NM_VPN_CONNECTION (active), + STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); } + + /* FIXME: map device DEACTIVATING state to VPN DEACTIVATING state and + * block device deactivation on VPN deactivation. + */ } static void @@ -486,27 +551,24 @@ vpn_service_state_to_string (NMVPNServiceState state) return "unknown"; } +static const char *state_table[] = { + [STATE_UNKNOWN] = "unknown", + [STATE_WAITING] = "waiting", + [STATE_PREPARE] = "prepare", + [STATE_NEED_AUTH] = "need-auth", + [STATE_CONNECT] = "connect", + [STATE_IP_CONFIG_GET] = "ip-config-get", + [STATE_ACTIVATED] = "activated", + [STATE_DEACTIVATING] = "deactivating", + [STATE_DISCONNECTED] = "disconnected", + [STATE_FAILED] = "failed", +}; + static const char * -vpn_state_to_string (NMVPNConnectionState state) +vpn_state_to_string (VpnState state) { - switch (state) { - case NM_VPN_CONNECTION_STATE_PREPARE: - return "prepare"; - case NM_VPN_CONNECTION_STATE_NEED_AUTH: - return "need-auth"; - case NM_VPN_CONNECTION_STATE_CONNECT: - return "connect"; - case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: - return "ip-config-get"; - case NM_VPN_CONNECTION_STATE_ACTIVATED: - return "activated"; - case NM_VPN_CONNECTION_STATE_FAILED: - return "failed"; - case NM_VPN_CONNECTION_STATE_DISCONNECTED: - return "disconnected"; - default: - break; - } + if (state >= 0 && state < G_N_ELEMENTS (state_table)) + return state_table[state]; return "unknown"; } @@ -559,17 +621,16 @@ plugin_state_changed (DBusGProxy *proxy, */ nm_connection_clear_secrets (priv->connection); - switch (nm_vpn_connection_get_vpn_state (connection)) { - case NM_VPN_CONNECTION_STATE_PREPARE: - case NM_VPN_CONNECTION_STATE_NEED_AUTH: - case NM_VPN_CONNECTION_STATE_CONNECT: - case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: - case NM_VPN_CONNECTION_STATE_ACTIVATED: + switch (priv->vpn_state) { + case STATE_WAITING: + case STATE_PREPARE: + case STATE_NEED_AUTH: + case STATE_CONNECT: + case STATE_IP_CONFIG_GET: + case STATE_ACTIVATED: nm_log_info (LOGD_VPN, "VPN plugin state change reason: %s (%d)", vpn_reason_to_string (priv->failure_reason), priv->failure_reason); - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_FAILED, - priv->failure_reason); + _set_vpn_state (connection, STATE_FAILED, priv->failure_reason); /* Reset the failure reason */ priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN; @@ -740,9 +801,7 @@ nm_vpn_connection_apply_config (NMVPNConnection *connection) nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.", nm_connection_get_id (priv->connection)); - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_ACTIVATED, - NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_ACTIVATED, NM_VPN_CONNECTION_STATE_REASON_NONE); return TRUE; } @@ -782,9 +841,7 @@ nm_vpn_connection_config_maybe_complete (NMVPNConnection *connection, nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.", nm_connection_get_id (priv->connection)); - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_FAILED, - NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID); + _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID); } #define LOG_INVALID_ARG(property) \ @@ -878,11 +935,8 @@ nm_vpn_connection_config_get (DBusGProxy *proxy, nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.", nm_connection_get_id (priv->connection)); - if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) { - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_IP_CONFIG_GET, - NM_VPN_CONNECTION_STATE_REASON_NONE); - } + if (priv->vpn_state == STATE_CONNECT) + _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE); if (!process_generic_config (connection, config_hash)) return; @@ -935,11 +989,8 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, GValue *val; int i; - if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) { - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_IP_CONFIG_GET, - NM_VPN_CONNECTION_STATE_REASON_NONE); - } + if (priv->vpn_state == STATE_CONNECT) + _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE); if (priv->has_ip4) { nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received.", @@ -1094,11 +1145,8 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy, nm_log_info (LOGD_VPN, "VPN connection '%s' (IP6 Config Get) reply received.", nm_connection_get_id (priv->connection)); - if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) { - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_IP_CONFIG_GET, - NM_VPN_CONNECTION_STATE_REASON_NONE); - } + if (priv->vpn_state == STATE_CONNECT) + _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE); if (g_hash_table_size (config_hash) == 0) { priv->has_ip6 = FALSE; @@ -1231,19 +1279,15 @@ connect_timeout_cb (gpointer user_data) { NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); - NMVPNConnectionState state; priv->connect_timeout = 0; /* Cancel activation if it's taken too long */ - state = nm_vpn_connection_get_vpn_state (connection); - if (state == NM_VPN_CONNECTION_STATE_CONNECT || - state == NM_VPN_CONNECTION_STATE_IP_CONFIG_GET) { + if (priv->vpn_state == STATE_CONNECT || + priv->vpn_state == STATE_IP_CONFIG_GET) { nm_log_warn (LOGD_VPN, "VPN connection '%s' connect timeout exceeded.", nm_connection_get_id (priv->connection)); - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_FAILED, - NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT); + _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT); } return FALSE; @@ -1280,9 +1324,7 @@ connect_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect: '%s'.", nm_connection_get_id (priv->connection), err->message); g_error_free (err); - nm_vpn_connection_set_vpn_state (self, - NM_VPN_CONNECTION_STATE_FAILED, - NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); } static void @@ -1311,9 +1353,7 @@ connect_interactive_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect interactively: '%s'.", nm_connection_get_id (priv->connection), err->message); g_error_free (err); - nm_vpn_connection_set_vpn_state (self, - NM_VPN_CONNECTION_STATE_FAILED, - NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); } } @@ -1353,9 +1393,9 @@ really_activate (NMVPNConnection *connection, const char *username) GHashTable *details; g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); - g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_NEED_AUTH); priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + g_return_if_fail (priv->vpn_state == STATE_NEED_AUTH); dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID); @@ -1408,9 +1448,7 @@ really_activate (NMVPNConnection *connection, const char *username) g_object_unref (agent_mgr); g_hash_table_destroy (details); - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_CONNECT, - NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE); } void @@ -1420,10 +1458,15 @@ nm_vpn_connection_activate (NMVPNConnection *connection) DBusGConnection *bus; g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); - g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_PREPARE); priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + /* FIXME: remove when VPN activation can be queued */ + if (priv->vpn_state == STATE_WAITING) + _set_vpn_state (connection, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE); + + g_return_if_fail (priv->vpn_state == STATE_PREPARE); + bus = nm_dbus_manager_get_connection (nm_dbus_manager_get ()); priv->proxy = dbus_g_proxy_new_for_name (bus, nm_vpn_connection_get_service (connection), @@ -1448,9 +1491,7 @@ nm_vpn_connection_activate (NMVPNConnection *connection) G_CALLBACK (plugin_interactive_secrets_required), connection, NULL); - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_NEED_AUTH, - NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE); /* Kick off the secrets requests; first we get existing system secrets * and ask the plugin if these are sufficient, next we get all existing @@ -1473,7 +1514,7 @@ nm_vpn_connection_get_vpn_state (NMVPNConnection *connection) { g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NM_VPN_CONNECTION_STATE_UNKNOWN); - return NM_VPN_CONNECTION_GET_PRIVATE (connection)->vpn_state; + return _state_to_nm_vpn_state (NM_VPN_CONNECTION_GET_PRIVATE (connection)->vpn_state); } const char * @@ -1538,9 +1579,7 @@ nm_vpn_connection_fail (NMVPNConnection *connection, { g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_FAILED, - reason); + _set_vpn_state (connection, STATE_FAILED, reason); } void @@ -1549,9 +1588,7 @@ nm_vpn_connection_disconnect (NMVPNConnection *connection, { g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_DISCONNECTED, - reason); + _set_vpn_state (connection, STATE_DISCONNECTED, reason); } /******************************************************************************/ @@ -1747,13 +1784,11 @@ plugin_interactive_secrets_required (DBusGProxy *proxy, nm_log_info (LOGD_VPN, "VPN plugin requested secrets; state %s (%d)", vpn_state_to_string (priv->vpn_state), priv->vpn_state); - g_return_if_fail (priv->vpn_state == NM_VPN_CONNECTION_STATE_CONNECT || - priv->vpn_state == NM_VPN_CONNECTION_STATE_NEED_AUTH); + g_return_if_fail (priv->vpn_state == STATE_CONNECT || + priv->vpn_state == STATE_NEED_AUTH); priv->secrets_idx = SECRETS_REQ_INTERACTIVE; - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_NEED_AUTH, - NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE); /* Copy hints and add message to the end */ hints = g_malloc0 (sizeof (char *) * (secrets_len + 2)); @@ -1773,7 +1808,7 @@ nm_vpn_connection_init (NMVPNConnection *self) { NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - priv->vpn_state = NM_VPN_CONNECTION_STATE_PREPARE; + priv->vpn_state = STATE_WAITING; priv->secrets_idx = SECRETS_REQ_SYSTEM; } @@ -1840,19 +1875,19 @@ get_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_VPN_STATE: - g_value_set_uint (value, priv->vpn_state); + g_value_set_uint (value, _state_to_nm_vpn_state (priv->vpn_state)); break; case PROP_BANNER: g_value_set_string (value, priv->banner ? priv->banner : ""); break; case PROP_IP4_CONFIG: - if (priv->vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED && priv->ip4_config) + if (priv->vpn_state == STATE_ACTIVATED && priv->ip4_config) g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config)); else g_value_set_boxed (value, "/"); break; case PROP_IP6_CONFIG: - if (priv->vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED && priv->ip6_config) + if (priv->vpn_state == STATE_ACTIVATED && priv->ip6_config) g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config)); else g_value_set_boxed (value, "/"); From 05969395651024173f165f31da2b84468af1273e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 16 May 2014 10:51:19 -0500 Subject: [PATCH 09/43] vpn: implement placeholder DEACTIVATING state --- src/vpn-manager/nm-vpn-connection.c | 52 +++++++++++++++++++++++++++++ src/vpn-manager/nm-vpn-connection.h | 4 +++ src/vpn-manager/nm-vpn-manager.c | 27 +-------------- 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 770b064f3..dcd45817a 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -84,7 +84,9 @@ typedef struct { char *username; VpnState vpn_state; + guint deactivating_idle_id; NMVPNConnectionStateReason failure_reason; + DBusGProxy *proxy; GHashTable *connect_hash; guint connect_timeout; @@ -133,6 +135,10 @@ static void plugin_interactive_secrets_required (DBusGProxy *proxy, const char **secrets, gpointer user_data); +static void _set_vpn_state (NMVPNConnection *connection, + VpnState vpn_state, + NMVPNConnectionStateReason reason); + /*********************************************************************/ static NMVPNConnectionState @@ -239,6 +245,28 @@ vpn_cleanup (NMVPNConnection *connection, NMDevice *parent_dev) nm_connection_clear_secrets (priv->connection); } +static gboolean +deactivating_to_disconnected (gpointer user_data) +{ + NMVPNConnection *self = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + priv->deactivating_idle_id = 0; + _set_vpn_state (self, STATE_DISCONNECTED, NM_VPN_CONNECTION_STATE_REASON_NONE); + return G_SOURCE_REMOVE; +} + +static void +clear_deactivating_idle (NMVPNConnection *self) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + if (priv->deactivating_idle_id) { + g_source_remove (priv->deactivating_idle_id); + priv->deactivating_idle_id = 0; + } +} + static void _set_vpn_state (NMVPNConnection *connection, VpnState vpn_state, @@ -275,6 +303,8 @@ _set_vpn_state (NMVPNConnection *connection, priv->secrets_id = 0; } + clear_deactivating_idle (connection); + /* The connection gets destroyed by the VPN manager when it enters the * disconnected/failed state, but we need to keep it around for a bit * to send out signals and handle the dispatcher. So ref it. @@ -313,6 +343,9 @@ _set_vpn_state (NMVPNConnection *connection, NULL, NULL); break; + case STATE_DEACTIVATING: + priv->deactivating_idle_id = g_idle_add (deactivating_to_disconnected, connection); + break; case STATE_FAILED: case STATE_DISCONNECTED: if ( old_vpn_state >= STATE_ACTIVATED @@ -1591,6 +1624,23 @@ nm_vpn_connection_disconnect (NMVPNConnection *connection, _set_vpn_state (connection, STATE_DISCONNECTED, reason); } +gboolean +nm_vpn_connection_deactivate (NMVPNConnection *connection, + NMVPNConnectionStateReason reason) +{ + NMVPNConnectionPrivate *priv; + gboolean success = FALSE; + + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), FALSE); + + priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + if (priv->vpn_state > STATE_UNKNOWN && priv->vpn_state <= STATE_DEACTIVATING) { + _set_vpn_state (connection, STATE_DEACTIVATING, reason); + success = TRUE; + } + return success; +} + /******************************************************************************/ static void @@ -1838,6 +1888,8 @@ dispose (GObject *object) priv->connect_timeout = 0; } + clear_deactivating_idle (NM_VPN_CONNECTION (object)); + if (priv->secrets_id) { nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection), priv->secrets_id); diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h index b98dcbe62..afa06e9c9 100644 --- a/src/vpn-manager/nm-vpn-connection.h +++ b/src/vpn-manager/nm-vpn-connection.h @@ -74,10 +74,14 @@ void nm_vpn_connection_activate (NMVPNConnection *connect NMConnection * nm_vpn_connection_get_connection (NMVPNConnection *connection); NMVPNConnectionState nm_vpn_connection_get_vpn_state (NMVPNConnection *connection); const char * nm_vpn_connection_get_banner (NMVPNConnection *connection); + +gboolean nm_vpn_connection_deactivate (NMVPNConnection *connection, + NMVPNConnectionStateReason reason); void nm_vpn_connection_fail (NMVPNConnection *connection, NMVPNConnectionStateReason reason); void nm_vpn_connection_disconnect (NMVPNConnection *connection, NMVPNConnectionStateReason reason); + NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connection); NMIP6Config * nm_vpn_connection_get_ip6_config (NMVPNConnection *connection); const char * nm_vpn_connection_get_ip_iface (NMVPNConnection *connection); diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index 7fa753a39..bee31dbc2 100644 --- a/src/vpn-manager/nm-vpn-manager.c +++ b/src/vpn-manager/nm-vpn-manager.c @@ -156,32 +156,7 @@ nm_vpn_manager_deactivate_connection (NMVPNManager *self, NMVPNConnection *connection, NMVPNConnectionStateReason reason) { - NMVPNManagerPrivate *priv; - GHashTableIter iter; - gpointer data; - const GSList *active, *aiter; - gboolean success = FALSE; - - g_return_val_if_fail (self, FALSE); - g_return_val_if_fail (NM_IS_VPN_MANAGER (self), FALSE); - g_return_val_if_fail (connection != NULL, FALSE); - - priv = NM_VPN_MANAGER_GET_PRIVATE (self); - g_hash_table_iter_init (&iter, priv->services); - while (g_hash_table_iter_next (&iter, NULL, &data) && (success == FALSE)) { - active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data)); - for (aiter = active; aiter; aiter = g_slist_next (aiter)) { - NMVPNConnection *candidate = aiter->data; - - if (connection == candidate) { - nm_vpn_connection_disconnect (connection, reason); - success = TRUE; - break; - } - } - } - - return success; + return nm_vpn_connection_deactivate (connection, reason); } static void From f3c67726dc13c0f0b4d9090b8d89b9a1f3fff396 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 16 May 2014 11:28:58 -0500 Subject: [PATCH 10/43] vpn: simplify VPN connection lookup --- src/vpn-manager/nm-vpn-manager.c | 16 +++------------- src/vpn-manager/nm-vpn-service.c | 12 ++++++++---- src/vpn-manager/nm-vpn-service.h | 3 ++- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index bee31dbc2..066cfc0ea 100644 --- a/src/vpn-manager/nm-vpn-manager.c +++ b/src/vpn-manager/nm-vpn-manager.c @@ -80,25 +80,15 @@ find_active_vpn_connection (NMVPNManager *self, NMConnection *connection) { NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self); GHashTableIter iter; - gpointer data; - const GSList *active, *aiter; + NMVPNService *service; NMVPNConnection *found = NULL; g_return_val_if_fail (connection, NULL); g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); g_hash_table_iter_init (&iter, priv->services); - while (g_hash_table_iter_next (&iter, NULL, &data) && (found == NULL)) { - active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data)); - for (aiter = active; aiter; aiter = g_slist_next (aiter)) { - NMVPNConnection *vpn = NM_VPN_CONNECTION (aiter->data); - - if (nm_vpn_connection_get_connection (vpn) == connection) { - found = vpn; - break; - } - } - } + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service) && !found) + found = nm_vpn_service_get_vpn_for_connection (service, connection); return found; } diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index 43d9cf335..3af65fc2b 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -284,12 +284,16 @@ nm_vpn_service_activate (NMVPNService *service, return TRUE; } -const GSList * -nm_vpn_service_get_active_connections (NMVPNService *service) +NMVPNConnection * +nm_vpn_service_get_vpn_for_connection (NMVPNService *service, NMConnection *connection) { - g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL); + GSList *iter; - return NM_VPN_SERVICE_GET_PRIVATE (service)->connections; + for (iter = NM_VPN_SERVICE_GET_PRIVATE (service)->connections; iter; iter = iter->next) { + if (nm_vpn_connection_get_connection (NM_VPN_CONNECTION (iter->data)) == connection) + return NM_VPN_CONNECTION (iter->data); + } + return NULL; } static void diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h index cfb199730..9f8228fc0 100644 --- a/src/vpn-manager/nm-vpn-service.h +++ b/src/vpn-manager/nm-vpn-service.h @@ -56,7 +56,8 @@ gboolean nm_vpn_service_activate (NMVPNService *service, NMVPNConnection *vpn, GError **error); -const GSList *nm_vpn_service_get_active_connections (NMVPNService *service); +NMVPNConnection *nm_vpn_service_get_vpn_for_connection (NMVPNService *service, + NMConnection *connection); void nm_vpn_service_connections_stop (NMVPNService *service, gboolean fail, From b6558ecf47b5eca09ef19f00010986d1b8ab2e95 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 16 May 2014 11:46:58 -0500 Subject: [PATCH 11/43] vpn: consolidate nm_vpn_connection_fail() and nm_vpn_connection_disconnect() --- src/vpn-manager/nm-vpn-connection.c | 24 ++++++++---------------- src/vpn-manager/nm-vpn-connection.h | 5 ++--- src/vpn-manager/nm-vpn-manager.c | 2 +- src/vpn-manager/nm-vpn-service.c | 5 +---- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index dcd45817a..1f457df1e 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -1607,21 +1607,13 @@ nm_vpn_connection_get_ip6_internal_gateway (NMVPNConnection *connection) } void -nm_vpn_connection_fail (NMVPNConnection *connection, +nm_vpn_connection_stop (NMVPNConnection *connection, + gboolean fail, NMVPNConnectionStateReason reason) { g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); - _set_vpn_state (connection, STATE_FAILED, reason); -} - -void -nm_vpn_connection_disconnect (NMVPNConnection *connection, - NMVPNConnectionStateReason reason) -{ - g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); - - _set_vpn_state (connection, STATE_DISCONNECTED, reason); + _set_vpn_state (connection, fail ? STATE_FAILED : STATE_DISCONNECTED, reason); } gboolean @@ -1661,7 +1653,7 @@ plugin_need_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_dat priv->secrets_idx + 1, g_quark_to_string (error->domain), error->message); - nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); g_error_free (error); return; } @@ -1673,7 +1665,7 @@ plugin_need_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_dat nm_log_err (LOGD_VPN, "(%s/%s) final secrets request failed to provide sufficient secrets", nm_connection_get_uuid (priv->connection), nm_connection_get_id (priv->connection)); - nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); } else { nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated additional secrets required", nm_connection_get_uuid (priv->connection), @@ -1705,7 +1697,7 @@ plugin_new_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data nm_connection_get_id (priv->connection), g_quark_to_string (error->domain), error->message); - nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); g_error_free (error); } } @@ -1730,7 +1722,7 @@ get_secrets_cb (NMSettingsConnection *connection, if (error) { nm_log_err (LOGD_VPN, "Failed to request VPN secrets #%d: (%d) %s", priv->secrets_idx + 1, error->code, error->message); - nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); } else { /* Cache the username for later */ if (agent_username) { @@ -1814,7 +1806,7 @@ get_secrets (NMVPNConnection *self, nm_log_err (LOGD_VPN, "failed to request VPN secrets #%d: (%d) %s", priv->secrets_idx + 1, error->code, error->message); } - nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); g_clear_error (&error); } } diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h index afa06e9c9..e5563b000 100644 --- a/src/vpn-manager/nm-vpn-connection.h +++ b/src/vpn-manager/nm-vpn-connection.h @@ -77,9 +77,8 @@ const char * nm_vpn_connection_get_banner (NMVPNConnection *connect gboolean nm_vpn_connection_deactivate (NMVPNConnection *connection, NMVPNConnectionStateReason reason); -void nm_vpn_connection_fail (NMVPNConnection *connection, - NMVPNConnectionStateReason reason); -void nm_vpn_connection_disconnect (NMVPNConnection *connection, +void nm_vpn_connection_stop (NMVPNConnection *connection, + gboolean fail, NMVPNConnectionStateReason reason); NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connection); diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index 066cfc0ea..5b53931ad 100644 --- a/src/vpn-manager/nm-vpn-manager.c +++ b/src/vpn-manager/nm-vpn-manager.c @@ -136,7 +136,7 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager, existing = find_active_vpn_connection (manager, nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (vpn))); if (existing) - nm_vpn_connection_disconnect (vpn, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); + nm_vpn_connection_stop (vpn, FALSE, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); return nm_vpn_service_activate (service, vpn, error); } diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index 3af65fc2b..d175a9a7d 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -140,10 +140,7 @@ nm_vpn_service_connections_stop (NMVPNService *service, NMVPNConnection *vpn = NM_VPN_CONNECTION (iter->data); g_signal_handlers_disconnect_by_func (vpn, G_CALLBACK (connection_vpn_state_changed), service); - if (fail) - nm_vpn_connection_fail (vpn, reason); - else - nm_vpn_connection_disconnect (vpn, reason); + nm_vpn_connection_stop (vpn, fail, reason); g_object_unref (vpn); } g_clear_pointer (&priv->connections, g_slist_free); From e957a25db36d4baae2fd3919becb765a329c04ca Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 16 May 2014 13:02:49 -0500 Subject: [PATCH 12/43] vpn: queue additional VPN connections If a VPN connection is already active, tell it to deactivate and queue the new VPN connection for activation when the first one is disconnected. --- src/vpn-manager/nm-vpn-connection.c | 6 +- src/vpn-manager/nm-vpn-manager.c | 23 ------ src/vpn-manager/nm-vpn-service.c | 113 +++++++++++++++++++--------- src/vpn-manager/nm-vpn-service.h | 3 - 4 files changed, 79 insertions(+), 66 deletions(-) diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 1f457df1e..c6410d780 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -1494,11 +1494,7 @@ nm_vpn_connection_activate (NMVPNConnection *connection) priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); - /* FIXME: remove when VPN activation can be queued */ - if (priv->vpn_state == STATE_WAITING) - _set_vpn_state (connection, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE); - - g_return_if_fail (priv->vpn_state == STATE_PREPARE); + _set_vpn_state (connection, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE); bus = nm_dbus_manager_get_connection (nm_dbus_manager_get ()); priv->proxy = dbus_g_proxy_new_for_name (bus, diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index 5b53931ad..276dd0365 100644 --- a/src/vpn-manager/nm-vpn-manager.c +++ b/src/vpn-manager/nm-vpn-manager.c @@ -75,29 +75,11 @@ get_service_by_namefile (NMVPNManager *self, const char *namefile) return NULL; } -static NMVPNConnection * -find_active_vpn_connection (NMVPNManager *self, NMConnection *connection) -{ - NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self); - GHashTableIter iter; - NMVPNService *service; - NMVPNConnection *found = NULL; - - g_return_val_if_fail (connection, NULL); - g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - - g_hash_table_iter_init (&iter, priv->services); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service) && !found) - found = nm_vpn_service_get_vpn_for_connection (service, connection); - return found; -} - gboolean nm_vpn_manager_activate_connection (NMVPNManager *manager, NMVPNConnection *vpn, GError **error) { - NMVPNConnection *existing = NULL; NMConnection *connection; NMSettingVPN *s_vpn; NMVPNService *service; @@ -133,11 +115,6 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager, return FALSE; } - existing = find_active_vpn_connection (manager, - nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (vpn))); - if (existing) - nm_vpn_connection_stop (vpn, FALSE, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); - return nm_vpn_service_activate (service, vpn, error); } diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index d175a9a7d..e38309bb6 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -43,8 +43,10 @@ typedef struct { char *program; char *namefile; + NMVPNConnection *active; + GSList *pending; + GPid pid; - GSList *connections; guint start_timeout; guint child_watch; gulong name_owner_id; @@ -54,6 +56,8 @@ typedef struct { #define VPN_CONNECTION_GROUP "VPN Connection" +static gboolean start_pending_vpn (NMVPNService *self); + NMVPNService * nm_vpn_service_new (const char *namefile, GError **error) { @@ -118,12 +122,17 @@ connection_vpn_state_changed (NMVPNConnection *connection, NMVPNConnectionStateReason reason, gpointer user_data) { - NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data); + NMVPNService *self = NM_VPN_SERVICE (user_data); + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); if (new_state == NM_VPN_CONNECTION_STATE_FAILED || new_state == NM_VPN_CONNECTION_STATE_DISCONNECTED) { - /* Remove the connection from our list */ - priv->connections = g_slist_remove (priv->connections, connection); + g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_vpn_state_changed), self); + if (connection == priv->active) { + priv->active = NULL; + start_pending_vpn (self); + } else + priv->pending = g_slist_remove (priv->pending, connection); g_object_unref (connection); } } @@ -136,14 +145,22 @@ nm_vpn_service_connections_stop (NMVPNService *service, NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); GSList *iter; - for (iter = priv->connections; iter; iter = iter->next) { + /* Just add priv->active to the beginning of priv->pending, + * since we're going to clear priv->pending immediately anyway. + */ + if (priv->active) { + priv->pending = g_slist_prepend (priv->pending, priv->active); + priv->active = NULL; + } + + for (iter = priv->pending; iter; iter = iter->next) { NMVPNConnection *vpn = NM_VPN_CONNECTION (iter->data); g_signal_handlers_disconnect_by_func (vpn, G_CALLBACK (connection_vpn_state_changed), service); nm_vpn_connection_stop (vpn, fail, reason); g_object_unref (vpn); } - g_clear_pointer (&priv->connections, g_slist_free); + g_clear_pointer (&priv->pending, g_slist_free); } /* @@ -217,8 +234,6 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error) GError *spawn_error = NULL; g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE); - g_return_val_if_fail (error != NULL, FALSE); - g_return_val_if_fail (*error == NULL, FALSE); vpn_argv[0] = priv->program; vpn_argv[1] = NULL; @@ -250,6 +265,45 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error) return success; } +static gboolean +start_active_vpn (NMVPNService *self, GError **error) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); + + if (!priv->active) + return TRUE; + + if (nm_dbus_manager_name_has_owner (nm_dbus_manager_get (), priv->dbus_service)) { + /* Just activate the VPN */ + nm_vpn_connection_activate (priv->active); + return TRUE; + } else if (priv->start_timeout == 0) { + /* VPN service not running, start it */ + nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name); + return nm_vpn_service_daemon_exec (self, error); + } + + /* Already started VPN service, waiting for it to appear on D-Bus */ + return TRUE; +} + +static gboolean +start_pending_vpn (NMVPNService *self) +{ + NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); + + g_assert (priv->active == NULL); + + if (!priv->pending) + return TRUE; + + /* Make next VPN active */ + priv->active = g_slist_nth_data (priv->pending, 0); + priv->pending = g_slist_remove (priv->pending, priv->active); + + return start_active_vpn (self, NULL); +} + gboolean nm_vpn_service_activate (NMVPNService *service, NMVPNConnection *vpn, @@ -268,29 +322,20 @@ nm_vpn_service_activate (NMVPNService *service, G_CALLBACK (connection_vpn_state_changed), service); - priv->connections = g_slist_prepend (priv->connections, g_object_ref (vpn)); + /* Queue up the new VPN connection */ + priv->pending = g_slist_append (priv->pending, g_object_ref (vpn)); - if (nm_dbus_manager_name_has_owner (nm_dbus_manager_get (), priv->dbus_service)) - nm_vpn_connection_activate (vpn); - else if (priv->start_timeout == 0) { - nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name); - if (!nm_vpn_service_daemon_exec (service, error)) - return FALSE; + /* Tell the active VPN to deactivate and wait for it to quit before we + * start the next VPN. The just-queued VPN will then be started from + * connection_vpn_state_changed(). + */ + if (priv->active) { + nm_vpn_connection_deactivate (priv->active, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); + return TRUE; } - return TRUE; -} - -NMVPNConnection * -nm_vpn_service_get_vpn_for_connection (NMVPNService *service, NMConnection *connection) -{ - GSList *iter; - - for (iter = NM_VPN_SERVICE_GET_PRIVATE (service)->connections; iter; iter = iter->next) { - if (nm_vpn_connection_get_connection (NM_VPN_CONNECTION (iter->data)) == connection) - return NM_VPN_CONNECTION (iter->data); - } - return NULL; + /* Otherwise start the next VPN */ + return start_pending_vpn (service); } static void @@ -302,9 +347,7 @@ _name_owner_changed (NMDBusManager *mgr, { NMVPNService *service = NM_VPN_SERVICE (user_data); NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); - gboolean old_owner_good; - gboolean new_owner_good; - GSList *iter; + gboolean old_owner_good, new_owner_good, success; if (strcmp (name, priv->dbus_service)) return; @@ -319,11 +362,11 @@ _name_owner_changed (NMDBusManager *mgr, new_owner_good = (new && new[0]); if (!old_owner_good && new_owner_good) { - /* service just appeared */ + /* service appeared */ nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", priv->name); - - for (iter = priv->connections; iter; iter = iter->next) - nm_vpn_connection_activate (NM_VPN_CONNECTION (iter->data)); + /* Expect success because the VPN service has already appeared */ + success = start_active_vpn (service, NULL); + g_warn_if_fail (success); } else if (old_owner_good && !new_owner_good) { /* service went away */ nm_log_info (LOGD_VPN, "VPN service '%s' disappeared", priv->name); diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h index 9f8228fc0..1edde7b52 100644 --- a/src/vpn-manager/nm-vpn-service.h +++ b/src/vpn-manager/nm-vpn-service.h @@ -56,9 +56,6 @@ gboolean nm_vpn_service_activate (NMVPNService *service, NMVPNConnection *vpn, GError **error); -NMVPNConnection *nm_vpn_service_get_vpn_for_connection (NMVPNService *service, - NMConnection *connection); - void nm_vpn_service_connections_stop (NMVPNService *service, gboolean fail, NMVPNConnectionStateReason reason); From fbb38ebefe54d63adecd7622c8a068713e3ecd06 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 19 May 2014 17:44:22 -0500 Subject: [PATCH 13/43] vpn: remove pointless child watch on VPN service daemons D-Bus already watches the life-cycle, and we'll get a NameOwnerChanged signal when the VPN service daemon quit. So the GLib child watch is just duplicated code that we don't need. Remove it. --- src/vpn-manager/nm-vpn-service.c | 85 ++++++++------------------------ 1 file changed, 20 insertions(+), 65 deletions(-) diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index e38309bb6..ac329422f 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -46,10 +46,8 @@ typedef struct { NMVPNConnection *active; GSList *pending; - GPid pid; guint start_timeout; - guint child_watch; - gulong name_owner_id; + gboolean service_running; } NMVPNServicePrivate; #define NM_VPN_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE, NMVPNServicePrivate)) @@ -90,6 +88,8 @@ nm_vpn_service_new (const char *namefile, GError **error) if (!priv->name) goto error; + priv->service_running = nm_dbus_manager_name_has_owner (nm_dbus_manager_get (), priv->dbus_service); + g_key_file_free (kf); return self; @@ -163,14 +163,8 @@ nm_vpn_service_connections_stop (NMVPNService *service, g_clear_pointer (&priv->pending, g_slist_free); } -/* - * nm_vpn_service_child_setup - * - * Set the process group ID of the newly forked process - * - */ static void -nm_vpn_service_child_setup (gpointer user_data G_GNUC_UNUSED) +_daemon_setup (gpointer user_data G_GNUC_UNUSED) { /* We are in the child process at this point */ pid_t pid = getpid (); @@ -183,38 +177,8 @@ nm_vpn_service_child_setup (gpointer user_data G_GNUC_UNUSED) nm_unblock_posix_signals (NULL); } -static void -vpn_service_watch_cb (GPid pid, gint status, gpointer user_data) -{ - NMVPNService *service = NM_VPN_SERVICE (user_data); - NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); - - if (WIFEXITED (status)) { - guint err = WEXITSTATUS (status); - - if (err != 0) { - nm_log_warn (LOGD_VPN, "VPN service '%s' exited with error: %d", - priv->name, WSTOPSIG (status)); - } - } else if (WIFSTOPPED (status)) { - nm_log_warn (LOGD_VPN, "VPN service '%s' stopped unexpectedly with signal %d", - priv->name, WSTOPSIG (status)); - } else if (WIFSIGNALED (status)) { - nm_log_warn (LOGD_VPN, "VPN service '%s' died with signal %d", - priv->name, WTERMSIG (status)); - } else { - nm_log_warn (LOGD_VPN, "VPN service '%s' died from an unknown cause", - priv->name); - } - - priv->pid = 0; - priv->child_watch = 0; - - nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); -} - static gboolean -nm_vpn_service_timeout (gpointer data) +_daemon_exec_timeout (gpointer data) { NMVPNService *self = NM_VPN_SERVICE (data); NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); @@ -222,13 +186,14 @@ nm_vpn_service_timeout (gpointer data) nm_log_warn (LOGD_VPN, "VPN service '%s' start timed out", priv->name); priv->start_timeout = 0; nm_vpn_service_connections_stop (self, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT); - return FALSE; + return G_SOURCE_REMOVE; } static gboolean nm_vpn_service_daemon_exec (NMVPNService *service, GError **error) { NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); + GPid pid; char *vpn_argv[2]; gboolean success = FALSE; GError *spawn_error = NULL; @@ -238,15 +203,11 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error) vpn_argv[0] = priv->program; vpn_argv[1] = NULL; - success = g_spawn_async (NULL, vpn_argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, - nm_vpn_service_child_setup, NULL, &priv->pid, - &spawn_error); + success = g_spawn_async (NULL, vpn_argv, NULL, 0, _daemon_setup, NULL, &pid, &spawn_error); if (success) { nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %d", - priv->name, priv->dbus_service, priv->pid); - - priv->child_watch = g_child_watch_add (priv->pid, vpn_service_watch_cb, service); - priv->start_timeout = g_timeout_add_seconds (5, nm_vpn_service_timeout, service); + priv->name, priv->dbus_service, pid); + priv->start_timeout = g_timeout_add_seconds (5, _daemon_exec_timeout, service); } else { nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.", priv->name, @@ -273,7 +234,7 @@ start_active_vpn (NMVPNService *self, GError **error) if (!priv->active) return TRUE; - if (nm_dbus_manager_name_has_owner (nm_dbus_manager_get (), priv->dbus_service)) { + if (priv->service_running) { /* Just activate the VPN */ nm_vpn_connection_activate (priv->active); return TRUE; @@ -363,12 +324,14 @@ _name_owner_changed (NMDBusManager *mgr, if (!old_owner_good && new_owner_good) { /* service appeared */ + priv->service_running = TRUE; nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", priv->name); /* Expect success because the VPN service has already appeared */ success = start_active_vpn (service, NULL); g_warn_if_fail (success); } else if (old_owner_good && !new_owner_good) { /* service went away */ + priv->service_running = FALSE; nm_log_info (LOGD_VPN, "VPN service '%s' disappeared", priv->name); nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); } @@ -379,12 +342,10 @@ _name_owner_changed (NMDBusManager *mgr, static void nm_vpn_service_init (NMVPNService *self) { - NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self); - - priv->name_owner_id = g_signal_connect (nm_dbus_manager_get (), - NM_DBUS_MANAGER_NAME_OWNER_CHANGED, - G_CALLBACK (_name_owner_changed), - self); + g_signal_connect (nm_dbus_manager_get (), + NM_DBUS_MANAGER_NAME_OWNER_CHANGED, + G_CALLBACK (_name_owner_changed), + self); } static void @@ -402,15 +363,9 @@ dispose (GObject *object) FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); - if (priv->name_owner_id) { - g_signal_handler_disconnect (nm_dbus_manager_get (), priv->name_owner_id); - priv->name_owner_id = 0; - } - - if (priv->child_watch) { - g_source_remove (priv->child_watch); - priv->child_watch = 0; - } + g_signal_handlers_disconnect_by_func (nm_dbus_manager_get (), + G_CALLBACK (_name_owner_changed), + self); G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object); } From d1095e00cbc132dc4795eda73de1f87462b6b131 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 19 May 2014 18:38:04 -0500 Subject: [PATCH 14/43] vpn: stop all connections of a service outside of dispose Future patches will allow blocking dispatcher calls, which we don't want to happen when deactivating a VPN connection during normal operation. So move code that stops VPN connections outside of the VPNService object's dispose() function and require the object that owns the VPNService (the VPNManager) to stop connections at the right times. When quitting, blocking calls are acceptable (because NetworkManager's D-Bus interface is no longer useful, plus we can't easily schedule callbacks because no mainloop is running), so it's ok to stop connections from NMVPNManager:dispose. --- src/vpn-manager/nm-vpn-manager.c | 18 +++++++++++++++++- src/vpn-manager/nm-vpn-service.c | 19 ++++++++++--------- src/vpn-manager/nm-vpn-service.h | 4 ++-- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index 276dd0365..00ce4e8f5 100644 --- a/src/vpn-manager/nm-vpn-manager.c +++ b/src/vpn-manager/nm-vpn-manager.c @@ -186,7 +186,7 @@ vpn_dir_changed (GFileMonitor *monitor, const char *service_name = nm_vpn_service_get_dbus_service (service); /* Stop active VPN connections and destroy the service */ - nm_vpn_service_connections_stop (service, TRUE, + nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); nm_log_info (LOGD_VPN, "VPN: unloaded %s", service_name); g_hash_table_remove (priv->services, service_name); @@ -257,6 +257,21 @@ nm_vpn_manager_init (NMVPNManager *self) } } +static void +stop_all_services (NMVPNManager *self) +{ + NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self); + GHashTableIter iter; + NMVPNService *service; + + g_hash_table_iter_init (&iter, priv->services); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service)) { + nm_vpn_service_stop_connections (service, + TRUE, + NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); + } +} + static void dispose (GObject *object) { @@ -270,6 +285,7 @@ dispose (GObject *object) } if (priv->services) { + stop_all_services (NM_VPN_MANAGER (object)); g_hash_table_destroy (priv->services); priv->services = NULL; } diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index ac329422f..3752797fb 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -138,8 +138,8 @@ connection_vpn_state_changed (NMVPNConnection *connection, } void -nm_vpn_service_connections_stop (NMVPNService *service, - gboolean fail, +nm_vpn_service_stop_connections (NMVPNService *service, + gboolean quitting, NMVPNConnectionStateReason reason) { NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service); @@ -157,7 +157,8 @@ nm_vpn_service_connections_stop (NMVPNService *service, NMVPNConnection *vpn = NM_VPN_CONNECTION (iter->data); g_signal_handlers_disconnect_by_func (vpn, G_CALLBACK (connection_vpn_state_changed), service); - nm_vpn_connection_stop (vpn, fail, reason); + /* Quitting terminates the VPN cleanly, otherwise failure is assumed */ + nm_vpn_connection_stop (vpn, quitting ? FALSE : TRUE, reason); g_object_unref (vpn); } g_clear_pointer (&priv->pending, g_slist_free); @@ -185,7 +186,7 @@ _daemon_exec_timeout (gpointer data) nm_log_warn (LOGD_VPN, "VPN service '%s' start timed out", priv->name); priv->start_timeout = 0; - nm_vpn_service_connections_stop (self, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT); + nm_vpn_service_stop_connections (self, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT); return G_SOURCE_REMOVE; } @@ -218,7 +219,7 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error) NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED, "%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error"); - nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); + nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); if (spawn_error) g_error_free (spawn_error); } @@ -333,7 +334,7 @@ _name_owner_changed (NMDBusManager *mgr, /* service went away */ priv->service_running = FALSE; nm_log_info (LOGD_VPN, "VPN service '%s' disappeared", priv->name); - nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); + nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); } } @@ -359,9 +360,9 @@ dispose (GObject *object) priv->start_timeout = 0; } - nm_vpn_service_connections_stop (NM_VPN_SERVICE (object), - FALSE, - NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED); + /* VPNService owner is required to stop connections before releasing */ + g_assert (priv->active == NULL); + g_assert (priv->pending == NULL); g_signal_handlers_disconnect_by_func (nm_dbus_manager_get (), G_CALLBACK (_name_owner_changed), diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h index 1edde7b52..4545d1f07 100644 --- a/src/vpn-manager/nm-vpn-service.h +++ b/src/vpn-manager/nm-vpn-service.h @@ -56,8 +56,8 @@ gboolean nm_vpn_service_activate (NMVPNService *service, NMVPNConnection *vpn, GError **error); -void nm_vpn_service_connections_stop (NMVPNService *service, - gboolean fail, +void nm_vpn_service_stop_connections (NMVPNService *service, + gboolean quitting, NMVPNConnectionStateReason reason); #endif /* NM_VPN_VPN_SERVICE_H */ From a2fc57beef2c0d15b12671aa3b429b6d2ddef512 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 14:19:34 -0500 Subject: [PATCH 15/43] trivial: remove redundant check check_if_startup_complete() already checks priv->startup and returns if it's FALSE. This if() is redundant. --- src/nm-manager.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/nm-manager.c b/src/nm-manager.c index 47245b648..d3d6662c4 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -754,8 +754,7 @@ remove_device (NMManager *manager, NMDevice *device, gboolean quitting) nm_dbus_manager_unregister_object (priv->dbus_mgr, device); g_object_unref (device); - if (priv->startup) - check_if_startup_complete (manager); + check_if_startup_complete (manager); } static void From 1c737f230f1cb61a5c997b7ed332064bb4dc2ccf Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 15:03:27 -0500 Subject: [PATCH 16/43] core: re-order NMDevice functions to match current idoms Move the GObject-related stuff to the bottom of the file and get rid of no-longer-necessary forward prototypes since the stuff that required those prototypes is now below the implementation. --- src/devices/nm-device.c | 4154 +++++++++++++++++++-------------------- 1 file changed, 2066 insertions(+), 2088 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 445611ddc..49b31994c 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -78,24 +78,12 @@ static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *con #include "nm-device-glue.h" -#define DBUS_G_TYPE_UINT_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID)) +static void nm_device_config_device_interface_init (NMConfigDeviceInterface *iface); -/* default to installed helper, but can be modified for testing */ -const char *nm_device_autoipd_helper_path = LIBEXECDIR "/nm-avahi-autoipd.action"; +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMDevice, nm_device, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (NM_TYPE_CONFIG_DEVICE, nm_device_config_device_interface_init)) -/***********************************************************/ -#define NM_DEVICE_ERROR (nm_device_error_quark ()) - -static GQuark -nm_device_error_quark (void) -{ - static GQuark quark = 0; - if (!quark) - quark = g_quark_from_static_string ("nm-device-error"); - return quark; -} - -/***********************************************************/ +#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate)) enum { STATE_CHANGED, @@ -146,17 +134,8 @@ enum { LAST_PROP }; -#define DEFAULT_AUTOCONNECT TRUE - /***********************************************************/ -static void nm_device_config_device_interface_init (NMConfigDeviceInterface *iface); - -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMDevice, nm_device, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (NM_TYPE_CONFIG_DEVICE, nm_device_config_device_interface_init)) - -#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate)) - #define PENDING_ACTION_DHCP4 "dhcp4" #define PENDING_ACTION_DHCP6 "dhcp6" #define PENDING_ACTION_AUTOCONF6 "autoconf6" @@ -335,56 +314,198 @@ static gboolean nm_device_set_ip6_config (NMDevice *dev, gboolean commit, NMDeviceStateReason *reason); -static gboolean check_connection_available (NMDevice *device, - NMConnection *connection, - const char *specific_object); - -static gboolean spec_match_list (NMDevice *device, const GSList *specs); - -static void _clear_available_connections (NMDevice *device, gboolean do_signal); - -static void dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release); -static void dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release); - -static const char *reason_to_string (NMDeviceStateReason reason); - -static void ip_check_gw_ping_cleanup (NMDevice *self); - -static void cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data); -static void cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data); -static void cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data); - -static const char *state_to_string (NMDeviceState state); - -static void link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDevice *device); -static void check_carrier (NMDevice *device); - -static void nm_device_queued_ip_config_change_clear (NMDevice *self); -static void update_ip_config (NMDevice *self, gboolean initial); -static void device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformSignalChangeType change_type, NMPlatformReason reason, gpointer user_data); - static void nm_device_slave_notify_enslave (NMDevice *dev, gboolean success); static void nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason); static void addrconf6_start_with_link_ready (NMDevice *self); -static void -nm_device_init (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); +/***********************************************************/ - priv->type = NM_DEVICE_TYPE_UNKNOWN; - priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED; - priv->state = NM_DEVICE_STATE_UNMANAGED; - priv->state_reason = NM_DEVICE_STATE_REASON_NONE; - priv->dhcp_timeout = 0; - priv->rfkill_type = RFKILL_TYPE_UNKNOWN; - priv->autoconnect = DEFAULT_AUTOCONNECT; - priv->unmanaged_flags = NM_UNMANAGED_INTERNAL; - priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); - priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); +static GQuark +nm_device_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-device-error"); + return quark; } +#define NM_DEVICE_ERROR (nm_device_error_quark ()) + +/***********************************************************/ + +#define QUEUED_PREFIX "queued state change to " + +static const char * +queued_state_to_string (NMDeviceState state) +{ + switch (state) { + case NM_DEVICE_STATE_UNMANAGED: + return QUEUED_PREFIX "unmanaged"; + case NM_DEVICE_STATE_UNAVAILABLE: + return QUEUED_PREFIX "unavailable"; + case NM_DEVICE_STATE_DISCONNECTED: + return QUEUED_PREFIX "disconnected"; + case NM_DEVICE_STATE_PREPARE: + return QUEUED_PREFIX "prepare"; + case NM_DEVICE_STATE_CONFIG: + return QUEUED_PREFIX "config"; + case NM_DEVICE_STATE_NEED_AUTH: + return QUEUED_PREFIX "need-auth"; + case NM_DEVICE_STATE_IP_CONFIG: + return QUEUED_PREFIX "ip-config"; + case NM_DEVICE_STATE_IP_CHECK: + return QUEUED_PREFIX "ip-check"; + case NM_DEVICE_STATE_SECONDARIES: + return QUEUED_PREFIX "secondaries"; + case NM_DEVICE_STATE_ACTIVATED: + return QUEUED_PREFIX "activated"; + case NM_DEVICE_STATE_DEACTIVATING: + return QUEUED_PREFIX "deactivating"; + case NM_DEVICE_STATE_FAILED: + return QUEUED_PREFIX "failed"; + default: + break; + } + return QUEUED_PREFIX "unknown"; +} + +static const char * +state_to_string (NMDeviceState state) +{ + return queued_state_to_string (state) + strlen (QUEUED_PREFIX); +} + +static const char * +reason_to_string (NMDeviceStateReason reason) +{ + switch (reason) { + case NM_DEVICE_STATE_REASON_NONE: + return "none"; + case NM_DEVICE_STATE_REASON_NOW_MANAGED: + return "managed"; + case NM_DEVICE_STATE_REASON_NOW_UNMANAGED: + return "unmanaged"; + case NM_DEVICE_STATE_REASON_CONFIG_FAILED: + return "config-failed"; + case NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE: + return "ip-config-unavailable"; + case NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED: + return "ip-config-expired"; + case NM_DEVICE_STATE_REASON_NO_SECRETS: + return "no-secrets"; + case NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT: + return "supplicant-disconnect"; + case NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED: + return "supplicant-config-failed"; + case NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED: + return "supplicant-failed"; + case NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT: + return "supplicant-timeout"; + case NM_DEVICE_STATE_REASON_PPP_START_FAILED: + return "ppp-start-failed"; + case NM_DEVICE_STATE_REASON_PPP_DISCONNECT: + return "ppp-disconnect"; + case NM_DEVICE_STATE_REASON_PPP_FAILED: + return "ppp-failed"; + case NM_DEVICE_STATE_REASON_DHCP_START_FAILED: + return "dhcp-start-failed"; + case NM_DEVICE_STATE_REASON_DHCP_ERROR: + return "dhcp-error"; + case NM_DEVICE_STATE_REASON_DHCP_FAILED: + return "dhcp-failed"; + case NM_DEVICE_STATE_REASON_SHARED_START_FAILED: + return "sharing-start-failed"; + case NM_DEVICE_STATE_REASON_SHARED_FAILED: + return "sharing-failed"; + case NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED: + return "autoip-start-failed"; + case NM_DEVICE_STATE_REASON_AUTOIP_ERROR: + return "autoip-error"; + case NM_DEVICE_STATE_REASON_AUTOIP_FAILED: + return "autoip-failed"; + case NM_DEVICE_STATE_REASON_MODEM_BUSY: + return "modem-busy"; + case NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE: + return "modem-no-dialtone"; + case NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER: + return "modem-no-carrier"; + case NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT: + return "modem-dial-timeout"; + case NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED: + return "modem-dial-failed"; + case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: + return "modem-init-failed"; + case NM_DEVICE_STATE_REASON_GSM_APN_FAILED: + return "gsm-apn-failed"; + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: + return "gsm-registration-idle"; + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: + return "gsm-registration-denied"; + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT: + return "gsm-registration-timeout"; + case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED: + return "gsm-registration-failed"; + case NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED: + return "gsm-pin-check-failed"; + case NM_DEVICE_STATE_REASON_FIRMWARE_MISSING: + return "firmware-missing"; + case NM_DEVICE_STATE_REASON_REMOVED: + return "removed"; + case NM_DEVICE_STATE_REASON_SLEEPING: + return "sleeping"; + case NM_DEVICE_STATE_REASON_CONNECTION_REMOVED: + return "connection-removed"; + case NM_DEVICE_STATE_REASON_USER_REQUESTED: + return "user-requested"; + case NM_DEVICE_STATE_REASON_CARRIER: + return "carrier-changed"; + case NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED: + return "connection-assumed"; + case NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE: + return "supplicant-available"; + case NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND: + return "modem-not-found"; + case NM_DEVICE_STATE_REASON_BT_FAILED: + return "bluetooth-failed"; + case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: + return "gsm-sim-not-inserted"; + case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: + return "gsm-sim-pin-required"; + case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: + return "gsm-sim-puk-required"; + case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: + return "gsm-sim-wrong"; + case NM_DEVICE_STATE_REASON_INFINIBAND_MODE: + return "infiniband-mode"; + case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED: + return "dependency-failed"; + case NM_DEVICE_STATE_REASON_BR2684_FAILED: + return "br2684-bridge-failed"; + case NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE: + return "modem-manager-unavailable"; + case NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: + return "SSID not found"; + case NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED: + return "secondary-connection-failed"; + case NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED: + return "DCB-FCoE-failed"; + case NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED: + return "teamd-control-failed"; + case NM_DEVICE_STATE_REASON_MODEM_FAILED: + return "modem-failed"; + case NM_DEVICE_STATE_REASON_MODEM_AVAILABLE: + return "modem-available"; + case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: + return "sim-pin-incorrect"; + default: + break; + } + return "unknown"; +} + +/***********************************************************/ + static inline gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value) { @@ -433,163 +554,12 @@ restore_ip6_properties (NMDevice *self) nm_device_ipv6_sysctl_set (self, key, value); } -/* - * Get driver info from SIOCETHTOOL ioctl() for 'iface' - * Returns driver and firmware versions to 'driver_version and' 'firmware_version' - */ -static gboolean -device_get_driver_info (const char *iface, char **driver_version, char **firmware_version) -{ - struct ethtool_drvinfo drvinfo; - struct ifreq req; - int fd; - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_warn (LOGD_HW, "couldn't open control socket."); - return FALSE; - } - - /* Get driver and firmware version info */ - memset (&drvinfo, 0, sizeof (drvinfo)); - memset (&req, 0, sizeof (struct ifreq)); - strncpy (req.ifr_name, iface, IFNAMSIZ); - drvinfo.cmd = ETHTOOL_GDRVINFO; - req.ifr_data = &drvinfo; - - errno = 0; - if (ioctl (fd, SIOCETHTOOL, &req) < 0) { - nm_log_dbg (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d", - iface, errno); - close (fd); - return FALSE; - } - if (driver_version) - *driver_version = g_strdup (drvinfo.version); - if (firmware_version) - *firmware_version = g_strdup (drvinfo.fw_version); - - close (fd); - return TRUE; -} - static gboolean device_has_capability (NMDevice *device, NMDeviceCapabilities caps) { return !!(NM_DEVICE_GET_PRIVATE (device)->capabilities & caps); } -static GObject* -constructor (GType type, - guint n_construct_params, - GObjectConstructParam *construct_params) -{ - GObject *object; - NMDevice *dev; - NMDevicePrivate *priv; - NMPlatform *platform; - static guint32 id = 0; - - object = G_OBJECT_CLASS (nm_device_parent_class)->constructor (type, - n_construct_params, - construct_params); - if (!object) - return NULL; - - dev = NM_DEVICE (object); - priv = NM_DEVICE_GET_PRIVATE (dev); - - if (!priv->iface) { - nm_log_err (LOGD_DEVICE, "No device interface provided, ignoring"); - goto error; - } - - if (!priv->udi) { - /* Use a placeholder UDI until we get a real one */ - priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++); - } - - if (NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities) - priv->capabilities |= NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities (dev); - - priv->fw_manager = nm_firewall_manager_get (); - - device_get_driver_info (priv->iface, &priv->driver_version, &priv->firmware_version); - - /* Watch for external IP config changes */ - platform = nm_platform_get (); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), dev); - - priv->initialized = TRUE; - return object; - -error: - g_object_unref (dev); - return NULL; -} - -static void -constructed (GObject *object) -{ - NMDevice *dev = NM_DEVICE (object); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); - - nm_device_update_hw_address (dev); - - if (NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address) - NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address (dev); - - if (NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address) - NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address (dev); - - /* Have to call update_initial_hw_address() before calling get_ignore_carrier() */ - if (device_has_capability (dev, NM_DEVICE_CAP_CARRIER_DETECT)) { - priv->ignore_carrier = nm_config_get_ignore_carrier (nm_config_get (), NM_CONFIG_DEVICE (dev)); - - check_carrier (dev); - nm_log_info (LOGD_HW, - "(%s): carrier is %s%s", - nm_device_get_iface (NM_DEVICE (dev)), - priv->carrier ? "ON" : "OFF", - priv->ignore_carrier ? " (but ignored)" : ""); - } else { - /* Fake online link when carrier detection is not available. */ - priv->carrier = TRUE; - } - - if (priv->ifindex > 0) { - priv->is_software = nm_platform_link_is_software (priv->ifindex); - priv->physical_port_id = nm_platform_link_get_physical_port_id (priv->ifindex); - } - - if (priv->ifindex > 0) - priv->mtu = nm_platform_link_get_mtu (priv->ifindex); - - priv->con_provider = nm_connection_provider_get (); - g_assert (priv->con_provider); - g_signal_connect (priv->con_provider, - NM_CP_SIGNAL_CONNECTION_ADDED, - G_CALLBACK (cp_connection_added), - dev); - - g_signal_connect (priv->con_provider, - NM_CP_SIGNAL_CONNECTION_REMOVED, - G_CALLBACK (cp_connection_removed), - dev); - - g_signal_connect (priv->con_provider, - NM_CP_SIGNAL_CONNECTION_UPDATED, - G_CALLBACK (cp_connection_updated), - dev); - - if (G_OBJECT_CLASS (nm_device_parent_class)->constructed) - G_OBJECT_CLASS (nm_device_parent_class)->constructed (object); -} - static gboolean nm_device_is_up (NMDevice *self) { @@ -725,40 +695,6 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface) g_free (old_ip_iface); } -static guint -get_hw_address_length (NMDevice *dev, gboolean *out_permanent) -{ - size_t len; - - if (nm_platform_link_get_address (nm_device_get_ip_ifindex (dev), &len)) - return len; - else - return 0; -} - -static guint -nm_device_get_hw_address_length (NMDevice *dev, gboolean *out_permanent) -{ - return NM_DEVICE_GET_CLASS (dev)->get_hw_address_length (dev, out_permanent); -} - -const guint8 * -nm_device_get_hw_address (NMDevice *dev, guint *out_len) -{ - NMDevicePrivate *priv; - - g_return_val_if_fail (NM_IS_DEVICE (dev), NULL); - priv = NM_DEVICE_GET_PRIVATE (dev); - - if (out_len) - *out_len = priv->hw_addr_len; - - if (priv->hw_addr_len == 0) - return NULL; - else - return priv->hw_addr; -} - /* * Get/set functions for driver */ @@ -2633,6 +2569,9 @@ aipd_child_setup (gpointer user_data G_GNUC_UNUSED) nm_unblock_posix_signals (NULL); } +/* default to installed helper, but can be modified for testing */ +const char *nm_device_autoipd_helper_path = LIBEXECDIR "/nm-avahi-autoipd.action"; + static NMActStageReturn aipd_start (NMDevice *self, NMDeviceStateReason *reason) { @@ -2707,6 +2646,39 @@ aipd_start (NMDevice *self, NMDeviceStateReason *reason) /*********************************************/ /* DHCPv4 stuff */ +static void +dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->dhcp4_config) { + g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG); + g_object_unref (priv->dhcp4_config); + priv->dhcp4_config = NULL; + } + + if (priv->dhcp4_client) { + /* Stop any ongoing DHCP transaction on this device */ + if (priv->dhcp4_state_sigid) { + g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_state_sigid); + priv->dhcp4_state_sigid = 0; + } + + if (priv->dhcp4_timeout_sigid) { + g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid); + priv->dhcp4_timeout_sigid = 0; + } + + nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE); + + if (stop) + nm_dhcp_client_stop (priv->dhcp4_client, release); + + g_object_unref (priv->dhcp4_client); + priv->dhcp4_client = NULL; + } +} + static void dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data) { @@ -3116,6 +3088,45 @@ act_stage3_ip4_config_start (NMDevice *self, /*********************************************/ /* DHCPv6 stuff */ +static void +dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE; + + if (priv->dhcp6_ip6_config) { + g_object_unref (priv->dhcp6_ip6_config); + priv->dhcp6_ip6_config = NULL; + } + + if (priv->dhcp6_config) { + g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG); + g_object_unref (priv->dhcp6_config); + priv->dhcp6_config = NULL; + } + + if (priv->dhcp6_client) { + if (priv->dhcp6_state_sigid) { + g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_state_sigid); + priv->dhcp6_state_sigid = 0; + } + + if (priv->dhcp6_timeout_sigid) { + g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid); + priv->dhcp6_timeout_sigid = 0; + } + + nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE); + + if (stop) + nm_dhcp_client_stop (priv->dhcp6_client, release); + + g_object_unref (priv->dhcp6_client); + priv->dhcp6_client = NULL; + } +} + static void dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data) { @@ -3485,8 +3496,6 @@ linklocal6_start (NMDevice *self) /******************************************/ -static void dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release); - static void print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr) { @@ -4771,78 +4780,6 @@ clear_act_request (NMDevice *self) g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION); } -static void -dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->dhcp4_config) { - g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG); - g_object_unref (priv->dhcp4_config); - priv->dhcp4_config = NULL; - } - - if (priv->dhcp4_client) { - /* Stop any ongoing DHCP transaction on this device */ - if (priv->dhcp4_state_sigid) { - g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_state_sigid); - priv->dhcp4_state_sigid = 0; - } - - if (priv->dhcp4_timeout_sigid) { - g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid); - priv->dhcp4_timeout_sigid = 0; - } - - nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE); - - if (stop) - nm_dhcp_client_stop (priv->dhcp4_client, release); - - g_object_unref (priv->dhcp4_client); - priv->dhcp4_client = NULL; - } -} - -static void -dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE; - - if (priv->dhcp6_ip6_config) { - g_object_unref (priv->dhcp6_ip6_config); - priv->dhcp6_ip6_config = NULL; - } - - if (priv->dhcp6_config) { - g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG); - g_object_unref (priv->dhcp6_config); - priv->dhcp6_config = NULL; - } - - if (priv->dhcp6_client) { - if (priv->dhcp6_state_sigid) { - g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_state_sigid); - priv->dhcp6_state_sigid = 0; - } - - if (priv->dhcp6_timeout_sigid) { - g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid); - priv->dhcp6_timeout_sigid = 0; - } - - nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE); - - if (stop) - nm_dhcp_client_stop (priv->dhcp6_client, release); - - g_object_unref (priv->dhcp6_client); - priv->dhcp6_client = NULL; - } -} - static void dnsmasq_cleanup (NMDevice *self) { @@ -4987,118 +4924,6 @@ nm_device_set_is_nm_owned (NMDevice *device, return TRUE; } -/* - * nm_device_cleanup - * - * Remove a device's routing table entries and IP addresses. - * - */ -static void -nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason) -{ - NMDevicePrivate *priv; - NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; - NMConnection *connection = NULL; - int ifindex; - - g_return_if_fail (NM_IS_DEVICE (self)); - - if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED) { - nm_log_info (LOGD_DEVICE, "(%s): preparing device", - nm_device_get_iface (self)); - } else { - nm_log_info (LOGD_DEVICE, "(%s): deactivating device (reason '%s') [%d]", - nm_device_get_iface (self), reason_to_string (reason), reason); - } - - /* Save whether or not we tried IPv6 for later */ - priv = NM_DEVICE_GET_PRIVATE (self); - - /* Clean up when device was deactivated during call to firewall */ - if (priv->fw_call) { - nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call); - priv->fw_call = NULL; - } - - if (priv->act_request) - connection = nm_act_request_get_connection (priv->act_request); - if (connection) { - nm_firewall_manager_remove_from_zone (priv->fw_manager, - nm_device_get_ip_iface (self), - NULL); - } - - ip_check_gw_ping_cleanup (self); - - /* Break the activation chain */ - activation_source_clear (self, TRUE, AF_INET); - activation_source_clear (self, TRUE, AF_INET6); - - /* Clear any queued transitions */ - nm_device_queued_state_clear (self); - nm_device_queued_ip_config_change_clear (self); - - priv->ip4_state = priv->ip6_state = IP_NONE; - - dhcp4_cleanup (self, TRUE, FALSE); - arp_cleanup (self); - dhcp6_cleanup (self, TRUE, FALSE); - linklocal6_cleanup (self); - addrconf6_cleanup (self); - dnsmasq_cleanup (self); - aipd_cleanup (self); - - /* Turn off kernel IPv6 */ - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); - nm_device_ipv6_sysctl_set (self, "accept_ra", "0"); - nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0"); - - /* Call device type-specific deactivation */ - if (NM_DEVICE_GET_CLASS (self)->deactivate) - NM_DEVICE_GET_CLASS (self)->deactivate (self); - - /* master: release slaves */ - nm_device_master_release_slaves (self); - - /* slave: mark no longer enslaved */ - g_clear_object (&priv->master); - priv->enslaved = FALSE; - g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER); - - /* Tear down an existing activation request */ - clear_act_request (self); - - /* Take out any entries in the routing table and any IP address the device had. */ - ifindex = nm_device_get_ip_ifindex (self); - if (ifindex > 0) { - nm_platform_route_flush (ifindex); - nm_platform_address_flush (ifindex); - } - - /* Clean up nameservers and addresses */ - nm_device_set_ip4_config (self, NULL, TRUE, &ignored); - nm_device_set_ip6_config (self, NULL, TRUE, &ignored); - g_clear_object (&priv->ext_ip4_config); - g_clear_object (&priv->vpn4_config); - g_clear_object (&priv->vpn6_config); - g_clear_object (&priv->ext_ip6_config); - - /* Clear legacy IPv4 address property */ - priv->ip4_address = 0; - g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS); - - /* Only clear ip_iface after flushing all routes and addreses, since - * those are identified by ip_iface, not by iface (which might be a tty - * or ATM device). - */ - nm_device_set_ip_iface (self, NULL); - - /* Check if the device was deactivated, and if so, delete_link. - * Don't call delete_link synchronously because we are currently - * handling a state change -- which is not reentrant. */ - delete_on_deactivate_check_and_schedule (self, ifindex); -} - static void disconnect_cb (NMDevice *device, DBusGMethodInvocation *context, @@ -5810,760 +5635,6 @@ take_down (NMDevice *device) return FALSE; } -static void -dispose (GObject *object) -{ - NMDevice *self = NM_DEVICE (object); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean deconfigure = TRUE; - NMPlatform *platform; - - if (priv->disposed || !priv->initialized) - goto out; - - priv->disposed = TRUE; - - /* Don't down can-assume-connection capable devices that are activated with - * a connection that can be assumed. - */ - if (nm_device_can_assume_connections (self) && (priv->state == NM_DEVICE_STATE_ACTIVATED)) { - NMConnection *connection; - const char *method; - - connection = nm_device_get_connection (self); - if (connection) { - /* Only static or DHCP IPv4 connections can be left up. - * All IPv6 connections can be left up, so we don't have - * to check that. - */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if ( !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) - || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) - || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - deconfigure = FALSE; - } - } - - ip_check_gw_ping_cleanup (self); - - /* Clear any queued transitions */ - nm_device_queued_state_clear (self); - nm_device_queued_ip_config_change_clear (self); - - /* Clean up and stop address configuration */ - dhcp4_cleanup (self, deconfigure, FALSE); - arp_cleanup (self); - dhcp6_cleanup (self, deconfigure, FALSE); - linklocal6_cleanup (self); - addrconf6_cleanup (self); - dnsmasq_cleanup (self); - - g_warn_if_fail (priv->slaves == NULL); - g_assert (priv->master_ready_id == 0); - - /* Take the device itself down and clear its IP configuration */ - if (nm_device_get_managed (self) && deconfigure) { - NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; - - if (nm_device_get_act_request (self)) - nm_device_cleanup (self, NM_DEVICE_STATE_REASON_REMOVED); - nm_device_set_ip4_config (self, NULL, TRUE, &ignored); - nm_device_set_ip6_config (self, NULL, TRUE, &ignored); - - nm_device_take_down (self, FALSE); - - restore_ip6_properties (self); - - /* do a final check whether we should delete_link */ - delete_on_deactivate_check_and_schedule (self, nm_device_get_ip_ifindex (self)); - } - g_clear_object (&priv->dev_ip4_config); - g_clear_object (&priv->ext_ip4_config); - g_clear_object (&priv->vpn4_config); - g_clear_object (&priv->ip4_config); - - g_clear_object (&priv->ip6_config); - g_clear_object (&priv->ac_ip6_config); - g_clear_object (&priv->dhcp6_ip6_config); - g_clear_object (&priv->vpn6_config); - g_clear_object (&priv->ext_ip6_config); - - g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref); - - if (priv->recheck_assume_id) { - g_source_remove (priv->recheck_assume_id); - priv->recheck_assume_id = 0; - } - - link_disconnect_action_cancel (self); - - if (priv->con_provider) { - g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_added, self); - g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_removed, self); - g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_updated, self); - priv->con_provider = NULL; - } - - g_hash_table_unref (priv->available_connections); - priv->available_connections = NULL; - - if (priv->carrier_wait_id) { - g_source_remove (priv->carrier_wait_id); - priv->carrier_wait_id = 0; - } - - g_clear_pointer (&priv->physical_port_id, g_free); - - activation_source_clear (self, TRUE, AF_INET); - activation_source_clear (self, TRUE, AF_INET6); - - clear_act_request (self); - g_clear_object (&priv->queued_act_request); - - platform = nm_platform_get (); - g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self); - g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); - -out: - G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); -} - -static void -finalize (GObject *object) -{ - NMDevice *self = NM_DEVICE (object); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->fw_manager) - g_object_unref (priv->fw_manager); - - g_slist_free_full (priv->pending_actions, g_free); - - g_free (priv->udi); - g_free (priv->path); - g_free (priv->iface); - g_free (priv->ip_iface); - g_free (priv->driver); - g_free (priv->driver_version); - g_free (priv->firmware_version); - g_free (priv->type_desc); - if (priv->dhcp_anycast_address) - g_byte_array_free (priv->dhcp_anycast_address, TRUE); - - G_OBJECT_CLASS (nm_device_parent_class)->finalize (object); -} - - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); - NMPlatformLink *platform_device; - const char *hw_addr; - - switch (prop_id) { - case PROP_PLATFORM_DEVICE: - platform_device = g_value_get_pointer (value); - if (platform_device) { - g_free (priv->udi); - priv->udi = g_strdup (platform_device->udi); - g_free (priv->iface); - priv->iface = g_strdup (platform_device->name); - priv->ifindex = platform_device->ifindex; - g_free (priv->driver); - priv->driver = g_strdup (platform_device->driver); - } - break; - case PROP_UDI: - if (g_value_get_string (value)) { - g_free (priv->udi); - priv->udi = g_value_dup_string (value); - } - break; - case PROP_IFACE: - if (g_value_get_string (value)) { - g_free (priv->iface); - priv->ifindex = 0; - priv->iface = g_value_dup_string (value); - - /* Only look up the ifindex if it appears to be an actual kernel - * interface name. eg Bluetooth devices won't have one until we know - * the IP interface. - */ - if (priv->iface && !strchr (priv->iface, ':')) { - priv->ifindex = nm_platform_link_get_ifindex (priv->iface); - if (priv->ifindex <= 0) - nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", priv->iface); - } - } - break; - case PROP_DRIVER: - if (g_value_get_string (value)) { - g_free (priv->driver); - priv->driver = g_value_dup_string (value); - } - break; - case PROP_DRIVER_VERSION: - g_free (priv->driver_version); - priv->driver_version = g_strdup (g_value_get_string (value)); - break; - case PROP_FIRMWARE_VERSION: - g_free (priv->firmware_version); - priv->firmware_version = g_strdup (g_value_get_string (value)); - break; - case PROP_MTU: - priv->mtu = g_value_get_uint (value); - break; - case PROP_IP4_ADDRESS: - priv->ip4_address = g_value_get_uint (value); - break; - case PROP_AUTOCONNECT: - priv->autoconnect = g_value_get_boolean (value); - break; - case PROP_FIRMWARE_MISSING: - priv->firmware_missing = g_value_get_boolean (value); - break; - case PROP_DEVICE_TYPE: - g_return_if_fail (priv->type == NM_DEVICE_TYPE_UNKNOWN); - priv->type = g_value_get_uint (value); - break; - case PROP_TYPE_DESC: - g_free (priv->type_desc); - priv->type_desc = g_value_dup_string (value); - break; - case PROP_RFKILL_TYPE: - priv->rfkill_type = g_value_get_uint (value); - break; - case PROP_IS_MASTER: - priv->is_master = g_value_get_boolean (value); - break; - case PROP_HW_ADDRESS: - priv->hw_addr_len = nm_device_get_hw_address_length (NM_DEVICE (object), NULL); - - hw_addr = g_value_get_string (value); - if (!hw_addr) - break; - if (priv->hw_addr_len == 0) { - g_warn_if_fail (*hw_addr == '\0'); - break; - } - - if (!nm_utils_hwaddr_aton_len (hw_addr, priv->hw_addr, priv->hw_addr_len)) { - g_warning ("Could not parse hw-address '%s'", hw_addr); - memset (priv->hw_addr, 0, sizeof (priv->hw_addr)); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static gboolean -ip_config_valid (NMDeviceState state) -{ - return (state == NM_DEVICE_STATE_UNMANAGED) || - (state >= NM_DEVICE_STATE_IP_CHECK && - state <= NM_DEVICE_STATE_DEACTIVATING); -} - -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMDevice *self = NM_DEVICE (object); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *ac_path = NULL; - GPtrArray *array; - GHashTableIter iter; - NMConnection *connection; - - switch (prop_id) { - case PROP_UDI: - g_value_set_string (value, priv->udi); - break; - case PROP_IFACE: - g_value_set_string (value, priv->iface); - break; - case PROP_IP_IFACE: - if (ip_config_valid (priv->state)) - g_value_set_string (value, nm_device_get_ip_iface (self)); - else - g_value_set_string (value, NULL); - break; - case PROP_IFINDEX: - g_value_set_int (value, priv->ifindex); - break; - case PROP_DRIVER: - g_value_set_string (value, priv->driver); - break; - case PROP_DRIVER_VERSION: - g_value_set_string (value, priv->driver_version); - break; - case PROP_FIRMWARE_VERSION: - g_value_set_string (value, priv->firmware_version); - break; - case PROP_CAPABILITIES: - g_value_set_uint (value, (priv->capabilities & ~NM_DEVICE_CAP_INTERNAL_MASK)); - break; - case PROP_IP4_ADDRESS: - g_value_set_uint (value, priv->ip4_address); - break; - case PROP_CARRIER: - g_value_set_boolean (value, priv->carrier); - break; - case PROP_MTU: - g_value_set_uint (value, priv->mtu); - break; - case PROP_IP4_CONFIG: - if (ip_config_valid (priv->state) && priv->ip4_config) - g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config)); - else - g_value_set_boxed (value, "/"); - break; - case PROP_DHCP4_CONFIG: - if (ip_config_valid (priv->state) && priv->dhcp4_client) - g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config)); - else - g_value_set_boxed (value, "/"); - break; - case PROP_IP6_CONFIG: - if (ip_config_valid (priv->state) && priv->ip6_config) - g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config)); - else - g_value_set_boxed (value, "/"); - break; - case PROP_DHCP6_CONFIG: - if (ip_config_valid (priv->state) && priv->dhcp6_client) - g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config)); - else - g_value_set_boxed (value, "/"); - break; - case PROP_STATE: - g_value_set_uint (value, priv->state); - break; - case PROP_STATE_REASON: - g_value_take_boxed (value, dbus_g_type_specialized_construct (DBUS_G_TYPE_UINT_STRUCT)); - dbus_g_type_struct_set (value, - 0, priv->state, - 1, priv->state_reason, - G_MAXUINT); - break; - case PROP_ACTIVE_CONNECTION: - if (priv->act_request) - ac_path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (priv->act_request)); - g_value_set_boxed (value, ac_path ? ac_path : "/"); - break; - case PROP_DEVICE_TYPE: - g_value_set_uint (value, priv->type); - break; - case PROP_MANAGED: - g_value_set_boolean (value, nm_device_get_managed (self)); - break; - case PROP_AUTOCONNECT: - g_value_set_boolean (value, priv->autoconnect); - break; - case PROP_FIRMWARE_MISSING: - g_value_set_boolean (value, priv->firmware_missing); - break; - case PROP_TYPE_DESC: - g_value_set_string (value, priv->type_desc); - break; - case PROP_RFKILL_TYPE: - g_value_set_uint (value, priv->rfkill_type); - break; - case PROP_AVAILABLE_CONNECTIONS: - array = g_ptr_array_sized_new (g_hash_table_size (priv->available_connections)); - g_hash_table_iter_init (&iter, priv->available_connections); - while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) - g_ptr_array_add (array, g_strdup (nm_connection_get_path (connection))); - g_value_take_boxed (value, array); - break; - case PROP_PHYSICAL_PORT_ID: - g_value_set_string (value, priv->physical_port_id); - break; - case PROP_IS_MASTER: - g_value_set_boolean (value, priv->is_master); - break; - case PROP_MASTER: - g_value_set_object (value, priv->master); - break; - case PROP_HW_ADDRESS: - if (priv->hw_addr_len) - g_value_take_string (value, nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len)); - else - g_value_set_string (value, NULL); - break; - case PROP_HAS_PENDING_ACTION: - g_value_set_boolean (value, nm_device_has_pending_action (self)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -nm_device_class_init (NMDeviceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (object_class, sizeof (NMDevicePrivate)); - - /* Virtual methods */ - object_class->dispose = dispose; - object_class->finalize = finalize; - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->constructor = constructor; - object_class->constructed = constructed; - - klass->link_changed = link_changed; - - klass->is_available = is_available; - klass->act_stage1_prepare = act_stage1_prepare; - klass->act_stage2_config = act_stage2_config; - klass->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - klass->act_stage3_ip6_config_start = act_stage3_ip6_config_start; - klass->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout; - klass->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; - klass->have_any_ready_slaves = have_any_ready_slaves; - - klass->spec_match_list = spec_match_list; - klass->can_auto_connect = can_auto_connect; - klass->check_connection_compatible = check_connection_compatible; - klass->check_connection_available = check_connection_available; - klass->is_up = is_up; - klass->bring_up = bring_up; - klass->take_down = take_down; - klass->carrier_changed = carrier_changed; - klass->get_hw_address_length = get_hw_address_length; - - /* Properties */ - g_object_class_install_property - (object_class, PROP_PLATFORM_DEVICE, - g_param_spec_pointer (NM_DEVICE_PLATFORM_DEVICE, - "Platform Device", - "NMPlatform device object", - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_UDI, - g_param_spec_string (NM_DEVICE_UDI, - "UDI", - "Unique Device Identifier", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - - g_object_class_install_property - (object_class, PROP_IFACE, - g_param_spec_string (NM_DEVICE_IFACE, - "Interface", - "Interface", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_IP_IFACE, - g_param_spec_string (NM_DEVICE_IP_IFACE, - "IP Interface", - "IP Interface", - NULL, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_DRIVER, - g_param_spec_string (NM_DEVICE_DRIVER, - "Driver", - "Driver", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_DRIVER_VERSION, - g_param_spec_string (NM_DEVICE_DRIVER_VERSION, - "Driver Version", - "Driver Version", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_FIRMWARE_VERSION, - g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION, - "Firmware Version", - "Firmware Version", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_CAPABILITIES, - g_param_spec_uint (NM_DEVICE_CAPABILITIES, - "Capabilities", - "Capabilities", - 0, G_MAXUINT32, NM_DEVICE_CAP_NONE, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_CARRIER, - g_param_spec_boolean (NM_DEVICE_CARRIER, - "Carrier", - "Carrier", - FALSE, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_MTU, - g_param_spec_uint (NM_DEVICE_MTU, - "MTU", - "MTU", - 0, G_MAXUINT32, 1500, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_IP4_ADDRESS, - g_param_spec_uint (NM_DEVICE_IP4_ADDRESS, - "IP4 address", - "IP4 address", - 0, G_MAXUINT32, 0, /* FIXME */ - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_IP4_CONFIG, - g_param_spec_boxed (NM_DEVICE_IP4_CONFIG, - "IP4 Config", - "IP4 Config", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_DHCP4_CONFIG, - g_param_spec_boxed (NM_DEVICE_DHCP4_CONFIG, - "DHCP4 Config", - "DHCP4 Config", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_IP6_CONFIG, - g_param_spec_boxed (NM_DEVICE_IP6_CONFIG, - "IP6 Config", - "IP6 Config", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_DHCP6_CONFIG, - g_param_spec_boxed (NM_DEVICE_DHCP6_CONFIG, - "DHCP6 Config", - "DHCP6 Config", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_STATE, - g_param_spec_uint (NM_DEVICE_STATE, - "State", - "State", - 0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN, - G_PARAM_READABLE)); - g_object_class_install_property - (object_class, PROP_STATE_REASON, - g_param_spec_boxed (NM_DEVICE_STATE_REASON, - "StateReason", - "StateReason", - DBUS_G_TYPE_UINT_STRUCT, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_ACTIVE_CONNECTION, - g_param_spec_boxed (NM_DEVICE_ACTIVE_CONNECTION, - "ActiveConnection", - "ActiveConnection", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_DEVICE_TYPE, - g_param_spec_uint (NM_DEVICE_DEVICE_TYPE, - "DeviceType", - "DeviceType", - 0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_MANAGED, - g_param_spec_boolean (NM_DEVICE_MANAGED, - "Managed", - "Managed", - FALSE, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_AUTOCONNECT, - g_param_spec_boolean (NM_DEVICE_AUTOCONNECT, - "Autoconnect", - "Autoconnect", - DEFAULT_AUTOCONNECT, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_FIRMWARE_MISSING, - g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING, - "FirmwareMissing", - "Firmware missing", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_TYPE_DESC, - g_param_spec_string (NM_DEVICE_TYPE_DESC, - "Type Description", - "Device type description", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_RFKILL_TYPE, - g_param_spec_uint (NM_DEVICE_RFKILL_TYPE, - "Rfkill Type", - "Type of rfkill switch (if any) supported by this device", - RFKILL_TYPE_WLAN, - RFKILL_TYPE_MAX, - RFKILL_TYPE_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_IFINDEX, - g_param_spec_int (NM_DEVICE_IFINDEX, - "Ifindex", - "Ifindex", - 0, G_MAXINT, 0, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_AVAILABLE_CONNECTIONS, - g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, - "AvailableConnections", - "AvailableConnections", - DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_PHYSICAL_PORT_ID, - g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID, - "PhysicalPortId", - "PhysicalPortId", - NULL, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_IS_MASTER, - g_param_spec_boolean (NM_DEVICE_IS_MASTER, - "IsMaster", - "IsMaster", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_MASTER, - g_param_spec_object (NM_DEVICE_MASTER, - "Master", - "Master", - NM_TYPE_DEVICE, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_HW_ADDRESS, - g_param_spec_string (NM_DEVICE_HW_ADDRESS, - "Hardware Address", - "Hardware address", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_HAS_PENDING_ACTION, - g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION, - "Has pending action", - "Has pending action", - FALSE, - G_PARAM_READABLE)); - - /* Signals */ - signals[STATE_CHANGED] = - g_signal_new ("state-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMDeviceClass, state_changed), - NULL, NULL, NULL, - G_TYPE_NONE, 3, - G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); - - signals[AUTOCONNECT_ALLOWED] = - g_signal_new ("autoconnect-allowed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - autoconnect_allowed_accumulator, NULL, NULL, - G_TYPE_BOOLEAN, 0); - - signals[AUTH_REQUEST] = - g_signal_new (NM_DEVICE_AUTH_REQUEST, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - /* dbus-glib context, connection, permission, allow_interaction, callback, user_data */ - G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER); - - signals[IP4_CONFIG_CHANGED] = - g_signal_new (NM_DEVICE_IP4_CONFIG_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT); - - signals[IP6_CONFIG_CHANGED] = - g_signal_new (NM_DEVICE_IP6_CONFIG_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT); - - signals[REMOVED] = - g_signal_new (NM_DEVICE_REMOVED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - - signals[RECHECK_AUTO_ACTIVATE] = - g_signal_new (NM_DEVICE_RECHECK_AUTO_ACTIVATE, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - - signals[RECHECK_ASSUME] = - g_signal_new (NM_DEVICE_RECHECK_ASSUME, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - - nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), - G_TYPE_FROM_CLASS (klass), - &dbus_glib_nm_device_object_info); - - dbus_g_error_domain_register (NM_DEVICE_ERROR, NULL, NM_TYPE_DEVICE_ERROR); -} - -static void -nm_device_config_device_interface_init (NMConfigDeviceInterface *iface) -{ - iface->spec_match_list = (gboolean (*) (NMConfigDevice *, const GSList *)) nm_device_spec_match_list; - iface->get_hw_address = (const guint8 * (*) (NMConfigDevice *, guint *)) nm_device_get_hw_address; -} - void nm_device_set_firmware_missing (NMDevice *self, gboolean new_missing) { @@ -6584,174 +5655,859 @@ nm_device_get_firmware_missing (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->firmware_missing; } -#define QUEUED_PREFIX "queued state change to " - -static const char * -queued_state_to_string (NMDeviceState state) +static NMIP4Config * +find_ip4_lease_config (NMDevice *device, + NMConnection *connection, + NMIP4Config *ext_ip4_config) { - switch (state) { - case NM_DEVICE_STATE_UNMANAGED: - return QUEUED_PREFIX "unmanaged"; - case NM_DEVICE_STATE_UNAVAILABLE: - return QUEUED_PREFIX "unavailable"; - case NM_DEVICE_STATE_DISCONNECTED: - return QUEUED_PREFIX "disconnected"; - case NM_DEVICE_STATE_PREPARE: - return QUEUED_PREFIX "prepare"; - case NM_DEVICE_STATE_CONFIG: - return QUEUED_PREFIX "config"; - case NM_DEVICE_STATE_NEED_AUTH: - return QUEUED_PREFIX "need-auth"; - case NM_DEVICE_STATE_IP_CONFIG: - return QUEUED_PREFIX "ip-config"; - case NM_DEVICE_STATE_IP_CHECK: - return QUEUED_PREFIX "ip-check"; - case NM_DEVICE_STATE_SECONDARIES: - return QUEUED_PREFIX "secondaries"; - case NM_DEVICE_STATE_ACTIVATED: - return QUEUED_PREFIX "activated"; - case NM_DEVICE_STATE_DEACTIVATING: - return QUEUED_PREFIX "deactivating"; - case NM_DEVICE_STATE_FAILED: - return QUEUED_PREFIX "failed"; - default: - break; + const char *ip_iface = nm_device_get_ip_iface (device); + GSList *leases, *liter; + NMIP4Config *found = NULL; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (ext_ip4_config), NULL); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + leases = nm_dhcp_manager_get_lease_ip_configs (nm_dhcp_manager_get (), + ip_iface, + nm_connection_get_uuid (connection), + FALSE); + for (liter = leases; liter && !found; liter = liter->next) { + NMIP4Config *lease_config = liter->data; + const NMPlatformIP4Address *address = nm_ip4_config_get_address (lease_config, 0); + guint32 gateway = nm_ip4_config_get_gateway (lease_config); + + g_assert (address); + if (!nm_ip4_config_address_exists (ext_ip4_config, address)) + continue; + if (gateway != nm_ip4_config_get_gateway (ext_ip4_config)) + continue; + found = g_object_ref (lease_config); } - return QUEUED_PREFIX "unknown"; + + g_slist_free_full (leases, g_object_unref); + return found; } -static const char * -state_to_string (NMDeviceState state) +static void +capture_lease_config (NMDevice *device, + NMIP4Config *ext_ip4_config, + NMIP4Config **out_ip4_config, + NMIP6Config *ext_ip6_config, + NMIP6Config **out_ip6_config) { - return queued_state_to_string (state) + strlen (QUEUED_PREFIX); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + const GSList *connections, *citer; + guint i; + gboolean dhcp_used = FALSE; + + /* Ensure at least one address on the device has a non-infinite lifetime, + * otherwise DHCP cannot possibly be active on the device right now. + */ + if (ext_ip4_config && out_ip4_config) { + for (i = 0; i < nm_ip4_config_get_num_addresses (ext_ip4_config); i++) { + const NMPlatformIP4Address *addr = nm_ip4_config_get_address (ext_ip4_config, i); + + if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { + dhcp_used = TRUE; + break; + } + } + } else if (ext_ip6_config && out_ip6_config) { + for (i = 0; i < nm_ip6_config_get_num_addresses (ext_ip6_config); i++) { + const NMPlatformIP6Address *addr = nm_ip6_config_get_address (ext_ip6_config, i); + + if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { + dhcp_used = TRUE; + break; + } + } + } else { + g_return_if_fail ( (ext_ip6_config && out_ip6_config) + || (ext_ip4_config && out_ip4_config)); + } + + if (!dhcp_used) + return; + + connections = nm_connection_provider_get_connections (priv->con_provider); + for (citer = connections; citer; citer = citer->next) { + NMConnection *candidate = citer->data; + const char *method; + + if (!nm_device_check_connection_compatible (device, candidate)) + continue; + + /* IPv4 leases */ + method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG); + if (out_ip4_config && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { + *out_ip4_config = find_ip4_lease_config (device, candidate, ext_ip4_config); + if (*out_ip4_config) + return; + } + + /* IPv6 leases */ + method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP6_CONFIG); + if (out_ip6_config && strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) { + /* FIXME: implement find_ip6_lease_config() */ + } + } } -static const char * -reason_to_string (NMDeviceStateReason reason) +static void +update_ip_config (NMDevice *self, gboolean initial) { - switch (reason) { - case NM_DEVICE_STATE_REASON_NONE: - return "none"; - case NM_DEVICE_STATE_REASON_NOW_MANAGED: - return "managed"; - case NM_DEVICE_STATE_REASON_NOW_UNMANAGED: - return "unmanaged"; - case NM_DEVICE_STATE_REASON_CONFIG_FAILED: - return "config-failed"; - case NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE: - return "ip-config-unavailable"; - case NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED: - return "ip-config-expired"; - case NM_DEVICE_STATE_REASON_NO_SECRETS: - return "no-secrets"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT: - return "supplicant-disconnect"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED: - return "supplicant-config-failed"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED: - return "supplicant-failed"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT: - return "supplicant-timeout"; - case NM_DEVICE_STATE_REASON_PPP_START_FAILED: - return "ppp-start-failed"; - case NM_DEVICE_STATE_REASON_PPP_DISCONNECT: - return "ppp-disconnect"; - case NM_DEVICE_STATE_REASON_PPP_FAILED: - return "ppp-failed"; - case NM_DEVICE_STATE_REASON_DHCP_START_FAILED: - return "dhcp-start-failed"; - case NM_DEVICE_STATE_REASON_DHCP_ERROR: - return "dhcp-error"; - case NM_DEVICE_STATE_REASON_DHCP_FAILED: - return "dhcp-failed"; - case NM_DEVICE_STATE_REASON_SHARED_START_FAILED: - return "sharing-start-failed"; - case NM_DEVICE_STATE_REASON_SHARED_FAILED: - return "sharing-failed"; - case NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED: - return "autoip-start-failed"; - case NM_DEVICE_STATE_REASON_AUTOIP_ERROR: - return "autoip-error"; - case NM_DEVICE_STATE_REASON_AUTOIP_FAILED: - return "autoip-failed"; - case NM_DEVICE_STATE_REASON_MODEM_BUSY: - return "modem-busy"; - case NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE: - return "modem-no-dialtone"; - case NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER: - return "modem-no-carrier"; - case NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT: - return "modem-dial-timeout"; - case NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED: - return "modem-dial-failed"; - case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: - return "modem-init-failed"; - case NM_DEVICE_STATE_REASON_GSM_APN_FAILED: - return "gsm-apn-failed"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: - return "gsm-registration-idle"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: - return "gsm-registration-denied"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT: - return "gsm-registration-timeout"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED: - return "gsm-registration-failed"; - case NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED: - return "gsm-pin-check-failed"; - case NM_DEVICE_STATE_REASON_FIRMWARE_MISSING: - return "firmware-missing"; - case NM_DEVICE_STATE_REASON_REMOVED: - return "removed"; - case NM_DEVICE_STATE_REASON_SLEEPING: - return "sleeping"; - case NM_DEVICE_STATE_REASON_CONNECTION_REMOVED: - return "connection-removed"; - case NM_DEVICE_STATE_REASON_USER_REQUESTED: - return "user-requested"; - case NM_DEVICE_STATE_REASON_CARRIER: - return "carrier-changed"; - case NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED: - return "connection-assumed"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE: - return "supplicant-available"; - case NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND: - return "modem-not-found"; - case NM_DEVICE_STATE_REASON_BT_FAILED: - return "bluetooth-failed"; - case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: - return "gsm-sim-not-inserted"; - case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: - return "gsm-sim-pin-required"; - case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: - return "gsm-sim-puk-required"; - case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: - return "gsm-sim-wrong"; - case NM_DEVICE_STATE_REASON_INFINIBAND_MODE: - return "infiniband-mode"; - case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED: - return "dependency-failed"; - case NM_DEVICE_STATE_REASON_BR2684_FAILED: - return "br2684-bridge-failed"; - case NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE: - return "modem-manager-unavailable"; - case NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: - return "SSID not found"; - case NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED: - return "secondary-connection-failed"; - case NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED: - return "DCB-FCoE-failed"; - case NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED: - return "teamd-control-failed"; - case NM_DEVICE_STATE_REASON_MODEM_FAILED: - return "modem-failed"; - case NM_DEVICE_STATE_REASON_MODEM_AVAILABLE: - return "modem-available"; - case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: - return "sim-pin-incorrect"; - default: - break; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + int ifindex; + gboolean linklocal6_just_completed = FALSE; + gboolean capture_resolv_conf; + NMDnsManagerResolvConfMode resolv_conf_mode; + + ifindex = nm_device_get_ip_ifindex (self); + if (!ifindex) + return; + + resolv_conf_mode = nm_dns_manager_get_resolv_conf_mode (nm_dns_manager_get ()); + capture_resolv_conf = initial && (resolv_conf_mode == NM_DNS_MANAGER_RESOLV_CONF_EXPLICIT); + + /* IPv4 */ + g_clear_object (&priv->ext_ip4_config); + priv->ext_ip4_config = nm_ip4_config_capture (ifindex, capture_resolv_conf); + + if (priv->ext_ip4_config) { + if (initial) { + g_clear_object (&priv->dev_ip4_config); + capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL); + } + if (priv->dev_ip4_config) + nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config); + if (priv->vpn4_config) + nm_ip4_config_subtract (priv->ext_ip4_config, priv->vpn4_config); + + ip4_config_merge_and_apply (self, NULL, FALSE, NULL); } - return "unknown"; + + /* IPv6 */ + g_clear_object (&priv->ext_ip6_config); + priv->ext_ip6_config = nm_ip6_config_capture (ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + if (priv->ext_ip6_config) { + + /* Check this before modifying ext_ip6_config */ + linklocal6_just_completed = priv->linklocal6_timeout_id && + linklocal6_config_is_ready (priv->ext_ip6_config); + + if (priv->ac_ip6_config) + nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config); + if (priv->dhcp6_ip6_config) + nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6_ip6_config); + if (priv->vpn6_config) + nm_ip6_config_subtract (priv->ext_ip6_config, priv->vpn6_config); + + ip6_config_merge_and_apply (self, FALSE, NULL); + } + + if (linklocal6_just_completed) { + /* linklocal6 is ready now, do the state transition... we are also + * invoked as g_idle_add, so no problems with reentrance doing it now. + */ + linklocal6_complete (self); + } +} + +void +nm_device_capture_initial_config (NMDevice *dev) +{ + update_ip_config (dev, TRUE); +} + +static gboolean +queued_ip_config_change (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + /* Wait for any queued state changes */ + if (priv->queued_state.id) + return TRUE; + + priv->queued_ip_config_id = 0; + update_ip_config (self, FALSE); + return FALSE; +} + +static void +device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformSignalChangeType change_type, NMPlatformReason reason, gpointer user_data) +{ + NMDevice *self = user_data; + + if (nm_device_get_ip_ifindex (self) == ifindex) { + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (!priv->queued_ip_config_id) + priv->queued_ip_config_id = g_idle_add (queued_ip_config_change, self); + + nm_log_dbg (LOGD_DEVICE, "(%s): queued IP config change", + nm_device_get_iface (self)); + } +} + +static void +nm_device_queued_ip_config_change_clear (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->queued_ip_config_id) { + nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued IP config change", + nm_device_get_iface (self)); + g_source_remove (priv->queued_ip_config_id); + priv->queued_ip_config_id = 0; + } +} + +/** + * nm_device_get_managed(): + * @device: the #NMDevice + * + * Returns: %TRUE if the device is managed + */ +gboolean +nm_device_get_managed (NMDevice *device) +{ + NMDevicePrivate *priv; + gboolean managed; + + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (device); + + /* Return the composite of all managed flags. However, if the device + * is a default-unmanaged device, and would be managed except for the + * default-unmanaged flag (eg, only NM_UNMANAGED_DEFAULT is set) then + * the device is managed whenever it's not in the UNMANAGED state. + */ + managed = !(priv->unmanaged_flags & ~NM_UNMANAGED_DEFAULT); + if (managed && (priv->unmanaged_flags & NM_UNMANAGED_DEFAULT)) + managed = (priv->state > NM_DEVICE_STATE_UNMANAGED); + + return managed; +} + +/** + * nm_device_get_default_unmanaged(): + * @device: the #NMDevice + * + * Returns: %TRUE if the device is by default unmanaged + */ +gboolean +nm_device_get_default_unmanaged (NMDevice *device) +{ + return nm_device_get_unmanaged_flag (device, NM_UNMANAGED_DEFAULT); +} + +/** + * nm_device_get_unmanaged_flag(): + * @device: the #NMDevice + * + * Returns: %TRUE if the device is unmanaged for @flag. + */ +gboolean +nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag) +{ + return NM_DEVICE_GET_PRIVATE (device)->unmanaged_flags & flag; +} + +void +nm_device_set_unmanaged (NMDevice *device, + NMUnmanagedFlags flag, + gboolean unmanaged, + NMDeviceStateReason reason) +{ + NMDevicePrivate *priv; + gboolean was_managed, now_managed; + + g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (flag <= NM_UNMANAGED_LAST); + + priv = NM_DEVICE_GET_PRIVATE (device); + + was_managed = nm_device_get_managed (device); + if (unmanaged) + priv->unmanaged_flags |= flag; + else + priv->unmanaged_flags &= ~flag; + now_managed = nm_device_get_managed (device); + + if (was_managed != now_managed) { + nm_log_dbg (LOGD_DEVICE, "(%s): now %s", + nm_device_get_iface (device), + unmanaged ? "unmanaged" : "managed"); + + g_object_notify (G_OBJECT (device), NM_DEVICE_MANAGED); + + if (unmanaged) + nm_device_state_changed (device, NM_DEVICE_STATE_UNMANAGED, reason); + else + nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE, reason); + } +} + +/** + * nm_device_set_initial_unmanaged_flag(): + * @device: the #NMDevice + * @flag: an #NMUnmanagedFlag + * @unmanaged: %TRUE or %FALSE to set or clear @flag + * + * Like nm_device_set_unmanaged() but must be set before the device is exported + * and does not trigger state changes. Should only be used when initializing + * a device. + */ +void +nm_device_set_initial_unmanaged_flag (NMDevice *device, + NMUnmanagedFlags flag, + gboolean unmanaged) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (flag <= NM_UNMANAGED_LAST); + + priv = NM_DEVICE_GET_PRIVATE (device); + g_return_if_fail (priv->path == NULL); + + if (unmanaged) + priv->unmanaged_flags |= flag; + else + priv->unmanaged_flags &= ~flag; +} + +void +nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout) +{ + g_return_if_fail (NM_IS_DEVICE (device)); + + NM_DEVICE_GET_PRIVATE (device)->dhcp_timeout = timeout; +} + +void +nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (device)); + + priv = NM_DEVICE_GET_PRIVATE (device); + + if (priv->dhcp_anycast_address) { + g_byte_array_free (priv->dhcp_anycast_address, TRUE); + priv->dhcp_anycast_address = NULL; + } + + if (addr) { + priv->dhcp_anycast_address = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (priv->dhcp_anycast_address, addr, ETH_ALEN); + } +} + +gboolean +nm_device_get_autoconnect (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + return NM_DEVICE_GET_PRIVATE (device)->autoconnect; +} + +/** + * nm_device_connection_is_available(): + * @device: the #NMDevice + * @connection: the #NMConnection to check for availability + * @allow_device_override: set to %TRUE to let the device do specific checks + * + * Check if @connection is available to be activated on @device. Normally this + * only checks if the connection is in @device's AvailableConnections property. + * If @allow_device_override is %TRUE then the device is asked to do specific + * checks that may bypass the AvailableConnections property. + * + * Returns: %TRUE if @connection can be activated on @device + */ +gboolean +nm_device_connection_is_available (NMDevice *device, + NMConnection *connection, + gboolean allow_device_override) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + gboolean available = FALSE; + + if (nm_device_get_default_unmanaged (device) && (priv->state == NM_DEVICE_STATE_UNMANAGED)) { + /* default-unmanaged devices in UNMANAGED state have no available connections + * so we must manually check whether the connection is available here. + */ + if ( nm_device_check_connection_compatible (device, connection) + && NM_DEVICE_GET_CLASS (device)->check_connection_available (device, connection, NULL)) + return TRUE; + } + + available = !!g_hash_table_lookup (priv->available_connections, connection); + if (!available && allow_device_override) { + /* FIXME: hack for hidden WiFi becuase clients didn't consistently + * set the 'hidden' property to indicate hidden SSID networks. If + * activating but the network isn't available let the device recheck + * availability. + */ + if ( nm_device_check_connection_compatible (device, connection) + && NM_DEVICE_GET_CLASS (device)->check_connection_available_wifi_hidden) + available = NM_DEVICE_GET_CLASS (device)->check_connection_available_wifi_hidden (device, connection); + } + + return available; +} + +static void +_signal_available_connections_changed (NMDevice *device) +{ + g_object_notify (G_OBJECT (device), NM_DEVICE_AVAILABLE_CONNECTIONS); +} + +static void +_clear_available_connections (NMDevice *device, gboolean do_signal) +{ + g_hash_table_remove_all (NM_DEVICE_GET_PRIVATE (device)->available_connections); + if (do_signal == TRUE) + _signal_available_connections_changed (device); +} + +static gboolean +_try_add_available_connection (NMDevice *self, NMConnection *connection) +{ + if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED) + return FALSE; + + if (nm_device_check_connection_compatible (self, connection)) { + if (NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, NULL)) { + g_hash_table_insert (NM_DEVICE_GET_PRIVATE (self)->available_connections, + g_object_ref (connection), + GUINT_TO_POINTER (1)); + return TRUE; + } + } + return FALSE; +} + +static gboolean +_del_available_connection (NMDevice *device, NMConnection *connection) +{ + return g_hash_table_remove (NM_DEVICE_GET_PRIVATE (device)->available_connections, connection); +} + +static gboolean +connection_requires_carrier (NMConnection *connection) +{ + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + const char *method; + gboolean ip4_carrier_wanted = FALSE, ip6_carrier_wanted = FALSE; + gboolean ip4_used = FALSE, ip6_used = FALSE; + + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + if ( strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) != 0 + && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) != 0) { + ip4_carrier_wanted = TRUE; + + /* If IPv4 wants a carrier and cannot fail, the whole connection + * requires a carrier regardless of the IPv6 method. + */ + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (s_ip4 && !nm_setting_ip4_config_get_may_fail (s_ip4)) + return TRUE; + } + ip4_used = (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) != 0); + + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) != 0 + && strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) != 0) { + ip6_carrier_wanted = TRUE; + + /* If IPv6 wants a carrier and cannot fail, the whole connection + * requires a carrier regardless of the IPv4 method. + */ + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (s_ip6 && !nm_setting_ip6_config_get_may_fail (s_ip6)) + return TRUE; + } + ip6_used = (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) != 0); + + /* If an IP version wants a carrier and and the other IP version isn't + * used, the connection requires carrier since it will just fail without one. + */ + if (ip4_carrier_wanted && !ip6_used) + return TRUE; + if (ip6_carrier_wanted && !ip4_used) + return TRUE; + + /* If both want a carrier, the whole connection wants a carrier */ + return ip4_carrier_wanted && ip6_carrier_wanted; +} + +static gboolean +check_connection_available (NMDevice *device, + NMConnection *connection, + const char *specific_object) +{ + /* Connections which require a network connection are not available when + * the device has no carrier, even with ignore-carrer=TRUE. + */ + if (NM_DEVICE_GET_PRIVATE (device)->carrier == FALSE) + return connection_requires_carrier (connection) ? FALSE : TRUE; + + return TRUE; +} + +void +nm_device_recheck_available_connections (NMDevice *device) +{ + NMDevicePrivate *priv; + const GSList *connections, *iter; + + g_return_if_fail (NM_IS_DEVICE (device)); + + priv = NM_DEVICE_GET_PRIVATE(device); + + if (priv->con_provider) { + _clear_available_connections (device, FALSE); + + connections = nm_connection_provider_get_connections (priv->con_provider); + for (iter = connections; iter; iter = g_slist_next (iter)) + _try_add_available_connection (device, NM_CONNECTION (iter->data)); + + _signal_available_connections_changed (device); + } +} + +/** + * nm_device_get_available_connections: + * @device: the #NMDevice + * @specific_object: a specific object path if any + * + * Returns a list of connections available to activate on the device, taking + * into account any device-specific details given by @specific_object (like + * WiFi access point path). + * + * Returns: caller-owned #GPtrArray of #NMConnections + */ +GPtrArray * +nm_device_get_available_connections (NMDevice *device, const char *specific_object) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + GHashTableIter iter; + guint num_available; + NMConnection *connection = NULL; + GPtrArray *array = NULL; + + num_available = g_hash_table_size (priv->available_connections); + if (num_available > 0) { + array = g_ptr_array_sized_new (num_available); + g_hash_table_iter_init (&iter, priv->available_connections); + while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) { + /* If a specific object is given, only include connections that are + * compatible with it. + */ + if ( !specific_object + || NM_DEVICE_GET_CLASS (device)->check_connection_available (device, connection, specific_object)) + g_ptr_array_add (array, connection); + } + } + return array; +} + +static void +cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) +{ + if (_try_add_available_connection (NM_DEVICE (user_data), connection)) + _signal_available_connections_changed (NM_DEVICE (user_data)); +} + +static void +cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) +{ + if (_del_available_connection (NM_DEVICE (user_data), connection)) + _signal_available_connections_changed (NM_DEVICE (user_data)); +} + +static void +cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) +{ + gboolean added, deleted; + + /* FIXME: don't remove it from the hash if it's just going to get re-added */ + deleted = _del_available_connection (NM_DEVICE (user_data), connection); + added = _try_add_available_connection (NM_DEVICE (user_data), connection); + + /* Only signal if the connection was removed OR added, but not both */ + if (added != deleted) + _signal_available_connections_changed (NM_DEVICE (user_data)); +} + +gboolean +nm_device_supports_vlans (NMDevice *device) +{ + return nm_platform_link_supports_vlans (nm_device_get_ifindex (device)); +} + +/** + * nm_device_add_pending_action(): + * @device: the #NMDevice to add the pending action to + * @action: a static string that identifies the action + * @assert_not_yet_pending: if %TRUE, assert that the @action is currently not yet pending. + * Otherwise, ignore duplicate scheduling of the same action silently. + * + * Adds a pending action to the device. + * + * Returns: %TRUE if the action was added (and not already added before). %FALSE + * if the same action is already scheduled. In the latter case, the action was not scheduled + * a second time. + */ +gboolean +nm_device_add_pending_action (NMDevice *device, const char *action, gboolean assert_not_yet_pending) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + GSList *iter; + guint count = 0; + + g_return_val_if_fail (action, FALSE); + + /* Check if the action is already pending. Cannot add duplicate actions */ + for (iter = priv->pending_actions; iter; iter = iter->next) { + if (!strcmp (action, iter->data)) { + if (assert_not_yet_pending) { + nm_log_warn (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending", + nm_device_get_iface (device), + count + g_slist_length (iter), + action); + g_return_val_if_reached (FALSE); + } else { + nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending (expected)", + nm_device_get_iface (device), + count + g_slist_length (iter), + action); + } + return FALSE; + } + count++; + } + + priv->pending_actions = g_slist_append (priv->pending_actions, g_strdup (action)); + count++; + + nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s'", + nm_device_get_iface (device), + count, + action); + + if (count == 1) + g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION); + + return TRUE; +} + +/** + * nm_device_remove_pending_action(): + * @device: the #NMDevice to remove the pending action from + * @action: a static string that identifies the action + * @assert_is_pending: if %TRUE, assert that the @action is pending. + * If %FALSE, don't do anything if the current action is not pending and + * return %FALSE. + * + * Removes a pending action previously added by nm_device_add_pending_action(). + * + * Returns: whether the @action was pending and is now removed. + */ +gboolean +nm_device_remove_pending_action (NMDevice *device, const char *action, gboolean assert_is_pending) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + GSList *iter; + guint count = 0; + + g_return_val_if_fail (action, FALSE); + + for (iter = priv->pending_actions; iter; iter = iter->next) { + if (!strcmp (action, iter->data)) { + nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s'", + nm_device_get_iface (device), + count + g_slist_length (iter->next), /* length excluding 'iter' */ + action); + g_free (iter->data); + priv->pending_actions = g_slist_delete_link (priv->pending_actions, iter); + if (priv->pending_actions == NULL) + g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION); + return TRUE; + } + count++; + } + + if (assert_is_pending) { + nm_log_warn (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending", + nm_device_get_iface (device), + count, + action); + g_return_val_if_reached (FALSE); + } else { + nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending (expected)", + nm_device_get_iface (device), + count, + action); + } + return FALSE; +} + +gboolean +nm_device_has_pending_action (NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + + return !!priv->pending_actions; +} + +const char * +nm_device_get_physical_port_id (NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + + return priv->physical_port_id; +} + +/** + * nm_device_get_mtu: + * @device: the #NMDevice + * + * Returns: MTU of the #NMDevice + */ +guint32 +nm_device_get_mtu (NMDevice *device) +{ + return NM_DEVICE_GET_PRIVATE (device)->mtu; +} + +/***********************************************************/ + +/* + * nm_device_cleanup + * + * Remove a device's routing table entries and IP addresses. + * + */ +static void +nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason) +{ + NMDevicePrivate *priv; + NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; + NMConnection *connection = NULL; + int ifindex; + + g_return_if_fail (NM_IS_DEVICE (self)); + + if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED) { + nm_log_info (LOGD_DEVICE, "(%s): preparing device", + nm_device_get_iface (self)); + } else { + nm_log_info (LOGD_DEVICE, "(%s): deactivating device (reason '%s') [%d]", + nm_device_get_iface (self), reason_to_string (reason), reason); + } + + /* Save whether or not we tried IPv6 for later */ + priv = NM_DEVICE_GET_PRIVATE (self); + + /* Clean up when device was deactivated during call to firewall */ + if (priv->fw_call) { + nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call); + priv->fw_call = NULL; + } + + if (priv->act_request) + connection = nm_act_request_get_connection (priv->act_request); + if (connection) { + nm_firewall_manager_remove_from_zone (priv->fw_manager, + nm_device_get_ip_iface (self), + NULL); + } + + ip_check_gw_ping_cleanup (self); + + /* Break the activation chain */ + activation_source_clear (self, TRUE, AF_INET); + activation_source_clear (self, TRUE, AF_INET6); + + /* Clear any queued transitions */ + nm_device_queued_state_clear (self); + nm_device_queued_ip_config_change_clear (self); + + priv->ip4_state = priv->ip6_state = IP_NONE; + + dhcp4_cleanup (self, TRUE, FALSE); + arp_cleanup (self); + dhcp6_cleanup (self, TRUE, FALSE); + linklocal6_cleanup (self); + addrconf6_cleanup (self); + dnsmasq_cleanup (self); + aipd_cleanup (self); + + /* Turn off kernel IPv6 */ + nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); + nm_device_ipv6_sysctl_set (self, "accept_ra", "0"); + nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0"); + + /* Call device type-specific deactivation */ + if (NM_DEVICE_GET_CLASS (self)->deactivate) + NM_DEVICE_GET_CLASS (self)->deactivate (self); + + /* master: release slaves */ + nm_device_master_release_slaves (self); + + /* slave: mark no longer enslaved */ + g_clear_object (&priv->master); + priv->enslaved = FALSE; + g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER); + + /* Tear down an existing activation request */ + clear_act_request (self); + + /* Take out any entries in the routing table and any IP address the device had. */ + ifindex = nm_device_get_ip_ifindex (self); + if (ifindex > 0) { + nm_platform_route_flush (ifindex); + nm_platform_address_flush (ifindex); + } + + /* Clean up nameservers and addresses */ + nm_device_set_ip4_config (self, NULL, TRUE, &ignored); + nm_device_set_ip6_config (self, NULL, TRUE, &ignored); + g_clear_object (&priv->ext_ip4_config); + g_clear_object (&priv->vpn4_config); + g_clear_object (&priv->vpn6_config); + g_clear_object (&priv->ext_ip6_config); + + /* Clear legacy IPv4 address property */ + priv->ip4_address = 0; + g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS); + + /* Only clear ip_iface after flushing all routes and addreses, since + * those are identified by ip_iface, not by iface (which might be a tty + * or ATM device). + */ + nm_device_set_ip_iface (self, NULL); + + /* Check if the device was deactivated, and if so, delete_link. + * Don't call delete_link synchronously because we are currently + * handling a state change -- which is not reentrant. */ + delete_on_deactivate_check_and_schedule (self, ifindex); +} + +/***********************************************************/ + +static gboolean +ip_config_valid (NMDeviceState state) +{ + return (state == NM_DEVICE_STATE_UNMANAGED) || + (state >= NM_DEVICE_STATE_IP_CHECK && + state <= NM_DEVICE_STATE_DEACTIVATING); } static void @@ -7086,654 +6842,30 @@ nm_device_get_state (NMDevice *device) return NM_DEVICE_GET_PRIVATE (device)->state; } -static NMIP4Config * -find_ip4_lease_config (NMDevice *device, - NMConnection *connection, - NMIP4Config *ext_ip4_config) +/***********************************************************/ +/* NMConfigDevice interface related stuff */ + +static guint +nm_device_get_hw_address_length (NMDevice *dev, gboolean *out_permanent) { - const char *ip_iface = nm_device_get_ip_iface (device); - GSList *leases, *liter; - NMIP4Config *found = NULL; - - g_return_val_if_fail (NM_IS_IP4_CONFIG (ext_ip4_config), NULL); - g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - - leases = nm_dhcp_manager_get_lease_ip_configs (nm_dhcp_manager_get (), - ip_iface, - nm_connection_get_uuid (connection), - FALSE); - for (liter = leases; liter && !found; liter = liter->next) { - NMIP4Config *lease_config = liter->data; - const NMPlatformIP4Address *address = nm_ip4_config_get_address (lease_config, 0); - guint32 gateway = nm_ip4_config_get_gateway (lease_config); - - g_assert (address); - if (!nm_ip4_config_address_exists (ext_ip4_config, address)) - continue; - if (gateway != nm_ip4_config_get_gateway (ext_ip4_config)) - continue; - found = g_object_ref (lease_config); - } - - g_slist_free_full (leases, g_object_unref); - return found; + return NM_DEVICE_GET_CLASS (dev)->get_hw_address_length (dev, out_permanent); } -static void -capture_lease_config (NMDevice *device, - NMIP4Config *ext_ip4_config, - NMIP4Config **out_ip4_config, - NMIP6Config *ext_ip6_config, - NMIP6Config **out_ip6_config) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - const GSList *connections, *citer; - guint i; - gboolean dhcp_used = FALSE; - - /* Ensure at least one address on the device has a non-infinite lifetime, - * otherwise DHCP cannot possibly be active on the device right now. - */ - if (ext_ip4_config && out_ip4_config) { - for (i = 0; i < nm_ip4_config_get_num_addresses (ext_ip4_config); i++) { - const NMPlatformIP4Address *addr = nm_ip4_config_get_address (ext_ip4_config, i); - - if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { - dhcp_used = TRUE; - break; - } - } - } else if (ext_ip6_config && out_ip6_config) { - for (i = 0; i < nm_ip6_config_get_num_addresses (ext_ip6_config); i++) { - const NMPlatformIP6Address *addr = nm_ip6_config_get_address (ext_ip6_config, i); - - if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { - dhcp_used = TRUE; - break; - } - } - } else { - g_return_if_fail ( (ext_ip6_config && out_ip6_config) - || (ext_ip4_config && out_ip4_config)); - } - - if (!dhcp_used) - return; - - connections = nm_connection_provider_get_connections (priv->con_provider); - for (citer = connections; citer; citer = citer->next) { - NMConnection *candidate = citer->data; - const char *method; - - if (!nm_device_check_connection_compatible (device, candidate)) - continue; - - /* IPv4 leases */ - method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG); - if (out_ip4_config && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { - *out_ip4_config = find_ip4_lease_config (device, candidate, ext_ip4_config); - if (*out_ip4_config) - return; - } - - /* IPv6 leases */ - method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP6_CONFIG); - if (out_ip6_config && strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) { - /* FIXME: implement find_ip6_lease_config() */ - } - } -} - -static void -update_ip_config (NMDevice *self, gboolean initial) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ifindex; - gboolean linklocal6_just_completed = FALSE; - gboolean capture_resolv_conf; - NMDnsManagerResolvConfMode resolv_conf_mode; - - ifindex = nm_device_get_ip_ifindex (self); - if (!ifindex) - return; - - resolv_conf_mode = nm_dns_manager_get_resolv_conf_mode (nm_dns_manager_get ()); - capture_resolv_conf = initial && (resolv_conf_mode == NM_DNS_MANAGER_RESOLV_CONF_EXPLICIT); - - /* IPv4 */ - g_clear_object (&priv->ext_ip4_config); - priv->ext_ip4_config = nm_ip4_config_capture (ifindex, capture_resolv_conf); - - if (priv->ext_ip4_config) { - if (initial) { - g_clear_object (&priv->dev_ip4_config); - capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL); - } - if (priv->dev_ip4_config) - nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config); - if (priv->vpn4_config) - nm_ip4_config_subtract (priv->ext_ip4_config, priv->vpn4_config); - - ip4_config_merge_and_apply (self, NULL, FALSE, NULL); - } - - /* IPv6 */ - g_clear_object (&priv->ext_ip6_config); - priv->ext_ip6_config = nm_ip6_config_capture (ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - if (priv->ext_ip6_config) { - - /* Check this before modifying ext_ip6_config */ - linklocal6_just_completed = priv->linklocal6_timeout_id && - linklocal6_config_is_ready (priv->ext_ip6_config); - - if (priv->ac_ip6_config) - nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config); - if (priv->dhcp6_ip6_config) - nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6_ip6_config); - if (priv->vpn6_config) - nm_ip6_config_subtract (priv->ext_ip6_config, priv->vpn6_config); - - ip6_config_merge_and_apply (self, FALSE, NULL); - } - - if (linklocal6_just_completed) { - /* linklocal6 is ready now, do the state transition... we are also - * invoked as g_idle_add, so no problems with reentrance doing it now. - */ - linklocal6_complete (self); - } -} - -void -nm_device_capture_initial_config (NMDevice *dev) -{ - update_ip_config (dev, TRUE); -} - -static gboolean -queued_ip_config_change (gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - /* Wait for any queued state changes */ - if (priv->queued_state.id) - return TRUE; - - priv->queued_ip_config_id = 0; - update_ip_config (self, FALSE); - return FALSE; -} - -static void -device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformSignalChangeType change_type, NMPlatformReason reason, gpointer user_data) -{ - NMDevice *self = user_data; - - if (nm_device_get_ip_ifindex (self) == ifindex) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (!priv->queued_ip_config_id) - priv->queued_ip_config_id = g_idle_add (queued_ip_config_change, self); - - nm_log_dbg (LOGD_DEVICE, "(%s): queued IP config change", - nm_device_get_iface (self)); - } -} - -void -nm_device_queued_ip_config_change_clear (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->queued_ip_config_id) { - nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued IP config change", - nm_device_get_iface (self)); - g_source_remove (priv->queued_ip_config_id); - priv->queued_ip_config_id = 0; - } -} - -/** - * nm_device_get_managed(): - * @device: the #NMDevice - * - * Returns: %TRUE if the device is managed - */ -gboolean -nm_device_get_managed (NMDevice *device) +const guint8 * +nm_device_get_hw_address (NMDevice *dev, guint *out_len) { NMDevicePrivate *priv; - gboolean managed; - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + g_return_val_if_fail (NM_IS_DEVICE (dev), NULL); + priv = NM_DEVICE_GET_PRIVATE (dev); - priv = NM_DEVICE_GET_PRIVATE (device); + if (out_len) + *out_len = priv->hw_addr_len; - /* Return the composite of all managed flags. However, if the device - * is a default-unmanaged device, and would be managed except for the - * default-unmanaged flag (eg, only NM_UNMANAGED_DEFAULT is set) then - * the device is managed whenever it's not in the UNMANAGED state. - */ - managed = !(priv->unmanaged_flags & ~NM_UNMANAGED_DEFAULT); - if (managed && (priv->unmanaged_flags & NM_UNMANAGED_DEFAULT)) - managed = (priv->state > NM_DEVICE_STATE_UNMANAGED); - - return managed; -} - -/** - * nm_device_get_default_unmanaged(): - * @device: the #NMDevice - * - * Returns: %TRUE if the device is by default unmanaged - */ -gboolean -nm_device_get_default_unmanaged (NMDevice *device) -{ - return nm_device_get_unmanaged_flag (device, NM_UNMANAGED_DEFAULT); -} - -/** - * nm_device_get_unmanaged_flag(): - * @device: the #NMDevice - * - * Returns: %TRUE if the device is unmanaged for @flag. - */ -gboolean -nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag) -{ - return NM_DEVICE_GET_PRIVATE (device)->unmanaged_flags & flag; -} - -void -nm_device_set_unmanaged (NMDevice *device, - NMUnmanagedFlags flag, - gboolean unmanaged, - NMDeviceStateReason reason) -{ - NMDevicePrivate *priv; - gboolean was_managed, now_managed; - - g_return_if_fail (NM_IS_DEVICE (device)); - g_return_if_fail (flag <= NM_UNMANAGED_LAST); - - priv = NM_DEVICE_GET_PRIVATE (device); - - was_managed = nm_device_get_managed (device); - if (unmanaged) - priv->unmanaged_flags |= flag; + if (priv->hw_addr_len == 0) + return NULL; else - priv->unmanaged_flags &= ~flag; - now_managed = nm_device_get_managed (device); - - if (was_managed != now_managed) { - nm_log_dbg (LOGD_DEVICE, "(%s): now %s", - nm_device_get_iface (device), - unmanaged ? "unmanaged" : "managed"); - - g_object_notify (G_OBJECT (device), NM_DEVICE_MANAGED); - - if (unmanaged) - nm_device_state_changed (device, NM_DEVICE_STATE_UNMANAGED, reason); - else - nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE, reason); - } -} - -/** - * nm_device_set_initial_unmanaged_flag(): - * @device: the #NMDevice - * @flag: an #NMUnmanagedFlag - * @unmanaged: %TRUE or %FALSE to set or clear @flag - * - * Like nm_device_set_unmanaged() but must be set before the device is exported - * and does not trigger state changes. Should only be used when initializing - * a device. - */ -void -nm_device_set_initial_unmanaged_flag (NMDevice *device, - NMUnmanagedFlags flag, - gboolean unmanaged) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (device)); - g_return_if_fail (flag <= NM_UNMANAGED_LAST); - - priv = NM_DEVICE_GET_PRIVATE (device); - g_return_if_fail (priv->path == NULL); - - if (unmanaged) - priv->unmanaged_flags |= flag; - else - priv->unmanaged_flags &= ~flag; -} - -/** - * nm_device_spec_match_list: - * @device: an #NMDevice - * @specs: (element-type utf8): a list of device specs - * - * Checks if @device matches any of the specifications in @specs. The - * currently-supported spec types are: - * - * "mac:00:11:22:33:44:55" - matches a device with the given - * hardware address - * - * "interface-name:foo0" - matches a device with the given - * interface name - * - * "s390-subchannels:00.11.22" - matches a device with the given - * z/VM / s390 subchannels. - * - * "*" - matches any device - * - * Returns: #TRUE if @device matches one of the specs in @specs - */ -gboolean -nm_device_spec_match_list (NMDevice *device, const GSList *specs) -{ - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - - if (!specs) - return FALSE; - - return NM_DEVICE_GET_CLASS (device)->spec_match_list (device, specs); -} - -static gboolean -spec_match_list (NMDevice *device, const GSList *specs) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - char *hwaddr_str; - gboolean matched = FALSE; - - if (nm_match_spec_string (specs, "*")) - return TRUE; - - if (priv->hw_addr_len) { - hwaddr_str = nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len); - matched = nm_match_spec_hwaddr (specs, hwaddr_str); - g_free (hwaddr_str); - } - - if (!matched) - matched = nm_match_spec_interface_name (specs, nm_device_get_iface (device)); - - return matched; -} - -void -nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout) -{ - g_return_if_fail (NM_IS_DEVICE (device)); - - NM_DEVICE_GET_PRIVATE (device)->dhcp_timeout = timeout; -} - -void -nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (device)); - - priv = NM_DEVICE_GET_PRIVATE (device); - - if (priv->dhcp_anycast_address) { - g_byte_array_free (priv->dhcp_anycast_address, TRUE); - priv->dhcp_anycast_address = NULL; - } - - if (addr) { - priv->dhcp_anycast_address = g_byte_array_sized_new (ETH_ALEN); - g_byte_array_append (priv->dhcp_anycast_address, addr, ETH_ALEN); - } -} - -gboolean -nm_device_get_autoconnect (NMDevice *device) -{ - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - - return NM_DEVICE_GET_PRIVATE (device)->autoconnect; -} - -/** - * nm_device_connection_is_available(): - * @device: the #NMDevice - * @connection: the #NMConnection to check for availability - * @allow_device_override: set to %TRUE to let the device do specific checks - * - * Check if @connection is available to be activated on @device. Normally this - * only checks if the connection is in @device's AvailableConnections property. - * If @allow_device_override is %TRUE then the device is asked to do specific - * checks that may bypass the AvailableConnections property. - * - * Returns: %TRUE if @connection can be activated on @device - */ -gboolean -nm_device_connection_is_available (NMDevice *device, - NMConnection *connection, - gboolean allow_device_override) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - gboolean available = FALSE; - - if (nm_device_get_default_unmanaged (device) && (priv->state == NM_DEVICE_STATE_UNMANAGED)) { - /* default-unmanaged devices in UNMANAGED state have no available connections - * so we must manually check whether the connection is available here. - */ - if ( nm_device_check_connection_compatible (device, connection) - && NM_DEVICE_GET_CLASS (device)->check_connection_available (device, connection, NULL)) - return TRUE; - } - - available = !!g_hash_table_lookup (priv->available_connections, connection); - if (!available && allow_device_override) { - /* FIXME: hack for hidden WiFi becuase clients didn't consistently - * set the 'hidden' property to indicate hidden SSID networks. If - * activating but the network isn't available let the device recheck - * availability. - */ - if ( nm_device_check_connection_compatible (device, connection) - && NM_DEVICE_GET_CLASS (device)->check_connection_available_wifi_hidden) - available = NM_DEVICE_GET_CLASS (device)->check_connection_available_wifi_hidden (device, connection); - } - - return available; -} - -static void -_signal_available_connections_changed (NMDevice *device) -{ - g_object_notify (G_OBJECT (device), NM_DEVICE_AVAILABLE_CONNECTIONS); -} - -static void -_clear_available_connections (NMDevice *device, gboolean do_signal) -{ - g_hash_table_remove_all (NM_DEVICE_GET_PRIVATE (device)->available_connections); - if (do_signal == TRUE) - _signal_available_connections_changed (device); -} - -static gboolean -_try_add_available_connection (NMDevice *self, NMConnection *connection) -{ - if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED) - return FALSE; - - if (nm_device_check_connection_compatible (self, connection)) { - if (NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, NULL)) { - g_hash_table_insert (NM_DEVICE_GET_PRIVATE (self)->available_connections, - g_object_ref (connection), - GUINT_TO_POINTER (1)); - return TRUE; - } - } - return FALSE; -} - -static gboolean -_del_available_connection (NMDevice *device, NMConnection *connection) -{ - return g_hash_table_remove (NM_DEVICE_GET_PRIVATE (device)->available_connections, connection); -} - -static gboolean -connection_requires_carrier (NMConnection *connection) -{ - NMSettingIP4Config *s_ip4; - NMSettingIP6Config *s_ip6; - const char *method; - gboolean ip4_carrier_wanted = FALSE, ip6_carrier_wanted = FALSE; - gboolean ip4_used = FALSE, ip6_used = FALSE; - - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if ( strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) != 0 - && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) != 0) { - ip4_carrier_wanted = TRUE; - - /* If IPv4 wants a carrier and cannot fail, the whole connection - * requires a carrier regardless of the IPv6 method. - */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4 && !nm_setting_ip4_config_get_may_fail (s_ip4)) - return TRUE; - } - ip4_used = (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) != 0); - - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) != 0 - && strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) != 0) { - ip6_carrier_wanted = TRUE; - - /* If IPv6 wants a carrier and cannot fail, the whole connection - * requires a carrier regardless of the IPv4 method. - */ - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (s_ip6 && !nm_setting_ip6_config_get_may_fail (s_ip6)) - return TRUE; - } - ip6_used = (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) != 0); - - /* If an IP version wants a carrier and and the other IP version isn't - * used, the connection requires carrier since it will just fail without one. - */ - if (ip4_carrier_wanted && !ip6_used) - return TRUE; - if (ip6_carrier_wanted && !ip4_used) - return TRUE; - - /* If both want a carrier, the whole connection wants a carrier */ - return ip4_carrier_wanted && ip6_carrier_wanted; -} - -static gboolean -check_connection_available (NMDevice *device, - NMConnection *connection, - const char *specific_object) -{ - /* Connections which require a network connection are not available when - * the device has no carrier, even with ignore-carrer=TRUE. - */ - if (NM_DEVICE_GET_PRIVATE (device)->carrier == FALSE) - return connection_requires_carrier (connection) ? FALSE : TRUE; - - return TRUE; -} - -void -nm_device_recheck_available_connections (NMDevice *device) -{ - NMDevicePrivate *priv; - const GSList *connections, *iter; - - g_return_if_fail (NM_IS_DEVICE (device)); - - priv = NM_DEVICE_GET_PRIVATE(device); - - if (priv->con_provider) { - _clear_available_connections (device, FALSE); - - connections = nm_connection_provider_get_connections (priv->con_provider); - for (iter = connections; iter; iter = g_slist_next (iter)) - _try_add_available_connection (device, NM_CONNECTION (iter->data)); - - _signal_available_connections_changed (device); - } -} - -/** - * nm_device_get_available_connections: - * @device: the #NMDevice - * @specific_object: a specific object path if any - * - * Returns a list of connections available to activate on the device, taking - * into account any device-specific details given by @specific_object (like - * WiFi access point path). - * - * Returns: caller-owned #GPtrArray of #NMConnections - */ -GPtrArray * -nm_device_get_available_connections (NMDevice *device, const char *specific_object) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - GHashTableIter iter; - guint num_available; - NMConnection *connection = NULL; - GPtrArray *array = NULL; - - num_available = g_hash_table_size (priv->available_connections); - if (num_available > 0) { - array = g_ptr_array_sized_new (num_available); - g_hash_table_iter_init (&iter, priv->available_connections); - while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) { - /* If a specific object is given, only include connections that are - * compatible with it. - */ - if ( !specific_object - || NM_DEVICE_GET_CLASS (device)->check_connection_available (device, connection, specific_object)) - g_ptr_array_add (array, connection); - } - } - return array; -} - -static void -cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) -{ - if (_try_add_available_connection (NM_DEVICE (user_data), connection)) - _signal_available_connections_changed (NM_DEVICE (user_data)); -} - -static void -cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) -{ - if (_del_available_connection (NM_DEVICE (user_data), connection)) - _signal_available_connections_changed (NM_DEVICE (user_data)); -} - -static void -cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) -{ - gboolean added, deleted; - - /* FIXME: don't remove it from the hash if it's just going to get re-added */ - deleted = _del_available_connection (NM_DEVICE (user_data), connection); - added = _try_add_available_connection (NM_DEVICE (user_data), connection); - - /* Only signal if the connection was removed OR added, but not both */ - if (added != deleted) - _signal_available_connections_changed (NM_DEVICE (user_data)); -} - -gboolean -nm_device_supports_vlans (NMDevice *device) -{ - return nm_platform_link_supports_vlans (nm_device_get_ifindex (device)); + return priv->hw_addr; } gboolean @@ -7844,137 +6976,983 @@ nm_device_set_hw_addr (NMDevice *device, const guint8 *addr, } /** - * nm_device_add_pending_action(): - * @device: the #NMDevice to add the pending action to - * @action: a static string that identifies the action - * @assert_not_yet_pending: if %TRUE, assert that the @action is currently not yet pending. - * Otherwise, ignore duplicate scheduling of the same action silently. + * nm_device_spec_match_list: + * @device: an #NMDevice + * @specs: (element-type utf8): a list of device specs * - * Adds a pending action to the device. + * Checks if @device matches any of the specifications in @specs. The + * currently-supported spec types are: * - * Returns: %TRUE if the action was added (and not already added before). %FALSE - * if the same action is already scheduled. In the latter case, the action was not scheduled - * a second time. + * "mac:00:11:22:33:44:55" - matches a device with the given + * hardware address + * + * "interface-name:foo0" - matches a device with the given + * interface name + * + * "s390-subchannels:00.11.22" - matches a device with the given + * z/VM / s390 subchannels. + * + * "*" - matches any device + * + * Returns: #TRUE if @device matches one of the specs in @specs */ gboolean -nm_device_add_pending_action (NMDevice *device, const char *action, gboolean assert_not_yet_pending) +nm_device_spec_match_list (NMDevice *device, const GSList *specs) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + if (!specs) + return FALSE; + + return NM_DEVICE_GET_CLASS (device)->spec_match_list (device, specs); +} + +static gboolean +spec_match_list (NMDevice *device, const GSList *specs) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - GSList *iter; - guint count = 0; + char *hwaddr_str; + gboolean matched = FALSE; - g_return_val_if_fail (action, FALSE); + if (nm_match_spec_string (specs, "*")) + return TRUE; - /* Check if the action is already pending. Cannot add duplicate actions */ - for (iter = priv->pending_actions; iter; iter = iter->next) { - if (!strcmp (action, iter->data)) { - if (assert_not_yet_pending) { - nm_log_warn (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending", - nm_device_get_iface (device), - count + g_slist_length (iter), - action); - g_return_val_if_reached (FALSE); - } else { - nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending (expected)", - nm_device_get_iface (device), - count + g_slist_length (iter), - action); - } - return FALSE; - } - count++; + if (priv->hw_addr_len) { + hwaddr_str = nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len); + matched = nm_match_spec_hwaddr (specs, hwaddr_str); + g_free (hwaddr_str); } - priv->pending_actions = g_slist_append (priv->pending_actions, g_strdup (action)); - count++; + if (!matched) + matched = nm_match_spec_interface_name (specs, nm_device_get_iface (device)); - nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s'", - nm_device_get_iface (device), - count, - action); + return matched; +} - if (count == 1) - g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION); +static guint +get_hw_address_length (NMDevice *dev, gboolean *out_permanent) +{ + size_t len; + if (nm_platform_link_get_address (nm_device_get_ip_ifindex (dev), &len)) + return len; + else + return 0; +} + +/***********************************************************/ + +#define DEFAULT_AUTOCONNECT TRUE + +static void +nm_device_init (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->type = NM_DEVICE_TYPE_UNKNOWN; + priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED; + priv->state = NM_DEVICE_STATE_UNMANAGED; + priv->state_reason = NM_DEVICE_STATE_REASON_NONE; + priv->dhcp_timeout = 0; + priv->rfkill_type = RFKILL_TYPE_UNKNOWN; + priv->autoconnect = DEFAULT_AUTOCONNECT; + priv->unmanaged_flags = NM_UNMANAGED_INTERNAL; + priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); + priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); +} + +static void +nm_device_config_device_interface_init (NMConfigDeviceInterface *iface) +{ + iface->spec_match_list = (gboolean (*) (NMConfigDevice *, const GSList *)) nm_device_spec_match_list; + iface->get_hw_address = (const guint8 * (*) (NMConfigDevice *, guint *)) nm_device_get_hw_address; +} + +/* + * Get driver info from SIOCETHTOOL ioctl() for 'iface' + * Returns driver and firmware versions to 'driver_version and' 'firmware_version' + */ +static gboolean +device_get_driver_info (const char *iface, char **driver_version, char **firmware_version) +{ + struct ethtool_drvinfo drvinfo; + struct ifreq req; + int fd; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_warn (LOGD_HW, "couldn't open control socket."); + return FALSE; + } + + /* Get driver and firmware version info */ + memset (&drvinfo, 0, sizeof (drvinfo)); + memset (&req, 0, sizeof (struct ifreq)); + strncpy (req.ifr_name, iface, IFNAMSIZ); + drvinfo.cmd = ETHTOOL_GDRVINFO; + req.ifr_data = &drvinfo; + + errno = 0; + if (ioctl (fd, SIOCETHTOOL, &req) < 0) { + nm_log_dbg (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d", + iface, errno); + close (fd); + return FALSE; + } + if (driver_version) + *driver_version = g_strdup (drvinfo.version); + if (firmware_version) + *firmware_version = g_strdup (drvinfo.fw_version); + + close (fd); return TRUE; } -/** - * nm_device_remove_pending_action(): - * @device: the #NMDevice to remove the pending action from - * @action: a static string that identifies the action - * @assert_is_pending: if %TRUE, assert that the @action is pending. - * If %FALSE, don't do anything if the current action is not pending and - * return %FALSE. - * - * Removes a pending action previously added by nm_device_add_pending_action(). - * - * Returns: whether the @action was pending and is now removed. - */ -gboolean -nm_device_remove_pending_action (NMDevice *device, const char *action, gboolean assert_is_pending) +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - GSList *iter; - guint count = 0; + GObject *object; + NMDevice *dev; + NMDevicePrivate *priv; + NMPlatform *platform; + static guint32 id = 0; - g_return_val_if_fail (action, FALSE); + object = G_OBJECT_CLASS (nm_device_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; - for (iter = priv->pending_actions; iter; iter = iter->next) { - if (!strcmp (action, iter->data)) { - nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s'", - nm_device_get_iface (device), - count + g_slist_length (iter->next), /* length excluding 'iter' */ - action); - g_free (iter->data); - priv->pending_actions = g_slist_delete_link (priv->pending_actions, iter); - if (priv->pending_actions == NULL) - g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION); - return TRUE; - } - count++; + dev = NM_DEVICE (object); + priv = NM_DEVICE_GET_PRIVATE (dev); + + if (!priv->iface) { + nm_log_err (LOGD_DEVICE, "No device interface provided, ignoring"); + goto error; } - if (assert_is_pending) { - nm_log_warn (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending", - nm_device_get_iface (device), - count, - action); - g_return_val_if_reached (FALSE); + if (!priv->udi) { + /* Use a placeholder UDI until we get a real one */ + priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++); + } + + if (NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities) + priv->capabilities |= NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities (dev); + + priv->fw_manager = nm_firewall_manager_get (); + + device_get_driver_info (priv->iface, &priv->driver_version, &priv->firmware_version); + + /* Watch for external IP config changes */ + platform = nm_platform_get (); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), dev); + + priv->initialized = TRUE; + return object; + +error: + g_object_unref (dev); + return NULL; +} + +static void +constructed (GObject *object) +{ + NMDevice *dev = NM_DEVICE (object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); + + nm_device_update_hw_address (dev); + + if (NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address) + NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address (dev); + + if (NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address) + NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address (dev); + + /* Have to call update_initial_hw_address() before calling get_ignore_carrier() */ + if (device_has_capability (dev, NM_DEVICE_CAP_CARRIER_DETECT)) { + priv->ignore_carrier = nm_config_get_ignore_carrier (nm_config_get (), NM_CONFIG_DEVICE (dev)); + + check_carrier (dev); + nm_log_info (LOGD_HW, + "(%s): carrier is %s%s", + nm_device_get_iface (NM_DEVICE (dev)), + priv->carrier ? "ON" : "OFF", + priv->ignore_carrier ? " (but ignored)" : ""); } else { - nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending (expected)", - nm_device_get_iface (device), - count, - action); + /* Fake online link when carrier detection is not available. */ + priv->carrier = TRUE; } - return FALSE; + + if (priv->ifindex > 0) { + priv->is_software = nm_platform_link_is_software (priv->ifindex); + priv->physical_port_id = nm_platform_link_get_physical_port_id (priv->ifindex); + } + + if (priv->ifindex > 0) + priv->mtu = nm_platform_link_get_mtu (priv->ifindex); + + priv->con_provider = nm_connection_provider_get (); + g_assert (priv->con_provider); + g_signal_connect (priv->con_provider, + NM_CP_SIGNAL_CONNECTION_ADDED, + G_CALLBACK (cp_connection_added), + dev); + + g_signal_connect (priv->con_provider, + NM_CP_SIGNAL_CONNECTION_REMOVED, + G_CALLBACK (cp_connection_removed), + dev); + + g_signal_connect (priv->con_provider, + NM_CP_SIGNAL_CONNECTION_UPDATED, + G_CALLBACK (cp_connection_updated), + dev); + + G_OBJECT_CLASS (nm_device_parent_class)->constructed (object); } -gboolean -nm_device_has_pending_action (NMDevice *device) +static void +dispose (GObject *object) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevice *self = NM_DEVICE (object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean deconfigure = TRUE; + NMPlatform *platform; - return !!priv->pending_actions; + if (priv->disposed || !priv->initialized) + goto out; + + priv->disposed = TRUE; + + /* Don't down can-assume-connection capable devices that are activated with + * a connection that can be assumed. + */ + if (nm_device_can_assume_connections (self) && (priv->state == NM_DEVICE_STATE_ACTIVATED)) { + NMConnection *connection; + const char *method; + + connection = nm_device_get_connection (self); + if (connection) { + /* Only static or DHCP IPv4 connections can be left up. + * All IPv6 connections can be left up, so we don't have + * to check that. + */ + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + if ( !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) + || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) + || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + deconfigure = FALSE; + } + } + + ip_check_gw_ping_cleanup (self); + + /* Clear any queued transitions */ + nm_device_queued_state_clear (self); + nm_device_queued_ip_config_change_clear (self); + + /* Clean up and stop address configuration */ + dhcp4_cleanup (self, deconfigure, FALSE); + arp_cleanup (self); + dhcp6_cleanup (self, deconfigure, FALSE); + linklocal6_cleanup (self); + addrconf6_cleanup (self); + dnsmasq_cleanup (self); + + g_warn_if_fail (priv->slaves == NULL); + g_assert (priv->master_ready_id == 0); + + /* Take the device itself down and clear its IP configuration */ + if (nm_device_get_managed (self) && deconfigure) { + NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; + + if (nm_device_get_act_request (self)) + nm_device_cleanup (self, NM_DEVICE_STATE_REASON_REMOVED); + nm_device_set_ip4_config (self, NULL, TRUE, &ignored); + nm_device_set_ip6_config (self, NULL, TRUE, &ignored); + + nm_device_take_down (self, FALSE); + + restore_ip6_properties (self); + + /* do a final check whether we should delete_link */ + delete_on_deactivate_check_and_schedule (self, nm_device_get_ip_ifindex (self)); + } + g_clear_object (&priv->dev_ip4_config); + g_clear_object (&priv->ext_ip4_config); + g_clear_object (&priv->vpn4_config); + g_clear_object (&priv->ip4_config); + + g_clear_object (&priv->ip6_config); + g_clear_object (&priv->ac_ip6_config); + g_clear_object (&priv->dhcp6_ip6_config); + g_clear_object (&priv->vpn6_config); + g_clear_object (&priv->ext_ip6_config); + + g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref); + + if (priv->recheck_assume_id) { + g_source_remove (priv->recheck_assume_id); + priv->recheck_assume_id = 0; + } + + link_disconnect_action_cancel (self); + + if (priv->con_provider) { + g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_added, self); + g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_removed, self); + g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_updated, self); + priv->con_provider = NULL; + } + + g_hash_table_unref (priv->available_connections); + priv->available_connections = NULL; + + if (priv->carrier_wait_id) { + g_source_remove (priv->carrier_wait_id); + priv->carrier_wait_id = 0; + } + + g_clear_pointer (&priv->physical_port_id, g_free); + + activation_source_clear (self, TRUE, AF_INET); + activation_source_clear (self, TRUE, AF_INET6); + + clear_act_request (self); + g_clear_object (&priv->queued_act_request); + + platform = nm_platform_get (); + g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self); + g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); + +out: + G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); } -const char * -nm_device_get_physical_port_id (NMDevice *device) +static void +finalize (GObject *object) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevice *self = NM_DEVICE (object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - return priv->physical_port_id; + if (priv->fw_manager) + g_object_unref (priv->fw_manager); + + g_slist_free_full (priv->pending_actions, g_free); + + g_free (priv->udi); + g_free (priv->path); + g_free (priv->iface); + g_free (priv->ip_iface); + g_free (priv->driver); + g_free (priv->driver_version); + g_free (priv->firmware_version); + g_free (priv->type_desc); + if (priv->dhcp_anycast_address) + g_byte_array_free (priv->dhcp_anycast_address, TRUE); + + G_OBJECT_CLASS (nm_device_parent_class)->finalize (object); } -/** - * nm_device_get_mtu: - * @device: the #NMDevice - * - * Returns: MTU of the #NMDevice - */ -guint32 -nm_device_get_mtu (NMDevice *device) +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) { - return NM_DEVICE_GET_PRIVATE (device)->mtu; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); + NMPlatformLink *platform_device; + const char *hw_addr; + + switch (prop_id) { + case PROP_PLATFORM_DEVICE: + platform_device = g_value_get_pointer (value); + if (platform_device) { + g_free (priv->udi); + priv->udi = g_strdup (platform_device->udi); + g_free (priv->iface); + priv->iface = g_strdup (platform_device->name); + priv->ifindex = platform_device->ifindex; + g_free (priv->driver); + priv->driver = g_strdup (platform_device->driver); + } + break; + case PROP_UDI: + if (g_value_get_string (value)) { + g_free (priv->udi); + priv->udi = g_value_dup_string (value); + } + break; + case PROP_IFACE: + if (g_value_get_string (value)) { + g_free (priv->iface); + priv->ifindex = 0; + priv->iface = g_value_dup_string (value); + + /* Only look up the ifindex if it appears to be an actual kernel + * interface name. eg Bluetooth devices won't have one until we know + * the IP interface. + */ + if (priv->iface && !strchr (priv->iface, ':')) { + priv->ifindex = nm_platform_link_get_ifindex (priv->iface); + if (priv->ifindex <= 0) + nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", priv->iface); + } + } + break; + case PROP_DRIVER: + if (g_value_get_string (value)) { + g_free (priv->driver); + priv->driver = g_value_dup_string (value); + } + break; + case PROP_DRIVER_VERSION: + g_free (priv->driver_version); + priv->driver_version = g_strdup (g_value_get_string (value)); + break; + case PROP_FIRMWARE_VERSION: + g_free (priv->firmware_version); + priv->firmware_version = g_strdup (g_value_get_string (value)); + break; + case PROP_MTU: + priv->mtu = g_value_get_uint (value); + break; + case PROP_IP4_ADDRESS: + priv->ip4_address = g_value_get_uint (value); + break; + case PROP_AUTOCONNECT: + priv->autoconnect = g_value_get_boolean (value); + break; + case PROP_FIRMWARE_MISSING: + priv->firmware_missing = g_value_get_boolean (value); + break; + case PROP_DEVICE_TYPE: + g_return_if_fail (priv->type == NM_DEVICE_TYPE_UNKNOWN); + priv->type = g_value_get_uint (value); + break; + case PROP_TYPE_DESC: + g_free (priv->type_desc); + priv->type_desc = g_value_dup_string (value); + break; + case PROP_RFKILL_TYPE: + priv->rfkill_type = g_value_get_uint (value); + break; + case PROP_IS_MASTER: + priv->is_master = g_value_get_boolean (value); + break; + case PROP_HW_ADDRESS: + priv->hw_addr_len = nm_device_get_hw_address_length (NM_DEVICE (object), NULL); + + hw_addr = g_value_get_string (value); + if (!hw_addr) + break; + if (priv->hw_addr_len == 0) { + g_warn_if_fail (*hw_addr == '\0'); + break; + } + + if (!nm_utils_hwaddr_aton_len (hw_addr, priv->hw_addr, priv->hw_addr_len)) { + g_warning ("Could not parse hw-address '%s'", hw_addr); + memset (priv->hw_addr, 0, sizeof (priv->hw_addr)); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +#define DBUS_TYPE_STATE_REASON_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID)) + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDevice *self = NM_DEVICE (object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const char *ac_path = NULL; + GPtrArray *array; + GHashTableIter iter; + NMConnection *connection; + + switch (prop_id) { + case PROP_UDI: + g_value_set_string (value, priv->udi); + break; + case PROP_IFACE: + g_value_set_string (value, priv->iface); + break; + case PROP_IP_IFACE: + if (ip_config_valid (priv->state)) + g_value_set_string (value, nm_device_get_ip_iface (self)); + else + g_value_set_string (value, NULL); + break; + case PROP_IFINDEX: + g_value_set_int (value, priv->ifindex); + break; + case PROP_DRIVER: + g_value_set_string (value, priv->driver); + break; + case PROP_DRIVER_VERSION: + g_value_set_string (value, priv->driver_version); + break; + case PROP_FIRMWARE_VERSION: + g_value_set_string (value, priv->firmware_version); + break; + case PROP_CAPABILITIES: + g_value_set_uint (value, (priv->capabilities & ~NM_DEVICE_CAP_INTERNAL_MASK)); + break; + case PROP_IP4_ADDRESS: + g_value_set_uint (value, priv->ip4_address); + break; + case PROP_CARRIER: + g_value_set_boolean (value, priv->carrier); + break; + case PROP_MTU: + g_value_set_uint (value, priv->mtu); + break; + case PROP_IP4_CONFIG: + if (ip_config_valid (priv->state) && priv->ip4_config) + g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config)); + else + g_value_set_boxed (value, "/"); + break; + case PROP_DHCP4_CONFIG: + if (ip_config_valid (priv->state) && priv->dhcp4_client) + g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config)); + else + g_value_set_boxed (value, "/"); + break; + case PROP_IP6_CONFIG: + if (ip_config_valid (priv->state) && priv->ip6_config) + g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config)); + else + g_value_set_boxed (value, "/"); + break; + case PROP_DHCP6_CONFIG: + if (ip_config_valid (priv->state) && priv->dhcp6_client) + g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config)); + else + g_value_set_boxed (value, "/"); + break; + case PROP_STATE: + g_value_set_uint (value, priv->state); + break; + case PROP_STATE_REASON: + g_value_take_boxed (value, dbus_g_type_specialized_construct (DBUS_TYPE_STATE_REASON_STRUCT)); + dbus_g_type_struct_set (value, 0, priv->state, 1, priv->state_reason, G_MAXUINT); + break; + case PROP_ACTIVE_CONNECTION: + if (priv->act_request) + ac_path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (priv->act_request)); + g_value_set_boxed (value, ac_path ? ac_path : "/"); + break; + case PROP_DEVICE_TYPE: + g_value_set_uint (value, priv->type); + break; + case PROP_MANAGED: + g_value_set_boolean (value, nm_device_get_managed (self)); + break; + case PROP_AUTOCONNECT: + g_value_set_boolean (value, priv->autoconnect); + break; + case PROP_FIRMWARE_MISSING: + g_value_set_boolean (value, priv->firmware_missing); + break; + case PROP_TYPE_DESC: + g_value_set_string (value, priv->type_desc); + break; + case PROP_RFKILL_TYPE: + g_value_set_uint (value, priv->rfkill_type); + break; + case PROP_AVAILABLE_CONNECTIONS: + array = g_ptr_array_sized_new (g_hash_table_size (priv->available_connections)); + g_hash_table_iter_init (&iter, priv->available_connections); + while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) + g_ptr_array_add (array, g_strdup (nm_connection_get_path (connection))); + g_value_take_boxed (value, array); + break; + case PROP_PHYSICAL_PORT_ID: + g_value_set_string (value, priv->physical_port_id); + break; + case PROP_IS_MASTER: + g_value_set_boolean (value, priv->is_master); + break; + case PROP_MASTER: + g_value_set_object (value, priv->master); + break; + case PROP_HW_ADDRESS: + if (priv->hw_addr_len) + g_value_take_string (value, nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len)); + else + g_value_set_string (value, NULL); + break; + case PROP_HAS_PENDING_ACTION: + g_value_set_boolean (value, nm_device_has_pending_action (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_device_class_init (NMDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMDevicePrivate)); + + /* Virtual methods */ + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->constructor = constructor; + object_class->constructed = constructed; + + klass->link_changed = link_changed; + + klass->is_available = is_available; + klass->act_stage1_prepare = act_stage1_prepare; + klass->act_stage2_config = act_stage2_config; + klass->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + klass->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + klass->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout; + klass->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; + klass->have_any_ready_slaves = have_any_ready_slaves; + + klass->spec_match_list = spec_match_list; + klass->can_auto_connect = can_auto_connect; + klass->check_connection_compatible = check_connection_compatible; + klass->check_connection_available = check_connection_available; + klass->is_up = is_up; + klass->bring_up = bring_up; + klass->take_down = take_down; + klass->carrier_changed = carrier_changed; + klass->get_hw_address_length = get_hw_address_length; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_PLATFORM_DEVICE, + g_param_spec_pointer (NM_DEVICE_PLATFORM_DEVICE, + "Platform Device", + "NMPlatform device object", + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_UDI, + g_param_spec_string (NM_DEVICE_UDI, + "UDI", + "Unique Device Identifier", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property + (object_class, PROP_IFACE, + g_param_spec_string (NM_DEVICE_IFACE, + "Interface", + "Interface", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_IP_IFACE, + g_param_spec_string (NM_DEVICE_IP_IFACE, + "IP Interface", + "IP Interface", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_DRIVER, + g_param_spec_string (NM_DEVICE_DRIVER, + "Driver", + "Driver", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_DRIVER_VERSION, + g_param_spec_string (NM_DEVICE_DRIVER_VERSION, + "Driver Version", + "Driver Version", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_FIRMWARE_VERSION, + g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION, + "Firmware Version", + "Firmware Version", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_CAPABILITIES, + g_param_spec_uint (NM_DEVICE_CAPABILITIES, + "Capabilities", + "Capabilities", + 0, G_MAXUINT32, NM_DEVICE_CAP_NONE, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_CARRIER, + g_param_spec_boolean (NM_DEVICE_CARRIER, + "Carrier", + "Carrier", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_MTU, + g_param_spec_uint (NM_DEVICE_MTU, + "MTU", + "MTU", + 0, G_MAXUINT32, 1500, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_IP4_ADDRESS, + g_param_spec_uint (NM_DEVICE_IP4_ADDRESS, + "IP4 address", + "IP4 address", + 0, G_MAXUINT32, 0, /* FIXME */ + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_IP4_CONFIG, + g_param_spec_boxed (NM_DEVICE_IP4_CONFIG, + "IP4 Config", + "IP4 Config", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_DHCP4_CONFIG, + g_param_spec_boxed (NM_DEVICE_DHCP4_CONFIG, + "DHCP4 Config", + "DHCP4 Config", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_IP6_CONFIG, + g_param_spec_boxed (NM_DEVICE_IP6_CONFIG, + "IP6 Config", + "IP6 Config", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_DHCP6_CONFIG, + g_param_spec_boxed (NM_DEVICE_DHCP6_CONFIG, + "DHCP6 Config", + "DHCP6 Config", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_STATE, + g_param_spec_uint (NM_DEVICE_STATE, + "State", + "State", + 0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_STATE_REASON, + g_param_spec_boxed (NM_DEVICE_STATE_REASON, + "StateReason", + "StateReason", + DBUS_TYPE_STATE_REASON_STRUCT, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_ACTIVE_CONNECTION, + g_param_spec_boxed (NM_DEVICE_ACTIVE_CONNECTION, + "ActiveConnection", + "ActiveConnection", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_DEVICE_TYPE, + g_param_spec_uint (NM_DEVICE_DEVICE_TYPE, + "DeviceType", + "DeviceType", + 0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_MANAGED, + g_param_spec_boolean (NM_DEVICE_MANAGED, + "Managed", + "Managed", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_AUTOCONNECT, + g_param_spec_boolean (NM_DEVICE_AUTOCONNECT, + "Autoconnect", + "Autoconnect", + DEFAULT_AUTOCONNECT, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_FIRMWARE_MISSING, + g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING, + "FirmwareMissing", + "Firmware missing", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_TYPE_DESC, + g_param_spec_string (NM_DEVICE_TYPE_DESC, + "Type Description", + "Device type description", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_RFKILL_TYPE, + g_param_spec_uint (NM_DEVICE_RFKILL_TYPE, + "Rfkill Type", + "Type of rfkill switch (if any) supported by this device", + RFKILL_TYPE_WLAN, + RFKILL_TYPE_MAX, + RFKILL_TYPE_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_IFINDEX, + g_param_spec_int (NM_DEVICE_IFINDEX, + "Ifindex", + "Ifindex", + 0, G_MAXINT, 0, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_AVAILABLE_CONNECTIONS, + g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, + "AvailableConnections", + "AvailableConnections", + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_PHYSICAL_PORT_ID, + g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID, + "PhysicalPortId", + "PhysicalPortId", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_IS_MASTER, + g_param_spec_boolean (NM_DEVICE_IS_MASTER, + "IsMaster", + "IsMaster", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_MASTER, + g_param_spec_object (NM_DEVICE_MASTER, + "Master", + "Master", + NM_TYPE_DEVICE, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_HW_ADDRESS, + "Hardware Address", + "Hardware address", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_HAS_PENDING_ACTION, + g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION, + "Has pending action", + "Has pending action", + FALSE, + G_PARAM_READABLE)); + + /* Signals */ + signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMDeviceClass, state_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 3, + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); + + signals[AUTOCONNECT_ALLOWED] = + g_signal_new ("autoconnect-allowed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + autoconnect_allowed_accumulator, NULL, NULL, + G_TYPE_BOOLEAN, 0); + + signals[AUTH_REQUEST] = + g_signal_new (NM_DEVICE_AUTH_REQUEST, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + /* dbus-glib context, connection, permission, allow_interaction, callback, user_data */ + G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER); + + signals[IP4_CONFIG_CHANGED] = + g_signal_new (NM_DEVICE_IP4_CONFIG_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT); + + signals[IP6_CONFIG_CHANGED] = + g_signal_new (NM_DEVICE_IP6_CONFIG_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT); + + signals[REMOVED] = + g_signal_new (NM_DEVICE_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + + signals[RECHECK_AUTO_ACTIVATE] = + g_signal_new (NM_DEVICE_RECHECK_AUTO_ACTIVATE, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + + signals[RECHECK_ASSUME] = + g_signal_new (NM_DEVICE_RECHECK_ASSUME, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), + G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_device_object_info); + + dbus_g_error_domain_register (NM_DEVICE_ERROR, NULL, NM_TYPE_DEVICE_ERROR); } From 1856a1c315483965dd527a7ce856ea6f37b677e1 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 15:13:59 -0500 Subject: [PATCH 17/43] core: refactor state/reason string functions to use static tables --- src/devices/nm-device.c | 236 ++++++++++++++-------------------------- 1 file changed, 84 insertions(+), 152 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 49b31994c..2b8fa4a5e 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -336,38 +336,28 @@ nm_device_error_quark (void) #define QUEUED_PREFIX "queued state change to " +static const char *state_table[] = { + [NM_DEVICE_STATE_UNKNOWN] = QUEUED_PREFIX "unknown", + [NM_DEVICE_STATE_UNMANAGED] = QUEUED_PREFIX "unmanaged", + [NM_DEVICE_STATE_UNAVAILABLE] = QUEUED_PREFIX "unavailable", + [NM_DEVICE_STATE_DISCONNECTED] = QUEUED_PREFIX "disconnected", + [NM_DEVICE_STATE_PREPARE] = QUEUED_PREFIX "prepare", + [NM_DEVICE_STATE_CONFIG] = QUEUED_PREFIX "config", + [NM_DEVICE_STATE_NEED_AUTH] = QUEUED_PREFIX "need-auth", + [NM_DEVICE_STATE_IP_CONFIG] = QUEUED_PREFIX "ip-config", + [NM_DEVICE_STATE_IP_CHECK] = QUEUED_PREFIX "ip-check", + [NM_DEVICE_STATE_SECONDARIES] = QUEUED_PREFIX "secondaries", + [NM_DEVICE_STATE_ACTIVATED] = QUEUED_PREFIX "activated", + [NM_DEVICE_STATE_DEACTIVATING] = QUEUED_PREFIX "deactivating", + [NM_DEVICE_STATE_FAILED] = QUEUED_PREFIX "failed", +}; + static const char * queued_state_to_string (NMDeviceState state) { - switch (state) { - case NM_DEVICE_STATE_UNMANAGED: - return QUEUED_PREFIX "unmanaged"; - case NM_DEVICE_STATE_UNAVAILABLE: - return QUEUED_PREFIX "unavailable"; - case NM_DEVICE_STATE_DISCONNECTED: - return QUEUED_PREFIX "disconnected"; - case NM_DEVICE_STATE_PREPARE: - return QUEUED_PREFIX "prepare"; - case NM_DEVICE_STATE_CONFIG: - return QUEUED_PREFIX "config"; - case NM_DEVICE_STATE_NEED_AUTH: - return QUEUED_PREFIX "need-auth"; - case NM_DEVICE_STATE_IP_CONFIG: - return QUEUED_PREFIX "ip-config"; - case NM_DEVICE_STATE_IP_CHECK: - return QUEUED_PREFIX "ip-check"; - case NM_DEVICE_STATE_SECONDARIES: - return QUEUED_PREFIX "secondaries"; - case NM_DEVICE_STATE_ACTIVATED: - return QUEUED_PREFIX "activated"; - case NM_DEVICE_STATE_DEACTIVATING: - return QUEUED_PREFIX "deactivating"; - case NM_DEVICE_STATE_FAILED: - return QUEUED_PREFIX "failed"; - default: - break; - } - return QUEUED_PREFIX "unknown"; + if (state >= 0 && state < G_N_ELEMENTS (state_table)) + return state_table[state]; + return state_table[NM_DEVICE_STATE_UNKNOWN]; } static const char * @@ -376,132 +366,74 @@ state_to_string (NMDeviceState state) return queued_state_to_string (state) + strlen (QUEUED_PREFIX); } +static const char *reason_table[] = { + [NM_DEVICE_STATE_REASON_NONE] = "none", + [NM_DEVICE_STATE_REASON_NOW_MANAGED] = "managed", + [NM_DEVICE_STATE_REASON_NOW_UNMANAGED] = "unmanaged", + [NM_DEVICE_STATE_REASON_CONFIG_FAILED] = "config-failed", + [NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE] = "ip-config-unavailable", + [NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED] = "ip-config-expired", + [NM_DEVICE_STATE_REASON_NO_SECRETS] = "no-secrets", + [NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT] = "supplicant-disconnect", + [NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED] = "supplicant-config-failed", + [NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED] = "supplicant-failed", + [NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT] = "supplicant-timeout", + [NM_DEVICE_STATE_REASON_PPP_START_FAILED] = "ppp-start-failed", + [NM_DEVICE_STATE_REASON_PPP_DISCONNECT] = "ppp-disconnect", + [NM_DEVICE_STATE_REASON_PPP_FAILED] = "ppp-failed", + [NM_DEVICE_STATE_REASON_DHCP_START_FAILED] = "dhcp-start-failed", + [NM_DEVICE_STATE_REASON_DHCP_ERROR] = "dhcp-error", + [NM_DEVICE_STATE_REASON_DHCP_FAILED] = "dhcp-failed", + [NM_DEVICE_STATE_REASON_SHARED_START_FAILED] = "sharing-start-failed", + [NM_DEVICE_STATE_REASON_SHARED_FAILED] = "sharing-failed", + [NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED] = "autoip-start-failed", + [NM_DEVICE_STATE_REASON_AUTOIP_ERROR] = "autoip-error", + [NM_DEVICE_STATE_REASON_AUTOIP_FAILED] = "autoip-failed", + [NM_DEVICE_STATE_REASON_MODEM_BUSY] = "modem-busy", + [NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE] = "modem-no-dialtone", + [NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER] = "modem-no-carrier", + [NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT] = "modem-dial-timeout", + [NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED] = "modem-dial-failed", + [NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED] = "modem-init-failed", + [NM_DEVICE_STATE_REASON_GSM_APN_FAILED] = "gsm-apn-failed", + [NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING] = "gsm-registration-idle", + [NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED] = "gsm-registration-denied", + [NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT] = "gsm-registration-timeout", + [NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED] = "gsm-registration-failed", + [NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED] = "gsm-pin-check-failed", + [NM_DEVICE_STATE_REASON_FIRMWARE_MISSING] = "firmware-missing", + [NM_DEVICE_STATE_REASON_REMOVED] = "removed", + [NM_DEVICE_STATE_REASON_SLEEPING] = "sleeping", + [NM_DEVICE_STATE_REASON_CONNECTION_REMOVED] = "connection-removed", + [NM_DEVICE_STATE_REASON_USER_REQUESTED] = "user-requested", + [NM_DEVICE_STATE_REASON_CARRIER] = "carrier-changed", + [NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED] = "connection-assumed", + [NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE] = "supplicant-available", + [NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND] = "modem-not-found", + [NM_DEVICE_STATE_REASON_BT_FAILED] = "bluetooth-failed", + [NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED] = "gsm-sim-not-inserted", + [NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED] = "gsm-sim-pin-required", + [NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED] = "gsm-sim-puk-required", + [NM_DEVICE_STATE_REASON_GSM_SIM_WRONG] = "gsm-sim-wrong", + [NM_DEVICE_STATE_REASON_INFINIBAND_MODE] = "infiniband-mode", + [NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED] = "dependency-failed", + [NM_DEVICE_STATE_REASON_BR2684_FAILED] = "br2684-bridge-failed", + [NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE] = "modem-manager-unavailable", + [NM_DEVICE_STATE_REASON_SSID_NOT_FOUND] = "ssid-not-found", + [NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED] = "secondary-connection-failed", + [NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED] = "dcb-fcoe-failed", + [NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED] = "teamd-control-failed", + [NM_DEVICE_STATE_REASON_MODEM_FAILED] = "modem-failed", + [NM_DEVICE_STATE_REASON_MODEM_AVAILABLE] = "modem-available", + [NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT] = "sim-pin-incorrect", +}; + static const char * reason_to_string (NMDeviceStateReason reason) { - switch (reason) { - case NM_DEVICE_STATE_REASON_NONE: - return "none"; - case NM_DEVICE_STATE_REASON_NOW_MANAGED: - return "managed"; - case NM_DEVICE_STATE_REASON_NOW_UNMANAGED: - return "unmanaged"; - case NM_DEVICE_STATE_REASON_CONFIG_FAILED: - return "config-failed"; - case NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE: - return "ip-config-unavailable"; - case NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED: - return "ip-config-expired"; - case NM_DEVICE_STATE_REASON_NO_SECRETS: - return "no-secrets"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT: - return "supplicant-disconnect"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED: - return "supplicant-config-failed"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED: - return "supplicant-failed"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT: - return "supplicant-timeout"; - case NM_DEVICE_STATE_REASON_PPP_START_FAILED: - return "ppp-start-failed"; - case NM_DEVICE_STATE_REASON_PPP_DISCONNECT: - return "ppp-disconnect"; - case NM_DEVICE_STATE_REASON_PPP_FAILED: - return "ppp-failed"; - case NM_DEVICE_STATE_REASON_DHCP_START_FAILED: - return "dhcp-start-failed"; - case NM_DEVICE_STATE_REASON_DHCP_ERROR: - return "dhcp-error"; - case NM_DEVICE_STATE_REASON_DHCP_FAILED: - return "dhcp-failed"; - case NM_DEVICE_STATE_REASON_SHARED_START_FAILED: - return "sharing-start-failed"; - case NM_DEVICE_STATE_REASON_SHARED_FAILED: - return "sharing-failed"; - case NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED: - return "autoip-start-failed"; - case NM_DEVICE_STATE_REASON_AUTOIP_ERROR: - return "autoip-error"; - case NM_DEVICE_STATE_REASON_AUTOIP_FAILED: - return "autoip-failed"; - case NM_DEVICE_STATE_REASON_MODEM_BUSY: - return "modem-busy"; - case NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE: - return "modem-no-dialtone"; - case NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER: - return "modem-no-carrier"; - case NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT: - return "modem-dial-timeout"; - case NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED: - return "modem-dial-failed"; - case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: - return "modem-init-failed"; - case NM_DEVICE_STATE_REASON_GSM_APN_FAILED: - return "gsm-apn-failed"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: - return "gsm-registration-idle"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: - return "gsm-registration-denied"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT: - return "gsm-registration-timeout"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED: - return "gsm-registration-failed"; - case NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED: - return "gsm-pin-check-failed"; - case NM_DEVICE_STATE_REASON_FIRMWARE_MISSING: - return "firmware-missing"; - case NM_DEVICE_STATE_REASON_REMOVED: - return "removed"; - case NM_DEVICE_STATE_REASON_SLEEPING: - return "sleeping"; - case NM_DEVICE_STATE_REASON_CONNECTION_REMOVED: - return "connection-removed"; - case NM_DEVICE_STATE_REASON_USER_REQUESTED: - return "user-requested"; - case NM_DEVICE_STATE_REASON_CARRIER: - return "carrier-changed"; - case NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED: - return "connection-assumed"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE: - return "supplicant-available"; - case NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND: - return "modem-not-found"; - case NM_DEVICE_STATE_REASON_BT_FAILED: - return "bluetooth-failed"; - case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: - return "gsm-sim-not-inserted"; - case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: - return "gsm-sim-pin-required"; - case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: - return "gsm-sim-puk-required"; - case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: - return "gsm-sim-wrong"; - case NM_DEVICE_STATE_REASON_INFINIBAND_MODE: - return "infiniband-mode"; - case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED: - return "dependency-failed"; - case NM_DEVICE_STATE_REASON_BR2684_FAILED: - return "br2684-bridge-failed"; - case NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE: - return "modem-manager-unavailable"; - case NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: - return "SSID not found"; - case NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED: - return "secondary-connection-failed"; - case NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED: - return "DCB-FCoE-failed"; - case NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED: - return "teamd-control-failed"; - case NM_DEVICE_STATE_REASON_MODEM_FAILED: - return "modem-failed"; - case NM_DEVICE_STATE_REASON_MODEM_AVAILABLE: - return "modem-available"; - case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: - return "sim-pin-incorrect"; - default: - break; - } - return "unknown"; + if (reason >= 0 && reason < G_N_ELEMENTS (reason_table)) + return reason_table[reason]; + return reason_table[NM_DEVICE_STATE_REASON_UNKNOWN]; } /***********************************************************/ From ef2edfce4f6bc7074619d9309fe973938c4590c5 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 15:17:13 -0500 Subject: [PATCH 18/43] trivial: re-organize IPv6 /proc property util functions --- src/devices/nm-device.c | 84 ++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 2b8fa4a5e..399bcfe94 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -444,48 +444,6 @@ nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *val return nm_platform_sysctl_set (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), value); } -static const char *ip6_properties_to_save[] = { - "accept_ra", - "accept_ra_defrtr", - "accept_ra_pinfo", - "accept_ra_rtr_pref", - "disable_ipv6", - "hop_limit", - "use_tempaddr", -}; - -static void -save_ip6_properties (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *ifname = nm_device_get_ip_iface (self); - char *value; - int i; - - g_hash_table_remove_all (priv->ip6_saved_properties); - - for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) { - value = nm_platform_sysctl_get (nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i])); - if (value) { - g_hash_table_insert (priv->ip6_saved_properties, - (char *) ip6_properties_to_save[i], - value); - } - } -} - -static void -restore_ip6_properties (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - GHashTableIter iter; - gpointer key, value; - - g_hash_table_iter_init (&iter, priv->ip6_saved_properties); - while (g_hash_table_iter_next (&iter, &key, &value)) - nm_device_ipv6_sysctl_set (self, key, value); -} - static gboolean device_has_capability (NMDevice *device, NMDeviceCapabilities caps) { @@ -3714,6 +3672,48 @@ addrconf6_cleanup (NMDevice *self) /******************************************/ +static const char *ip6_properties_to_save[] = { + "accept_ra", + "accept_ra_defrtr", + "accept_ra_pinfo", + "accept_ra_rtr_pref", + "disable_ipv6", + "hop_limit", + "use_tempaddr", +}; + +static void +save_ip6_properties (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const char *ifname = nm_device_get_ip_iface (self); + char *value; + int i; + + g_hash_table_remove_all (priv->ip6_saved_properties); + + for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) { + value = nm_platform_sysctl_get (nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i])); + if (value) { + g_hash_table_insert (priv->ip6_saved_properties, + (char *) ip6_properties_to_save[i], + value); + } + } +} + +static void +restore_ip6_properties (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, priv->ip6_saved_properties); + while (g_hash_table_iter_next (&iter, &key, &value)) + nm_device_ipv6_sysctl_set (self, key, value); +} + static NMSettingIP6ConfigPrivacy use_tempaddr_clamp (NMSettingIP6ConfigPrivacy use_tempaddr) { From c958540bcc53ca3a4acdcb50debcecbce917e167 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 16:09:22 -0500 Subject: [PATCH 19/43] core: clean up NMDevice simple getters/setters Group most of the simple getters together near the top, and remove getters that have no callers (mtu, firmware-version). --- src/devices/nm-device.c | 176 ++++++++++++++++------------------------ src/devices/nm-device.h | 5 -- 2 files changed, 71 insertions(+), 110 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 399bcfe94..b8244649f 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -450,24 +450,7 @@ device_has_capability (NMDevice *device, NMDeviceCapabilities caps) return !!(NM_DEVICE_GET_PRIVATE (device)->capabilities & caps); } -static gboolean -nm_device_is_up (NMDevice *self) -{ - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - if (NM_DEVICE_GET_CLASS (self)->is_up) - return NM_DEVICE_GET_CLASS (self)->is_up (self); - - return TRUE; -} - -static gboolean -is_up (NMDevice *device) -{ - int ifindex = nm_device_get_ip_ifindex (device); - - return ifindex > 0 ? nm_platform_link_is_up (ifindex) : TRUE; -} +/***********************************************************/ void nm_device_set_path (NMDevice *self, const char *path) @@ -498,9 +481,6 @@ nm_device_get_udi (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->udi; } -/* - * Get/set functions for iface - */ const char * nm_device_get_iface (NMDevice *self) { @@ -585,9 +565,6 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface) g_free (old_ip_iface); } -/* - * Get/set functions for driver - */ const char * nm_device_get_driver (NMDevice *self) { @@ -604,18 +581,6 @@ nm_device_get_driver_version (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->driver_version; } -const char * -nm_device_get_firmware_version (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - - return NM_DEVICE_GET_PRIVATE (self)->firmware_version; -} - - -/* - * Get/set functions for type - */ NMDeviceType nm_device_get_device_type (NMDevice *self) { @@ -683,6 +648,44 @@ nm_device_get_type_desc (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->type_desc; } +gboolean +nm_device_has_carrier (NMDevice *device) +{ + return NM_DEVICE_GET_PRIVATE (device)->carrier; +} + +NMActRequest * +nm_device_get_act_request (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return NM_DEVICE_GET_PRIVATE (self)->act_request; +} + +NMConnection * +nm_device_get_connection (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + return priv->act_request ? nm_act_request_get_connection (priv->act_request) : NULL; +} + +RfKillType +nm_device_get_rfkill_type (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + return NM_DEVICE_GET_PRIVATE (self)->rfkill_type; +} + +static const char * +nm_device_get_physical_port_id (NMDevice *device) +{ + return NM_DEVICE_GET_PRIVATE (device)->physical_port_id; +} + +/***********************************************************/ + static gboolean nm_device_uses_generated_connection (NMDevice *self) { @@ -895,12 +898,6 @@ carrier_changed (NMDevice *device, gboolean carrier) } } -gboolean -nm_device_has_carrier (NMDevice *device) -{ - return NM_DEVICE_GET_PRIVATE (device)->carrier; -} - #define LINK_DISCONNECT_DELAY 4 static gboolean @@ -1163,15 +1160,6 @@ nm_device_owns_iface (NMDevice *device, const char *iface) return FALSE; } -static void -check_carrier (NMDevice *device) -{ - int ifindex = nm_device_get_ip_ifindex (device); - - if (!device_has_capability (device, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) - nm_device_set_carrier (device, nm_platform_link_is_connected (ifindex)); -} - static void slave_state_changed (NMDevice *slave, NMDeviceState slave_new_state, @@ -1499,28 +1487,6 @@ nm_device_get_enslaved (NMDevice *device) return NM_DEVICE_GET_PRIVATE (device)->enslaved; } -/* - * nm_device_get_act_request - * - * Return the devices activation request, if any. - * - */ -NMActRequest * -nm_device_get_act_request (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - - return NM_DEVICE_GET_PRIVATE (self)->act_request; -} - -NMConnection * -nm_device_get_connection (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - return priv->act_request ? nm_act_request_get_connection (priv->act_request) : NULL; -} - static gboolean is_available (NMDevice *device) { @@ -1576,12 +1542,12 @@ nm_device_set_enabled (NMDevice *self, gboolean enabled) NM_DEVICE_GET_CLASS (self)->set_enabled (self, enabled); } -RfKillType -nm_device_get_rfkill_type (NMDevice *self) +gboolean +nm_device_get_autoconnect (NMDevice *device) { - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - return NM_DEVICE_GET_PRIVATE (self)->rfkill_type; + return NM_DEVICE_GET_PRIVATE (device)->autoconnect; } static gboolean @@ -5439,6 +5405,25 @@ carrier_wait_timeout (gpointer user_data) return G_SOURCE_REMOVE; } +static gboolean +nm_device_is_up (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + if (NM_DEVICE_GET_CLASS (self)->is_up) + return NM_DEVICE_GET_CLASS (self)->is_up (self); + + return TRUE; +} + +static gboolean +is_up (NMDevice *device) +{ + int ifindex = nm_device_get_ip_ifindex (device); + + return ifindex > 0 ? nm_platform_link_is_up (ifindex) : TRUE; +} + gboolean nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) { @@ -5496,6 +5481,15 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) return TRUE; } +static void +check_carrier (NMDevice *device) +{ + int ifindex = nm_device_get_ip_ifindex (device); + + if (!device_has_capability (device, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) + nm_device_set_carrier (device, nm_platform_link_is_connected (ifindex)); +} + static gboolean bring_up (NMDevice *device, gboolean *no_firmware) { @@ -5939,14 +5933,6 @@ nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr) } } -gboolean -nm_device_get_autoconnect (NMDevice *device) -{ - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - - return NM_DEVICE_GET_PRIVATE (device)->autoconnect; -} - /** * nm_device_connection_is_available(): * @device: the #NMDevice @@ -6298,26 +6284,6 @@ nm_device_has_pending_action (NMDevice *device) return !!priv->pending_actions; } -const char * -nm_device_get_physical_port_id (NMDevice *device) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - - return priv->physical_port_id; -} - -/** - * nm_device_get_mtu: - * @device: the #NMDevice - * - * Returns: MTU of the #NMDevice - */ -guint32 -nm_device_get_mtu (NMDevice *device) -{ - return NM_DEVICE_GET_PRIVATE (device)->mtu; -} - /***********************************************************/ /* diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 1e2f39a31..bc9a52ec9 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -228,7 +228,6 @@ const char * nm_device_get_ip_iface (NMDevice *dev); int nm_device_get_ip_ifindex(NMDevice *dev); const char * nm_device_get_driver (NMDevice *dev); const char * nm_device_get_driver_version (NMDevice *dev); -const char * nm_device_get_firmware_version (NMDevice *dev); const char * nm_device_get_type_desc (NMDevice *dev); NMDeviceType nm_device_get_device_type (NMDevice *dev); @@ -356,10 +355,6 @@ gboolean nm_device_has_pending_action (NMDevice *device); GPtrArray *nm_device_get_available_connections (NMDevice *device, const char *specific_object); -const char *nm_device_get_physical_port_id (NMDevice *device); - -guint32 nm_device_get_mtu (NMDevice *device); - gboolean nm_device_connection_is_available (NMDevice *device, NMConnection *connection, gboolean allow_device_override); From b4c368692ddddbd8aca36625fd6a3a296689deff Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 16:25:21 -0500 Subject: [PATCH 20/43] core: let NMDevice export itself Saves some code and a memory allocation. --- src/devices/nm-device.c | 11 +++++++---- src/devices/nm-device.h | 2 +- src/nm-manager.c | 8 +------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index b8244649f..ae53f0a60 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -453,16 +453,19 @@ device_has_capability (NMDevice *device, NMDeviceCapabilities caps) /***********************************************************/ void -nm_device_set_path (NMDevice *self, const char *path) +nm_device_dbus_export (NMDevice *device) { + static guint32 devcount = 0; NMDevicePrivate *priv; - g_return_if_fail (self != NULL); + g_return_if_fail (NM_IS_DEVICE (device)); - priv = NM_DEVICE_GET_PRIVATE (self); + priv = NM_DEVICE_GET_PRIVATE (device); g_return_if_fail (priv->path == NULL); - priv->path = g_strdup (path); + priv->path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++); + nm_log_info (LOGD_DEVICE, "(%s): exported as %s", priv->iface, priv->path); + nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, device); } const char * diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index bc9a52ec9..301528337 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -218,7 +218,7 @@ typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device, GType nm_device_get_type (void); const char * nm_device_get_path (NMDevice *dev); -void nm_device_set_path (NMDevice *dev, const char *path); +void nm_device_dbus_export (NMDevice *device); const char * nm_device_get_udi (NMDevice *dev); const char * nm_device_get_iface (NMDevice *dev); diff --git a/src/nm-manager.c b/src/nm-manager.c index d3d6662c4..4fd8957f8 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1682,8 +1682,6 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); const char *iface, *driver, *type_desc; - char *path; - static guint32 devcount = 0; const GSList *unmanaged_specs; gboolean user_unmanaged, sleeping; NMConnection *connection = NULL; @@ -1760,11 +1758,7 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con) sleeping = manager_sleeping (self); nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_INTERNAL, sleeping); - path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++); - nm_device_set_path (device, path); - nm_dbus_manager_register_object (priv->dbus_mgr, path, device); - nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path); - g_free (path); + nm_device_dbus_export (device); /* Don't generate a connection e.g. for devices NM just created, or * for the loopback, or when we're sleeping. */ From f3fbbf4a772fc30f5622450216f9a3cfd720e64e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 16:39:57 -0500 Subject: [PATCH 21/43] core: rearrange and remove some NMDevice getters For any function in nm-device.h which is not used outside of nm-device.c, remove the public prototypes. Functions that are actually used get moved above their caller, and functions that have no callers are removed. --- src/devices/nm-device.c | 102 ++++++++++++++++++---------------------- src/devices/nm-device.h | 8 ---- 2 files changed, 45 insertions(+), 65 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index ae53f0a60..4f368807a 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -314,11 +314,14 @@ static gboolean nm_device_set_ip6_config (NMDevice *dev, gboolean commit, NMDeviceStateReason *reason); +static gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure); static void nm_device_slave_notify_enslave (NMDevice *dev, gboolean success); static void nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason); static void addrconf6_start_with_link_ready (NMDevice *self); +static gboolean nm_device_get_default_unmanaged (NMDevice *device); + /***********************************************************/ static GQuark @@ -852,7 +855,7 @@ carrier_changed (NMDevice *device, gboolean carrier) if (priv->ignore_carrier && !carrier) return; - if (nm_device_is_master (device)) { + if (priv->is_master) { /* Bridge/bond/team carrier does not affect its own activation, * but when carrier comes on, if there are slaves waiting, * it will restart them. @@ -1217,7 +1220,7 @@ slave_state_changed (NMDevice *slave, * * Returns: %TRUE on success, %FALSE on failure */ -gboolean +static gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); @@ -1326,18 +1329,6 @@ nm_device_master_check_slave_physical_port (NMDevice *dev, NMDevice *slave, } } -/** - * nm_device_is_master: - * @dev: the device - * - * Returns: whether @dev can enslave other devices (eg, bridge or bond or team) - */ -gboolean -nm_device_is_master (NMDevice *dev) -{ - return NM_DEVICE_GET_PRIVATE (dev)->is_master; -} - /* release all slaves */ static void nm_device_master_release_slaves (NMDevice *self) @@ -2892,7 +2883,7 @@ act_stage3_ip4_config_start (NMDevice *self, g_assert_cmpstr (method, ==, NM_SETTING_IP4_CONFIG_METHOD_DISABLED); if ( strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) != 0 - && nm_device_is_master (self) + && priv->is_master && !priv->carrier) { nm_log_info (LOGD_IP4 | LOGD_DEVICE, "(%s): IPv4 config waiting until carrier is on", @@ -3778,7 +3769,7 @@ act_stage3_ip6_config_start (NMDevice *self, g_assert_cmpstr (method, ==, NM_SETTING_IP6_CONFIG_METHOD_IGNORE); if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) != 0 - && nm_device_is_master (self) + && priv->is_master && !priv->carrier) { nm_log_info (LOGD_IP6 | LOGD_DEVICE, "(%s): IPv6 config waiting until carrier is on", ip_iface); @@ -4725,6 +4716,30 @@ _update_ip4_address (NMDevice *self) close (fd); } +gboolean +nm_device_get_is_nm_owned (NMDevice *device) +{ + return NM_DEVICE_GET_PRIVATE (device)->is_nm_owned; +} + +gboolean +nm_device_set_is_nm_owned (NMDevice *device, + gboolean is_nm_owned) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (device); + + if (is_nm_owned == priv->is_nm_owned) + return TRUE; + if (!is_nm_owned) + return FALSE; + priv->is_nm_owned = TRUE; + return TRUE; +} + /* * delete_on_deactivate_link_delete * @@ -4772,12 +4787,12 @@ delete_on_deactivate_unschedule (NMDevice *self) static void delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) { - NMDevicePrivate *priv; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); DeleteOnDeactivateData *data; if (ifindex <= 0) return; - if (!nm_device_get_is_nm_owned (self)) + if (!priv->is_nm_owned) return; if (!nm_device_is_software (self)) return; @@ -4787,8 +4802,6 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) return; delete_on_deactivate_unschedule (self); /* always cancel and reschedule */ - priv = NM_DEVICE_GET_PRIVATE (self); - data = g_new (DeleteOnDeactivateData, 1); g_object_add_weak_pointer (G_OBJECT (self), (void **) &data->device); data->device = self; @@ -4800,31 +4813,6 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) ifindex, nm_device_get_iface (self), data->idle_add_id); } -gboolean -nm_device_get_is_nm_owned (NMDevice *device) -{ - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - return NM_DEVICE_GET_PRIVATE (device)->is_nm_owned; -} - -gboolean -nm_device_set_is_nm_owned (NMDevice *device, - gboolean is_nm_owned) -{ - NMDevicePrivate *priv; - - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - - priv = NM_DEVICE_GET_PRIVATE (device); - - if (is_nm_owned == priv->is_nm_owned) - return TRUE; - if (!is_nm_owned) - return FALSE; - priv->is_nm_owned = TRUE; - return TRUE; -} - static void disconnect_cb (NMDevice *device, DBusGMethodInvocation *context, @@ -5820,18 +5808,6 @@ nm_device_get_managed (NMDevice *device) return managed; } -/** - * nm_device_get_default_unmanaged(): - * @device: the #NMDevice - * - * Returns: %TRUE if the device is by default unmanaged - */ -gboolean -nm_device_get_default_unmanaged (NMDevice *device) -{ - return nm_device_get_unmanaged_flag (device, NM_UNMANAGED_DEFAULT); -} - /** * nm_device_get_unmanaged_flag(): * @device: the #NMDevice @@ -5844,6 +5820,18 @@ nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag) return NM_DEVICE_GET_PRIVATE (device)->unmanaged_flags & flag; } +/** + * nm_device_get_default_unmanaged(): + * @device: the #NMDevice + * + * Returns: %TRUE if the device is by default unmanaged + */ +static gboolean +nm_device_get_default_unmanaged (NMDevice *device) +{ + return nm_device_get_unmanaged_flag (device, NM_UNMANAGED_DEFAULT); +} + void nm_device_set_unmanaged (NMDevice *device, NMUnmanagedFlags flag, diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 301528337..404324f43 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -247,9 +247,7 @@ void nm_device_set_vpn6_config (NMDevice *dev, NMIP6Config *config) void nm_device_capture_initial_config (NMDevice *dev); /* Master */ -gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure); GSList * nm_device_master_get_slaves (NMDevice *dev); -gboolean nm_device_is_master (NMDevice *dev); /* Slave */ NMDevice * nm_device_get_master (NMDevice *dev); @@ -276,9 +274,6 @@ gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection * gboolean nm_device_can_assume_connections (NMDevice *device); -NMConnection * nm_device_find_assumable_connection (NMDevice *device, - const GSList *connections); - gboolean nm_device_spec_match_list (NMDevice *device, const GSList *specs); gboolean nm_device_is_activating (NMDevice *dev); @@ -312,7 +307,6 @@ typedef enum { } NMUnmanagedFlags; gboolean nm_device_get_managed (NMDevice *device); -gboolean nm_device_get_default_unmanaged (NMDevice *device); gboolean nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag); void nm_device_set_unmanaged (NMDevice *device, NMUnmanagedFlags flag, @@ -340,8 +334,6 @@ void nm_device_queue_state (NMDevice *self, NMDeviceState state, NMDeviceStateReason reason); -void nm_device_queue_ip_config_change (NMDevice *self); - gboolean nm_device_get_firmware_missing (NMDevice *self); void nm_device_queue_activation (NMDevice *device, NMActRequest *req); From 4809898e087f810d873fb9a78e76e9e1f4356793 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 16:56:29 -0500 Subject: [PATCH 22/43] trivial: simplify nm_device_set_is_nm_owned() Nothing used its return value or passed FALSE. --- src/devices/nm-device.c | 18 ++++-------------- src/devices/nm-device.h | 3 +-- src/nm-manager.c | 2 +- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 4f368807a..e3ef0c869 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -4722,22 +4722,12 @@ nm_device_get_is_nm_owned (NMDevice *device) return NM_DEVICE_GET_PRIVATE (device)->is_nm_owned; } -gboolean -nm_device_set_is_nm_owned (NMDevice *device, - gboolean is_nm_owned) +void +nm_device_set_nm_owned (NMDevice *device) { - NMDevicePrivate *priv; + g_return_if_fail (NM_IS_DEVICE (device)); - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - - priv = NM_DEVICE_GET_PRIVATE (device); - - if (is_nm_owned == priv->is_nm_owned) - return TRUE; - if (!is_nm_owned) - return FALSE; - priv->is_nm_owned = TRUE; - return TRUE; + NM_DEVICE_GET_PRIVATE (device)->is_nm_owned = TRUE; } /* diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 404324f43..d63faecd0 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -317,8 +317,7 @@ void nm_device_set_initial_unmanaged_flag (NMDevice *device, gboolean unmanaged); gboolean nm_device_get_is_nm_owned (NMDevice *device); -gboolean nm_device_set_is_nm_owned (NMDevice *device, - gboolean is_nm_owned); +void nm_device_set_nm_owned (NMDevice *device); gboolean nm_device_get_autoconnect (NMDevice *device); diff --git a/src/nm-manager.c b/src/nm-manager.c index 4fd8957f8..c1a116b29 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1086,7 +1086,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) } if (device) { - nm_device_set_is_nm_owned (device, TRUE); + nm_device_set_nm_owned (device); add_device (self, device, FALSE); g_object_unref (device); } From 76aa8ce40eafbf523b9390bfff0ffb1227984368 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 22 May 2014 13:59:06 -0500 Subject: [PATCH 23/43] core: simplify DHCP cleanup dhcp6_cleanup() frees priv->dhcp6_ip6_config so dispose() doesn't need to do that. Also use g_clear_object() when appropriate. Lastly, notify that the DHCP4/6 config objects have changed *after* clearing them, so that the PropertiesChanged signal is emitted with a blank path to indicate the object is gone. Previously the PC signal would have been emitted with the valid path of the DHCP4/6 config object, but the object would already be dead. --- src/devices/nm-device.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index e3ef0c869..99dd13892 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2502,9 +2502,8 @@ dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (priv->dhcp4_config) { + g_clear_object (&priv->dhcp4_config); g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG); - g_object_unref (priv->dhcp4_config); - priv->dhcp4_config = NULL; } if (priv->dhcp4_client) { @@ -2524,8 +2523,7 @@ dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release) if (stop) nm_dhcp_client_stop (priv->dhcp4_client, release); - g_object_unref (priv->dhcp4_client); - priv->dhcp4_client = NULL; + g_clear_object (&priv->dhcp4_client); } } @@ -2944,16 +2942,11 @@ dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE; - - if (priv->dhcp6_ip6_config) { - g_object_unref (priv->dhcp6_ip6_config); - priv->dhcp6_ip6_config = NULL; - } + g_clear_object (&priv->dhcp6_ip6_config); if (priv->dhcp6_config) { + g_clear_object (&priv->dhcp6_config); g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG); - g_object_unref (priv->dhcp6_config); - priv->dhcp6_config = NULL; } if (priv->dhcp6_client) { @@ -2972,8 +2965,7 @@ dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release) if (stop) nm_dhcp_client_stop (priv->dhcp6_client, release); - g_object_unref (priv->dhcp6_client); - priv->dhcp6_client = NULL; + g_clear_object (&priv->dhcp6_client); } } @@ -7169,11 +7161,10 @@ dispose (GObject *object) g_clear_object (&priv->vpn4_config); g_clear_object (&priv->ip4_config); - g_clear_object (&priv->ip6_config); g_clear_object (&priv->ac_ip6_config); - g_clear_object (&priv->dhcp6_ip6_config); - g_clear_object (&priv->vpn6_config); g_clear_object (&priv->ext_ip6_config); + g_clear_object (&priv->vpn6_config); + g_clear_object (&priv->ip6_config); g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref); From 4b6f0d50a456a29e17785fb8b13c11ce018b31db Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 8 May 2014 14:32:02 -0500 Subject: [PATCH 24/43] core: fix deactivation of assumed connections on device removal (bgo #729833) The following procedure leaves an NMActiveConnection around for a deactivated device, which causes errors in libnm-glib clients when they cannot create the GObject for the non-existent device of the AC. 1) allow a device which can assume connections to be activated 2) stop NM, which should leave the device's IP configuration up 3) start NM and allow it to assume the device's existing connection 4) remove the device, either by unplugging it or 'rmmod' The device is removed by nm-manager.c::remove_device(), but the device object is not moved to UNMANAGED state, leaving the NMActiveConnection completely unaware the device has gone away. The nm-manager.c::remove_device() code did not correctly handle moving a forcibly removed (eg, by unplugging or 'ip link del' or 'rmmod') device to the UNMANAGED state when the device was active with an assumed connection. To fix this, make the conditions when the device should be deactivated on removal much more explicit. A device should be deactivated on removal if: 1) it is forcibly removed, eg by the kernel network interface being removed due to 'ip link del' or hotplugging, or internally by NM due to a parent WWAN interface taking priority over a WWAN ethernet interface 2) if the device cannot assume connections, in which case NetworkManager must have activated the device and since we cannot assume the connection on restart, we should deactivate it 3) if the device is not activated, to ensure that its IPv6 parameters and other things get reset to the pre-NetworkManager values https://bugzilla.gnome.org/show_bug.cgi?id=729833 --- src/nm-manager.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/nm-manager.c b/src/nm-manager.c index c1a116b29..452f4acd4 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -727,20 +727,23 @@ remove_device (NMManager *manager, NMDevice *device, gboolean quitting) NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); if (nm_device_get_managed (device)) { - /* Leave configured interfaces up when quitting so they can be - * taken over again if NM starts up, and to ensure connectivity while - * NM is gone. Assumed connections don't get taken down even if they - * haven't been fully activated. + NMActRequest *req = nm_device_get_act_request (device); + gboolean unmanage = FALSE; + + /* Leave activated interfaces up when quitting so their configuration + * can be taken over when NM restarts. This ensures connectivity while + * NM is stopped. Devices which do not support connection assumption + * cannot be left up. */ + if (!quitting) /* Forced removal; device already gone */ + unmanage = TRUE; + else if (!nm_device_can_assume_connections (device)) + unmanage = TRUE; + else if (!req) + unmanage = TRUE; - if ( !nm_device_can_assume_connections (device) - || (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED) - || !quitting) { - NMActRequest *req = nm_device_get_act_request (device); - - if (!req || !nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (req))) - nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED); - } + if (unmanage) + nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED); } g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager); From 47622420da876fa8351be0139190b9f17320449b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 22 May 2014 14:04:01 -0500 Subject: [PATCH 25/43] core: (trivial) some NMDevice dispose/finalize cleanups Ensure autoip4 is cleaned up when disposing, like we clean up DHCP. Move things that only free stuff to finalize(), and move things that unref stuff to dispose (eg, the Firewall Manager stuff). --- src/devices/nm-device.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 99dd13892..308ff2faa 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -7136,6 +7136,7 @@ dispose (GObject *object) linklocal6_cleanup (self); addrconf6_cleanup (self); dnsmasq_cleanup (self); + aipd_cleanup (self); g_warn_if_fail (priv->slaves == NULL); g_assert (priv->master_ready_id == 0); @@ -7190,8 +7191,6 @@ dispose (GObject *object) priv->carrier_wait_id = 0; } - g_clear_pointer (&priv->physical_port_id, g_free); - activation_source_clear (self, TRUE, AF_INET); activation_source_clear (self, TRUE, AF_INET6); @@ -7202,6 +7201,15 @@ dispose (GObject *object) g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self); g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); + /* Clean up when device was deactivated during call to firewall */ + if (priv->fw_manager) { + if (priv->fw_call) { + nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call); + priv->fw_call = NULL; + } + g_clear_object (&priv->fw_manager); + } + out: G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); } @@ -7212,11 +7220,8 @@ finalize (GObject *object) NMDevice *self = NM_DEVICE (object); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->fw_manager) - g_object_unref (priv->fw_manager); - g_slist_free_full (priv->pending_actions, g_free); - + g_clear_pointer (&priv->physical_port_id, g_free); g_free (priv->udi); g_free (priv->path); g_free (priv->iface); From 90242d74a9bca04fdb4ab80801b7d7db024ac064 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 22 May 2014 14:51:55 -0500 Subject: [PATCH 26/43] core: consolidate generic device cleanup tasks dispose() and nm_device_cleanup() both do common tasks; consolidate these common tasks in one place and call when appropriate. --- src/devices/nm-device.c | 195 +++++++++++++++++++--------------------- 1 file changed, 90 insertions(+), 105 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 308ff2faa..7f73b74e3 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -6259,6 +6259,91 @@ nm_device_has_pending_action (NMDevice *device) /***********************************************************/ +static void +_cleanup_generic_pre (NMDevice *self, gboolean deconfigure) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + /* Clean up when device was deactivated during call to firewall */ + if (priv->fw_manager) { + NMConnection *connection; + + if (priv->fw_call) { + nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call); + priv->fw_call = NULL; + } + + connection = nm_device_get_connection (self); + if (deconfigure && connection) { + nm_firewall_manager_remove_from_zone (priv->fw_manager, + nm_device_get_ip_iface (self), + NULL); + } + } + + ip_check_gw_ping_cleanup (self); + + /* Break the activation chain */ + activation_source_clear (self, TRUE, AF_INET); + activation_source_clear (self, TRUE, AF_INET6); + + /* Clear any queued transitions */ + nm_device_queued_state_clear (self); + nm_device_queued_ip_config_change_clear (self); + + priv->ip4_state = priv->ip6_state = IP_NONE; + + dhcp4_cleanup (self, deconfigure, FALSE); + arp_cleanup (self); + dhcp6_cleanup (self, deconfigure, FALSE); + linklocal6_cleanup (self); + addrconf6_cleanup (self); + dnsmasq_cleanup (self); + aipd_cleanup (self); +} + +static void +_cleanup_generic_post (NMDevice *self, gboolean deconfigure) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; + + /* Clean up IP configs; this does not actually deconfigure the + * interface; the caller must flush routes and addresses explicitly. + */ + nm_device_set_ip4_config (self, NULL, TRUE, &ignored); + nm_device_set_ip6_config (self, NULL, TRUE, &ignored); + g_clear_object (&priv->dev_ip4_config); + g_clear_object (&priv->ext_ip4_config); + g_clear_object (&priv->vpn4_config); + g_clear_object (&priv->ip4_config); + g_clear_object (&priv->ac_ip6_config); + g_clear_object (&priv->ext_ip6_config); + g_clear_object (&priv->vpn6_config); + g_clear_object (&priv->ip6_config); + + clear_act_request (self); + + /* Clear legacy IPv4 address property */ + if (priv->ip4_address) { + priv->ip4_address = 0; + g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS); + } + + if (deconfigure) { + /* Check if the device was deactivated, and if so, delete_link. + * Don't call delete_link synchronously because we are currently + * handling a state change -- which is not reentrant. */ + delete_on_deactivate_check_and_schedule (self, nm_device_get_ip_ifindex (self)); + } + + /* ip_iface should be cleared after flushing all routes and addreses, since + * those are identified by ip_iface, not by iface (which might be a tty + * or ATM device). + */ + nm_device_set_ip_iface (self, NULL); +} + /* * nm_device_cleanup * @@ -6269,8 +6354,6 @@ static void nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason) { NMDevicePrivate *priv; - NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; - NMConnection *connection = NULL; int ifindex; g_return_if_fail (NM_IS_DEVICE (self)); @@ -6286,39 +6369,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason) /* Save whether or not we tried IPv6 for later */ priv = NM_DEVICE_GET_PRIVATE (self); - /* Clean up when device was deactivated during call to firewall */ - if (priv->fw_call) { - nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call); - priv->fw_call = NULL; - } - - if (priv->act_request) - connection = nm_act_request_get_connection (priv->act_request); - if (connection) { - nm_firewall_manager_remove_from_zone (priv->fw_manager, - nm_device_get_ip_iface (self), - NULL); - } - - ip_check_gw_ping_cleanup (self); - - /* Break the activation chain */ - activation_source_clear (self, TRUE, AF_INET); - activation_source_clear (self, TRUE, AF_INET6); - - /* Clear any queued transitions */ - nm_device_queued_state_clear (self); - nm_device_queued_ip_config_change_clear (self); - - priv->ip4_state = priv->ip6_state = IP_NONE; - - dhcp4_cleanup (self, TRUE, FALSE); - arp_cleanup (self); - dhcp6_cleanup (self, TRUE, FALSE); - linklocal6_cleanup (self); - addrconf6_cleanup (self); - dnsmasq_cleanup (self); - aipd_cleanup (self); + _cleanup_generic_pre (self, TRUE); /* Turn off kernel IPv6 */ nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); @@ -6337,9 +6388,6 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason) priv->enslaved = FALSE; g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER); - /* Tear down an existing activation request */ - clear_act_request (self); - /* Take out any entries in the routing table and any IP address the device had. */ ifindex = nm_device_get_ip_ifindex (self); if (ifindex > 0) { @@ -6347,28 +6395,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason) nm_platform_address_flush (ifindex); } - /* Clean up nameservers and addresses */ - nm_device_set_ip4_config (self, NULL, TRUE, &ignored); - nm_device_set_ip6_config (self, NULL, TRUE, &ignored); - g_clear_object (&priv->ext_ip4_config); - g_clear_object (&priv->vpn4_config); - g_clear_object (&priv->vpn6_config); - g_clear_object (&priv->ext_ip6_config); - - /* Clear legacy IPv4 address property */ - priv->ip4_address = 0; - g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS); - - /* Only clear ip_iface after flushing all routes and addreses, since - * those are identified by ip_iface, not by iface (which might be a tty - * or ATM device). - */ - nm_device_set_ip_iface (self, NULL); - - /* Check if the device was deactivated, and if so, delete_link. - * Don't call delete_link synchronously because we are currently - * handling a state change -- which is not reentrant. */ - delete_on_deactivate_check_and_schedule (self, ifindex); + _cleanup_generic_post (self, TRUE); } /***********************************************************/ @@ -7123,49 +7150,18 @@ dispose (GObject *object) } } - ip_check_gw_ping_cleanup (self); - - /* Clear any queued transitions */ - nm_device_queued_state_clear (self); - nm_device_queued_ip_config_change_clear (self); - - /* Clean up and stop address configuration */ - dhcp4_cleanup (self, deconfigure, FALSE); - arp_cleanup (self); - dhcp6_cleanup (self, deconfigure, FALSE); - linklocal6_cleanup (self); - addrconf6_cleanup (self); - dnsmasq_cleanup (self); - aipd_cleanup (self); + _cleanup_generic_pre (self, deconfigure); g_warn_if_fail (priv->slaves == NULL); g_assert (priv->master_ready_id == 0); - /* Take the device itself down and clear its IP configuration */ if (nm_device_get_managed (self) && deconfigure) { - NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; - if (nm_device_get_act_request (self)) nm_device_cleanup (self, NM_DEVICE_STATE_REASON_REMOVED); - nm_device_set_ip4_config (self, NULL, TRUE, &ignored); - nm_device_set_ip6_config (self, NULL, TRUE, &ignored); - nm_device_take_down (self, FALSE); - restore_ip6_properties (self); - - /* do a final check whether we should delete_link */ - delete_on_deactivate_check_and_schedule (self, nm_device_get_ip_ifindex (self)); } - g_clear_object (&priv->dev_ip4_config); - g_clear_object (&priv->ext_ip4_config); - g_clear_object (&priv->vpn4_config); - g_clear_object (&priv->ip4_config); - - g_clear_object (&priv->ac_ip6_config); - g_clear_object (&priv->ext_ip6_config); - g_clear_object (&priv->vpn6_config); - g_clear_object (&priv->ip6_config); + _cleanup_generic_post (self, deconfigure); g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref); @@ -7191,24 +7187,13 @@ dispose (GObject *object) priv->carrier_wait_id = 0; } - activation_source_clear (self, TRUE, AF_INET); - activation_source_clear (self, TRUE, AF_INET6); - - clear_act_request (self); g_clear_object (&priv->queued_act_request); platform = nm_platform_get (); g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self); g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); - /* Clean up when device was deactivated during call to firewall */ - if (priv->fw_manager) { - if (priv->fw_call) { - nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call); - priv->fw_call = NULL; - } - g_clear_object (&priv->fw_manager); - } + g_clear_object (&priv->fw_manager); out: G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); From c93ae45b42dc94fce8088a6291664aa403fc6da6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 23 May 2014 15:41:46 -0500 Subject: [PATCH 27/43] core: don't do anything interesting in NMDevice dispose() The NMDevice dispose() function contained some badly-duplicated logic about when to deactivate a device on its last ref. This logic should only run when the device is removed by the manager, since the manager controls the device's life-cycle, and the manager knows best when to clean up the device. But since it was tied to the device's refcount, it could have run later than the manager wanted, or not at all. It gets better. Dispose duplicated logic that was already done in nm_device_cleanup(), and then *called* nm_device_cleanup() if the device was still activated and managed. But the manager already unmanages the device when removing it, which triggers a call to nm_device_cleanup(), takes the device down, and resets the IPv6 sysctl properties, which dispose() duplicated too. So by the time dispose() runs, the device should already be unmanaged if the manager wants to deconfigure it, and most of the dispose() code should be a no-op. Clean all that up and remove duplicated functions. Now, the flow should be like this: 1) manager decides to remove the device and calls remove_device() 2) if the device should be deconfigured, the manager unmanages the device 3) the NMDevice state change handler tears down the active connection via nm_device_cleanup() and resets IPv6 sysctl properties 4) when the device's last reference is finally released, only internal data members are freed in dispose() because the device should already have been cleaned up by the manager and be unmanaged 5) if the device should be left running because it has an assumable connection, then the device is not unmanaged, and no cleanup happens in the state change handler or in dispose() --- src/devices/nm-device.c | 122 ++++++++++++++++++++++++---------------- src/devices/nm-device.h | 2 +- src/nm-manager.c | 2 +- 3 files changed, 76 insertions(+), 50 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 7f73b74e3..201eace3f 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -175,8 +175,6 @@ typedef struct { } DeleteOnDeactivateData; typedef struct { - gboolean disposed; - gboolean initialized; gboolean in_state_changed; NMDeviceState state; @@ -1847,6 +1845,18 @@ nm_device_check_connection_compatible (NMDevice *device, NMConnection *connectio return NM_DEVICE_GET_CLASS (device)->check_connection_compatible (device, connection); } +static gboolean +string_in_list (const char *str, const char **array, gsize array_len) +{ + gsize i; + + for (i = 0; i < array_len; i++) { + if (strcmp (str, array[i]) == 0) + return TRUE; + } + return FALSE; +} + /** * nm_device_can_assume_connections: * @device: #NMDevice instance @@ -1854,21 +1864,72 @@ nm_device_check_connection_compatible (NMDevice *device, NMConnection *connectio * This is a convenience function to determine whether connection assumption * is available for this device. * - * Use this function when you need to determine whether full cleanup should - * be performed for this device or whether the device should be kept running - * between NetworkManager runs. - * - * Returns: %TRUE for assumable connections and %FALS for full-cleanup connections. - * - * FIXME: Consider turning this method into (a) a device capability or (b) a class - * method. + * Returns: %TRUE if the device is capable of assuming connections, %FALSE if not */ -gboolean +static gboolean nm_device_can_assume_connections (NMDevice *device) { return !!NM_DEVICE_GET_CLASS (device)->update_connection; } +/** + * nm_device_can_assume_active_connection: + * @device: #NMDevice instance + * + * This is a convenience function to determine whether the device's active + * connection can be assumed if NetworkManager restarts. This method returns + * %TRUE if and only if the device can assume connections, and the device has + * an active connection, and that active connection can be assumed. + * + * Returns: %TRUE if the device's active connection can be assumed, or %FALSE + * if there is no active connection or the active connection cannot be + * assumed. + */ +gboolean +nm_device_can_assume_active_connection (NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMConnection *connection; + const char *method; + const char *assumable_ip6_methods[] = { + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + }; + const char *assumable_ip4_methods[] = { + NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + }; + + if (!nm_device_can_assume_connections (device)) + return FALSE; + + connection = nm_device_get_connection (device); + if (!connection) + return FALSE; + + /* Can't assume connections that aren't yet configured + * FIXME: what about bridges/bonds waiting for slaves? + */ + if (priv->state < NM_DEVICE_STATE_IP_CONFIG) + return FALSE; + if (priv->ip4_state != IP_DONE && priv->ip6_state != IP_DONE) + return FALSE; + + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + if (!string_in_list (method, assumable_ip6_methods, G_N_ELEMENTS (assumable_ip6_methods))) + return FALSE; + + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (!string_in_list (method, assumable_ip4_methods, G_N_ELEMENTS (assumable_ip4_methods))) + return FALSE; + + return TRUE; +} + static gboolean nm_device_emit_recheck_assume (gpointer self) { @@ -7051,7 +7112,6 @@ constructor (GType type, g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev); g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), dev); - priv->initialized = TRUE; return object; error: @@ -7121,47 +7181,14 @@ dispose (GObject *object) { NMDevice *self = NM_DEVICE (object); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean deconfigure = TRUE; NMPlatform *platform; - if (priv->disposed || !priv->initialized) - goto out; - - priv->disposed = TRUE; - - /* Don't down can-assume-connection capable devices that are activated with - * a connection that can be assumed. - */ - if (nm_device_can_assume_connections (self) && (priv->state == NM_DEVICE_STATE_ACTIVATED)) { - NMConnection *connection; - const char *method; - - connection = nm_device_get_connection (self); - if (connection) { - /* Only static or DHCP IPv4 connections can be left up. - * All IPv6 connections can be left up, so we don't have - * to check that. - */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if ( !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) - || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) - || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - deconfigure = FALSE; - } - } - - _cleanup_generic_pre (self, deconfigure); + _cleanup_generic_pre (self, FALSE); g_warn_if_fail (priv->slaves == NULL); g_assert (priv->master_ready_id == 0); - if (nm_device_get_managed (self) && deconfigure) { - if (nm_device_get_act_request (self)) - nm_device_cleanup (self, NM_DEVICE_STATE_REASON_REMOVED); - nm_device_take_down (self, FALSE); - restore_ip6_properties (self); - } - _cleanup_generic_post (self, deconfigure); + _cleanup_generic_post (self, FALSE); g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref); @@ -7195,7 +7222,6 @@ dispose (GObject *object) g_clear_object (&priv->fw_manager); -out: G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); } diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index d63faecd0..3241bceaf 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -272,7 +272,7 @@ gboolean nm_device_complete_connection (NMDevice *device, gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection); -gboolean nm_device_can_assume_connections (NMDevice *device); +gboolean nm_device_can_assume_active_connection (NMDevice *device); gboolean nm_device_spec_match_list (NMDevice *device, const GSList *specs); diff --git a/src/nm-manager.c b/src/nm-manager.c index 452f4acd4..14e071e0b 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -737,7 +737,7 @@ remove_device (NMManager *manager, NMDevice *device, gboolean quitting) */ if (!quitting) /* Forced removal; device already gone */ unmanage = TRUE; - else if (!nm_device_can_assume_connections (device)) + else if (!nm_device_can_assume_active_connection (device)) unmanage = TRUE; else if (!req) unmanage = TRUE; From 4cc13befd3cc242c5065ce6ea91e335b705b2f36 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 14 May 2014 14:51:43 -0500 Subject: [PATCH 28/43] dispatcher: rename executable to 'nm-dispatcher' --- callouts/Makefile.am | 10 +++++----- .../{nm-dispatcher-action.h => nm-dispatcher-api.h} | 0 callouts/nm-dispatcher-utils.c | 2 +- callouts/{nm-dispatcher-action.c => nm-dispatcher.c} | 2 +- callouts/org.freedesktop.nm_dispatcher.service.in | 2 +- callouts/tests/test-dispatcher-envp.c | 2 +- contrib/fedora/rpm/NetworkManager.spec | 2 +- data/NetworkManager-dispatcher.service.in | 2 +- src/nm-dispatcher.c | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) rename callouts/{nm-dispatcher-action.h => nm-dispatcher-api.h} (100%) rename callouts/{nm-dispatcher-action.c => nm-dispatcher.c} (99%) diff --git a/callouts/Makefile.am b/callouts/Makefile.am index 8b018be70..a7ef42984 100644 --- a/callouts/Makefile.am +++ b/callouts/Makefile.am @@ -26,7 +26,7 @@ dbusservice_DATA = \ nm-avahi-autoipd.conf libexec_PROGRAMS = \ - nm-dispatcher.action \ + nm-dispatcher \ nm-avahi-autoipd.action @@ -38,13 +38,13 @@ nm_avahi_autoipd_action_LDADD = \ $(GLIB_LIBS) -nm_dispatcher_action_SOURCES = \ - nm-dispatcher-action.c \ - nm-dispatcher-action.h \ +nm_dispatcher_SOURCES = \ + nm-dispatcher.c \ + nm-dispatcher-api.h \ nm-dispatcher-utils.c \ nm-dispatcher-utils.h -nm_dispatcher_action_LDADD = \ +nm_dispatcher_LDADD = \ $(top_builddir)/libnm-util/libnm-util.la \ $(DBUS_LIBS) \ $(GLIB_LIBS) diff --git a/callouts/nm-dispatcher-action.h b/callouts/nm-dispatcher-api.h similarity index 100% rename from callouts/nm-dispatcher-action.h rename to callouts/nm-dispatcher-api.h diff --git a/callouts/nm-dispatcher-utils.c b/callouts/nm-dispatcher-utils.c index 1f99a0b6c..b1d11daf3 100644 --- a/callouts/nm-dispatcher-utils.c +++ b/callouts/nm-dispatcher-utils.c @@ -30,7 +30,7 @@ #include #include -#include "nm-dispatcher-action.h" +#include "nm-dispatcher-api.h" #include "nm-utils.h" #include "nm-dispatcher-utils.h" diff --git a/callouts/nm-dispatcher-action.c b/callouts/nm-dispatcher.c similarity index 99% rename from callouts/nm-dispatcher-action.c rename to callouts/nm-dispatcher.c index 337a5c616..e9cd2e74e 100644 --- a/callouts/nm-dispatcher-action.c +++ b/callouts/nm-dispatcher.c @@ -37,7 +37,7 @@ #include -#include "nm-dispatcher-action.h" +#include "nm-dispatcher-api.h" #include "nm-dispatcher-utils.h" #include "nm-glib-compat.h" diff --git a/callouts/org.freedesktop.nm_dispatcher.service.in b/callouts/org.freedesktop.nm_dispatcher.service.in index 9feb3b468..ff037cca9 100644 --- a/callouts/org.freedesktop.nm_dispatcher.service.in +++ b/callouts/org.freedesktop.nm_dispatcher.service.in @@ -1,6 +1,6 @@ [D-BUS Service] Name=org.freedesktop.nm_dispatcher -Exec=@libexecdir@/nm-dispatcher.action +Exec=@libexecdir@/nm-dispatcher User=root SystemdService=dbus-org.freedesktop.nm-dispatcher.service diff --git a/callouts/tests/test-dispatcher-envp.c b/callouts/tests/test-dispatcher-envp.c index 8dd18430e..a91ae2b27 100644 --- a/callouts/tests/test-dispatcher-envp.c +++ b/callouts/tests/test-dispatcher-envp.c @@ -29,7 +29,7 @@ #include "nm-setting-connection.h" #include "nm-dispatcher-utils.h" #include "nm-dbus-glib-types.h" -#include "nm-dispatcher-action.h" +#include "nm-dispatcher-api.h" #include "nm-utils.h" /*******************************************/ diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec index 616f219b3..3a2f29b2b 100644 --- a/contrib/fedora/rpm/NetworkManager.spec +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -441,7 +441,7 @@ fi %{_bindir}/nm-online %{_libexecdir}/nm-dhcp-helper %{_libexecdir}/nm-avahi-autoipd.action -%{_libexecdir}/nm-dispatcher.action +%{_libexecdir}/nm-dispatcher %dir %{_libdir}/NetworkManager %{_libdir}/NetworkManager/libnm-settings-plugin*.so %{_mandir}/man1/* diff --git a/data/NetworkManager-dispatcher.service.in b/data/NetworkManager-dispatcher.service.in index e1b90036f..c450478ba 100644 --- a/data/NetworkManager-dispatcher.service.in +++ b/data/NetworkManager-dispatcher.service.in @@ -4,7 +4,7 @@ Description=Network Manager Script Dispatcher Service [Service] Type=dbus BusName=org.freedesktop.nm_dispatcher -ExecStart=@libexecdir@/nm-dispatcher.action +ExecStart=@libexecdir@/nm-dispatcher # We want to allow scripts to spawn long-running daemons, so tell # systemd to not clean up when nm-dispatcher exits diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 63773862c..bc10e920c 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -23,7 +23,7 @@ #include #include "nm-dispatcher.h" -#include "nm-dispatcher-action.h" +#include "nm-dispatcher-api.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-logging.h" From 5150cb88c26d13e8c7c188f4f10614b6d318ad89 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 14 May 2014 15:50:33 -0500 Subject: [PATCH 29/43] dispatcher: only dispatch if scripts exist If there are no dispatcher scripts, don't bother dispatching any events. This saves some time configuring networking if the event would have no effect anyway. --- callouts/nm-dispatcher-api.h | 2 ++ callouts/nm-dispatcher.c | 2 -- src/main.c | 3 ++ src/nm-dispatcher.c | 55 ++++++++++++++++++++++++++++++++++++ src/nm-dispatcher.h | 2 ++ 5 files changed, 62 insertions(+), 2 deletions(-) diff --git a/callouts/nm-dispatcher-api.h b/callouts/nm-dispatcher-api.h index 9fea487ad..96db78928 100644 --- a/callouts/nm-dispatcher-api.h +++ b/callouts/nm-dispatcher-api.h @@ -20,6 +20,8 @@ #include +#define NMD_SCRIPT_DIR NMCONFDIR "/dispatcher.d" + /* dbus-glib types for dispatcher call return value */ #define DISPATCHER_TYPE_RESULT (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID)) #define DISPATCHER_TYPE_RESULT_ARRAY (dbus_g_type_get_collection ("GPtrArray", DISPATCHER_TYPE_RESULT)) diff --git a/callouts/nm-dispatcher.c b/callouts/nm-dispatcher.c index e9cd2e74e..3747fd563 100644 --- a/callouts/nm-dispatcher.c +++ b/callouts/nm-dispatcher.c @@ -41,8 +41,6 @@ #include "nm-dispatcher-utils.h" #include "nm-glib-compat.h" -#define NMD_SCRIPT_DIR NMCONFDIR "/dispatcher.d" - static GMainLoop *loop = NULL; static gboolean debug = FALSE; diff --git a/src/main.c b/src/main.c index 4bd2b4324..0ba65b0b8 100644 --- a/src/main.c +++ b/src/main.c @@ -54,6 +54,7 @@ #include "nm-config.h" #include "nm-posix-signals.h" #include "nm-session-monitor.h" +#include "nm-dispatcher.h" #if !defined(NM_DIST_VERSION) # define NM_DIST_VERSION VERSION @@ -604,6 +605,8 @@ main (int argc, char *argv[]) dhcp_mgr = nm_dhcp_manager_get (); g_assert (dhcp_mgr != NULL); + nm_dispatcher_init (); + settings = nm_settings_new (&error); if (!settings) { nm_log_err (LOGD_CORE, "failed to initialize settings storage: %s", diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index bc10e920c..24abd0418 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -31,6 +31,7 @@ #include "nm-dbus-glib-types.h" #include "nm-glib-compat.h" +static gboolean do_dispatch = TRUE; static GSList *requests = NULL; static void @@ -134,12 +135,15 @@ fill_vpn_props (NMIP4Config *ip4_config, typedef struct { DispatcherFunc callback; gpointer user_data; + guint idle_id; } DispatchInfo; static void dispatcher_info_free (DispatchInfo *info) { requests = g_slist_remove (requests, info); + if (info->idle_id) + g_source_remove (info->idle_id); g_free (info); } @@ -261,6 +265,18 @@ action_to_string (DispatcherAction action) g_assert_not_reached (); } +static gboolean +dispatcher_idle_cb (gpointer user_data) +{ + DispatchInfo *info = user_data; + + info->idle_id = 0; + if (info->callback) + info->callback (info, info->user_data); + dispatcher_info_free (info); + return G_SOURCE_REMOVE; +} + static gconstpointer _dispatcher_call (DispatcherAction action, NMConnection *connection, @@ -299,6 +315,16 @@ _dispatcher_call (DispatcherAction action, if (action == DISPATCHER_ACTION_VPN_UP) g_return_val_if_fail (vpn_ip4_config != NULL, NULL); + if (do_dispatch == FALSE) { + info = g_malloc0 (sizeof (*info)); + info->callback = callback; + info->user_data = user_data; + info->idle_id = g_idle_add (dispatcher_idle_cb, info); + requests = g_slist_append (requests, info); + nm_log_dbg (LOGD_DISPATCH, "ignoring request; no scripts in " NMD_SCRIPT_DIR); + return info; + } + g_connection = nm_dbus_manager_get_connection (nm_dbus_manager_get ()); proxy = dbus_g_proxy_new_for_name (g_connection, NM_DISPATCHER_DBUS_SERVICE, @@ -416,3 +442,32 @@ nm_dispatcher_call_cancel (gconstpointer call) ((DispatchInfo *) call)->callback = NULL; } +static void +dispatcher_dir_changed (GFileMonitor *monitor) +{ + GDir *dir; + + /* Default to dispatching on any errors */ + do_dispatch = TRUE; + dir = g_dir_open (NMD_SCRIPT_DIR, 0, NULL); + if (dir) { + do_dispatch = !!g_dir_read_name (dir); + g_dir_close (dir); + } +} + +void +nm_dispatcher_init (void) +{ + GFile *file; + static GFileMonitor *monitor; + + file = g_file_new_for_path (NMD_SCRIPT_DIR); + monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); + if (monitor) { + g_signal_connect (monitor, "changed", G_CALLBACK (dispatcher_dir_changed), NULL); + dispatcher_dir_changed (monitor); + } + g_object_unref (file); +} + diff --git a/src/nm-dispatcher.h b/src/nm-dispatcher.h index 05a6c8751..179eb2f31 100644 --- a/src/nm-dispatcher.h +++ b/src/nm-dispatcher.h @@ -61,4 +61,6 @@ gconstpointer nm_dispatcher_call_vpn (DispatcherAction action, void nm_dispatcher_call_cancel (gconstpointer call); +void nm_dispatcher_init (void); + #endif /* NM_DISPATCHER_H */ From 90b747fa116db600723227648afd5049ad53999e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 14 May 2014 21:21:10 -0500 Subject: [PATCH 30/43] dispatcher: add synchronous dispatcher calls On shutdown we can't defer the response to a callback, so we need to use synchronous D-Bus calls. Second, sometimes we want to block on the dispatcher response, like for pre-down. --- src/devices/nm-device.c | 9 +- src/nm-dispatcher.c | 339 ++++++++++++++++++++-------- src/nm-dispatcher.h | 35 ++- src/nm-policy.c | 2 +- src/vpn-manager/nm-vpn-connection.c | 2 + 5 files changed, 274 insertions(+), 113 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 201eace3f..c879b5632 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2654,6 +2654,7 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config) nm_device_get_connection (self), self, NULL, + NULL, NULL); } } @@ -3101,7 +3102,7 @@ dhcp6_lease_change (NMDevice *device) nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); } else { /* Notify dispatcher scripts of new DHCPv6 config */ - nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, connection, device, NULL, NULL); + nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, connection, device, NULL, NULL, NULL); } } @@ -4560,6 +4561,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data) nm_device_get_connection (self), self, NULL, + NULL, NULL); } @@ -4654,6 +4656,7 @@ nm_device_activate_ip6_config_commit (gpointer user_data) nm_device_get_connection (self), self, NULL, + NULL, NULL); } @@ -6635,7 +6638,7 @@ nm_device_state_changed (NMDevice *device, case NM_DEVICE_STATE_ACTIVATED: nm_log_info (LOGD_DEVICE, "Activation (%s) successful, device activated.", nm_device_get_iface (device)); - nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), device, NULL, NULL); + nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), device, NULL, NULL, NULL); break; case NM_DEVICE_STATE_FAILED: connection = nm_device_get_connection (device); @@ -6685,7 +6688,7 @@ nm_device_state_changed (NMDevice *device, delete_on_deactivate_unschedule (device); if (old_state == NM_DEVICE_STATE_ACTIVATED) - nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device, NULL, NULL); + nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device, NULL, NULL, NULL); /* IP-related properties are only valid when the device has IP configuration. * If it no longer does, ensure their change notifications are emitted. diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 24abd0418..7eef2cb90 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -165,66 +165,82 @@ dispatch_result_to_string (DispatchResult result) g_assert_not_reached (); } +static void +dispatcher_results_process (GPtrArray *results) +{ + guint i; + + g_return_if_fail (results != NULL); + + for (i = 0; i < results->len; i++) { + GValueArray *item = g_ptr_array_index (results, i); + GValue *tmp; + const char *script, *err; + DispatchResult result; + + if (item->n_values != 3) { + nm_log_dbg (LOGD_DISPATCH, "Unexpected number of items in " + "dispatcher result (got %d, expectd 3)", + item->n_values); + continue; + } + + /* Script */ + tmp = g_value_array_get_nth (item, 0); + if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) { + nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 0 invalid type %s", + i, G_VALUE_TYPE_NAME (tmp)); + continue; + } + script = g_value_get_string (tmp); + + /* Result */ + tmp = g_value_array_get_nth (item, 1); + if (G_VALUE_TYPE (tmp) != G_TYPE_UINT) { + nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 1 invalid type %s", + i, G_VALUE_TYPE_NAME (tmp)); + continue; + } + result = g_value_get_uint (tmp); + + /* Error */ + tmp = g_value_array_get_nth (item, 2); + if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) { + nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 2 invalid type %s", + i, G_VALUE_TYPE_NAME (tmp)); + continue; + } + err = g_value_get_string (tmp); + + if (result == DISPATCH_RESULT_SUCCESS) + nm_log_dbg (LOGD_DISPATCH, "Dispatcher script \"%s\" succeeded", script); + else { + nm_log_warn (LOGD_DISPATCH, "Dispatcher script \"%s\" failed with %s: %s", + script, dispatch_result_to_string (result), err); + } + } +} + +static void +free_results (GPtrArray *results) +{ + g_return_if_fail (results != NULL); + g_ptr_array_foreach (results, (GFunc) g_value_array_free, NULL); + g_ptr_array_free (results, TRUE); +} + static void dispatcher_done_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) { DispatchInfo *info = user_data; GError *error = NULL; GPtrArray *results = NULL; - guint i; if (dbus_g_proxy_end_call (proxy, call, &error, DISPATCHER_TYPE_RESULT_ARRAY, &results, G_TYPE_INVALID)) { - for (i = 0; results && (i < results->len); i++) { - GValueArray *item = g_ptr_array_index (results, i); - GValue *tmp; - const char *script, *err; - DispatchResult result; - - if (item->n_values != 3) { - nm_log_dbg (LOGD_DISPATCH, "Unexpected number of items in " - "dispatcher result (got %d, expectd 3)", - item->n_values); - goto next; - } - - /* Script */ - tmp = g_value_array_get_nth (item, 0); - if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) { - nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 0 invalid type %s", - i, G_VALUE_TYPE_NAME (tmp)); - goto next; - } - script = g_value_get_string (tmp); - - /* Result */ - tmp = g_value_array_get_nth (item, 1); - if (G_VALUE_TYPE (tmp) != G_TYPE_UINT) { - nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 1 invalid type %s", - i, G_VALUE_TYPE_NAME (tmp)); - goto next; - } - result = g_value_get_uint (tmp); - - /* Error */ - tmp = g_value_array_get_nth (item, 2); - if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) { - nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 2 invalid type %s", - i, G_VALUE_TYPE_NAME (tmp)); - goto next; - } - err = g_value_get_string (tmp); - - if (result != DISPATCH_RESULT_SUCCESS) { - nm_log_warn (LOGD_DISPATCH, "Dispatcher script \"%s\" failed with %s: %s", - script, dispatch_result_to_string (result), err); - } - -next: - g_value_array_free (item); - } - g_ptr_array_free (results, TRUE); + dispatcher_results_process (results); + free_results (results); } else { g_assert (error); nm_log_warn (LOGD_DISPATCH, "Dispatcher failed: (%d) %s", error->code, error->message); @@ -277,15 +293,17 @@ dispatcher_idle_cb (gpointer user_data) return G_SOURCE_REMOVE; } -static gconstpointer +static gboolean _dispatcher_call (DispatcherAction action, + gboolean blocking, NMConnection *connection, NMDevice *device, const char *vpn_iface, NMIP4Config *vpn_ip4_config, NMIP6Config *vpn_ip6_config, DispatcherFunc callback, - gpointer user_data) + gpointer user_data, + gconstpointer *out_call_id) { DBusGProxy *proxy; DBusGConnection *g_connection; @@ -298,14 +316,18 @@ _dispatcher_call (DispatcherAction action, GHashTable *device_dhcp6_props; GHashTable *vpn_ip4_props; GHashTable *vpn_ip6_props; - DispatchInfo *info; + DispatchInfo *info = NULL; + gboolean success = FALSE; + GError *error = NULL; + + g_assert (!blocking || (!callback && !user_data)); /* All actions except 'hostname' require a device */ if (action == DISPATCHER_ACTION_HOSTNAME) { nm_log_dbg (LOGD_DISPATCH, "dispatching action '%s'", action_to_string (action)); } else { - g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); nm_log_dbg (LOGD_DISPATCH, "(%s) dispatching action '%s'", nm_device_get_iface (device), action_to_string (action)); @@ -313,16 +335,18 @@ _dispatcher_call (DispatcherAction action, /* VPN actions require at least an IPv4 config (for now) */ if (action == DISPATCHER_ACTION_VPN_UP) - g_return_val_if_fail (vpn_ip4_config != NULL, NULL); + g_return_val_if_fail (vpn_ip4_config != NULL, FALSE); if (do_dispatch == FALSE) { - info = g_malloc0 (sizeof (*info)); - info->callback = callback; - info->user_data = user_data; - info->idle_id = g_idle_add (dispatcher_idle_cb, info); - requests = g_slist_append (requests, info); + if (blocking == FALSE && (out_call_id || callback)) { + info = g_malloc0 (sizeof (*info)); + info->callback = callback; + info->user_data = user_data; + info->idle_id = g_idle_add (dispatcher_idle_cb, info); + } nm_log_dbg (LOGD_DISPATCH, "ignoring request; no scripts in " NMD_SCRIPT_DIR); - return info; + success = TRUE; + goto done; } g_connection = nm_dbus_manager_get_connection (nm_dbus_manager_get ()); @@ -332,7 +356,7 @@ _dispatcher_call (DispatcherAction action, NM_DISPATCHER_DBUS_IFACE); if (!proxy) { nm_log_err (LOGD_DISPATCH, "could not get dispatcher proxy!"); - return NULL; + return FALSE; } if (connection) { @@ -367,29 +391,60 @@ _dispatcher_call (DispatcherAction action, fill_vpn_props (vpn_ip4_config, vpn_ip6_config, vpn_ip4_props, vpn_ip6_props); } - info = g_malloc0 (sizeof (*info)); - info->callback = callback; - info->user_data = user_data; - /* Send the action to the dispatcher */ - dbus_g_proxy_begin_call_with_timeout (proxy, "Action", - dispatcher_done_cb, - info, - (GDestroyNotify) dispatcher_info_free, - 30000, - G_TYPE_STRING, action_to_string (action), - DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash, - DBUS_TYPE_G_MAP_OF_VARIANT, connection_props, - DBUS_TYPE_G_MAP_OF_VARIANT, device_props, - DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props, - DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props, - DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props, - DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props, - G_TYPE_STRING, vpn_iface ? vpn_iface : "", - DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props, - DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props, - G_TYPE_BOOLEAN, nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH), - G_TYPE_INVALID); + if (blocking) { + GPtrArray *results = NULL; + + success = dbus_g_proxy_call_with_timeout (proxy, "Action", + 30000, + &error, + G_TYPE_STRING, action_to_string (action), + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash, + DBUS_TYPE_G_MAP_OF_VARIANT, connection_props, + DBUS_TYPE_G_MAP_OF_VARIANT, device_props, + DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props, + DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props, + DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props, + DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props, + G_TYPE_STRING, vpn_iface ? vpn_iface : "", + DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props, + DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props, + G_TYPE_BOOLEAN, nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH), + G_TYPE_INVALID, + DISPATCHER_TYPE_RESULT_ARRAY, &results, + G_TYPE_INVALID); + if (success) { + dispatcher_results_process (results); + free_results (results); + } else { + nm_log_warn (LOGD_DISPATCH, "Dispatcher failed: (%d) %s", error->code, error->message); + g_error_free (error); + } + } else { + info = g_malloc0 (sizeof (*info)); + info->callback = callback; + info->user_data = user_data; + dbus_g_proxy_begin_call_with_timeout (proxy, "Action", + dispatcher_done_cb, + info, + (GDestroyNotify) dispatcher_info_free, + 30000, + G_TYPE_STRING, action_to_string (action), + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash, + DBUS_TYPE_G_MAP_OF_VARIANT, connection_props, + DBUS_TYPE_G_MAP_OF_VARIANT, device_props, + DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props, + DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props, + DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props, + DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props, + G_TYPE_STRING, vpn_iface ? vpn_iface : "", + DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props, + DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props, + G_TYPE_BOOLEAN, nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH), + G_TYPE_INVALID); + success = TRUE; + } + g_hash_table_destroy (connection_hash); g_hash_table_destroy (connection_props); g_hash_table_destroy (device_props); @@ -400,46 +455,134 @@ _dispatcher_call (DispatcherAction action, g_hash_table_destroy (vpn_ip4_props); g_hash_table_destroy (vpn_ip6_props); - /* Track the request in case of cancelation */ - requests = g_slist_append (requests, info); +done: + if (success && info) { + /* Track the request in case of cancelation */ + requests = g_slist_append (requests, info); + if (out_call_id) + *out_call_id = (gconstpointer) info; + } - return info; + return success; } -gconstpointer +/** + * nm_dispatcher_call: + * @action: the %DispatcherAction + * @connection: the #NMConnection the action applies to + * @device: the #NMDevice the action applies to + * @callback: a caller-supplied callback to execute when done + * @user_data: caller-supplied pointer passed to @callback + * @out_call_id: on success, a call identifier which can be passed to + * nm_dispatcher_call_cancel() + * + * This method always invokes the dispatcher action asynchronously. To ignore + * the result, pass %NULL to @callback. + * + * Returns: %TRUE if the action was dispatched, %FALSE on failure + */ +gboolean nm_dispatcher_call (DispatcherAction action, NMConnection *connection, NMDevice *device, DispatcherFunc callback, - gpointer user_data) + gpointer user_data, + gconstpointer *out_call_id) { - return _dispatcher_call (action, connection, device, NULL, NULL, NULL, callback, user_data); + return _dispatcher_call (action, FALSE, connection, device, NULL, NULL, + NULL, callback, user_data, out_call_id); } -gconstpointer +/** + * nm_dispatcher_call_sync(): + * @action: the %DispatcherAction + * @connection: the #NMConnection the action applies to + * @device: the #NMDevice the action applies to + * + * This method always invokes the dispatcher action synchronously and it may + * take a long time to return. + * + * Returns: %TRUE if the action was dispatched, %FALSE on failure + */ +gboolean +nm_dispatcher_call_sync (DispatcherAction action, + NMConnection *connection, + NMDevice *device) +{ + return _dispatcher_call (action, TRUE, connection, device, NULL, NULL, + NULL, NULL, NULL, NULL); +} + +/** + * nm_dispatcher_call_vpn(): + * @action: the %DispatcherAction + * @connection: the #NMConnection the action applies to + * @parent_device: the parent #NMDevice of the VPN connection + * @vpn_iface: the IP interface of the VPN tunnel, if any + * @vpn_ip4_config: the #NMIP4Config of the VPN connection + * @vpn_ip6_config: the #NMIP6Config of the VPN connection + * @callback: a caller-supplied callback to execute when done + * @user_data: caller-supplied pointer passed to @callback + * @out_call_id: on success, a call identifier which can be passed to + * nm_dispatcher_call_cancel() + * + * This method always invokes the dispatcher action asynchronously. To ignore + * the result, pass %NULL to @callback. + * + * Returns: %TRUE if the action was dispatched, %FALSE on failure + */ +gboolean nm_dispatcher_call_vpn (DispatcherAction action, NMConnection *connection, - NMDevice *device, + NMDevice *parent_device, const char *vpn_iface, NMIP4Config *vpn_ip4_config, NMIP6Config *vpn_ip6_config, DispatcherFunc callback, - gpointer user_data) + gpointer user_data, + gconstpointer *out_call_id) { - return _dispatcher_call (action, connection, device, vpn_iface, vpn_ip4_config, vpn_ip6_config, callback, user_data); + return _dispatcher_call (action, FALSE, connection, parent_device, vpn_iface, + vpn_ip4_config, vpn_ip6_config, callback, user_data, out_call_id); +} + +/** + * nm_dispatcher_call_vpn_sync(): + * @action: the %DispatcherAction + * @connection: the #NMConnection the action applies to + * @parent_device: the parent #NMDevice of the VPN connection + * @vpn_iface: the IP interface of the VPN tunnel, if any + * @vpn_ip4_config: the #NMIP4Config of the VPN connection + * @vpn_ip6_config: the #NMIP6Config of the VPN connection + * + * This method always invokes the dispatcher action synchronously and it may + * take a long time to return. + * + * Returns: %TRUE if the action was dispatched, %FALSE on failure + */ +gboolean +nm_dispatcher_call_vpn_sync (DispatcherAction action, + NMConnection *connection, + NMDevice *parent_device, + const char *vpn_iface, + NMIP4Config *vpn_ip4_config, + NMIP6Config *vpn_ip6_config) +{ + return _dispatcher_call (action, TRUE, connection, parent_device, vpn_iface, + vpn_ip4_config, vpn_ip6_config, NULL, NULL, NULL); } void -nm_dispatcher_call_cancel (gconstpointer call) +nm_dispatcher_call_cancel (gconstpointer call_id) { - /* 'call' is really a DispatchInfo pointer, just opaque to callers. + /* 'call_id' is really a DispatchInfo pointer, just opaque to callers. * Look it up in our requests list, but don't access it directly before * we've made sure it's a valid request,since it may have long since been * freed. Canceling just means the callback doesn't get called, so set * the DispatcherInfo's callback to NULL. */ - if (g_slist_find (requests, call)) - ((DispatchInfo *) call)->callback = NULL; + if (g_slist_find (requests, call_id)) + ((DispatchInfo *) call_id)->callback = NULL; } static void diff --git a/src/nm-dispatcher.h b/src/nm-dispatcher.h index 179eb2f31..cf3e62613 100644 --- a/src/nm-dispatcher.h +++ b/src/nm-dispatcher.h @@ -42,24 +42,37 @@ typedef enum { DISPATCHER_ACTION_DHCP6_CHANGE } DispatcherAction; -typedef void (*DispatcherFunc) (gconstpointer call, gpointer user_data); +typedef void (*DispatcherFunc) (gconstpointer call_id, gpointer user_data); -gconstpointer nm_dispatcher_call (DispatcherAction action, +gboolean nm_dispatcher_call (DispatcherAction action, + NMConnection *connection, + NMDevice *device, + DispatcherFunc callback, + gpointer user_data, + gconstpointer *out_call_id); + +gboolean nm_dispatcher_call_sync (DispatcherAction action, NMConnection *connection, - NMDevice *device, - DispatcherFunc callback, - gpointer user_data); + NMDevice *device); -gconstpointer nm_dispatcher_call_vpn (DispatcherAction action, +gboolean nm_dispatcher_call_vpn (DispatcherAction action, + NMConnection *connection, + NMDevice *parent_device, + const char *vpn_iface, + NMIP4Config *vpn_ip4_config, + NMIP6Config *vpn_ip6_config, + DispatcherFunc callback, + gpointer user_data, + gconstpointer *out_call_id); + +gboolean nm_dispatcher_call_vpn_sync (DispatcherAction action, NMConnection *connection, - NMDevice *device, + NMDevice *parent_device, const char *vpn_iface, NMIP4Config *vpn_ip4_config, - NMIP6Config *vpn_ip6_config, - DispatcherFunc callback, - gpointer user_data); + NMIP6Config *vpn_ip6_config); -void nm_dispatcher_call_cancel (gconstpointer call); +void nm_dispatcher_call_cancel (gconstpointer call_id); void nm_dispatcher_init (void); diff --git a/src/nm-policy.c b/src/nm-policy.c index f307f999c..b54b94928 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -326,7 +326,7 @@ _set_hostname (NMPolicy *policy, nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname); if (set_system_hostname (priv->cur_hostname, msg)) - nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL); + nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL, NULL); } static void diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index c6410d780..3d43c3ece 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -341,6 +341,7 @@ _set_vpn_state (NMVPNConnection *connection, priv->ip4_config, priv->ip6_config, NULL, + NULL, NULL); break; case STATE_DEACTIVATING: @@ -358,6 +359,7 @@ _set_vpn_state (NMVPNConnection *connection, NULL, NULL, NULL, + NULL, NULL); } From 286e926ee848b12305d2c60288c0295b36860470 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 28 May 2014 16:45:56 -0500 Subject: [PATCH 31/43] dispatcher: robustify canceling dispatcher calls Thomas pointed out that using the address of the DispatcherInfo structure as the dispatcher call ID could cause a mis-cancelation if malloc re-used the same block in the future. While the code should be correctly clearing call IDs after the callback runs or is canceled, just use numeric IDs to avoid potential crashses. --- src/nm-dispatcher.c | 67 +++++++++++++++++++++++++++++++-------------- src/nm-dispatcher.h | 8 +++--- 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 7eef2cb90..87a89cab2 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -32,7 +32,7 @@ #include "nm-glib-compat.h" static gboolean do_dispatch = TRUE; -static GSList *requests = NULL; +static GHashTable *requests = NULL; static void dump_object_to_props (GObject *object, GHashTable *hash) @@ -133,6 +133,7 @@ fill_vpn_props (NMIP4Config *ip4_config, } typedef struct { + guint request_id; DispatcherFunc callback; gpointer user_data; guint idle_id; @@ -141,12 +142,28 @@ typedef struct { static void dispatcher_info_free (DispatchInfo *info) { - requests = g_slist_remove (requests, info); if (info->idle_id) g_source_remove (info->idle_id); g_free (info); } +static void +_ensure_requests (void) +{ + if (G_UNLIKELY (requests == NULL)) { + requests = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify) dispatcher_info_free); + } +} + +static void +dispatcher_info_cleanup (DispatchInfo *info) +{ + g_hash_table_remove (requests, GUINT_TO_POINTER (info->request_id)); +} + static const char * dispatch_result_to_string (DispatchResult result) { @@ -247,7 +264,7 @@ dispatcher_done_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) } if (info->callback) - info->callback (info, info->user_data); + info->callback (info->request_id, info->user_data); g_clear_error (&error); g_object_unref (proxy); @@ -288,8 +305,8 @@ dispatcher_idle_cb (gpointer user_data) info->idle_id = 0; if (info->callback) - info->callback (info, info->user_data); - dispatcher_info_free (info); + info->callback (info->request_id, info->user_data); + dispatcher_info_cleanup (info); return G_SOURCE_REMOVE; } @@ -303,7 +320,7 @@ _dispatcher_call (DispatcherAction action, NMIP6Config *vpn_ip6_config, DispatcherFunc callback, gpointer user_data, - gconstpointer *out_call_id) + guint *out_call_id) { DBusGProxy *proxy; DBusGConnection *g_connection; @@ -319,9 +336,12 @@ _dispatcher_call (DispatcherAction action, DispatchInfo *info = NULL; gboolean success = FALSE; GError *error = NULL; + static guint request_counter = 1; g_assert (!blocking || (!callback && !user_data)); + _ensure_requests (); + /* All actions except 'hostname' require a device */ if (action == DISPATCHER_ACTION_HOSTNAME) { nm_log_dbg (LOGD_DISPATCH, "dispatching action '%s'", @@ -340,6 +360,7 @@ _dispatcher_call (DispatcherAction action, if (do_dispatch == FALSE) { if (blocking == FALSE && (out_call_id || callback)) { info = g_malloc0 (sizeof (*info)); + info->request_id = request_counter++; info->callback = callback; info->user_data = user_data; info->idle_id = g_idle_add (dispatcher_idle_cb, info); @@ -422,12 +443,13 @@ _dispatcher_call (DispatcherAction action, } } else { info = g_malloc0 (sizeof (*info)); + info->request_id = request_counter++; info->callback = callback; info->user_data = user_data; dbus_g_proxy_begin_call_with_timeout (proxy, "Action", dispatcher_done_cb, info, - (GDestroyNotify) dispatcher_info_free, + (GDestroyNotify) dispatcher_info_cleanup, 30000, G_TYPE_STRING, action_to_string (action), DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash, @@ -458,10 +480,11 @@ _dispatcher_call (DispatcherAction action, done: if (success && info) { /* Track the request in case of cancelation */ - requests = g_slist_append (requests, info); + g_hash_table_insert (requests, GUINT_TO_POINTER (info->request_id), info); if (out_call_id) - *out_call_id = (gconstpointer) info; - } + *out_call_id = info->request_id; + } else if (out_call_id) + *out_call_id = 0; return success; } @@ -487,7 +510,7 @@ nm_dispatcher_call (DispatcherAction action, NMDevice *device, DispatcherFunc callback, gpointer user_data, - gconstpointer *out_call_id) + guint *out_call_id) { return _dispatcher_call (action, FALSE, connection, device, NULL, NULL, NULL, callback, user_data, out_call_id); @@ -540,7 +563,7 @@ nm_dispatcher_call_vpn (DispatcherAction action, NMIP6Config *vpn_ip6_config, DispatcherFunc callback, gpointer user_data, - gconstpointer *out_call_id) + guint *out_call_id) { return _dispatcher_call (action, FALSE, connection, parent_device, vpn_iface, vpn_ip4_config, vpn_ip6_config, callback, user_data, out_call_id); @@ -573,16 +596,20 @@ nm_dispatcher_call_vpn_sync (DispatcherAction action, } void -nm_dispatcher_call_cancel (gconstpointer call_id) +nm_dispatcher_call_cancel (guint call_id) { - /* 'call_id' is really a DispatchInfo pointer, just opaque to callers. - * Look it up in our requests list, but don't access it directly before - * we've made sure it's a valid request,since it may have long since been - * freed. Canceling just means the callback doesn't get called, so set - * the DispatcherInfo's callback to NULL. + DispatchInfo *info; + + _ensure_requests (); + + /* Canceling just means the callback doesn't get called, so set the + * DispatcherInfo's callback to NULL. */ - if (g_slist_find (requests, call_id)) - ((DispatchInfo *) call_id)->callback = NULL; + info = g_hash_table_lookup (requests, GUINT_TO_POINTER (call_id)); + if (info) + info->callback = NULL; + else + g_return_if_reached (); } static void diff --git a/src/nm-dispatcher.h b/src/nm-dispatcher.h index cf3e62613..7b50954fe 100644 --- a/src/nm-dispatcher.h +++ b/src/nm-dispatcher.h @@ -42,14 +42,14 @@ typedef enum { DISPATCHER_ACTION_DHCP6_CHANGE } DispatcherAction; -typedef void (*DispatcherFunc) (gconstpointer call_id, gpointer user_data); +typedef void (*DispatcherFunc) (guint call_id, gpointer user_data); gboolean nm_dispatcher_call (DispatcherAction action, NMConnection *connection, NMDevice *device, DispatcherFunc callback, gpointer user_data, - gconstpointer *out_call_id); + guint *out_call_id); gboolean nm_dispatcher_call_sync (DispatcherAction action, NMConnection *connection, @@ -63,7 +63,7 @@ gboolean nm_dispatcher_call_vpn (DispatcherAction action, NMIP6Config *vpn_ip6_config, DispatcherFunc callback, gpointer user_data, - gconstpointer *out_call_id); + guint *out_call_id); gboolean nm_dispatcher_call_vpn_sync (DispatcherAction action, NMConnection *connection, @@ -72,7 +72,7 @@ gboolean nm_dispatcher_call_vpn_sync (DispatcherAction action, NMIP4Config *vpn_ip4_config, NMIP6Config *vpn_ip6_config); -void nm_dispatcher_call_cancel (gconstpointer call_id); +void nm_dispatcher_call_cancel (guint call_id); void nm_dispatcher_init (void); From 19d7386b2f56fb2126e7140266e5a11ac90c2a08 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 21 May 2014 12:46:00 -0500 Subject: [PATCH 32/43] dispatcher: add PRE_UP states This event runs before a connection/device is announced as "activated" or "connected", to enable scripts to do things before applications begin using connectivity. For example, this could be used to manage /etc/resolv.conf outside of NetworkManager and ensure that resolv.conf had correct information before DNS is used. Note that this is different than the Debian or Gentoo "pre-up" event used in /etc/network/interfaces, as that event runs before any L2 configuration has started. If we really need an event like that, we'll add it later as "lower-up". --- src/nm-dispatcher.c | 4 ++++ src/nm-dispatcher.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 87a89cab2..6ebf427fd 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -276,12 +276,16 @@ action_to_string (DispatcherAction action) switch (action) { case DISPATCHER_ACTION_HOSTNAME: return "hostname"; + case DISPATCHER_ACTION_PRE_UP: + return "pre-up"; case DISPATCHER_ACTION_UP: return "up"; case DISPATCHER_ACTION_PRE_DOWN: return "pre-down"; case DISPATCHER_ACTION_DOWN: return "down"; + case DISPATCHER_ACTION_VPN_PRE_UP: + return "vpn-pre-up"; case DISPATCHER_ACTION_VPN_UP: return "vpn-up"; case DISPATCHER_ACTION_VPN_PRE_DOWN: diff --git a/src/nm-dispatcher.h b/src/nm-dispatcher.h index 7b50954fe..464f6310c 100644 --- a/src/nm-dispatcher.h +++ b/src/nm-dispatcher.h @@ -32,9 +32,11 @@ typedef enum { DISPATCHER_ACTION_HOSTNAME, + DISPATCHER_ACTION_PRE_UP, DISPATCHER_ACTION_UP, DISPATCHER_ACTION_PRE_DOWN, DISPATCHER_ACTION_DOWN, + DISPATCHER_ACTION_VPN_PRE_UP, DISPATCHER_ACTION_VPN_UP, DISPATCHER_ACTION_VPN_PRE_DOWN, DISPATCHER_ACTION_VPN_DOWN, From 27cb927d8fb870c360f5273549e2ee6a7639faa2 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 22 May 2014 11:41:58 -0500 Subject: [PATCH 33/43] dispatcher: convert action_to_string to a table Oddly, this increases the compiled+stripped size of the object file by 24 bytes (8296 -> 8320), but I think it produces more readable code. --- src/nm-dispatcher.c | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 6ebf427fd..51b9a8a19 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -270,36 +270,25 @@ dispatcher_done_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) g_object_unref (proxy); } +static const char *action_table[] = { + [DISPATCHER_ACTION_HOSTNAME] = "hostname", + [DISPATCHER_ACTION_PRE_UP] = "pre-up", + [DISPATCHER_ACTION_UP] = "up", + [DISPATCHER_ACTION_PRE_DOWN] = "pre-down", + [DISPATCHER_ACTION_DOWN] = "down", + [DISPATCHER_ACTION_VPN_PRE_UP] = "vpn-pre-up", + [DISPATCHER_ACTION_VPN_UP] = "vpn-up", + [DISPATCHER_ACTION_VPN_PRE_DOWN] = "vpn-pre-down", + [DISPATCHER_ACTION_VPN_DOWN] = "vpn-down", + [DISPATCHER_ACTION_DHCP4_CHANGE] = "dhcp4-change", + [DISPATCHER_ACTION_DHCP6_CHANGE] = "dhcp6-change", +}; + static const char * action_to_string (DispatcherAction action) { - switch (action) { - case DISPATCHER_ACTION_HOSTNAME: - return "hostname"; - case DISPATCHER_ACTION_PRE_UP: - return "pre-up"; - case DISPATCHER_ACTION_UP: - return "up"; - case DISPATCHER_ACTION_PRE_DOWN: - return "pre-down"; - case DISPATCHER_ACTION_DOWN: - return "down"; - case DISPATCHER_ACTION_VPN_PRE_UP: - return "vpn-pre-up"; - case DISPATCHER_ACTION_VPN_UP: - return "vpn-up"; - case DISPATCHER_ACTION_VPN_PRE_DOWN: - return "vpn-pre-down"; - case DISPATCHER_ACTION_VPN_DOWN: - return "vpn-down"; - case DISPATCHER_ACTION_DHCP4_CHANGE: - return "dhcp4-change"; - case DISPATCHER_ACTION_DHCP6_CHANGE: - return "dhcp6-change"; - default: - break; - } - g_assert_not_reached (); + g_assert (action >= 0 && action < G_N_ELEMENTS (action_table)); + return action_table[action]; } static gboolean From e1ec6a51737be00ad98cd013059924bc68b1712b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 09:36:17 -0500 Subject: [PATCH 34/43] vpn: implement PRE_DOWN dispatcher actions (bgo #387832) https://bugzilla.gnome.org/show_bug.cgi?id=387832 --- src/vpn-manager/nm-vpn-connection.c | 106 +++++++++++++++++----------- src/vpn-manager/nm-vpn-connection.h | 9 +-- src/vpn-manager/nm-vpn-manager.c | 2 +- src/vpn-manager/nm-vpn-service.c | 9 ++- 4 files changed, 77 insertions(+), 49 deletions(-) diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 3d43c3ece..052ebfe4b 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -84,7 +84,7 @@ typedef struct { char *username; VpnState vpn_state; - guint deactivating_idle_id; + guint dispatcher_id; NMVPNConnectionStateReason failure_reason; DBusGProxy *proxy; @@ -137,7 +137,8 @@ static void plugin_interactive_secrets_required (DBusGProxy *proxy, static void _set_vpn_state (NMVPNConnection *connection, VpnState vpn_state, - NMVPNConnectionStateReason reason); + NMVPNConnectionStateReason reason, + gboolean quitting); /*********************************************************************/ @@ -245,32 +246,32 @@ vpn_cleanup (NMVPNConnection *connection, NMDevice *parent_dev) nm_connection_clear_secrets (priv->connection); } -static gboolean -deactivating_to_disconnected (gpointer user_data) +static void +dispatcher_pre_down_done (guint call_id, gpointer user_data) { NMVPNConnection *self = NM_VPN_CONNECTION (user_data); NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - priv->deactivating_idle_id = 0; - _set_vpn_state (self, STATE_DISCONNECTED, NM_VPN_CONNECTION_STATE_REASON_NONE); - return G_SOURCE_REMOVE; + priv->dispatcher_id = 0; + _set_vpn_state (self, STATE_DISCONNECTED, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); } static void -clear_deactivating_idle (NMVPNConnection *self) +dispatcher_cleanup (NMVPNConnection *self) { NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - if (priv->deactivating_idle_id) { - g_source_remove (priv->deactivating_idle_id); - priv->deactivating_idle_id = 0; + if (priv->dispatcher_id) { + nm_dispatcher_call_cancel (priv->dispatcher_id); + priv->dispatcher_id = 0; } } static void _set_vpn_state (NMVPNConnection *connection, VpnState vpn_state, - NMVPNConnectionStateReason reason) + NMVPNConnectionStateReason reason, + gboolean quitting) { NMVPNConnectionPrivate *priv; VpnState old_vpn_state; @@ -303,7 +304,7 @@ _set_vpn_state (NMVPNConnection *connection, priv->secrets_id = 0; } - clear_deactivating_idle (connection); + dispatcher_cleanup (connection); /* The connection gets destroyed by the VPN manager when it enters the * disconnected/failed state, but we need to keep it around for a bit @@ -345,7 +346,27 @@ _set_vpn_state (NMVPNConnection *connection, NULL); break; case STATE_DEACTIVATING: - priv->deactivating_idle_id = g_idle_add (deactivating_to_disconnected, connection); + if (quitting) { + nm_dispatcher_call_vpn_sync (DISPATCHER_ACTION_VPN_PRE_DOWN, + priv->connection, + parent_dev, + priv->ip_iface, + priv->ip4_config, + priv->ip6_config); + } else { + if (!nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_PRE_DOWN, + priv->connection, + parent_dev, + priv->ip_iface, + priv->ip4_config, + priv->ip6_config, + dispatcher_pre_down_done, + connection, + &priv->dispatcher_id)) { + /* Just proceed on errors */ + dispatcher_pre_down_done (0, connection); + } + } break; case STATE_FAILED: case STATE_DISCONNECTED: @@ -386,11 +407,13 @@ device_state_changed (NMActiveConnection *active, if (new_state <= NM_DEVICE_STATE_DISCONNECTED) { _set_vpn_state (NM_VPN_CONNECTION (active), STATE_DISCONNECTED, - NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); + NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED, + FALSE); } else if (new_state == NM_DEVICE_STATE_FAILED) { _set_vpn_state (NM_VPN_CONNECTION (active), STATE_FAILED, - NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); + NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED, + FALSE); } /* FIXME: map device DEACTIVATING state to VPN DEACTIVATING state and @@ -665,7 +688,7 @@ plugin_state_changed (DBusGProxy *proxy, case STATE_ACTIVATED: nm_log_info (LOGD_VPN, "VPN plugin state change reason: %s (%d)", vpn_reason_to_string (priv->failure_reason), priv->failure_reason); - _set_vpn_state (connection, STATE_FAILED, priv->failure_reason); + _set_vpn_state (connection, STATE_FAILED, priv->failure_reason, FALSE); /* Reset the failure reason */ priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN; @@ -836,7 +859,7 @@ nm_vpn_connection_apply_config (NMVPNConnection *connection) nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.", nm_connection_get_id (priv->connection)); - _set_vpn_state (connection, STATE_ACTIVATED, NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_ACTIVATED, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); return TRUE; } @@ -876,7 +899,7 @@ nm_vpn_connection_config_maybe_complete (NMVPNConnection *connection, nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.", nm_connection_get_id (priv->connection)); - _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID); + _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE); } #define LOG_INVALID_ARG(property) \ @@ -971,7 +994,7 @@ nm_vpn_connection_config_get (DBusGProxy *proxy, nm_connection_get_id (priv->connection)); if (priv->vpn_state == STATE_CONNECT) - _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); if (!process_generic_config (connection, config_hash)) return; @@ -1025,7 +1048,7 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, int i; if (priv->vpn_state == STATE_CONNECT) - _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); if (priv->has_ip4) { nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received.", @@ -1181,7 +1204,7 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy, nm_connection_get_id (priv->connection)); if (priv->vpn_state == STATE_CONNECT) - _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); if (g_hash_table_size (config_hash) == 0) { priv->has_ip6 = FALSE; @@ -1322,7 +1345,7 @@ connect_timeout_cb (gpointer user_data) priv->vpn_state == STATE_IP_CONFIG_GET) { nm_log_warn (LOGD_VPN, "VPN connection '%s' connect timeout exceeded.", nm_connection_get_id (priv->connection)); - _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT); + _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE); } return FALSE; @@ -1359,7 +1382,7 @@ connect_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect: '%s'.", nm_connection_get_id (priv->connection), err->message); g_error_free (err); - _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE); } static void @@ -1388,7 +1411,7 @@ connect_interactive_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect interactively: '%s'.", nm_connection_get_id (priv->connection), err->message); g_error_free (err); - _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE); } } @@ -1483,7 +1506,7 @@ really_activate (NMVPNConnection *connection, const char *username) g_object_unref (agent_mgr); g_hash_table_destroy (details); - _set_vpn_state (connection, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); } void @@ -1496,7 +1519,7 @@ nm_vpn_connection_activate (NMVPNConnection *connection) priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); - _set_vpn_state (connection, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); bus = nm_dbus_manager_get_connection (nm_dbus_manager_get ()); priv->proxy = dbus_g_proxy_new_for_name (bus, @@ -1522,7 +1545,7 @@ nm_vpn_connection_activate (NMVPNConnection *connection) G_CALLBACK (plugin_interactive_secrets_required), connection, NULL); - _set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); /* Kick off the secrets requests; first we get existing system secrets * and ask the plugin if these are sufficient, next we get all existing @@ -1605,18 +1628,19 @@ nm_vpn_connection_get_ip6_internal_gateway (NMVPNConnection *connection) } void -nm_vpn_connection_stop (NMVPNConnection *connection, - gboolean fail, - NMVPNConnectionStateReason reason) +nm_vpn_connection_disconnect (NMVPNConnection *connection, + NMVPNConnectionStateReason reason, + gboolean quitting) { g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); - _set_vpn_state (connection, fail ? STATE_FAILED : STATE_DISCONNECTED, reason); + _set_vpn_state (connection, STATE_DISCONNECTED, reason, quitting); } gboolean nm_vpn_connection_deactivate (NMVPNConnection *connection, - NMVPNConnectionStateReason reason) + NMVPNConnectionStateReason reason, + gboolean quitting) { NMVPNConnectionPrivate *priv; gboolean success = FALSE; @@ -1625,7 +1649,7 @@ nm_vpn_connection_deactivate (NMVPNConnection *connection, priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); if (priv->vpn_state > STATE_UNKNOWN && priv->vpn_state <= STATE_DEACTIVATING) { - _set_vpn_state (connection, STATE_DEACTIVATING, reason); + _set_vpn_state (connection, STATE_DEACTIVATING, reason, quitting); success = TRUE; } return success; @@ -1651,7 +1675,7 @@ plugin_need_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_dat priv->secrets_idx + 1, g_quark_to_string (error->domain), error->message); - _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); g_error_free (error); return; } @@ -1663,7 +1687,7 @@ plugin_need_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_dat nm_log_err (LOGD_VPN, "(%s/%s) final secrets request failed to provide sufficient secrets", nm_connection_get_uuid (priv->connection), nm_connection_get_id (priv->connection)); - _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); } else { nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated additional secrets required", nm_connection_get_uuid (priv->connection), @@ -1695,7 +1719,7 @@ plugin_new_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data nm_connection_get_id (priv->connection), g_quark_to_string (error->domain), error->message); - _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); g_error_free (error); } } @@ -1720,7 +1744,7 @@ get_secrets_cb (NMSettingsConnection *connection, if (error) { nm_log_err (LOGD_VPN, "Failed to request VPN secrets #%d: (%d) %s", priv->secrets_idx + 1, error->code, error->message); - _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); } else { /* Cache the username for later */ if (agent_username) { @@ -1804,7 +1828,7 @@ get_secrets (NMVPNConnection *self, nm_log_err (LOGD_VPN, "failed to request VPN secrets #%d: (%d) %s", priv->secrets_idx + 1, error->code, error->message); } - _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); g_clear_error (&error); } } @@ -1828,7 +1852,7 @@ plugin_interactive_secrets_required (DBusGProxy *proxy, priv->vpn_state == STATE_NEED_AUTH); priv->secrets_idx = SECRETS_REQ_INTERACTIVE; - _set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE); + _set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); /* Copy hints and add message to the end */ hints = g_malloc0 (sizeof (char *) * (secrets_len + 2)); @@ -1878,7 +1902,7 @@ dispose (GObject *object) priv->connect_timeout = 0; } - clear_deactivating_idle (NM_VPN_CONNECTION (object)); + dispatcher_cleanup (NM_VPN_CONNECTION (object)); if (priv->secrets_id) { nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection), diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h index e5563b000..c9c88cda1 100644 --- a/src/vpn-manager/nm-vpn-connection.h +++ b/src/vpn-manager/nm-vpn-connection.h @@ -76,10 +76,11 @@ NMVPNConnectionState nm_vpn_connection_get_vpn_state (NMVPNConnection *connect const char * nm_vpn_connection_get_banner (NMVPNConnection *connection); gboolean nm_vpn_connection_deactivate (NMVPNConnection *connection, - NMVPNConnectionStateReason reason); -void nm_vpn_connection_stop (NMVPNConnection *connection, - gboolean fail, - NMVPNConnectionStateReason reason); + NMVPNConnectionStateReason reason, + gboolean quitting); +void nm_vpn_connection_disconnect (NMVPNConnection *connection, + NMVPNConnectionStateReason reason, + gboolean quitting); NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connection); NMIP6Config * nm_vpn_connection_get_ip6_config (NMVPNConnection *connection); diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index 00ce4e8f5..5550d4d6c 100644 --- a/src/vpn-manager/nm-vpn-manager.c +++ b/src/vpn-manager/nm-vpn-manager.c @@ -123,7 +123,7 @@ nm_vpn_manager_deactivate_connection (NMVPNManager *self, NMVPNConnection *connection, NMVPNConnectionStateReason reason) { - return nm_vpn_connection_deactivate (connection, reason); + return nm_vpn_connection_deactivate (connection, reason, FALSE); } static void diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index 3752797fb..9eb6b9682 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -157,8 +157,11 @@ nm_vpn_service_stop_connections (NMVPNService *service, NMVPNConnection *vpn = NM_VPN_CONNECTION (iter->data); g_signal_handlers_disconnect_by_func (vpn, G_CALLBACK (connection_vpn_state_changed), service); - /* Quitting terminates the VPN cleanly, otherwise failure is assumed */ - nm_vpn_connection_stop (vpn, quitting ? FALSE : TRUE, reason); + if (quitting) { + /* Deactivate to allow pre-down before disconnecting */ + nm_vpn_connection_deactivate (vpn, reason, quitting); + } + nm_vpn_connection_disconnect (vpn, reason, quitting); g_object_unref (vpn); } g_clear_pointer (&priv->pending, g_slist_free); @@ -292,7 +295,7 @@ nm_vpn_service_activate (NMVPNService *service, * connection_vpn_state_changed(). */ if (priv->active) { - nm_vpn_connection_deactivate (priv->active, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); + nm_vpn_connection_deactivate (priv->active, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED, FALSE); return TRUE; } From beb5529c42a9dda3f7e234b6a1988209179f71d2 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 4 Jun 2014 14:57:29 -0500 Subject: [PATCH 35/43] vpn: make DOWN dispatcher action block on quit Since the event loop isn't running on quit, but we want to ensure that scripts can fully process the DOWN event, block on scripts completing when disconnecting the VPN when quitting. --- src/vpn-manager/nm-vpn-connection.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 052ebfe4b..dea366be1 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -373,15 +373,24 @@ _set_vpn_state (NMVPNConnection *connection, if ( old_vpn_state >= STATE_ACTIVATED && old_vpn_state <= STATE_DEACTIVATING) { /* Let dispatcher scripts know we're about to go down */ - nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_DOWN, - priv->connection, - parent_dev, - priv->ip_iface, - NULL, - NULL, - NULL, - NULL, - NULL); + if (quitting) { + nm_dispatcher_call_vpn_sync (DISPATCHER_ACTION_VPN_DOWN, + priv->connection, + parent_dev, + priv->ip_iface, + NULL, + NULL); + } else { + nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_DOWN, + priv->connection, + parent_dev, + priv->ip_iface, + NULL, + NULL, + NULL, + NULL, + NULL); + } } /* Tear down and clean up the connection */ From ce3d2cf0e62982399506dce62651675f0d3cd66f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 21 May 2014 13:00:45 -0500 Subject: [PATCH 36/43] vpn: implement PRE_UP dispatcher actions --- src/vpn-manager/nm-vpn-connection.c | 51 +++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index dea366be1..f7476b411 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -70,6 +70,7 @@ typedef enum { STATE_NEED_AUTH, STATE_CONNECT, STATE_IP_CONFIG_GET, + STATE_PRE_UP, STATE_ACTIVATED, STATE_DEACTIVATING, STATE_DISCONNECTED, @@ -154,6 +155,7 @@ _state_to_nm_vpn_state (VpnState state) case STATE_CONNECT: return NM_VPN_CONNECTION_STATE_CONNECT; case STATE_IP_CONFIG_GET: + case STATE_PRE_UP: return NM_VPN_CONNECTION_STATE_IP_CONFIG_GET; case STATE_ACTIVATED: return NM_VPN_CONNECTION_STATE_ACTIVATED; @@ -185,6 +187,7 @@ _state_to_ac_state (VpnState vpn_state) case STATE_NEED_AUTH: case STATE_CONNECT: case STATE_IP_CONFIG_GET: + case STATE_PRE_UP: return NM_ACTIVE_CONNECTION_STATE_ACTIVATING; case STATE_ACTIVATED: return NM_ACTIVE_CONNECTION_STATE_ACTIVATED; @@ -256,6 +259,16 @@ dispatcher_pre_down_done (guint call_id, gpointer user_data) _set_vpn_state (self, STATE_DISCONNECTED, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); } +static void +dispatcher_pre_up_done (guint call_id, gpointer user_data) +{ + NMVPNConnection *self = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + priv->dispatcher_id = 0; + _set_vpn_state (self, STATE_ACTIVATED, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); +} + static void dispatcher_cleanup (NMVPNConnection *self) { @@ -330,6 +343,20 @@ _set_vpn_state (NMVPNConnection *connection, * secrets. */ break; + case STATE_PRE_UP: + if (!nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_PRE_UP, + priv->connection, + parent_dev, + priv->ip_iface, + priv->ip4_config, + priv->ip6_config, + dispatcher_pre_up_done, + connection, + &priv->dispatcher_id)) { + /* Just proceed on errors */ + dispatcher_pre_up_done (0, connection); + } + break; case STATE_ACTIVATED: /* Secrets no longer needed now that we're connected */ nm_connection_clear_secrets (priv->connection); @@ -625,6 +652,7 @@ static const char *state_table[] = { [STATE_NEED_AUTH] = "need-auth", [STATE_CONNECT] = "connect", [STATE_IP_CONFIG_GET] = "ip-config-get", + [STATE_PRE_UP] = "pre-up", [STATE_ACTIVATED] = "activated", [STATE_DEACTIVATING] = "deactivating", [STATE_DISCONNECTED] = "disconnected", @@ -688,22 +716,13 @@ plugin_state_changed (DBusGProxy *proxy, */ nm_connection_clear_secrets (priv->connection); - switch (priv->vpn_state) { - case STATE_WAITING: - case STATE_PREPARE: - case STATE_NEED_AUTH: - case STATE_CONNECT: - case STATE_IP_CONFIG_GET: - case STATE_ACTIVATED: + if ((priv->vpn_state >= STATE_WAITING) && (priv->vpn_state <= STATE_ACTIVATED)) { nm_log_info (LOGD_VPN, "VPN plugin state change reason: %s (%d)", vpn_reason_to_string (priv->failure_reason), priv->failure_reason); _set_vpn_state (connection, STATE_FAILED, priv->failure_reason, FALSE); /* Reset the failure reason */ priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN; - break; - default: - break; } } } @@ -868,7 +887,7 @@ nm_vpn_connection_apply_config (NMVPNConnection *connection) nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.", nm_connection_get_id (priv->connection)); - _set_vpn_state (connection, STATE_ACTIVATED, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); + _set_vpn_state (connection, STATE_PRE_UP, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE); return TRUE; } @@ -1941,6 +1960,12 @@ finalize (GObject *object) G_OBJECT_CLASS (nm_vpn_connection_parent_class)->finalize (object); } +static gboolean +ip_config_valid (VpnState state) +{ + return (state == STATE_PRE_UP || state == STATE_ACTIVATED); +} + static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) @@ -1956,13 +1981,13 @@ get_property (GObject *object, guint prop_id, g_value_set_string (value, priv->banner ? priv->banner : ""); break; case PROP_IP4_CONFIG: - if (priv->vpn_state == STATE_ACTIVATED && priv->ip4_config) + if (ip_config_valid (priv->vpn_state) && priv->ip4_config) g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config)); else g_value_set_boxed (value, "/"); break; case PROP_IP6_CONFIG: - if (priv->vpn_state == STATE_ACTIVATED && priv->ip6_config) + if (ip_config_valid (priv->vpn_state) && priv->ip6_config) g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config)); else g_value_set_boxed (value, "/"); From d00e2147de148bcec1d32023bda8a90dab552a18 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 17:10:42 -0500 Subject: [PATCH 37/43] core: implement PRE_DOWN dispatcher actions (bgo #387832) https://bugzilla.gnome.org/show_bug.cgi?id=387832 --- src/devices/nm-device.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index c879b5632..7060e28b4 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -216,6 +216,7 @@ typedef struct { guint act_source6_id; gpointer act_source6_func; guint recheck_assume_id; + guint dispatcher_id; /* Link stuff */ guint link_connected_id; @@ -6464,6 +6465,29 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason) /***********************************************************/ +static void +dispatcher_pre_down_done (guint call_id, gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + g_return_if_fail (call_id == priv->dispatcher_id); + + priv->dispatcher_id = 0; + nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); +} + +static void +dispatcher_cleanup (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->dispatcher_id) { + nm_dispatcher_call_cancel (priv->dispatcher_id); + priv->dispatcher_id = 0; + } +} + static gboolean ip_config_valid (NMDeviceState state) { @@ -6525,6 +6549,8 @@ nm_device_state_changed (NMDevice *device, /* Clear any queued transitions */ nm_device_queued_state_clear (device); + dispatcher_cleanup (device); + /* Cache the activation request for the dispatcher */ req = priv->act_request ? g_object_ref (priv->act_request) : NULL; @@ -6621,7 +6647,15 @@ nm_device_state_changed (NMDevice *device, } break; case NM_DEVICE_STATE_DEACTIVATING: - nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, reason); + if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_DOWN, + nm_act_request_get_connection (req), + device, + dispatcher_pre_down_done, + device, + &priv->dispatcher_id)) { + /* Just proceed on errors */ + dispatcher_pre_down_done (0, device); + } break; case NM_DEVICE_STATE_DISCONNECTED: if (priv->queued_act_request) { @@ -6687,7 +6721,8 @@ nm_device_state_changed (NMDevice *device, if (state > NM_DEVICE_STATE_DISCONNECTED) delete_on_deactivate_unschedule (device); - if (old_state == NM_DEVICE_STATE_ACTIVATED) + if ( (old_state == NM_DEVICE_STATE_ACTIVATED || old_state == NM_DEVICE_STATE_DEACTIVATING) + && (state != NM_DEVICE_STATE_DEACTIVATING)) nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device, NULL, NULL, NULL); /* IP-related properties are only valid when the device has IP configuration. @@ -7186,6 +7221,8 @@ dispose (GObject *object) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPlatform *platform; + dispatcher_cleanup (self); + _cleanup_generic_pre (self, FALSE); g_warn_if_fail (priv->slaves == NULL); From 38e6b7387fd8a1ecf8e539428dab2aea3713220a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 21 May 2014 12:49:23 -0500 Subject: [PATCH 38/43] core: implement PRE_UP dispatcher actions --- src/devices/nm-device.c | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 7060e28b4..a543c6f74 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -5252,6 +5252,36 @@ nm_device_get_ip6_config (NMDevice *self) /****************************************************************/ +static void +ip_check_pre_up_done (guint call_id, gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + g_return_if_fail (call_id == priv->dispatcher_id); + + priv->dispatcher_id = 0; + nm_device_queue_state (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE); +} + +static void +ip_check_pre_up (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + g_warn_if_fail (priv->dispatcher_id == 0); + + if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_UP, + nm_device_get_connection (self), + self, + ip_check_pre_up_done, + self, + &priv->dispatcher_id)) { + /* Just proceed on errors */ + ip_check_pre_up_done (0, self); + } +} + static void ip_check_gw_ping_cleanup (NMDevice *self) { @@ -5306,9 +5336,9 @@ ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data) } else nm_log_warn (log_domain, "(%s): ping stopped unexpectedly with status %d", iface, status); - /* We've got connectivity, proceed to secondaries */ + /* We've got connectivity, proceed to pre_up */ ip_check_gw_ping_cleanup (self); - nm_device_state_changed (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE); + ip_check_pre_up (self); } static gboolean @@ -5323,7 +5353,7 @@ ip_check_ping_timeout_cb (gpointer user_data) nm_device_get_iface (self)); ip_check_gw_ping_cleanup (self); - nm_device_state_changed (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE); + ip_check_pre_up (self); return FALSE; } @@ -5426,9 +5456,9 @@ nm_device_start_ip_check (NMDevice *self) if (buf[0]) spawn_ping (self, log_domain, ping_binary, buf, timeout); - /* If no ping was started, just advance to SECONDARIES */ + /* If no ping was started, just advance to pre_up */ if (!priv->gw_ping.pid) - nm_device_queue_state (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE); + ip_check_pre_up (self); } /****************************************************************/ From 7eaaa6a475b3d73e0967c9fa928268c1498f6b50 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 23 May 2014 18:25:05 -0500 Subject: [PATCH 39/43] core: block on dispatcher scripts when quitting Like VPN connections, block on dispatcher scripts when quitting. Since the event loop is no longer running we can't schedule callbacks. --- src/devices/nm-device.c | 67 ++++++++++++++++++++++++++++++++--------- src/devices/nm-device.h | 1 + src/nm-manager.c | 8 +++-- 3 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index a543c6f74..a6b4b548f 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -321,6 +321,11 @@ static void addrconf6_start_with_link_ready (NMDevice *self); static gboolean nm_device_get_default_unmanaged (NMDevice *device); +static void _set_state_full (NMDevice *device, + NMDeviceState state, + NMDeviceStateReason reason, + gboolean quitting); + /***********************************************************/ static GQuark @@ -5944,6 +5949,21 @@ nm_device_set_unmanaged (NMDevice *device, } } +void +nm_device_set_unmanaged_quitting (NMDevice *device) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + + /* It's OK to block here because we're quitting */ + if (nm_device_is_activating (device) || priv->state == NM_DEVICE_STATE_ACTIVATED) + _set_state_full (device, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_REMOVED, TRUE); + + nm_device_set_unmanaged (device, + NM_UNMANAGED_INTERNAL, + TRUE, + NM_DEVICE_STATE_REASON_REMOVED); +} + /** * nm_device_set_initial_unmanaged_flag(): * @device: the #NMDevice @@ -6536,10 +6556,11 @@ notify_ip_properties (NMDevice *device) g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP6_CONFIG); } -void -nm_device_state_changed (NMDevice *device, - NMDeviceState state, - NMDeviceStateReason reason) +static void +_set_state_full (NMDevice *device, + NMDeviceState state, + NMDeviceStateReason reason, + gboolean quitting) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); NMDeviceState old_state; @@ -6677,14 +6698,20 @@ nm_device_state_changed (NMDevice *device, } break; case NM_DEVICE_STATE_DEACTIVATING: - if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_DOWN, - nm_act_request_get_connection (req), - device, - dispatcher_pre_down_done, - device, - &priv->dispatcher_id)) { - /* Just proceed on errors */ - dispatcher_pre_down_done (0, device); + if (quitting) { + nm_dispatcher_call_sync (DISPATCHER_ACTION_PRE_DOWN, + nm_act_request_get_connection (req), + device); + } else { + if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_DOWN, + nm_act_request_get_connection (req), + device, + dispatcher_pre_down_done, + device, + &priv->dispatcher_id)) { + /* Just proceed on errors */ + dispatcher_pre_down_done (0, device); + } } break; case NM_DEVICE_STATE_DISCONNECTED: @@ -6752,8 +6779,12 @@ nm_device_state_changed (NMDevice *device, delete_on_deactivate_unschedule (device); if ( (old_state == NM_DEVICE_STATE_ACTIVATED || old_state == NM_DEVICE_STATE_DEACTIVATING) - && (state != NM_DEVICE_STATE_DEACTIVATING)) - nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device, NULL, NULL, NULL); + && (state != NM_DEVICE_STATE_DEACTIVATING)) { + if (quitting) + nm_dispatcher_call_sync (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device); + else + nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device, NULL, NULL, NULL); + } /* IP-related properties are only valid when the device has IP configuration. * If it no longer does, ensure their change notifications are emitted. @@ -6768,6 +6799,14 @@ nm_device_state_changed (NMDevice *device, priv->in_state_changed = FALSE; } +void +nm_device_state_changed (NMDevice *device, + NMDeviceState state, + NMDeviceStateReason reason) +{ + _set_state_full (device, state, reason, FALSE); +} + static gboolean queued_set_state (gpointer user_data) { diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 3241bceaf..f74486e6f 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -312,6 +312,7 @@ void nm_device_set_unmanaged (NMDevice *device, NMUnmanagedFlags flag, gboolean unmanaged, NMDeviceStateReason reason); +void nm_device_set_unmanaged_quitting (NMDevice *device); void nm_device_set_initial_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag, gboolean unmanaged); diff --git a/src/nm-manager.c b/src/nm-manager.c index 14e071e0b..6fc74df14 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -742,8 +742,12 @@ remove_device (NMManager *manager, NMDevice *device, gboolean quitting) else if (!req) unmanage = TRUE; - if (unmanage) - nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED); + if (unmanage) { + if (quitting) + nm_device_set_unmanaged_quitting (device); + else + nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED); + } } g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager); From 1383f4bc14a83b56e06210222ecf2e6b18dfc18e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 2 Jun 2014 14:07:46 -0500 Subject: [PATCH 40/43] dispatcher: use separate directories for pre-up/pre-down events To ensure that NetworkManager does not block needlessly for events which have no scripts, require scripts that respond to blocking events to opt into the action. --- callouts/nm-dispatcher-api.h | 17 ++++++++- callouts/nm-dispatcher.c | 20 ++++++++--- man/NetworkManager.xml | 49 +++++++++++++++++++++++-- src/nm-dispatcher.c | 69 ++++++++++++++++++++++++------------ 4 files changed, 124 insertions(+), 31 deletions(-) diff --git a/callouts/nm-dispatcher-api.h b/callouts/nm-dispatcher-api.h index 96db78928..eb6b33fe0 100644 --- a/callouts/nm-dispatcher-api.h +++ b/callouts/nm-dispatcher-api.h @@ -20,7 +20,9 @@ #include -#define NMD_SCRIPT_DIR NMCONFDIR "/dispatcher.d" +#define NMD_SCRIPT_DIR NMCONFDIR "/dispatcher.d" +#define NMD_PRE_UP_DIR NMD_SCRIPT_DIR "/pre-up.d" +#define NMD_PRE_DOWN_DIR NMD_SCRIPT_DIR "/pre-down.d" /* dbus-glib types for dispatcher call return value */ #define DISPATCHER_TYPE_RESULT (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID)) @@ -38,6 +40,19 @@ #define NMD_DEVICE_PROPS_STATE "state" #define NMD_DEVICE_PROPS_PATH "path" +/* Actions */ +#define NMD_ACTION_HOSTNAME "hostname" +#define NMD_ACTION_PRE_UP "pre-up" +#define NMD_ACTION_UP "up" +#define NMD_ACTION_PRE_DOWN "pre-down" +#define NMD_ACTION_DOWN "down" +#define NMD_ACTION_VPN_PRE_UP "vpn-pre-up" +#define NMD_ACTION_VPN_UP "vpn-up" +#define NMD_ACTION_VPN_PRE_DOWN "vpn-pre-down" +#define NMD_ACTION_VPN_DOWN "vpn-down" +#define NMD_ACTION_DHCP4_CHANGE "dhcp4-change" +#define NMD_ACTION_DHCP6_CHANGE "dhcp6-change" + typedef enum { DISPATCH_RESULT_UNKNOWN = 0, DISPATCH_RESULT_SUCCESS = 1, diff --git a/callouts/nm-dispatcher.c b/callouts/nm-dispatcher.c index 3747fd563..6c9c294f2 100644 --- a/callouts/nm-dispatcher.c +++ b/callouts/nm-dispatcher.c @@ -414,16 +414,26 @@ dispatch_one_script (Request *request) } static GSList * -find_scripts (void) +find_scripts (const char *str_action) { GDir *dir; const char *filename; GSList *sorted = NULL; GError *error = NULL; + const char *dirname; - if (!(dir = g_dir_open (NMD_SCRIPT_DIR, 0, &error))) { + if ( strcmp (str_action, NMD_ACTION_PRE_UP) == 0 + || strcmp (str_action, NMD_ACTION_VPN_PRE_UP) == 0) + dirname = NMD_PRE_UP_DIR; + else if ( strcmp (str_action, NMD_ACTION_PRE_DOWN) == 0 + || strcmp (str_action, NMD_ACTION_VPN_PRE_DOWN) == 0) + dirname = NMD_PRE_DOWN_DIR; + else + dirname = NMD_SCRIPT_DIR; + + if (!(dir = g_dir_open (dirname, 0, &error))) { g_warning ("Failed to open dispatcher directory '%s': (%d) %s", - NMD_SCRIPT_DIR, error->code, error->message); + dirname, error->code, error->message); g_error_free (error); return NULL; } @@ -436,7 +446,7 @@ find_scripts (void) if (!check_filename (filename)) continue; - path = g_build_filename (NMD_SCRIPT_DIR, filename, NULL); + path = g_build_filename (dirname, filename, NULL); err = stat (path, &st); if (err) @@ -476,7 +486,7 @@ impl_dispatch (Handler *h, char **p; char *iface = NULL; - sorted_scripts = find_scripts (); + sorted_scripts = find_scripts (str_action); if (!sorted_scripts) { dbus_g_method_return (context, g_ptr_array_new ()); diff --git a/man/NetworkManager.xml b/man/NetworkManager.xml index 70e49aa16..229c390cd 100644 --- a/man/NetworkManager.xml +++ b/man/NetworkManager.xml @@ -53,9 +53,9 @@ Dispatcher scripts NetworkManager will execute scripts in the - /etc/NetworkManager/dispatcher.d directory in alphabetical order - in response to network events. Each script should be a regular - executable file, owned by root. Furthermore, it must not be + /etc/NetworkManager/dispatcher.d directory or subdirectories in + alphabetical order in response to network events. Each script should + be a regular executable file owned by root. Furthermore, it must not be writable by group or other, and not setuid. @@ -64,22 +64,65 @@ The actions are: + + pre-up + The interface is connected to the network but is not + yet fully activated. Scripts acting on this event must be placed or + symlinked into the /etc/NetworkManager/dispatcher.d/pre-up.d directory, + and NetworkManager will wait for script execution to complete before + indicating to applications that the interface is fully activated. + + up The interface has been activated. + + pre-down + The interface will be deactivated but has not yet been + disconnected from the network. Scripts acting on this event must be + placed or symlinked into the /etc/NetworkManager/dispatcher.d/pre-down.d + directory, and NetworkManager will wait for script execution to complete + before disconnecting the interface from its network. Note that this + event is not emitted for forced disconnections, like when carrier is + lost or a wireless signal fades. It is only emitted when there is + an opportunity to cleanly handle a network disconnection event. + + down The interface has been deactivated. + + vpn-pre-up + The VPN is connected to the network but is not yet + fully activated. Scripts acting on this event must be placed or + symlinked into the /etc/NetworkManager/dispatcher.d/pre-up.d directory, + and NetworkManager will wait for script execution to complete before + indicating to applications that the VPN is fully activated. + + vpn-up A VPN connection has been activated. + + vpn-pre-down + The VPN will be deactivated but has not yet been + disconnected from the network. Scripts acting on this event must be + placed or symlinked into the /etc/NetworkManager/dispatcher.d/pre-down.d + directory, and NetworkManager will wait for script execution to complete + before disconnecting the VPN from its network. Note that this + event is not emitted for forced disconnections, like when the VPN + terminates unexpectedly or general connectivity is lost. It is only + emitted when there is an opportunity to cleanly handle a VPN + disconnection event. + + vpn-down diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 51b9a8a19..29b0329d4 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -271,17 +271,17 @@ dispatcher_done_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) } static const char *action_table[] = { - [DISPATCHER_ACTION_HOSTNAME] = "hostname", - [DISPATCHER_ACTION_PRE_UP] = "pre-up", - [DISPATCHER_ACTION_UP] = "up", - [DISPATCHER_ACTION_PRE_DOWN] = "pre-down", - [DISPATCHER_ACTION_DOWN] = "down", - [DISPATCHER_ACTION_VPN_PRE_UP] = "vpn-pre-up", - [DISPATCHER_ACTION_VPN_UP] = "vpn-up", - [DISPATCHER_ACTION_VPN_PRE_DOWN] = "vpn-pre-down", - [DISPATCHER_ACTION_VPN_DOWN] = "vpn-down", - [DISPATCHER_ACTION_DHCP4_CHANGE] = "dhcp4-change", - [DISPATCHER_ACTION_DHCP6_CHANGE] = "dhcp6-change", + [DISPATCHER_ACTION_HOSTNAME] = NMD_ACTION_HOSTNAME, + [DISPATCHER_ACTION_PRE_UP] = NMD_ACTION_PRE_UP, + [DISPATCHER_ACTION_UP] = NMD_ACTION_UP, + [DISPATCHER_ACTION_PRE_DOWN] = NMD_ACTION_PRE_DOWN, + [DISPATCHER_ACTION_DOWN] = NMD_ACTION_DOWN, + [DISPATCHER_ACTION_VPN_PRE_UP] = NMD_ACTION_VPN_PRE_UP, + [DISPATCHER_ACTION_VPN_UP] = NMD_ACTION_VPN_UP, + [DISPATCHER_ACTION_VPN_PRE_DOWN] = NMD_ACTION_VPN_PRE_DOWN, + [DISPATCHER_ACTION_VPN_DOWN] = NMD_ACTION_VPN_DOWN, + [DISPATCHER_ACTION_DHCP4_CHANGE] = NMD_ACTION_DHCP4_CHANGE, + [DISPATCHER_ACTION_DHCP6_CHANGE] = NMD_ACTION_DHCP6_CHANGE, }; static const char * @@ -605,32 +605,57 @@ nm_dispatcher_call_cancel (guint call_id) g_return_if_reached (); } +typedef struct { + const char *dir; + GFileMonitor *monitor; + gboolean has_scripts; +} Monitor; + +static Monitor monitors[3] = { + { NMD_SCRIPT_DIR, NULL, TRUE }, + { NMD_PRE_UP_DIR, NULL, TRUE }, + { NMD_PRE_DOWN_DIR, NULL, TRUE } +}; + static void -dispatcher_dir_changed (GFileMonitor *monitor) +dispatcher_dir_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + Monitor *item) { GDir *dir; + guint i; /* Default to dispatching on any errors */ - do_dispatch = TRUE; - dir = g_dir_open (NMD_SCRIPT_DIR, 0, NULL); + item->has_scripts = TRUE; + + dir = g_dir_open (item->dir, 0, NULL); if (dir) { - do_dispatch = !!g_dir_read_name (dir); + item->has_scripts = !!g_dir_read_name (dir); g_dir_close (dir); } + + /* Recheck all dirs for scripts and update global variable */ + do_dispatch = FALSE; + for (i = 0; i < G_N_ELEMENTS (monitors); i++) + do_dispatch |= monitors[i].has_scripts; } void nm_dispatcher_init (void) { GFile *file; - static GFileMonitor *monitor; + guint i; - file = g_file_new_for_path (NMD_SCRIPT_DIR); - monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); - if (monitor) { - g_signal_connect (monitor, "changed", G_CALLBACK (dispatcher_dir_changed), NULL); - dispatcher_dir_changed (monitor); + for (i = 0; i < G_N_ELEMENTS (monitors); i++) { + file = g_file_new_for_path (monitors[i].dir); + monitors[i].monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); + if (monitors[i].monitor) { + g_signal_connect (monitors[i].monitor, "changed", G_CALLBACK (dispatcher_dir_changed), &monitors[i]); + dispatcher_dir_changed (monitors[i].monitor, file, NULL, 0, &monitors[i]); + } + g_object_unref (file); } - g_object_unref (file); } From df601ad68c888aa86e37b51ae7e16fc6e7a1837b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 2 Jun 2014 16:26:05 -0500 Subject: [PATCH 41/43] dispatcher: enhance debug logging Attach a request ID to every request, and print that out in the debug messages so you can see which results match up with which dispatcher requests. --- src/nm-dispatcher.c | 86 +++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 34 deletions(-) diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 29b0329d4..5d3a56cfa 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -182,8 +182,19 @@ dispatch_result_to_string (DispatchResult result) g_assert_not_reached (); } +static gboolean +validate_element (guint request_id, GValue *val, GType expected_type, guint idx, guint eltnum) +{ + if (G_VALUE_TYPE (val) != expected_type) { + nm_log_dbg (LOGD_DISPATCH, "(%u) result %d element %d invalid type %s", + request_id, idx, eltnum, G_VALUE_TYPE_NAME (val)); + return FALSE; + } + return TRUE; +} + static void -dispatcher_results_process (GPtrArray *results) +dispatcher_results_process (guint request_id, GPtrArray *results) { guint i; @@ -196,44 +207,43 @@ dispatcher_results_process (GPtrArray *results) DispatchResult result; if (item->n_values != 3) { - nm_log_dbg (LOGD_DISPATCH, "Unexpected number of items in " - "dispatcher result (got %d, expectd 3)", - item->n_values); + nm_log_dbg (LOGD_DISPATCH, "(%u) unexpected number of items in " + "dispatcher result (got %d, expected 3)", + request_id, item->n_values); continue; } /* Script */ tmp = g_value_array_get_nth (item, 0); - if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) { - nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 0 invalid type %s", - i, G_VALUE_TYPE_NAME (tmp)); + if (!validate_element (request_id, tmp, G_TYPE_STRING, i, 0)) continue; - } script = g_value_get_string (tmp); + if (!script || strncmp (script, NMD_SCRIPT_DIR "/", STRLEN (NMD_SCRIPT_DIR "/"))) + continue; /* Result */ tmp = g_value_array_get_nth (item, 1); - if (G_VALUE_TYPE (tmp) != G_TYPE_UINT) { - nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 1 invalid type %s", - i, G_VALUE_TYPE_NAME (tmp)); + if (!validate_element (request_id, tmp, G_TYPE_UINT, i, 1)) continue; - } result = g_value_get_uint (tmp); /* Error */ tmp = g_value_array_get_nth (item, 2); - if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) { - nm_log_dbg (LOGD_DISPATCH, "Dispatcher result %d element 2 invalid type %s", - i, G_VALUE_TYPE_NAME (tmp)); + if (!validate_element (request_id, tmp, G_TYPE_STRING, i, 2)) continue; - } err = g_value_get_string (tmp); - if (result == DISPATCH_RESULT_SUCCESS) - nm_log_dbg (LOGD_DISPATCH, "Dispatcher script \"%s\" succeeded", script); - else { - nm_log_warn (LOGD_DISPATCH, "Dispatcher script \"%s\" failed with %s: %s", - script, dispatch_result_to_string (result), err); + + if (result == DISPATCH_RESULT_SUCCESS) { + nm_log_dbg (LOGD_DISPATCH, "(%u) %s succeeded", + request_id, + script + STRLEN (NMD_SCRIPT_DIR "/")); + } else { + nm_log_warn (LOGD_DISPATCH, "(%u) %s failed (%s): %s", + request_id, + script + STRLEN (NMD_SCRIPT_DIR "/"), + dispatch_result_to_string (result), + err ? err : ""); } } } @@ -256,11 +266,12 @@ dispatcher_done_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) if (dbus_g_proxy_end_call (proxy, call, &error, DISPATCHER_TYPE_RESULT_ARRAY, &results, G_TYPE_INVALID)) { - dispatcher_results_process (results); + dispatcher_results_process (info->request_id, results); free_results (results); } else { g_assert (error); - nm_log_warn (LOGD_DISPATCH, "Dispatcher failed: (%d) %s", error->code, error->message); + nm_log_warn (LOGD_DISPATCH, "(%u) failed: (%d) %s", + info->request_id, error->code, error->message); } if (info->callback) @@ -329,7 +340,12 @@ _dispatcher_call (DispatcherAction action, DispatchInfo *info = NULL; gboolean success = FALSE; GError *error = NULL; - static guint request_counter = 1; + static guint request_counter = 0; + guint reqid = ++request_counter; + + /* Wrapping protection */ + if (G_UNLIKELY (!reqid)) + reqid = ++request_counter; g_assert (!blocking || (!callback && !user_data)); @@ -337,13 +353,15 @@ _dispatcher_call (DispatcherAction action, /* All actions except 'hostname' require a device */ if (action == DISPATCHER_ACTION_HOSTNAME) { - nm_log_dbg (LOGD_DISPATCH, "dispatching action '%s'", - action_to_string (action)); + nm_log_dbg (LOGD_DISPATCH, "(%u) dispatching action '%s'", + reqid, action_to_string (action)); } else { g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - nm_log_dbg (LOGD_DISPATCH, "(%s) dispatching action '%s'", - nm_device_get_iface (device), action_to_string (action)); + nm_log_dbg (LOGD_DISPATCH, "(%u) (%s) dispatching action '%s'", + reqid, + vpn_iface ? vpn_iface : nm_device_get_iface (device), + action_to_string (action)); } /* VPN actions require at least an IPv4 config (for now) */ @@ -353,12 +371,12 @@ _dispatcher_call (DispatcherAction action, if (do_dispatch == FALSE) { if (blocking == FALSE && (out_call_id || callback)) { info = g_malloc0 (sizeof (*info)); - info->request_id = request_counter++; + info->request_id = reqid; info->callback = callback; info->user_data = user_data; info->idle_id = g_idle_add (dispatcher_idle_cb, info); } - nm_log_dbg (LOGD_DISPATCH, "ignoring request; no scripts in " NMD_SCRIPT_DIR); + nm_log_dbg (LOGD_DISPATCH, "(%u) ignoring request; no scripts in " NMD_SCRIPT_DIR, reqid); success = TRUE; goto done; } @@ -369,7 +387,7 @@ _dispatcher_call (DispatcherAction action, NM_DISPATCHER_DBUS_PATH, NM_DISPATCHER_DBUS_IFACE); if (!proxy) { - nm_log_err (LOGD_DISPATCH, "could not get dispatcher proxy!"); + nm_log_err (LOGD_DISPATCH, "(%u) could not get dispatcher proxy!", reqid); return FALSE; } @@ -428,15 +446,15 @@ _dispatcher_call (DispatcherAction action, DISPATCHER_TYPE_RESULT_ARRAY, &results, G_TYPE_INVALID); if (success) { - dispatcher_results_process (results); + dispatcher_results_process (reqid, results); free_results (results); } else { - nm_log_warn (LOGD_DISPATCH, "Dispatcher failed: (%d) %s", error->code, error->message); + nm_log_warn (LOGD_DISPATCH, "(%u) failed: (%d) %s", reqid, error->code, error->message); g_error_free (error); } } else { info = g_malloc0 (sizeof (*info)); - info->request_id = request_counter++; + info->request_id = reqid; info->callback = callback; info->user_data = user_data; dbus_g_proxy_begin_call_with_timeout (proxy, "Action", From fd5761ecb279abc6ecdfb9a3ebcd4c872a96ac56 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 3 Jun 2014 15:58:23 -0500 Subject: [PATCH 42/43] dispatcher: don't use NULL error domains glib doesn't like it, plus we don't use the error anyway, so just return the constant error string. --- callouts/nm-dispatcher.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/callouts/nm-dispatcher.c b/callouts/nm-dispatcher.c index 6c9c294f2..7d2b7365c 100644 --- a/callouts/nm-dispatcher.c +++ b/callouts/nm-dispatcher.c @@ -320,33 +320,33 @@ script_timeout_cb (gpointer user_data) } static inline gboolean -check_permissions (struct stat *s, GError **error) +check_permissions (struct stat *s, const char **out_error_msg) { g_return_val_if_fail (s != NULL, FALSE); - g_return_val_if_fail (error != NULL, FALSE); - g_return_val_if_fail (*error == NULL, FALSE); + g_return_val_if_fail (out_error_msg != NULL, FALSE); + g_return_val_if_fail (*out_error_msg == NULL, FALSE); /* Only accept regular files */ if (!S_ISREG (s->st_mode)) { - g_set_error (error, 0, 0, "not a regular file."); + *out_error_msg = "not a regular file."; return FALSE; } /* Only accept files owned by root */ if (s->st_uid != 0) { - g_set_error (error, 0, 0, "not owned by root."); + *out_error_msg = "not owned by root."; return FALSE; } /* Only accept files not writable by group or other, and not SUID */ if (s->st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { - g_set_error (error, 0, 0, "writable by group or other, or set-UID."); + *out_error_msg = "writable by group or other, or set-UID."; return FALSE; } /* Only accept files executable by the owner */ if (!(s->st_mode & S_IXUSR)) { - g_set_error (error, 0, 0, "not executable by owner."); + *out_error_msg = "not executable by owner."; return FALSE; } @@ -442,6 +442,7 @@ find_scripts (const char *str_action) char *path; struct stat st; int err; + const char *err_msg = NULL; if (!check_filename (filename)) continue; @@ -451,10 +452,9 @@ find_scripts (const char *str_action) err = stat (path, &st); if (err) g_warning ("Failed to stat '%s': %d", path, err); - else if (!check_permissions (&st, &error)) { - g_warning ("Cannot execute '%s': %s", path, error->message); - g_clear_error (&error); - } else { + else if (!check_permissions (&st, &err_msg)) + g_warning ("Cannot execute '%s': %s", path, err_msg); + else { /* success */ sorted = g_slist_insert_sorted (sorted, path, (GCompareFunc) g_strcmp0); } From 02252224e286dd3849467567ecbf0238a54e77a0 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 6 Jun 2014 11:03:25 -0500 Subject: [PATCH 43/43] dispatcher: bump overall timeout to 10 minutes (rh #982734) (rh #1048345) Since NM now handles long-running dispatcher scripts better, allow them to run for really long times. --- callouts/nm-dispatcher.c | 4 +++- src/nm-dispatcher.c | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/callouts/nm-dispatcher.c b/callouts/nm-dispatcher.c index 7d2b7365c..d66a0d8f9 100644 --- a/callouts/nm-dispatcher.c +++ b/callouts/nm-dispatcher.c @@ -383,6 +383,8 @@ child_setup (gpointer user_data G_GNUC_UNUSED) setpgid (pid, pid); } +#define SCRIPT_TIMEOUT 600 /* 10 minutes */ + static void dispatch_one_script (Request *request) { @@ -400,7 +402,7 @@ dispatch_one_script (Request *request) if (g_spawn_async ("/", argv, request->envp, G_SPAWN_DO_NOT_REAP_CHILD, child_setup, request, &script->pid, &error)) { request->script_watch_id = g_child_watch_add (script->pid, (GChildWatchFunc) script_watch_cb, script); - request->script_timeout_id = g_timeout_add_seconds (20, script_timeout_cb, script); + request->script_timeout_id = g_timeout_add_seconds (SCRIPT_TIMEOUT, script_timeout_cb, script); } else { g_warning ("Failed to execute script '%s': (%d) %s", script->script, error->code, error->message); diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 5d3a56cfa..7a0c8de17 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -31,6 +31,8 @@ #include "nm-dbus-glib-types.h" #include "nm-glib-compat.h" +#define CALL_TIMEOUT (1000 * 60 * 10) /* 10 mintues for all scripts */ + static gboolean do_dispatch = TRUE; static GHashTable *requests = NULL; @@ -428,7 +430,7 @@ _dispatcher_call (DispatcherAction action, GPtrArray *results = NULL; success = dbus_g_proxy_call_with_timeout (proxy, "Action", - 30000, + CALL_TIMEOUT, &error, G_TYPE_STRING, action_to_string (action), DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash, @@ -461,7 +463,7 @@ _dispatcher_call (DispatcherAction action, dispatcher_done_cb, info, (GDestroyNotify) dispatcher_info_cleanup, - 30000, + CALL_TIMEOUT, G_TYPE_STRING, action_to_string (action), DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash, DBUS_TYPE_G_MAP_OF_VARIANT, connection_props,