From 5761e328b81ce8894c2657ce0994ba401923ba35 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 Oct 2009 15:03:12 -0700 Subject: [PATCH 01/13] dns: honor resolv.conf symlinks (lp:324233) Based on a patch from Alexander Sack, but hugely modified by me to make use of allocated realpath results instead of stack-based arrays, and to fix an omission in the original patch that would still have used the non-realpath-resolved path to /etc/resolv.conf when doing the atomic rename of the tempfile to resolv.conf. --- src/named-manager/nm-named-manager.c | 57 ++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/src/named-manager/nm-named-manager.c b/src/named-manager/nm-named-manager.c index b89c80118c..c96a2e691b 100644 --- a/src/named-manager/nm-named-manager.c +++ b/src/named-manager/nm-named-manager.c @@ -388,14 +388,29 @@ update_resolv_conf (const char *domain, const char *iface, GError **error) { - const char *tmp_resolv_conf = RESOLV_CONF ".tmp"; - char tmp_resolv_conf_realpath [PATH_MAX]; + char *tmp_resolv_conf; + char *tmp_resolv_conf_realpath; + char *resolv_conf_realpath; FILE *f; int do_rename = 1; int old_errno = 0; - if (!realpath (tmp_resolv_conf, tmp_resolv_conf_realpath)) - strcpy (tmp_resolv_conf_realpath, tmp_resolv_conf); + g_return_val_if_fail (error != NULL, FALSE); + + /* Find the real path of resolv.conf; it could be a symlink to something */ + resolv_conf_realpath = realpath (RESOLV_CONF, NULL); + if (!resolv_conf_realpath) + resolv_conf_realpath = strdup (RESOLV_CONF); + + /* Build up the real path for the temp resolv.conf that we're about to + * write out. + */ + tmp_resolv_conf = g_strdup_printf ("%s.tmp", resolv_conf_realpath); + tmp_resolv_conf_realpath = realpath (tmp_resolv_conf, NULL); + if (!tmp_resolv_conf_realpath) + tmp_resolv_conf_realpath = strdup (tmp_resolv_conf); + g_free (tmp_resolv_conf); + tmp_resolv_conf = NULL; if ((f = fopen (tmp_resolv_conf_realpath, "w")) == NULL) { do_rename = 0; @@ -409,8 +424,11 @@ update_resolv_conf (const char *domain, g_strerror (old_errno), RESOLV_CONF, g_strerror (errno)); - return FALSE; + goto out; } + /* Update tmp_resolv_conf_realpath so the error message on fclose() + * failure will be correct. + */ strcpy (tmp_resolv_conf_realpath, RESOLV_CONF); } @@ -418,25 +436,34 @@ update_resolv_conf (const char *domain, if (fclose (f) < 0) { if (*error == NULL) { + /* only set an error here if write_resolv_conf() was successful, + * since its error is more important. + */ g_set_error (error, - NM_NAMED_MANAGER_ERROR, - NM_NAMED_MANAGER_ERROR_SYSTEM, - "Could not close %s: %s\n", - tmp_resolv_conf_realpath, - g_strerror (errno)); + NM_NAMED_MANAGER_ERROR, + NM_NAMED_MANAGER_ERROR_SYSTEM, + "Could not close %s: %s\n", + tmp_resolv_conf_realpath, + g_strerror (errno)); } } + /* Don't rename the tempfile over top of the existing resolv.conf if there + * was an error writing it out. + */ if (*error == NULL && do_rename) { - if (rename (tmp_resolv_conf, RESOLV_CONF) < 0) { + if (rename (tmp_resolv_conf_realpath, resolv_conf_realpath) < 0) { g_set_error (error, - NM_NAMED_MANAGER_ERROR, - NM_NAMED_MANAGER_ERROR_SYSTEM, - "Could not replace " RESOLV_CONF ": %s\n", - g_strerror (errno)); + NM_NAMED_MANAGER_ERROR, + NM_NAMED_MANAGER_ERROR_SYSTEM, + "Could not replace " RESOLV_CONF ": %s\n", + g_strerror (errno)); } } +out: + free (tmp_resolv_conf_realpath); + free (resolv_conf_realpath); return *error ? FALSE : TRUE; } From 4b2c810b1ba1f4ac6384bde3c749c94467b2886b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 Oct 2009 15:25:04 -0700 Subject: [PATCH 02/13] core: clear invalid tag on failed connections when sleeping So they'll get tried again on wakeup/resume. --- src/NetworkManagerPolicy.c | 24 +++++++++++++++++++++++- src/nm-manager.c | 14 ++++++++++++++ src/nm-manager.h | 1 + 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/NetworkManagerPolicy.c b/src/NetworkManagerPolicy.c index cd10b493f5..6c34de8cf6 100644 --- a/src/NetworkManagerPolicy.c +++ b/src/NetworkManagerPolicy.c @@ -711,6 +711,24 @@ hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data) update_system_hostname ((NMPolicy *) user_data, NULL); } +static void +sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data) +{ + gboolean sleeping = FALSE; + GSList *connections, *iter; + + g_object_get (G_OBJECT (manager), NM_MANAGER_SLEEPING, &sleeping, NULL); + + /* Clear the invalid flag on all connections so they'll get retried on wakeup */ + if (sleeping) { + connections = nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_SYSTEM); + connections = g_slist_concat (connections, nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_USER)); + for (iter = connections; iter; iter = g_slist_next (iter)) + g_object_set_data (G_OBJECT (iter->data), INVALID_TAG, NULL); + g_slist_free (connections); + } +} + static void schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds) { @@ -992,10 +1010,14 @@ nm_policy_new (NMManager *manager, NMVPNManager *vpn_manager) G_CALLBACK (global_state_changed), policy); policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); - id = g_signal_connect (manager, "notify::hostname", + id = g_signal_connect (manager, "notify::" NM_MANAGER_HOSTNAME, G_CALLBACK (hostname_changed), policy); policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + id = g_signal_connect (manager, "notify::" NM_MANAGER_SLEEPING, + G_CALLBACK (sleeping_changed), policy); + policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); + id = g_signal_connect (manager, "device-added", G_CALLBACK (device_added), policy); policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id); diff --git a/src/nm-manager.c b/src/nm-manager.c index eb0fd2bfbd..e082ab0f5d 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -214,6 +214,7 @@ enum { /* Not exported */ PROP_HOSTNAME, + PROP_SLEEPING, LAST_PROP }; @@ -2429,6 +2430,8 @@ impl_manager_sleep (NMManager *self, gboolean sleep, GError **error) } nm_manager_update_state (self); + + g_object_notify (G_OBJECT (self), NM_MANAGER_SLEEPING); return TRUE; } @@ -2761,6 +2764,9 @@ get_property (GObject *object, guint prop_id, case PROP_HOSTNAME: g_value_set_string (value, priv->hostname); break; + case PROP_SLEEPING: + g_value_set_boolean (value, priv->sleeping); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2884,6 +2890,14 @@ nm_manager_class_init (NMManagerClass *manager_class) NULL, G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT)); + g_object_class_install_property + (object_class, PROP_SLEEPING, + g_param_spec_boolean (NM_MANAGER_SLEEPING, + "Sleeping", + "Sleeping", + FALSE, + G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT)); + /* signals */ signals[DEVICE_ADDED] = g_signal_new ("device-added", diff --git a/src/nm-manager.h b/src/nm-manager.h index 0c6da150d6..8e48574e65 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -42,6 +42,7 @@ /* Not exported */ #define NM_MANAGER_HOSTNAME "hostname" +#define NM_MANAGER_SLEEPING "sleeping" typedef struct { GObject parent; From f8643cc072f78589a453a213856554ebef9aff3d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 21 Oct 2009 14:19:01 -0700 Subject: [PATCH 03/13] system-settings: fix PK Authority object lifetimes It's a singleton, but PolicyKit didn't increment the reference count when returning from polkit_authority_get() like we expected (which has since been fixed upstream). So for now, just don't unref the authority at all. Since we don't do that, there's a chance that some PolicyKit calls could be outstanding when either the NMSysconfigSettings object or one of the NMSysconfigConnection objects are around, so we make sure we cancel any PolicyKit calls when the object gets disposed. This is tricky, because canceling them from the dispose may mean that the callback gets called after the object is actually destroyed, so we have to be careful not to access any private object data from the callbacks in that situation. --- src/system-settings/nm-sysconfig-connection.c | 71 +++++++++- src/system-settings/nm-sysconfig-settings.c | 129 +++++++++++++++--- 2 files changed, 178 insertions(+), 22 deletions(-) diff --git a/src/system-settings/nm-sysconfig-connection.c b/src/system-settings/nm-sysconfig-connection.c index 11b022f9d0..7401da5571 100644 --- a/src/system-settings/nm-sysconfig-connection.c +++ b/src/system-settings/nm-sysconfig-connection.c @@ -42,6 +42,7 @@ G_DEFINE_TYPE_EXTENDED (NMSysconfigConnection, nm_sysconfig_connection, NM_TYPE_ typedef struct { PolkitAuthority *authority; + GSList *pk_calls; } NMSysconfigConnectionPrivate; /**************************************************************/ @@ -167,6 +168,7 @@ typedef struct { DBusGMethodInvocation *context; PolkitSubject *subject; GCancellable *cancellable; + gboolean disposed; /* Update */ NMConnection *connection; @@ -242,11 +244,26 @@ pk_update_cb (GObject *object, GAsyncResult *result, gpointer user_data) { PolkitCall *call = user_data; NMSysconfigConnection *self = call->self; - NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + NMSysconfigConnectionPrivate *priv; PolkitAuthorizationResult *pk_result; GError *error = NULL; GHashTable *settings; + /* If our NMSysconfigConnection is already gone, do nothing */ + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + + priv->pk_calls = g_slist_remove (priv->pk_calls, call); + pk_result = polkit_authority_check_authorization_finish (priv->authority, result, &error); @@ -321,6 +338,7 @@ dbus_update (NMExportedConnection *exported, call->cancellable, pk_update_cb, call); + priv->pk_calls = g_slist_prepend (priv->pk_calls, call); } static void @@ -343,10 +361,25 @@ pk_delete_cb (GObject *object, GAsyncResult *result, gpointer user_data) { PolkitCall *call = user_data; NMSysconfigConnection *self = call->self; - NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + NMSysconfigConnectionPrivate *priv; PolkitAuthorizationResult *pk_result; GError *error = NULL; + /* If our NMSysconfigConnection is already gone, do nothing */ + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + + priv->pk_calls = g_slist_remove (priv->pk_calls, call); + pk_result = polkit_authority_check_authorization_finish (priv->authority, result, &error); @@ -396,6 +429,7 @@ dbus_delete (NMExportedConnection *exported, call->cancellable, pk_delete_cb, call); + priv->pk_calls = g_slist_prepend (priv->pk_calls, call); } static void @@ -419,10 +453,25 @@ pk_secrets_cb (GObject *object, GAsyncResult *result, gpointer user_data) { PolkitCall *call = user_data; NMSysconfigConnection *self = call->self; - NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + NMSysconfigConnectionPrivate *priv; PolkitAuthorizationResult *pk_result; GError *error = NULL; + /* If our NMSysconfigConnection is already gone, do nothing */ + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + + priv->pk_calls = g_slist_remove (priv->pk_calls, call); + pk_result = polkit_authority_check_authorization_finish (priv->authority, result, &error); @@ -478,6 +527,7 @@ dbus_get_secrets (NMExportedConnection *exported, call->cancellable, pk_secrets_cb, call); + priv->pk_calls = g_slist_prepend (priv->pk_calls, call); } /**************************************************************/ @@ -501,10 +551,19 @@ nm_sysconfig_connection_init (NMSysconfigConnection *self) static void dispose (GObject *object) { - NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (object); + NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (object); + NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self); + GSList *iter; - if (priv->authority) - g_object_unref (priv->authority); + /* Cancel PolicyKit requests */ + for (iter = priv->pk_calls; iter; iter = g_slist_next (iter)) { + PolkitCall *call = iter->data; + + call->disposed = TRUE; + g_cancellable_cancel (call->cancellable); + } + g_slist_free (priv->pk_calls); + priv->pk_calls = NULL; G_OBJECT_CLASS (nm_sysconfig_connection_parent_class)->dispose (object); } diff --git a/src/system-settings/nm-sysconfig-settings.c b/src/system-settings/nm-sysconfig-settings.c index 9bba219e10..7d422e694a 100644 --- a/src/system-settings/nm-sysconfig-settings.c +++ b/src/system-settings/nm-sysconfig-settings.c @@ -79,8 +79,12 @@ static void unmanaged_specs_changed (NMSystemConfigInterface *config, gpointer u typedef struct { PolkitAuthority *authority; + guint auth_changed_id; char *config_file; + GSList *pk_calls; + GSList *permissions_calls; + GSList *plugins; gboolean connections_loaded; GHashTable *connections; @@ -516,6 +520,8 @@ typedef struct { NMSysconfigSettings *self; DBusGMethodInvocation *context; PolkitSubject *subject; + GCancellable *cancellable; + gboolean disposed; NMConnection *connection; NMSettingsAddConnectionFunc callback; @@ -545,6 +551,7 @@ polkit_call_new (NMSysconfigSettings *self, call = g_malloc0 (sizeof (PolkitCall)); call->self = self; + call->cancellable = g_cancellable_new (); call->context = context; if (connection) call->connection = g_object_ref (connection); @@ -567,6 +574,7 @@ polkit_call_free (PolkitCall *call) { if (call->connection) g_object_unref (call->connection); + g_object_unref (call->cancellable); g_free (call->hostname); g_object_unref (call->subject); g_free (call); @@ -610,10 +618,25 @@ pk_add_cb (GObject *object, GAsyncResult *result, gpointer user_data) { PolkitCall *call = user_data; NMSysconfigSettings *self = call->self; - NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + NMSysconfigSettingsPrivate *priv; PolkitAuthorizationResult *pk_result; GError *error = NULL, *add_error = NULL; + /* If NMSysconfigSettings is already gone, do nothing */ + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + priv->pk_calls = g_slist_remove (priv->pk_calls, call); + pk_result = polkit_authority_check_authorization_finish (priv->authority, result, &error); @@ -680,9 +703,10 @@ add_connection (NMSettingsService *service, NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY, NULL, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, - NULL, + call->cancellable, pk_add_cb, call); + priv->pk_calls = g_slist_append (priv->pk_calls, call); } static void @@ -690,12 +714,27 @@ pk_hostname_cb (GObject *object, GAsyncResult *result, gpointer user_data) { PolkitCall *call = user_data; NMSysconfigSettings *self = call->self; - NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + NMSysconfigSettingsPrivate *priv; PolkitAuthorizationResult *pk_result; GError *error = NULL; GSList *iter; gboolean success = FALSE; + /* If our NMSysconfigConnection is already gone, do nothing */ + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (call->context, error); + g_error_free (error); + polkit_call_free (call); + return; + } + + priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + priv->pk_calls = g_slist_remove (priv->pk_calls, call); + pk_result = polkit_authority_check_authorization_finish (priv->authority, result, &error); @@ -767,9 +806,10 @@ impl_settings_save_hostname (NMSysconfigSettings *self, NM_SYSCONFIG_POLICY_ACTION_HOSTNAME_MODIFY, NULL, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, - NULL, + call->cancellable, pk_hostname_cb, call); + priv->pk_calls = g_slist_append (priv->pk_calls, call); } static void @@ -783,7 +823,9 @@ pk_authority_changed_cb (GObject *object, gpointer user_data) typedef struct { PolkitCall *pk_call; const char *pk_action; + GCancellable *cancellable; NMSettingsSystemPermissions permission; + gboolean disposed; } PermissionsCall; static void @@ -792,10 +834,18 @@ permission_call_done (GObject *object, GAsyncResult *result, gpointer user_data) PermissionsCall *call = user_data; PolkitCall *pk_call = call->pk_call; NMSysconfigSettings *self = pk_call->self; - NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + NMSysconfigSettingsPrivate *priv; PolkitAuthorizationResult *pk_result; GError *error = NULL; + /* If NMSysconfigSettings is gone, just skip to the end */ + if (call->disposed) + goto done; + + priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + + priv->permissions_calls = g_slist_remove (priv->permissions_calls, call); + pk_result = polkit_authority_check_authorization_finish (priv->authority, result, &error); @@ -820,12 +870,21 @@ permission_call_done (GObject *object, GAsyncResult *result, gpointer user_data) g_object_unref (pk_result); +done: pk_call->permissions_calls--; if (pk_call->permissions_calls == 0) { - /* All the permissions calls are done, return the full permissions - * bitfield back to the user. - */ - dbus_g_method_return (pk_call->context, pk_call->permissions); + if (call->disposed) { + error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR, + NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, + "Request was canceled."); + dbus_g_method_return_error (pk_call->context, error); + g_error_free (error); + } else { + /* All the permissions calls are done, return the full permissions + * bitfield back to the user. + */ + dbus_g_method_return (pk_call->context, pk_call->permissions); + } polkit_call_free (pk_call); } @@ -850,6 +909,7 @@ start_permission_check (NMSysconfigSettings *self, call->pk_call = pk_call; call->pk_action = pk_action; call->permission = permission; + call->cancellable = g_cancellable_new (); pk_call->permissions_calls++; @@ -858,9 +918,10 @@ start_permission_check (NMSysconfigSettings *self, pk_action, NULL, 0, - NULL, + call->cancellable, permission_call_done, call); + priv->permissions_calls = g_slist_append (priv->permissions_calls, call); } static void @@ -1261,6 +1322,41 @@ nm_sysconfig_settings_new (const char *config_file, /***************************************************************/ +static void +dispose (GObject *object) +{ + NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (object); + NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self); + GSList *iter; + + if (priv->auth_changed_id) { + g_signal_handler_disconnect (priv->authority, priv->auth_changed_id); + priv->auth_changed_id = 0; + } + + /* Cancel PolicyKit requests */ + for (iter = priv->pk_calls; iter; iter = g_slist_next (iter)) { + PolkitCall *call = iter->data; + + call->disposed = TRUE; + g_cancellable_cancel (call->cancellable); + } + g_slist_free (priv->pk_calls); + priv->pk_calls = NULL; + + /* Cancel PolicyKit permissions requests */ + for (iter = priv->permissions_calls; iter; iter = g_slist_next (iter)) { + PermissionsCall *call = iter->data; + + call->disposed = TRUE; + g_cancellable_cancel (call->cancellable); + } + g_slist_free (priv->permissions_calls); + priv->permissions_calls = NULL; + + G_OBJECT_CLASS (nm_sysconfig_settings_parent_class)->dispose (object); +} + static void finalize (GObject *object) { @@ -1274,9 +1370,6 @@ finalize (GObject *object) g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL); g_slist_free (priv->plugins); - if (priv->authority) - g_object_unref (priv->authority); - g_free (priv->orig_hostname); g_free (priv->config_file); @@ -1334,6 +1427,7 @@ nm_sysconfig_settings_class_init (NMSysconfigSettingsClass *class) /* virtual methods */ object_class->notify = notify; object_class->get_property = get_property; + object_class->dispose = dispose; object_class->finalize = finalize; ss_class->list_connections = list_connections; ss_class->add_connection = add_connection; @@ -1379,9 +1473,12 @@ nm_sysconfig_settings_init (NMSysconfigSettings *self) priv->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); priv->authority = polkit_authority_get (); - if (priv->authority) - g_signal_connect (priv->authority, "changed", G_CALLBACK (pk_authority_changed_cb), self); - else + if (priv->authority) { + priv->auth_changed_id = g_signal_connect (priv->authority, + "changed", + G_CALLBACK (pk_authority_changed_cb), + self); + } else g_warning ("%s: failed to create PolicyKit authority.", __func__); /* Grab hostname on startup and use that if no plugins provide one */ From bf8d3b027bbc319d86847dc729684b406acf995c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 23 Oct 2009 15:38:06 -0700 Subject: [PATCH 04/13] introspection: document ip4-config argument formats --- introspection/nm-ip4-config.xml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/introspection/nm-ip4-config.xml b/introspection/nm-ip4-config.xml index 57ce29529d..70d3e1ce43 100644 --- a/introspection/nm-ip4-config.xml +++ b/introspection/nm-ip4-config.xml @@ -3,19 +3,26 @@ - Tuples of IPv4 address/prefix/gateway. + Array of tuples of IPv4 address/prefix/gateway. All 3 + elements of each tuple are in network byte order. Essentially: + [(addr, prefix, gateway), (addr, prefix, gateway), ...] + The nameservers in use. - The Windows Internet Name Service servers associated with the connection. + The Windows Internet Name Service servers associated with the connection. Each address is in network byte order. A list of domains this address belongs to. - Tuples of IPv4 route/prefix/next-hop/metric. + Tuples of IPv4 route/prefix/next-hop/metric. All 4 elements + of each tuple are in network byte order. 'route' and 'next hop' are IPv4 + addresses, while prefix and metric are simple unsigned integers. Essentially: + [(route, prefix, next-hop, metric), (route, prefix, next-hop, metric), ...] + From 294a5e31532b038e9ad31f4f9750fe6fa724da1c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 23 Oct 2009 16:49:38 -0700 Subject: [PATCH 05/13] modem: substitute known-good nameservers if PPP doesn't return any (lp:434477) Modem firmware PPP implementations suck. --- src/modem-manager/nm-modem.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/modem-manager/nm-modem.c b/src/modem-manager/nm-modem.c index 508ff6a44e..c7a51c05db 100644 --- a/src/modem-manager/nm-modem.c +++ b/src/modem-manager/nm-modem.c @@ -126,6 +126,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager, guint32 good_dns1 = htonl (0x04020201); /* GTE nameserver */ guint32 bad_dns2 = htonl (0x0A0B0C0E); guint32 good_dns2 = htonl (0x04020202); /* GTE nameserver */ + gboolean dns_workaround = FALSE; /* Ignore PPP IP4 events that come in after initial configuration */ if (nm_device_get_state (device) != NM_DEVICE_STATE_IP_CONFIG) @@ -157,11 +158,13 @@ ppp_ip4_config (NMPPPManager *ppp_manager, * could actually be valid in some cases, so only substitute if ppp * returns *only* the two bad nameservers. */ - if (found1 && found2) { - nm_ip4_config_reset_nameservers (config); - nm_ip4_config_add_nameserver (config, good_dns1); - nm_ip4_config_add_nameserver (config, good_dns2); - } + dns_workaround = (found1 && found2); + } + + if (!num || dns_workaround) { + nm_ip4_config_reset_nameservers (config); + nm_ip4_config_add_nameserver (config, good_dns1); + nm_ip4_config_add_nameserver (config, good_dns2); } nm_device_set_ip_iface (device, iface); From 671f00c86f047dfe57f43726a0eefadaad6f2734 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 28 Oct 2009 10:41:18 -0700 Subject: [PATCH 06/13] introspection: synchronize VPN ActiveConnection interface Due to dbus-glib limitations we still have to keep two copies of this, and furthermore PropertiesChanged won't yet trigger for the VPN bits since there's no way to push out signals on a different interface. --- introspection/nm-vpn-connection.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/introspection/nm-vpn-connection.xml b/introspection/nm-vpn-connection.xml index 233d8b0866..3a47cdf1b2 100644 --- a/introspection/nm-vpn-connection.xml +++ b/introspection/nm-vpn-connection.xml @@ -20,6 +20,17 @@ Whether this active connection is the default connection, i.e. whether it currently owns the default route. + + Whether this active connection is also a VPN connection. + + + + + + A dictionary mapping property names to variant boxed values + + + From 06a40dcf737e1732ad6c568b9533ed19fdc40bca Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 30 Oct 2009 11:39:19 -0700 Subject: [PATCH 07/13] core: move helper macro for activating state to general location --- src/NetworkManagerPolicy.c | 4 +--- src/nm-device-interface.h | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/NetworkManagerPolicy.c b/src/NetworkManagerPolicy.c index 6c34de8cf6..5b860aae0a 100644 --- a/src/NetworkManagerPolicy.c +++ b/src/NetworkManagerPolicy.c @@ -42,8 +42,6 @@ #include "nm-vpn-manager.h" #include "nm-modem.h" -#define STATE_IN_ACTIVATION_PHASE(state) ((state > NM_DEVICE_STATE_DISCONNECTED) && (state < NM_DEVICE_STATE_ACTIVATED)) - typedef struct LookupThread LookupThread; typedef void (*LookupCallback) (LookupThread *thread, gpointer user_data); @@ -788,7 +786,7 @@ device_state_changed (NMDevice *device, /* Mark the connection invalid if it failed during activation so that * it doesn't get automatically chosen over and over and over again. */ - if (connection && STATE_IN_ACTIVATION_PHASE (old_state)) { + if (connection && IS_ACTIVATING_STATE (old_state)) { g_object_set_data (G_OBJECT (connection), INVALID_TAG, GUINT_TO_POINTER (TRUE)); nm_info ("Marking connection '%s' invalid.", get_connection_id (connection)); nm_connection_clear_secrets (connection); diff --git a/src/nm-device-interface.h b/src/nm-device-interface.h index 0867bc2923..54b5c41a59 100644 --- a/src/nm-device-interface.h +++ b/src/nm-device-interface.h @@ -32,6 +32,9 @@ #define NM_IS_DEVICE_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_INTERFACE)) #define NM_DEVICE_INTERFACE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_DEVICE_INTERFACE, NMDeviceInterface)) +#define IS_ACTIVATING_STATE(state) \ + (state > NM_DEVICE_STATE_DISCONNECTED && state < NM_DEVICE_STATE_ACTIVATED) + typedef enum { NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING = 0, From 82011dff04123df5634c3a17ceae19c76c67c9e0 Mon Sep 17 00:00:00 2001 From: Alexander Sack Date: Fri, 30 Oct 2009 19:52:14 +0100 Subject: [PATCH 08/13] ppp: allow update of ppp secrets in all ACTIVATING stages (lp:432205) Previously, ppp code would flip device state to _NEED_AUTH before asking for secrets update; this is not the case anymore after landing of f28a0df4a66e8f6c98327691c9c90df0604bbd28; hence, we need to allow update of secrets in all ACTIVATING stages. This patch updates this behaviour for all device classes with ppp support. --- src/modem-manager/nm-modem-cdma.c | 2 ++ src/modem-manager/nm-modem-gsm.c | 2 ++ src/nm-device-ethernet.c | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/modem-manager/nm-modem-cdma.c b/src/modem-manager/nm-modem-cdma.c index 9ba0a78279..30feb28a79 100644 --- a/src/modem-manager/nm-modem-cdma.c +++ b/src/modem-manager/nm-modem-cdma.c @@ -169,6 +169,8 @@ real_connection_secrets_updated (NMDevice *dev, gboolean found = FALSE; GSList *iter; + g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (dev))); + if (caller == SECRETS_CALLER_PPP) { NMPPPManager *ppp_manager; NMSettingCdma *s_cdma = NULL; diff --git a/src/modem-manager/nm-modem-gsm.c b/src/modem-manager/nm-modem-gsm.c index 0493d7476d..2bd8b231a9 100644 --- a/src/modem-manager/nm-modem-gsm.c +++ b/src/modem-manager/nm-modem-gsm.c @@ -335,6 +335,8 @@ real_connection_secrets_updated (NMDevice *dev, gboolean found = FALSE; GSList *iter; + g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (dev))); + if (caller == SECRETS_CALLER_PPP) { NMPPPManager *ppp_manager; NMSettingGsm *s_gsm = NULL; diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index 6a75cba1fd..637107d6d9 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -632,8 +632,7 @@ real_connection_secrets_updated (NMDevice *dev, gboolean valid = FALSE; GSList *iter; - if (nm_device_get_state (dev) != NM_DEVICE_STATE_NEED_AUTH) - return; + g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (dev))); /* PPPoE? */ if (caller == SECRETS_CALLER_PPP) { @@ -663,6 +662,7 @@ real_connection_secrets_updated (NMDevice *dev, /* Only caller could be ourselves for 802.1x */ g_return_if_fail (caller == SECRETS_CALLER_ETHERNET); + g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH); for (iter = updated_settings; iter; iter = g_slist_next (iter)) { const char *setting_name = (const char *) iter->data; From df32cfbfd867c90af57e9904fce5fef87b22a5de Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 2 Nov 2009 10:57:31 -0800 Subject: [PATCH 09/13] libnm-util: don't allow blank or NULL VPN items or secrets (rh #532084) Weren't supposed to be allowed anyway; fix that and add a testcase for it. --- libnm-util/nm-setting-vpn.c | 8 ++ libnm-util/tests/Makefile.am | 19 +++- libnm-util/tests/test-general.c | 151 ++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 libnm-util/tests/test-general.c diff --git a/libnm-util/nm-setting-vpn.c b/libnm-util/nm-setting-vpn.c index def7432216..279fea8ebf 100644 --- a/libnm-util/nm-setting-vpn.c +++ b/libnm-util/nm-setting-vpn.c @@ -133,6 +133,10 @@ nm_setting_vpn_add_data_item (NMSettingVPN *setting, const char *item) { g_return_if_fail (NM_IS_SETTING_VPN (setting)); + g_return_if_fail (key != NULL); + g_return_if_fail (strlen (key) > 0); + g_return_if_fail (item != NULL); + g_return_if_fail (strlen (item) > 0); g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data, g_strdup (key), g_strdup (item)); @@ -171,6 +175,10 @@ nm_setting_vpn_add_secret (NMSettingVPN *setting, const char *secret) { g_return_if_fail (NM_IS_SETTING_VPN (setting)); + g_return_if_fail (key != NULL); + g_return_if_fail (strlen (key) > 0); + g_return_if_fail (secret != NULL); + g_return_if_fail (strlen (secret) > 0); g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, g_strdup (key), g_strdup (secret)); diff --git a/libnm-util/tests/Makefile.am b/libnm-util/tests/Makefile.am index d0e3477df3..2d78b972d4 100644 --- a/libnm-util/tests/Makefile.am +++ b/libnm-util/tests/Makefile.am @@ -4,7 +4,11 @@ INCLUDES = \ -I$(top_srcdir)/include \ -I$(top_srcdir)/libnm-util -noinst_PROGRAMS = test-settings-defaults test-crypto test-need-secrets +noinst_PROGRAMS = \ + test-settings-defaults \ + test-crypto \ + test-need-secrets \ + test-general test_settings_defaults_SOURCES = \ test-settings-defaults.c @@ -42,11 +46,24 @@ test_need_secrets_LDADD = \ $(GLIB_LIBS) \ $(DBUS_LIBS) +test_general_SOURCES = \ + test-general.c + +test_general_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) + +test_general_LDADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(GLIB_LIBS) \ + $(DBUS_LIBS) + if WITH_TESTS check-local: test-settings-defaults test-crypto test-need-secrets $(abs_builddir)/test-settings-defaults $(abs_builddir)/test-need-secrets + $(abs_builddir)/test-general # Cert with 8 bytes of tail padding $(abs_builddir)/test-crypto \ diff --git a/libnm-util/tests/test-general.c b/libnm-util/tests/test-general.c new file mode 100644 index 0000000000..d00ec994e1 --- /dev/null +++ b/libnm-util/tests/test-general.c @@ -0,0 +1,151 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 - 2009 Red Hat, Inc. + * + */ + +#include +#include +#include + +#include "nm-test-helpers.h" +#include + +#include "nm-setting-connection.h" +#include "nm-setting-vpn.h" + + +static void +vpn_check_func (const char *key, const char *value, gpointer user_data) +{ + const char *test = user_data; + + if (!strcmp (key, "foobar1")) { + ASSERT (strcmp (value, "blahblah1") == 0, + test, "unexpected vpn item '%s' / '%s'", key, value); + return; + } + + if (!strcmp (key, "foobar2")) { + ASSERT (strcmp (value, "blahblah2") == 0, + test, "unexpected vpn item '%s' / '%s'", key, value); + return; + } + + if (!strcmp (key, "foobar3")) { + ASSERT (strcmp (value, "blahblah3") == 0, + test, "unexpected vpn item '%s' / '%s'", key, value); + return; + } + + if (!strcmp (key, "foobar4")) { + ASSERT (strcmp (value, "blahblah4") == 0, + test, "unexpected vpn item '%s' / '%s'", key, value); + return; + } + + ASSERT (FALSE, test, "unexpected vpn item '%s'", key); +} + +static void +vpn_check_empty_func (const char *key, const char *value, gpointer user_data) +{ + const char *test = user_data; + + /* We don't expect any values */ + ASSERT (FALSE, test, "unexpected vpn item '%s'", key); +} + +static void +test_setting_vpn_items (void) +{ + NMSettingVPN *s_vpn; + + s_vpn = (NMSettingVPN *) nm_setting_vpn_new (); + ASSERT (s_vpn != NULL, + "vpn-items", + "error creating vpn setting"); + + nm_setting_vpn_add_data_item (s_vpn, "foobar1", "blahblah1"); + nm_setting_vpn_add_data_item (s_vpn, "foobar2", "blahblah2"); + nm_setting_vpn_add_data_item (s_vpn, "foobar3", "blahblah3"); + nm_setting_vpn_add_data_item (s_vpn, "foobar4", "blahblah4"); + + /* Ensure that added values are all present */ + nm_setting_vpn_foreach_data_item (s_vpn, vpn_check_func, "vpn-data"); + nm_setting_vpn_remove_data_item (s_vpn, "foobar1"); + nm_setting_vpn_remove_data_item (s_vpn, "foobar2"); + nm_setting_vpn_remove_data_item (s_vpn, "foobar3"); + nm_setting_vpn_remove_data_item (s_vpn, "foobar4"); + + nm_setting_vpn_add_secret (s_vpn, "foobar1", "blahblah1"); + nm_setting_vpn_add_secret (s_vpn, "foobar2", "blahblah2"); + nm_setting_vpn_add_secret (s_vpn, "foobar3", "blahblah3"); + nm_setting_vpn_add_secret (s_vpn, "foobar4", "blahblah4"); + + /* Ensure that added values are all present */ + nm_setting_vpn_foreach_secret (s_vpn, vpn_check_func, "vpn-secrets"); + nm_setting_vpn_remove_secret (s_vpn, "foobar1"); + nm_setting_vpn_remove_secret (s_vpn, "foobar2"); + nm_setting_vpn_remove_secret (s_vpn, "foobar3"); + nm_setting_vpn_remove_secret (s_vpn, "foobar4"); + + /* Try to add some blank values and make sure they are rejected */ + nm_setting_vpn_add_data_item (s_vpn, NULL, NULL); + nm_setting_vpn_add_data_item (s_vpn, "", ""); + nm_setting_vpn_add_data_item (s_vpn, "foobar1", NULL); + nm_setting_vpn_add_data_item (s_vpn, "foobar1", ""); + nm_setting_vpn_add_data_item (s_vpn, NULL, "blahblah1"); + nm_setting_vpn_add_data_item (s_vpn, "", "blahblah1"); + + nm_setting_vpn_foreach_data_item (s_vpn, vpn_check_empty_func, "vpn-data-empty"); + + /* Try to add some blank secrets and make sure they are rejected */ + nm_setting_vpn_add_secret (s_vpn, NULL, NULL); + nm_setting_vpn_add_secret (s_vpn, "", ""); + nm_setting_vpn_add_secret (s_vpn, "foobar1", NULL); + nm_setting_vpn_add_secret (s_vpn, "foobar1", ""); + nm_setting_vpn_add_secret (s_vpn, NULL, "blahblah1"); + nm_setting_vpn_add_secret (s_vpn, "", "blahblah1"); + + nm_setting_vpn_foreach_secret (s_vpn, vpn_check_empty_func, "vpn-secrets-empty"); + + g_object_unref (s_vpn); +} + +int main (int argc, char **argv) +{ + GError *error = NULL; + DBusGConnection *bus; + char *base; + + g_type_init (); + bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + + if (!nm_utils_init (&error)) + FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); + + /* The tests */ + test_setting_vpn_items (); + + base = g_path_get_basename (argv[0]); + fprintf (stdout, "%s: SUCCESS\n", base); + g_free (base); + return 0; +} + From dccdf5e1f3707dd61832b7321fd3e5506b46e820 Mon Sep 17 00:00:00 2001 From: Jirka Klimes Date: Mon, 2 Nov 2009 17:29:53 -0800 Subject: [PATCH 10/13] core: preserve wifi and networking enabled/disabled states (bgo #582447) With modifications by dcbw to create the state file if it doesn't exist, and a couple cleanups and formatting fixes. --- src/NetworkManager.c | 105 +++++++++++++++++++++++++++++++++++++- src/nm-device-olpc-mesh.c | 2 +- src/nm-manager.c | 102 ++++++++++++++++++++++++++++++++++-- src/nm-manager.h | 7 ++- 4 files changed, 209 insertions(+), 7 deletions(-) diff --git a/src/NetworkManager.c b/src/NetworkManager.c index f14c6cbf07..bb697c1ad5 100644 --- a/src/NetworkManager.c +++ b/src/NetworkManager.c @@ -56,6 +56,7 @@ #define NM_DEFAULT_PID_FILE LOCALSTATEDIR"/run/NetworkManager.pid" #define NM_DEFAULT_SYSTEM_CONF_FILE SYSCONFDIR"/NetworkManager/nm-system-settings.conf" +#define NM_DEFAULT_SYSTEM_STATE_FILE LOCALSTATEDIR"/lib/NetworkManager/NetworkManager.state" /* * Globals @@ -269,6 +270,94 @@ parse_config_file (const char *filename, char **plugins, GError **error) return TRUE; } +static gboolean +parse_state_file (const char *filename, + gboolean *net_enabled, + gboolean *wifi_enabled, + GError **error) +{ + GKeyFile *state_file; + GError *tmp_error = NULL; + gboolean wifi, net; + + g_return_val_if_fail (net_enabled != NULL, FALSE); + g_return_val_if_fail (wifi_enabled != NULL, FALSE); + + state_file = g_key_file_new (); + if (!state_file) { + g_set_error (error, 0, 0, + "Not enough memory to load state file."); + return FALSE; + } + + g_key_file_set_list_separator (state_file, ','); + if (!g_key_file_load_from_file (state_file, filename, G_KEY_FILE_KEEP_COMMENTS, &tmp_error)) { + /* This is kinda ugly; create the file and directory if it doesn't + * exist yet. We can't rely on distros necessarily creating the + * /var/lib/NetworkManager for us since we have to ensure that + * users upgrading NM get this working too. + */ + if ( tmp_error->domain == G_FILE_ERROR + && tmp_error->code == G_FILE_ERROR_NOENT) { + char *data, *dirname; + gsize len = 0; + gboolean ret = FALSE; + + /* try to create the directory if it doesn't exist */ + dirname = g_path_get_dirname (filename); + errno = 0; + if (mkdir (dirname, 0755) != 0) { + if (errno != EEXIST) { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES, + "Error creating state directory %s: %d", dirname, errno); + g_free (dirname); + return FALSE; + } + } + g_free (dirname); + + /* Write out the initial state to the state file */ + g_key_file_set_boolean (state_file, "main", "NetworkingEnabled", *net_enabled); + g_key_file_set_boolean (state_file, "main", "WirelessEnabled", *wifi_enabled); + + data = g_key_file_to_data (state_file, &len, NULL); + if (data) + ret = g_file_set_contents (filename, data, len, error); + g_free (data); + + return ret; + } else { + g_set_error_literal (error, tmp_error->domain, tmp_error->code, tmp_error->message); + g_clear_error (&tmp_error); + } + + /* Otherwise, file probably corrupt or inaccessible */ + return FALSE; + } + + /* Reading state bits of NetworkManager; an error leaves the passed-in state + * value unchanged. + */ + net = g_key_file_get_boolean (state_file, "main", "NetworkingEnabled", &tmp_error); + if (tmp_error) + g_set_error_literal (error, tmp_error->domain, tmp_error->code, tmp_error->message); + else + *net_enabled = net; + g_clear_error (&tmp_error); + + wifi = g_key_file_get_boolean (state_file, "main", "WirelessEnabled", error); + if (tmp_error) { + g_clear_error (error); + g_set_error_literal (error, tmp_error->domain, tmp_error->code, tmp_error->message); + } else + *wifi_enabled = wifi; + g_clear_error (&tmp_error); + + g_key_file_free (state_file); + + return TRUE; +} + /* * main * @@ -281,6 +370,8 @@ main (int argc, char *argv[]) gboolean g_fatal_warnings = FALSE; char *pidfile = NULL, *user_pidfile = NULL; char *config = NULL, *plugins = NULL; + char *state_file = NM_DEFAULT_SYSTEM_STATE_FILE; + gboolean wifi_enabled = TRUE, net_enabled = TRUE; gboolean success; NMPolicy *policy = NULL; NMVPNManager *vpn_manager = NULL; @@ -294,6 +385,7 @@ main (int argc, char *argv[]) { "no-daemon", 0, 0, G_OPTION_ARG_NONE, &become_daemon, "Don't become a daemon", NULL }, { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, "Make all warnings fatal", NULL }, { "pid-file", 0, 0, G_OPTION_ARG_FILENAME, &user_pidfile, "Specify the location of a PID file", "filename" }, + { "state-file", 0, 0, G_OPTION_ARG_FILENAME, &state_file, "State file location", "/path/to/state.file" }, { "config", 0, 0, G_OPTION_ARG_FILENAME, &config, "Config file location", "/path/to/config.file" }, { "plugins", 0, 0, G_OPTION_ARG_STRING, &plugins, "List of plugins separated by ,", "plugin1,plugin2" }, {NULL} @@ -352,6 +444,17 @@ main (int argc, char *argv[]) } } + g_clear_error (&error); + + /* Parse the state file */ + if (!parse_state_file (state_file, &net_enabled, &wifi_enabled, &error)) { + g_warning ("State file %s parsing failed: (%d) %s.", + state_file, + error ? error->code : -1, + (error && error->message) ? error->message : "unknown"); + /* Not a hard failure */ + } + pidfile = g_strdup (user_pidfile ? user_pidfile : NM_DEFAULT_PID_FILE); /* Tricky: become_daemon is FALSE by default, so unless it's TRUE because @@ -419,7 +522,7 @@ main (int argc, char *argv[]) goto done; } - manager = nm_manager_get (config, plugins, &error); + manager = nm_manager_get (config, plugins, state_file, net_enabled, wifi_enabled, &error); if (manager == NULL) { nm_error ("Failed to initialize the network manager: %s", error && error->message ? error->message : "(unknown)"); diff --git a/src/nm-device-olpc-mesh.c b/src/nm-device-olpc-mesh.c index 743f47cdf7..2eef45b216 100644 --- a/src/nm-device-olpc-mesh.c +++ b/src/nm-device-olpc-mesh.c @@ -909,7 +909,7 @@ check_companion_cb (gpointer user_data) if (priv->device_added_cb != 0) return FALSE; - manager = nm_manager_get (NULL, NULL, NULL); + manager = nm_manager_get (NULL, NULL, NULL, FALSE, FALSE, NULL); priv->device_added_cb = g_signal_connect (manager, "device-added", G_CALLBACK (device_added_cb), self); diff --git a/src/nm-manager.c b/src/nm-manager.c index e082ab0f5d..d13d674cca 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -149,6 +149,7 @@ typedef struct { typedef struct { char *config_file; + char *state_file; GSList *devices; NMState state; @@ -1100,11 +1101,62 @@ nm_manager_name_owner_changed (NMDBusManager *mgr, } } +/* Store value into key-file; supported types: boolean, int, string */ +static gboolean +write_value_to_state_file (const char *filename, + const char *group, + const char *key, + GType value_type, + gpointer value, + GError **error) +{ + GKeyFile *key_file; + char *data; + gsize len = 0; + gboolean ret = FALSE; + + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (group != NULL, FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (value_type == G_TYPE_BOOLEAN || + value_type == G_TYPE_INT || + value_type == G_TYPE_STRING, + FALSE); + + key_file = g_key_file_new (); + if (!key_file) + return FALSE; + + g_key_file_set_list_separator (key_file, ','); + g_key_file_load_from_file (key_file, filename, G_KEY_FILE_KEEP_COMMENTS, NULL); + switch (value_type) { + case G_TYPE_BOOLEAN: + g_key_file_set_boolean (key_file, group, key, *((gboolean *) value)); + break; + case G_TYPE_INT: + g_key_file_set_integer (key_file, group, key, *((gint *) value)); + break; + case G_TYPE_STRING: + g_key_file_set_string (key_file, group, key, *((const gchar **) value)); + break; + } + + data = g_key_file_to_data (key_file, &len, NULL); + if (data) { + ret = g_file_set_contents (filename, data, len, error); + g_free (data); + } + g_key_file_free (key_file); + + return ret; +} + static void manager_set_wireless_enabled (NMManager *manager, gboolean enabled) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); GSList *iter; + GError *error = NULL; if (priv->wireless_enabled == enabled) return; @@ -1117,6 +1169,19 @@ manager_set_wireless_enabled (NMManager *manager, gboolean enabled) g_object_notify (G_OBJECT (manager), NM_MANAGER_WIRELESS_ENABLED); + /* Update "WirelessEnabled" key in state file */ + if (priv->state_file) { + if (!write_value_to_state_file (priv->state_file, + "main", "WirelessEnabled", + G_TYPE_BOOLEAN, (gpointer) &priv->wireless_enabled, + &error)) { + g_warning ("Writing to state file %s failed: (%d) %s.", + priv->state_file, + error ? error->code : -1, + (error && error->message) ? error->message : "unknown"); + } + } + /* Don't touch devices if asleep/networking disabled */ if (priv->sleeping) return; @@ -2399,6 +2464,23 @@ impl_manager_sleep (NMManager *self, gboolean sleep, GError **error) priv->sleeping = sleep; + /* Update "NetworkingEnabled" key in state file */ + if (priv->state_file) { + GError *err = NULL; + gboolean networking_enabled = !sleep; + + if (!write_value_to_state_file (priv->state_file, + "main", "NetworkingEnabled", + G_TYPE_BOOLEAN, (gpointer) &networking_enabled, + &err)) { + g_warning ("Writing to state file %s failed: (%d) %s.", + priv->state_file, + err ? err->code : -1, + (err && err->message) ? err->message : "unknown"); + } + + } + if (sleep) { nm_info ("Sleeping..."); @@ -2569,9 +2651,10 @@ nm_manager_start (NMManager *self) break; } - nm_info ("Wireless now %s by radio killswitch", - (priv->wireless_hw_enabled && we) ? "enabled" : "disabled"); - manager_set_wireless_enabled (self, we); + nm_info ("Wireless %s by radio killswitch; %s by state file", + (priv->wireless_hw_enabled && we) ? "enabled" : "disabled", + (priv->wireless_enabled) ? "enabled" : "disabled"); + manager_set_wireless_enabled (self, priv->wireless_enabled && we); system_unmanaged_devices_changed_cb (priv->sys_settings, NULL, self); system_hostname_changed_cb (priv->sys_settings, NULL, self); @@ -2589,7 +2672,12 @@ nm_manager_start (NMManager *self) } NMManager * -nm_manager_get (const char *config_file, const char *plugins, GError **error) +nm_manager_get (const char *config_file, + const char *plugins, + const char *state_file, + gboolean initial_net_enabled, + gboolean initial_wifi_enabled, + GError **error) { static NMManager *singleton = NULL; NMManagerPrivate *priv; @@ -2615,6 +2703,12 @@ nm_manager_get (const char *config_file, const char *plugins, GError **error) priv->config_file = g_strdup (config_file); + priv->state_file = g_strdup (state_file); + + priv->sleeping = !initial_net_enabled; + + priv->wireless_enabled = initial_wifi_enabled; + g_signal_connect (priv->sys_settings, "notify::" NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS, G_CALLBACK (system_unmanaged_devices_changed_cb), singleton); g_signal_connect (priv->sys_settings, "notify::" NM_SETTINGS_SYSTEM_INTERFACE_HOSTNAME, diff --git a/src/nm-manager.h b/src/nm-manager.h index 8e48574e65..a17323d42f 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -74,7 +74,12 @@ typedef struct { GType nm_manager_get_type (void); -NMManager *nm_manager_get (const char *config_file, const char *plugins, GError **error); +NMManager *nm_manager_get (const char *config_file, + const char *plugins, + const char *state_file, + gboolean initial_net_enabled, + gboolean initial_wifi_enabled, + GError **error); void nm_manager_start (NMManager *manager); From 02a77d2b63fce06275ce65e20213fc726bfe225d Mon Sep 17 00:00:00 2001 From: Jirka Klimes Date: Tue, 3 Nov 2009 16:07:43 -0800 Subject: [PATCH 11/13] core: validate pidfile and quit early if NM is already running (rh #517362) Also, don't delete the pidfile if it wasn't written out in this run of NM. Cleanups and simplifications by dcbw. --- src/NetworkManager.c | 77 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 8 deletions(-) diff --git a/src/NetworkManager.c b/src/NetworkManager.c index bb697c1ad5..a79d5c86ec 100644 --- a/src/NetworkManager.c +++ b/src/NetworkManager.c @@ -228,22 +228,77 @@ setup_signals (void) sigaction (SIGUSR1, &action, NULL); } -static void +static gboolean write_pidfile (const char *pidfile) { char pid[16]; int fd; + gboolean success = FALSE; - if ((fd = open (pidfile, O_CREAT|O_WRONLY|O_TRUNC, 00644)) < 0) - { + if ((fd = open (pidfile, O_CREAT|O_WRONLY|O_TRUNC, 00644)) < 0) { nm_warning ("Opening %s failed: %s", pidfile, strerror (errno)); - return; + return FALSE; } + snprintf (pid, sizeof (pid), "%d", getpid ()); if (write (fd, pid, strlen (pid)) < 0) nm_warning ("Writing to %s failed: %s", pidfile, strerror (errno)); + else + success = TRUE; + if (close (fd)) nm_warning ("Closing %s failed: %s", pidfile, strerror (errno)); + + return success; +} + +/* Check whether the pidfile already exists and contains PID of a running NetworkManager + * Returns: FALSE - specified pidfile doesn't exist or doesn't contain PID of a running NM process + * TRUE - specified pidfile already exists and contains PID of a running NM process + */ +static gboolean +check_pidfile (const char *pidfile) +{ + char *contents = NULL; + gsize len = 0; + glong pid; + char *proc_cmdline = NULL; + gboolean nm_running = FALSE; + const char *process_name; + + if (!g_file_get_contents (pidfile, &contents, &len, NULL)) + return FALSE; + + if (len <= 0) + goto done; + + errno = 0; + pid = strtol (contents, NULL, 10); + if (pid <= 0 || pid > 65536 || errno) + goto done; + + g_free (contents); + proc_cmdline = g_strdup_printf ("/proc/%ld/cmdline", pid); + if (!g_file_get_contents (proc_cmdline, &contents, &len, NULL)) + goto done; + + process_name = strrchr (contents, '/'); + if (process_name) + process_name++; + else + process_name = contents; + if (strcmp (process_name, "NetworkManager") == 0) { + /* Check that the process exists */ + if (kill (pid, 0) == 0) { + g_warning ("NetworkManager is already running (pid %ld)", pid); + nm_running = TRUE; + } + } + +done: + g_free (proc_cmdline); + g_free (contents); + return nm_running; } static gboolean @@ -380,6 +435,7 @@ main (int argc, char *argv[]) NMSupplicantManager *sup_mgr = NULL; NMDHCPManager *dhcp_mgr = NULL; GError *error = NULL; + gboolean wrote_pidfile = FALSE; GOptionEntry options[] = { { "no-daemon", 0, 0, G_OPTION_ARG_NONE, &become_daemon, "Don't become a daemon", NULL }, @@ -423,6 +479,12 @@ main (int argc, char *argv[]) exit (1); } + pidfile = g_strdup (user_pidfile ? user_pidfile : NM_DEFAULT_PID_FILE); + + /* check pid file */ + if (check_pidfile (pidfile)) + exit (1); + /* Parse the config file */ if (config) { if (!parse_config_file (config, &plugins, &error)) { @@ -455,8 +517,6 @@ main (int argc, char *argv[]) /* Not a hard failure */ } - pidfile = g_strdup (user_pidfile ? user_pidfile : NM_DEFAULT_PID_FILE); - /* Tricky: become_daemon is FALSE by default, so unless it's TRUE because * of a CLI option, it'll become TRUE after this */ @@ -471,7 +531,8 @@ main (int argc, char *argv[]) saved_errno); exit (1); } - write_pidfile (pidfile); + if (write_pidfile (pidfile)) + wrote_pidfile = TRUE; } if (g_fatal_warnings) { @@ -593,7 +654,7 @@ done: nm_logging_shutdown (); - if (pidfile) + if (pidfile && wrote_pidfile) unlink (pidfile); g_free (pidfile); From ea78b0af0ea3dd6654f2eeccc545e0a08a0ffe6c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 3 Nov 2009 17:40:36 -0800 Subject: [PATCH 12/13] core: don't leak 'ifindex' into PropertiesChanged signal It's not part of the D-Bus spec; so it shouldn't be exposed. --- src/nm-device-ethernet.c | 2 +- src/nm-device-wifi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index 637107d6d9..672a902c28 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -1850,7 +1850,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) "Ifindex", "Interface index", 0, G_MAXUINT32, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); /* Signals */ signals[PROPERTIES_CHANGED] = diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index f2c9350fd0..c9022f0645 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -3589,7 +3589,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) "Ifindex", "Interface index", 0, G_MAXUINT32, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); g_object_class_install_property (object_class, PROP_SCANNING, g_param_spec_boolean (NM_DEVICE_WIFI_SCANNING, From 0d05bc9a63597da04e44c0a6b38c38ac325384ef Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 4 Nov 2009 10:42:21 -0800 Subject: [PATCH 13/13] core: don't leak 'device-type' into PropertiesChanged signal The device type is set at object construction before the object is ever exported, thus the first time a client gets the value it will be correct, and the value should never change. As such, the property never needs to be part of PropertiesChanged signals. --- src/nm-device-interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nm-device-interface.c b/src/nm-device-interface.c index c8475df4ca..f53a6028b8 100644 --- a/src/nm-device-interface.c +++ b/src/nm-device-interface.c @@ -151,7 +151,7 @@ nm_device_interface_init (gpointer g_iface) "DeviceType", "DeviceType", 0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); g_object_interface_install_property (g_iface, g_param_spec_boolean (NM_DEVICE_INTERFACE_MANAGED,