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), ...]
+
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
+
+
+
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;
+}
+
diff --git a/src/NetworkManager.c b/src/NetworkManager.c
index f14c6cbf07..a79d5c86ec 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
@@ -227,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
@@ -269,6 +325,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 +425,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;
@@ -289,11 +435,13 @@ 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 },
{ "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}
@@ -331,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)) {
@@ -352,7 +506,16 @@ main (int argc, char *argv[])
}
}
- pidfile = g_strdup (user_pidfile ? user_pidfile : NM_DEFAULT_PID_FILE);
+ 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 */
+ }
/* Tricky: become_daemon is FALSE by default, so unless it's TRUE because
* of a CLI option, it'll become TRUE after this
@@ -368,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) {
@@ -419,7 +583,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)");
@@ -490,7 +654,7 @@ done:
nm_logging_shutdown ();
- if (pidfile)
+ if (pidfile && wrote_pidfile)
unlink (pidfile);
g_free (pidfile);
diff --git a/src/NetworkManagerPolicy.c b/src/NetworkManagerPolicy.c
index cd10b493f5..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);
@@ -711,6 +709,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)
{
@@ -770,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);
@@ -992,10 +1008,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/modem-manager/nm-modem.c b/src/modem-manager/nm-modem.c
index a2b37fb0d8..a5ed6d8482 100644
--- a/src/modem-manager/nm-modem.c
+++ b/src/modem-manager/nm-modem.c
@@ -135,6 +135,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;
/* Work around a PPP bug (#1732) which causes many mobile broadband
* providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers.
@@ -162,11 +163,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);
}
priv->pending_ip4_config = g_object_ref (config);
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;
}
diff --git a/src/nm-device-cdma.c b/src/nm-device-cdma.c
index 78f5ed3351..4e8acb9ed8 100644
--- a/src/nm-device-cdma.c
+++ b/src/nm-device-cdma.c
@@ -120,6 +120,8 @@ real_connection_secrets_updated (NMDevice *device,
NMDeviceCdmaPrivate *priv = NM_DEVICE_CDMA_GET_PRIVATE (device);
NMActRequest *req;
+ g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (device)));
+
req = nm_device_get_act_request (device);
g_assert (req);
diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c
index 6a75cba1fd..672a902c28 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;
@@ -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-gsm.c b/src/nm-device-gsm.c
index 4f738ec3a5..bd717c2ca1 100644
--- a/src/nm-device-gsm.c
+++ b/src/nm-device-gsm.c
@@ -120,6 +120,8 @@ real_connection_secrets_updated (NMDevice *device,
NMDeviceGsmPrivate *priv = NM_DEVICE_GSM_GET_PRIVATE (device);
NMActRequest *req;
+ g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (device)));
+
req = nm_device_get_act_request (device);
g_assert (req);
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,
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,
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-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,
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 44e4b56e2a..0b0814d192 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -151,6 +151,7 @@ typedef struct {
typedef struct {
char *config_file;
+ char *state_file;
GSList *devices;
NMState state;
@@ -216,6 +217,7 @@ enum {
/* Not exported */
PROP_HOSTNAME,
+ PROP_SLEEPING,
LAST_PROP
};
@@ -1102,11 +1104,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;
@@ -1119,6 +1172,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;
@@ -2401,6 +2467,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...");
@@ -2432,6 +2515,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;
}
@@ -2569,9 +2654,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 +2675,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 +2706,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,
@@ -2764,6 +2861,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;
@@ -2887,6 +2987,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..a17323d42f 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;
@@ -73,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);
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 */