From 267bc993a710ec3923b6f243cb6a4b047fa49ec4 Mon Sep 17 00:00:00 2001 From: Thomas Bechtold Date: Fri, 21 Oct 2011 21:21:30 +0200 Subject: [PATCH] core: add internet connectivity check * use libsoup to compare a http response from a given uri with a given response (use g_str_has_prefix () to compare) * 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 | 8 +- src/main.c | 14 +- src/nm-config.c | 65 +++++++- src/nm-config.h | 6 + src/nm-connectivity.c | 349 ++++++++++++++++++++++++++++++++++++++++++ src/nm-connectivity.h | 68 ++++++++ src/nm-manager.c | 81 ++++++++-- src/nm-manager.h | 5 +- 9 files changed, 580 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 52c9dcf041..ce3ce9d7c2 100644 --- a/configure.ac +++ b/configure.ac @@ -288,6 +288,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 c814e0af6a..2540e51a17 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -190,7 +190,11 @@ NetworkManager_SOURCES = \ nm-rfkill.h \ nm-session-monitor.h \ nm-session-utils.c \ - nm-session-utils.h + nm-session-utils.h \ + nm-session-monitor.c \ + nm-session-monitor.h \ + nm-connectivity.c \ + nm-connectivity.h if SESSION_TRACKING_SYSTEMD NetworkManager_SOURCES += nm-session-monitor-systemd.c @@ -272,6 +276,7 @@ NetworkManager_CPPFLAGS = \ $(GMODULE_CFLAGS) \ $(POLKIT_CFLAGS) \ $(SYSTEMD_CFLAGS) \ + $(LIBSOUP_CFLAGS) \ -DG_DISABLE_DEPRECATED \ -DBINDIR=\"$(bindir)\" \ -DSBINDIR=\"$(sbindir)\" \ @@ -307,6 +312,7 @@ NetworkManager_LDADD = \ $(GMODULE_LIBS) \ $(POLKIT_LIBS) \ $(SYSTEMD_LIBS) \ + $(LIBSOUP_LIBS) \ $(LIBM) \ $(LIBDL) diff --git a/src/main.c b/src/main.c index 1246336b62..4a21959f5a 100644 --- a/src/main.c +++ b/src/main.c @@ -346,6 +346,9 @@ 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; + char *connectivity_response = NULL; gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE, wimax_enabled = TRUE; gboolean success, show_version = FALSE; NMPolicy *policy = NULL; @@ -379,6 +382,9 @@ main (int argc, char *argv[]) " AGENTS,SETTINGS,SUSPEND,CORE,DEVICE,OLPC,WIMAX,\n" " INFINIBAND,FIREWALL]"), "HW,RFKILL,WIFI" }, + { "connectivity-uri", 0, 0, G_OPTION_ARG_STRING, &connectivity_uri, "A http(s) address to check internet connectivity" }, + { "connectivity-interval", 0, 0, G_OPTION_ARG_INT, &connectivity_interval, "the interval in seconds how often a connectivity check will be done" }, + { "connectivity-response", 0, 0, G_OPTION_ARG_STRING, &connectivity_response, "the expected start of the response" }, {NULL} }; @@ -436,7 +442,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, connectivity_response, &error); if (config == NULL) { fprintf (stderr, _("Failed to read configuration: (%d) %s\n"), error ? error->code : -1, @@ -566,6 +573,9 @@ main (int argc, char *argv[]) wifi_enabled, wwan_enabled, wimax_enabled, + nm_config_get_connectivity_uri (config), + nm_config_get_connectivity_interval (config), + nm_config_get_connectivity_response (config), &error); if (manager == NULL) { nm_log_err (LOGD_CORE, "failed to initialize the network manager: %s", @@ -666,6 +676,8 @@ done: g_free (plugins); g_free (log_level); g_free (log_domains); + g_free (connectivity_uri); + g_free (connectivity_response); 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 aa89b17849..f64b8bdbc7 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -34,6 +34,9 @@ struct NMConfig { char **dns_plugins; char *log_level; char *log_domains; + char *connectivity_uri; + guint connectivity_interval; + char *connectivity_response; }; /************************************************************************/ @@ -97,6 +100,31 @@ 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; +} + +const char * +nm_config_get_connectivity_response (NMConfig *config) +{ + g_return_val_if_fail (config != NULL, NULL); + + return config->connectivity_response; +} + + /************************************************************************/ static gboolean @@ -105,6 +133,9 @@ 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, + const char *cli_connectivity_response, GError **error) { GKeyFile *kf; @@ -144,6 +175,22 @@ 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); + + if (cli_connectivity_response && strlen (cli_connectivity_response)) + config->connectivity_response = g_strdup (cli_connectivity_response); + else + config->connectivity_response = g_key_file_get_value (kf, "connectivity", "response", NULL); + success = TRUE; } @@ -156,6 +203,9 @@ 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, + const char *cli_connectivity_response, GError **error) { NMConfig *config; @@ -165,7 +215,9 @@ 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, cli_connectivity_response, + error)) { nm_config_free (config); return NULL; } @@ -180,7 +232,9 @@ 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, cli_connectivity_response, + &local)) return config; if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND) == FALSE) { @@ -192,7 +246,9 @@ 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, cli_connectivity_response, + &local)) return config; if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND) == FALSE) { @@ -230,7 +286,8 @@ 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); + g_free (config->connectivity_response); memset (config, 0, sizeof (*config)); g_free (config); } diff --git a/src/nm-config.h b/src/nm-config.h index 9abff58719..cce89dddc2 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -38,6 +38,9 @@ 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, + const char *cli_connectivity_check_response, GError **error); const char *nm_config_get_path (NMConfig *config); @@ -46,6 +49,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); +const char *nm_config_get_connectivity_response (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 0000000000..07f736c6f4 --- /dev/null +++ b/src/nm-connectivity.c @@ -0,0 +1,349 @@ +/* -*- 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 + */ + +#include + +#include +#include + +#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; + /* the expected response for the connectivity check */ + const gchar *check_response; + /* 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_CHECK_RESPONSE, + 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; + 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); + + soup_uri = soup_message_get_uri (msg); + + /* check response */ + if (msg->response_body->data && (g_str_has_prefix (msg->response_body->data, priv->check_response))) { + nm_log_dbg (LOGD_CORE, "Connectivity check for uri '%s' with expected response '%s' successful.", soup_uri_to_string (soup_uri, FALSE), priv->check_response); + connected_new = TRUE; + } else { + nm_log_dbg (LOGD_CORE, "Connectivity check for uri '%s' with expected response '%s' failed.", + soup_uri_to_string (soup_uri, FALSE), priv->check_response); + 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) && priv->check_response && strlen (priv->check_response)) { + /* check given url async */ + soup_uri = soup_uri_new (priv->check_uri); + if (soup_uri && 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); + soup_uri_free (soup_uri); + } + else { + nm_log_err (LOGD_CORE, "Invalid uri '%s' for connectivity check.", priv->check_uri); + } + } + else { + /* no uri/response 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, const gchar *check_response) +{ + 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; + priv->check_response = check_response; + + 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_CHECK_RESPONSE: + priv->check_response = g_value_get_string (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_CHECK_RESPONSE: + g_value_set_static_string (value, priv->check_response); + 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; + priv->check_response = NULL; +} + + +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; + priv->check_response = NULL; + + 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_CHECK_RESPONSE, + g_param_spec_string (NM_CONNECTIVITY_CHECK_RESPONSE, + "REsponse", + "Connectivity check reponse", + NULL, + 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 0000000000..14ba89aff9 --- /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 + */ + +#ifndef NM_CONNECTIVITY_H +#define NM_CONNECTIVITY_H + +#include +#include + +#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_CHECK_RESPONSE "check-response" +#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, const gchar *check_response); + +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 8914730cd9..971fa93c44 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -67,6 +67,8 @@ #include "nm-device-factory.h" #include "wifi-utils.h" #include "nm-enum-types.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" @@ -194,6 +196,7 @@ typedef struct { GSList *devices; NMState state; + NMConnectivity *connectivity; NMDBusManager *dbus_mgr; NMUdevManager *udev_mgr; @@ -424,21 +427,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; + } } } } @@ -459,6 +466,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: @@ -473,6 +481,10 @@ 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); } /* Removes a device from a device list; returns the start of the new device list */ @@ -3341,6 +3353,36 @@ handle_firmware_changed (gpointer user_data) return FALSE; } +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, @@ -3542,6 +3584,9 @@ nm_manager_new (NMSettings *settings, gboolean initial_wifi_enabled, gboolean initial_wwan_enabled, gboolean initial_wimax_enabled, + const gchar *connectivity_uri, + gint connectivity_interval, + const gchar *connectivity_response, GError **error) { NMManagerPrivate *priv; @@ -3557,6 +3602,11 @@ nm_manager_new (NMSettings *settings, priv = NM_MANAGER_GET_PRIVATE (singleton); + priv->connectivity = nm_connectivity_new (connectivity_uri, connectivity_interval, connectivity_response); + + 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); @@ -3655,6 +3705,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); diff --git a/src/nm-manager.h b/src/nm-manager.h index c3c08e462b..32515f2f24 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -84,7 +84,10 @@ 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, + const gchar *connectivity_response, GError **error); NMManager *nm_manager_get (void);