[PATCH] core: add internet connectivity check



Hi,

here's the patch again. Forgot some code.


Cheers,

Tom

* use libsoup to check internet connectivity with
  a http request to a configurable uri and check
  the response code for 200
* do periodically check the connectivity. Check interval
  is configurable
* check connectivity when device state change
  from/to NM_DEVICE_STATE_ACTIVATED
---
 configure.ac          |    4 +
 src/Makefile.am       |    6 +-
 src/main.c            |   10 ++-
 src/nm-config.c       |   46 ++++++-
 src/nm-config.h       |    5 +
 src/nm-connectivity.c |  330 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/nm-connectivity.h |   68 ++++++++++
 src/nm-manager.c      |   83 +++++++++++--
 src/nm-manager.h      |    4 +-
 9 files changed, 536 insertions(+), 20 deletions(-)
 create mode 100644 src/nm-connectivity.c
 create mode 100644 src/nm-connectivity.h

diff --git a/configure.ac b/configure.ac
index ac6da48..dabb4f8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -266,6 +266,10 @@ PKG_CHECK_MODULES(GIO, gio-2.0)
 AC_SUBST(GIO_CFLAGS)
 AC_SUBST(GIO_LIBS)
 
+PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.26])
+AC_SUBST(LIBSOUP_CFLAGS)
+AC_SUBST(LIBSOUP_LIBS)
+
 GOBJECT_INTROSPECTION_CHECK([0.9.6])
 
 # Qt4
diff --git a/src/Makefile.am b/src/Makefile.am
index cbcfdc6..d8e9a2d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -177,7 +177,9 @@ NetworkManager_SOURCES = \
 		nm-dhcp6-config.h \
 		nm-rfkill.h \
 		nm-session-monitor.c \
-		nm-session-monitor.h
+		nm-session-monitor.h \
+		nm-connectivity.c \
+		nm-connectivity.h
 
 nm-access-point-glue.h: $(top_srcdir)/introspection/nm-access-point.xml
 	$(AM_V_GEN) dbus-binding-tool --prefix=nm_access_point --mode=glib-server --output=$@ $<
@@ -240,6 +242,7 @@ NetworkManager_CPPFLAGS = \
 	$(LIBNL_CFLAGS) \
 	$(GMODULE_CFLAGS) \
 	$(POLKIT_CFLAGS) \
+	$(LIBSOUP_CFLAGS) \
 	-DG_DISABLE_DEPRECATED \
 	-DBINDIR=\"$(bindir)\" \
 	-DSBINDIR=\"$(sbindir)\" \
@@ -279,6 +282,7 @@ NetworkManager_LDADD = \
 	$(LIBNL_LIBS) \
 	$(GMODULE_LIBS) \
 	$(POLKIT_LIBS) \
+	$(LIBSOUP_LIBS) \
 	$(LIBM) \
 	$(LIBDL)
 
diff --git a/src/main.c b/src/main.c
index b7c0fd5..5abae17 100644
--- a/src/main.c
+++ b/src/main.c
@@ -419,6 +419,8 @@ main (int argc, char *argv[])
 	char *pidfile = NULL, *state_file = NULL;
 	char *config_path = NULL, *plugins = NULL;
 	char *log_level = NULL, *log_domains = NULL;
+	char *connectivity_uri = NULL;
+	gint connectivity_interval = -1;
 	gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE, wimax_enabled = TRUE;
 	gboolean success, show_version = FALSE;
 	NMPolicy *policy = NULL;
@@ -447,6 +449,8 @@ main (int argc, char *argv[])
 		        "                                           WIFI_SCAN,IP4,IP6,AUTOIP4,DNS,VPN,SHARING,SUPPLICANT,\n"
 		        "                                           AGENTS,SETTINGS,SUSPEND,CORE,DEVICE,OLPC,WIMAX]",
 		        "HW,RFKILL,WIFI" },
+		{ "connectivity-uri", 0, 0, G_OPTION_ARG_STRING, &connectivity_uri, "A http(s) address to check internet connectivity. eg http://example.com"; },
+		{ "connectivity-interval", 0, 0, G_OPTION_ARG_INT, &connectivity_interval, "the interval in seconds how often a connectivity check will be done" },
 		{NULL}
 	};
 
@@ -501,7 +505,8 @@ main (int argc, char *argv[])
 		exit (1);
 
 	/* Read the config file and CLI overrides */
-	config = nm_config_new (config_path, plugins, log_level, log_domains, &error);
+	config = nm_config_new (config_path, plugins, log_level, log_domains,
+							connectivity_uri, connectivity_interval,  &error);
 	if (config == NULL) {
 		fprintf (stderr, "Failed to read configuration: (%d) %s\n",
 		         error ? error->code : -1,
@@ -626,6 +631,8 @@ main (int argc, char *argv[])
 	                          wifi_enabled,
 	                          wwan_enabled,
 	                          wimax_enabled,
+                              nm_config_get_connectivity_uri (config),
+							  nm_config_get_connectivity_interval (config),
 	                          &error);
 	if (manager == NULL) {
 		nm_log_err (LOGD_CORE, "failed to initialize the network manager: %s",
@@ -716,6 +723,7 @@ done:
 	g_free (plugins);
 	g_free (log_level);
 	g_free (log_domains);
+	g_free (connectivity_uri);
 
 	nm_log_info (LOGD_CORE, "exiting (%s)", success ? "success" : "error");
 	exit (success ? 0 : 1);
diff --git a/src/nm-config.c b/src/nm-config.c
index 71f67d5..04c50f4 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -34,6 +34,8 @@ struct NMConfig {
 	char **dns_plugins;
 	char *log_level;
 	char *log_domains;
+	char *connectivity_uri;
+	guint connectivity_interval;
 };
 
 /************************************************************************/
@@ -116,6 +118,23 @@ nm_config_get_log_domains (NMConfig *config)
 	return config->log_domains;
 }
 
+const char *
+nm_config_get_connectivity_uri (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return config->connectivity_uri;
+}
+
+const guint
+nm_config_get_connectivity_interval (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, -1);
+
+	return config->connectivity_interval;
+}
+
+
 /************************************************************************/
 
 static gboolean
@@ -124,6 +143,8 @@ fill_from_file (NMConfig *config,
                 const char *cli_plugins,
                 const char *cli_log_level,
                 const char *cli_log_domains,
+                const char *cli_connectivity_uri,
+                const gint cli_connectivity_interval,
                 GError **error)
 {
 	GKeyFile *kf;
@@ -163,6 +184,17 @@ fill_from_file (NMConfig *config,
 			config->log_domains = g_strdup (cli_log_domains);
 		else
 			config->log_domains = g_key_file_get_value (kf, "logging", "domains", NULL);
+
+		if (cli_connectivity_uri && strlen (cli_connectivity_uri))
+			config->connectivity_uri = g_strdup (cli_connectivity_uri);
+		else
+			config->connectivity_uri = g_key_file_get_value (kf, "connectivity", "uri", NULL);
+
+		if (cli_connectivity_interval >= 0)
+			config->connectivity_interval = cli_connectivity_interval;
+		else
+			config->connectivity_interval = g_key_file_get_integer (kf, "connectivity", "interval", NULL);
+
 		success = TRUE;
 	}
 
@@ -175,6 +207,8 @@ nm_config_new (const char *cli_config_path,
                const char *cli_plugins,
                const char *cli_log_level,
                const char *cli_log_domains,
+               const char *cli_connectivity_uri,
+               const gint cli_connectivity_interval,
                GError **error)
 {
 	NMConfig *config;
@@ -184,7 +218,8 @@ nm_config_new (const char *cli_config_path,
 
 	if (cli_config_path) {
 		/* Bad user-specific config file path is a hard error */
-		if (!fill_from_file (config, cli_config_path, cli_plugins, cli_log_level, cli_log_domains, error)) {
+		if (!fill_from_file (config, cli_config_path, cli_plugins, cli_log_level, cli_log_domains,
+							 cli_connectivity_uri, cli_connectivity_interval, error)) {
 			nm_config_free (config);
 			return NULL;
 		}
@@ -199,7 +234,8 @@ nm_config_new (const char *cli_config_path,
 	 */
 
 	/* Try deprecated nm-system-settings.conf first */
-	if (fill_from_file (config, NM_OLD_SYSTEM_CONF_FILE, cli_plugins, cli_log_level, cli_log_domains, &local))
+	if (fill_from_file (config, NM_OLD_SYSTEM_CONF_FILE, cli_plugins, cli_log_level, cli_log_domains,
+						cli_connectivity_uri, cli_connectivity_interval, &local))
 		return config;
 
 	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND) == FALSE) {
@@ -211,7 +247,8 @@ nm_config_new (const char *cli_config_path,
 	g_clear_error (&local);
 
 	/* Try the standard config file location next */
-	if (fill_from_file (config, NM_DEFAULT_SYSTEM_CONF_FILE, cli_plugins, cli_log_level, cli_log_domains, &local))
+	if (fill_from_file (config, NM_DEFAULT_SYSTEM_CONF_FILE, cli_plugins, cli_log_level, cli_log_domains,
+						cli_connectivity_uri, cli_connectivity_interval, &local))
 		return config;
 
 	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND) == FALSE) {
@@ -226,6 +263,7 @@ nm_config_new (const char *cli_config_path,
 
 	/* ignore error if config file not found */
 	g_clear_error (&local);
+
 	return config;
 }
 
@@ -240,7 +278,7 @@ nm_config_free (NMConfig *config)
 	g_strfreev (config->dns_plugins);
 	g_free (config->log_level);
 	g_free (config->log_domains);
-
+	g_free (config->connectivity_uri);
 	memset (config, 0, sizeof (*config));
 	g_free (config);
 }
diff --git a/src/nm-config.h b/src/nm-config.h
index fae344f..c71b5be 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -40,6 +40,8 @@ NMConfig *nm_config_new (const char *cli_config_path,
                          const char *cli_plugins,
                          const char *cli_log_level,
                          const char *cli_log_domains,
+                         const char *cli_connectivity_check_uri,
+                         const gint connectivity_check_interval,
                          GError **error);
 
 const char *nm_config_get_path (NMConfig *config);
@@ -48,6 +50,9 @@ const char *nm_config_get_dhcp_client (NMConfig *config);
 const char **nm_config_get_dns_plugins (NMConfig *config);
 const char *nm_config_get_log_level (NMConfig *config);
 const char *nm_config_get_log_domains (NMConfig *config);
+const char *nm_config_get_connectivity_uri (NMConfig *config);
+const guint nm_config_get_connectivity_interval (NMConfig *config);
+
 
 void nm_config_free (NMConfig *config);
 
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
new file mode 100644
index 0000000..1c49991
--- /dev/null
+++ b/src/nm-connectivity.c
@@ -0,0 +1,330 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2011 Thomas Bechtold <thomasbechtold jpberlin de>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <libsoup/soup.h>
+
+#include "nm-connectivity.h"
+#include "nm-logging.h"
+#include "nm-manager.h"
+
+
+typedef struct {
+	//used for http requests
+	SoupSession *soup_session;
+	//indicates if a connectivity check is currently running
+	gboolean check_running;
+	//the uri to check
+	const gchar *check_uri;
+	//seconds when a check will be repeated
+	guint check_interval;
+	//indicates if the last connection check was successful
+	gboolean connected;
+	//the source id for the periodic check
+	guint check_interval_source_id;
+
+} NMConnectivityPrivate;
+
+G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT)
+
+#define NM_CONNECTIVITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONNECTIVITY, NMConnectivityPrivate))
+
+
+enum {
+	CONNECTED_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+	PROP_0,
+	PROP_CHECK_RUNNING,
+	PROP_CHECK_URI,
+	PROP_CHECK_INTERVAL,
+	PROP_CONNECTED,
+	LAST_PROP
+};
+
+
+static gboolean nm_connectivity_interval (NMConnectivity *connectivity)
+{
+	/* periodically check connectivity */
+	nm_connectivity_check (connectivity);
+	return TRUE;
+}
+
+
+static void
+nm_connectivity_check_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+	NMConnectivity *connectivity;
+	NMConnectivityPrivate *priv;
+	guint status_code;
+	SoupURI *soup_uri;
+	gboolean connected_new;
+
+	g_return_if_fail (NM_IS_CONNECTIVITY (user_data));
+	connectivity = NM_CONNECTIVITY (user_data);
+	priv = NM_CONNECTIVITY_GET_PRIVATE (connectivity);
+
+	g_object_get (msg, SOUP_MESSAGE_STATUS_CODE, &status_code, NULL);
+	soup_uri = soup_message_get_uri (msg);
+	/* just check connectivity_code */
+	if (status_code == SOUP_STATUS_OK) {
+		nm_log_dbg (LOGD_CORE, "Connectivity check for '%s' successful. status code is: %i", soup_uri_to_string (soup_uri, FALSE), status_code);
+		connected_new = TRUE;
+	} else {
+		nm_log_dbg (LOGD_CORE, "Connectivity check for '%s' not successful. status code is: %i", soup_uri_to_string (soup_uri, FALSE), status_code);
+		connected_new = FALSE;
+	}
+
+	/* update connectivity and emit signal */
+	if (priv->connected != connected_new) {
+			priv->connected = connected_new;
+			g_object_notify (G_OBJECT (connectivity), NM_CONNECTIVITY_CONNECTED);
+			g_signal_emit_by_name (connectivity, NM_CONNECTIVITY_SIGNAL_CONNECTED_CHANGED, priv->connected);
+		}
+
+	priv->check_running = FALSE;
+	g_object_notify (G_OBJECT (connectivity), NM_CONNECTIVITY_CHECK_RUNNING);
+}
+
+
+void
+nm_connectivity_check (NMConnectivity *connectivity)
+{
+	NMConnectivityPrivate *priv;
+	SoupURI *soup_uri;
+	SoupMessage *connectivity_check_msg;
+
+	g_return_if_fail (NM_IS_CONNECTIVITY (connectivity));
+	priv = NM_CONNECTIVITY_GET_PRIVATE (connectivity);
+
+	if (priv->check_running) return;
+
+	if (priv->check_uri && strlen (priv->check_uri) > 0) {
+		/* check given url async */
+		soup_uri = soup_uri_new (priv->check_uri);
+		if (SOUP_URI_VALID_FOR_HTTP (soup_uri)) {
+			connectivity_check_msg = soup_message_new_from_uri ("GET", soup_uri);
+			soup_session_queue_message (priv->soup_session, connectivity_check_msg, nm_connectivity_check_cb, connectivity);
+
+			priv->check_running = TRUE;
+			g_object_notify (G_OBJECT (connectivity), NM_CONNECTIVITY_CHECK_RUNNING);
+			nm_log_dbg (LOGD_CORE, "connectivity check with uri '%s' started.", priv->check_uri);
+		}
+		else {
+			nm_log_warn (LOGD_CORE, "Invalid uri '%s' for connectivity check.", soup_uri_to_string (soup_uri, FALSE));
+		}
+		soup_uri_free (soup_uri);
+	}
+	else {
+		/* no check uri given - default is connected so nm-manager can set NMState to GLOBAL */
+		if (!priv->connected)
+		{
+			priv->connected = TRUE;
+			g_object_notify (G_OBJECT (connectivity), NM_CONNECTIVITY_CONNECTED);
+			g_signal_emit_by_name (connectivity, NM_CONNECTIVITY_SIGNAL_CONNECTED_CHANGED, priv->connected);
+		}
+	}
+}
+
+
+NMConnectivity*
+nm_connectivity_new (const gchar *check_uri, guint check_interval)
+{
+	NMConnectivity *connectivity = g_object_new (NM_TYPE_CONNECTIVITY, NULL);
+
+	NMConnectivityPrivate *priv;
+	priv = NM_CONNECTIVITY_GET_PRIVATE (connectivity);
+
+	priv->check_uri = check_uri;
+	priv->check_interval = check_interval;
+
+	if (check_uri && strlen (check_uri) && check_interval > 0)
+		priv->check_interval_source_id = g_timeout_add_seconds (check_interval, (GSourceFunc) nm_connectivity_interval, connectivity);
+	else
+		priv->check_interval_source_id = 0;
+
+	return connectivity;
+}
+
+
+static void
+nm_connectivity_set_property (GObject *object, guint property_id,
+							  const GValue *value, GParamSpec *pspec)
+{
+	NMConnectivity *self = NM_CONNECTIVITY (object);
+	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+
+	switch (property_id)
+    {
+	case PROP_CHECK_RUNNING:
+		priv->check_running = g_value_get_boolean (value);
+		break;
+	case PROP_CHECK_URI:
+		priv->check_uri = g_value_get_string (value);
+		break;
+	case PROP_CHECK_INTERVAL:
+		priv->check_interval = g_value_get_uint (value);
+		break;
+	case PROP_CONNECTED:
+		priv->connected = g_value_get_boolean (value);
+		break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+nm_connectivity_get_property (GObject *object, guint property_id,
+							  GValue *value, GParamSpec *pspec)
+{
+	NMConnectivity *self = NM_CONNECTIVITY (object);
+	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+
+	switch (property_id)
+    {
+	case PROP_CHECK_RUNNING:
+		g_value_set_boolean (value, priv->check_running);
+		break;
+	case PROP_CHECK_URI:
+		g_value_set_static_string (value, priv->check_uri);
+		break;
+	case PROP_CHECK_INTERVAL:
+		g_value_set_uint (value, priv->check_interval);
+		break;
+	case PROP_CONNECTED:
+		g_value_set_boolean (value, priv->connected);
+		break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+
+
+static void
+nm_connectivity_init (NMConnectivity *self)
+{
+	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+	priv->soup_session = soup_session_async_new ();
+	priv->check_running = FALSE;
+	priv->connected = FALSE;
+	priv->check_uri = NULL;
+	priv->check_interval = 0;
+	priv->check_interval_source_id = 0;
+}
+
+
+static void
+nm_connectivity_dispose (GObject *object)
+{
+	NMConnectivity *connectivity = NM_CONNECTIVITY (object);
+	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (connectivity);
+
+	if (priv->soup_session)
+	{
+		soup_session_abort (priv->soup_session);
+		g_object_unref (priv->soup_session);
+		priv->soup_session = NULL;
+	}
+
+	priv->check_running = FALSE;
+	priv->connected = FALSE;
+
+	priv->check_uri = NULL;
+	priv->check_interval = 0;
+
+	if (priv->check_interval_source_id > 0)
+	{
+		g_warn_if_fail (g_source_remove (priv->check_interval_source_id) == TRUE);
+		priv->check_interval_source_id = 0;
+	}
+}
+
+
+static void
+nm_connectivity_class_init (NMConnectivityClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	g_type_class_add_private (klass, sizeof (NMConnectivityPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nm_connectivity_set_property;
+	object_class->get_property = nm_connectivity_get_property;
+	object_class->dispose = nm_connectivity_dispose;
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_CHECK_RUNNING,
+		 g_param_spec_string (NM_CONNECTIVITY_CHECK_RUNNING,
+		                      "Running",
+		                      "Is Connectivity chunk running",
+		                      NULL,
+		                      G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_CHECK_URI,
+		 g_param_spec_string (NM_CONNECTIVITY_CHECK_URI,
+		                      "URI",
+		                      "Connectivity check URI",
+		                      NULL,
+		                      G_PARAM_READWRITE));
+	g_object_class_install_property
+		(object_class, PROP_CHECK_INTERVAL,
+		 g_param_spec_uint (NM_CONNECTIVITY_CHECK_INTERVAL,
+							"Interval",
+							"Connectivity check interval in seconds",
+							0,
+							G_MAXUINT,
+							300,
+							G_PARAM_READWRITE));
+	g_object_class_install_property
+		(object_class, PROP_CONNECTED,
+		 g_param_spec_string (NM_CONNECTIVITY_CONNECTED,
+		                      "Connected",
+		                      "Is connected",
+		                      NULL,
+		                      G_PARAM_READABLE));
+
+	/* signals */
+	signals[CONNECTED_CHANGED] =
+		g_signal_new (NM_CONNECTIVITY_SIGNAL_CONNECTED_CHANGED,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMConnectivityClass, connected_changed),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__BOOLEAN,
+		              G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+
+
+gboolean
+nm_connectivity_get_connected (NMConnectivity *connectivity)
+{
+	g_return_val_if_fail (NM_IS_CONNECTIVITY (connectivity), FALSE);
+	return NM_CONNECTIVITY_GET_PRIVATE (connectivity)->connected;
+}
diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h
new file mode 100644
index 0000000..8e54844
--- /dev/null
+++ b/src/nm-connectivity.h
@@ -0,0 +1,68 @@
+
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2011 Thomas Bechtold <thomasbechtold jpberlin de>
+ */
+
+#ifndef NM_CONNECTIVITY_H
+#define NM_CONNECTIVITY_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "NetworkManager.h"
+
+#define NM_TYPE_CONNECTIVITY            (nm_connectivity_get_type ())
+#define NM_CONNECTIVITY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONNECTIVITY, NMConnectivity))
+#define NM_CONNECTIVITY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CONNECTIVITY, NMConnectivityClass))
+#define NM_IS_CONNECTIVITY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CONNECTIVITY))
+#define NM_IS_CONNECTIVITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_CONNECTIVITY))
+#define NM_CONNECTIVITY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONNECTIVITY, NMConnectivityClass))
+
+
+#define NM_CONNECTIVITY_CHECK_RUNNING "check-running"
+#define NM_CONNECTIVITY_CHECK_URI "check-uri"
+#define NM_CONNECTIVITY_CHECK_INTERVAL "check-interval"
+#define NM_CONNECTIVITY_CONNECTED "connected"
+
+
+#define NM_CONNECTIVITY_SIGNAL_CONNECTED_CHANGED "connected-changed"
+
+
+typedef struct {
+	GObject parent;
+} NMConnectivity;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*connected_changed) (NMConnectivity *connectivity, gboolean connected);
+} NMConnectivityClass;
+
+GType nm_connectivity_get_type (void);
+
+
+NMConnectivity *nm_connectivity_new (const gchar *check_uri, guint check_interval);
+
+void nm_connectivity_check (NMConnectivity *connectivity);
+
+gboolean nm_connectivity_get_connected (NMConnectivity *connectivity);
+
+#endif /* NM_CONNECTIVITY_H */
+
diff --git a/src/nm-manager.c b/src/nm-manager.c
index b09cceb..27f1d71 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -64,6 +64,8 @@
 #include "nm-manager-auth.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
+#include "nm-connectivity.h"
+
 
 #define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
 #define NM_AUTOIP_DBUS_IFACE   "org.freedesktop.nm_avahi_autoipd"
@@ -200,6 +202,7 @@ typedef struct {
 
 	GSList *devices;
 	NMState state;
+	NMConnectivity *connectivity;
 
 	NMDBusManager *dbus_mgr;
 	NMUdevManager *udev_mgr;
@@ -457,21 +460,25 @@ nm_manager_update_state (NMManager *manager)
 	if (manager_sleeping (manager))
 		new_state = NM_STATE_ASLEEP;
 	else {
-		for (iter = priv->devices; iter; iter = iter->next) {
-			NMDevice *dev = NM_DEVICE (iter->data);
-			NMDeviceState state = nm_device_get_state (dev);
+		if (nm_connectivity_get_connected (priv->connectivity))
+			new_state = NM_STATE_CONNECTED_GLOBAL;
+		else {
+			for (iter = priv->devices; iter; iter = iter->next) {
+				NMDevice *dev = NM_DEVICE (iter->data);
+				NMDeviceState state = nm_device_get_state (dev);
 
-			if (state == NM_DEVICE_STATE_ACTIVATED) {
-				/* FIXME: handle local-only and site too */
-				new_state = NM_STATE_CONNECTED_GLOBAL;
-				break;
-			}
+				if (state == NM_DEVICE_STATE_ACTIVATED) {
+					/* FIXME: handle local-only too */
+					new_state = NM_STATE_CONNECTED_SITE;
+					break;
+				}
 
-			if (nm_device_is_activating (dev))
-				new_state = NM_STATE_CONNECTING;
-			else if (new_state != NM_STATE_CONNECTING) {
-				if (state == NM_DEVICE_STATE_DEACTIVATING)
-					new_state = NM_STATE_DISCONNECTING;
+				if (nm_device_is_activating (dev))
+					new_state = NM_STATE_CONNECTING;
+				else if (new_state != NM_STATE_CONNECTING) {
+					if (state == NM_DEVICE_STATE_DEACTIVATING)
+						new_state = NM_STATE_DISCONNECTING;
+				}
 			}
 		}
 	}
@@ -492,6 +499,7 @@ manager_device_state_changed (NMDevice *device,
                               gpointer user_data)
 {
 	NMManager *manager = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
 
 	switch (new_state) {
 	case NM_DEVICE_STATE_UNMANAGED:
@@ -507,6 +515,12 @@ manager_device_state_changed (NMDevice *device,
 
 	nm_manager_update_state (manager);
 
+	/* trigger a connectivity check */
+	if (new_state == NM_DEVICE_STATE_ACTIVATED || old_state == NM_DEVICE_STATE_ACTIVATED)
+	{
+		nm_connectivity_check (priv->connectivity);
+	}
+
 	if (new_state == NM_DEVICE_STATE_ACTIVATED) {
 		NMActRequest *req;
 
@@ -2858,6 +2872,36 @@ handle_firmware_changed (gpointer user_data)
 }
 
 static void
+connectivity_connected_changed (NMConnectivity *connectivity, gboolean connected, gpointer user_data)
+{
+	NMManager *manager;
+	NMManagerPrivate *priv;
+	NMState new_state;
+	g_return_if_fail (NM_IS_MANAGER (user_data));
+
+	manager = NM_MANAGER (user_data);
+	priv = NM_MANAGER_GET_PRIVATE (manager);
+
+	new_state = NM_STATE_DISCONNECTED;
+
+	if (connected)
+		new_state = NM_STATE_CONNECTED_GLOBAL;
+	else
+	{
+		/* FIXME: handle local here, too */
+		new_state = NM_STATE_CONNECTED_SITE;
+	}
+
+	if (priv->state != new_state) {
+		priv->state = new_state;
+		g_object_notify (G_OBJECT (manager), NM_MANAGER_STATE);
+
+		g_signal_emit (manager, signals[STATE_CHANGED], 0, priv->state);
+		nm_log_dbg (LOGD_CORE, "connectivity changed to: %i", connected);
+	}
+}
+
+static void
 firmware_dir_changed (GFileMonitor *monitor,
                       GFile *file,
                       GFile *other_file,
@@ -3058,6 +3102,8 @@ nm_manager_new (NMSettings *settings,
                 gboolean initial_wifi_enabled,
                 gboolean initial_wwan_enabled,
                 gboolean initial_wimax_enabled,
+                const gchar *connectivity_uri,
+                gint connectivity_interval,
                 GError **error)
 {
 	NMManagerPrivate *priv;
@@ -3073,6 +3119,11 @@ nm_manager_new (NMSettings *settings,
 
 	priv = NM_MANAGER_GET_PRIVATE (singleton);
 
+	priv->connectivity = nm_connectivity_new (connectivity_uri, connectivity_interval);
+
+	g_signal_connect (priv->connectivity, NM_CONNECTIVITY_SIGNAL_CONNECTED_CHANGED,
+	                  G_CALLBACK (connectivity_connected_changed), singleton);
+
 	bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
 	g_assert (bus);
 	dbus_connection = dbus_g_connection_get_connection (bus);
@@ -3171,6 +3222,11 @@ dispose (GObject *object)
 		                                   TRUE);
 	}
 
+	if (priv->connectivity) {
+		g_object_unref (priv->connectivity);
+		priv->connectivity = NULL;
+	}
+
 	g_free (priv->hostname);
 
 	g_object_unref (priv->settings);
@@ -3491,6 +3547,7 @@ nm_manager_init (NMManager *manager)
 
 	priv->sleeping = FALSE;
 	priv->state = NM_STATE_DISCONNECTED;
+	priv->connectivity = NULL;
 
 	priv->dbus_mgr = nm_dbus_manager_get ();
 
diff --git a/src/nm-manager.h b/src/nm-manager.h
index b044971..b65e770 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -73,7 +73,9 @@ NMManager *nm_manager_new (NMSettings *settings,
                            gboolean initial_net_enabled,
                            gboolean initial_wifi_enabled,
                            gboolean initial_wwan_enabled,
-						   gboolean initial_wimax_enabled,
+                           gboolean initial_wimax_enabled,
+                           const gchar *connectivity_uri,
+                           gint connectivity_interval,
                            GError **error);
 
 NMManager *nm_manager_get (void);
-- 
1.7.5.4



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]