diff --git a/Makefile.am b/Makefile.am index f8c18d9620..f254109bc4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1428,7 +1428,6 @@ src_libNetworkManager_la_SOURCES = \ src/nm-config.h \ src/nm-config-data.c \ src/nm-config-data.h \ - src/nm-connectivity.c \ src/nm-connectivity.h \ src/nm-dcb.c \ src/nm-dcb.h \ @@ -1466,6 +1465,11 @@ src_libNetworkManager_la_SOURCES = \ \ $(NULL) +if WITH_CONCHECK +src_libNetworkManager_la_SOURCES += \ + src/nm-connectivity.c +endif + src_libNetworkManager_la_LIBADD = \ src/libNetworkManagerBase.la \ src/libsystemd-nm.la \ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 7a61cf6861..2cba9fddbb 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2013 Red Hat, Inc. + * Copyright (C) 2005 - 2017 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -66,6 +66,8 @@ #include "nm-lldp-listener.h" #include "nm-audit-manager.h" #include "nm-arping-manager.h" +#include "nm-connectivity.h" +#include "nm-dbus-interface.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF (NMDevice); @@ -189,6 +191,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice, PROP_REFRESH_RATE_MS, PROP_TX_BYTES, PROP_RX_BYTES, + PROP_CONNECTIVITY, ); typedef struct _NMDevicePrivate { @@ -442,6 +445,9 @@ typedef struct _NMDevicePrivate { NMSettings *settings; NMLldpListener *lldp_listener; + NMConnectivityState connectivity_state; + guint concheck_periodic_id; + guint64 concheck_seq; guint check_delete_unrealized_id; @@ -1474,6 +1480,17 @@ nm_device_get_priority (NMDevice *self) return 11000; } +static int +default_route_penalty (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->connectivity_state != NM_CONNECTIVITY_FULL) + return 20000; + else + return 0; +} + static guint32 _get_ipx_route_metric (NMDevice *self, gboolean is_v4) @@ -1697,6 +1714,172 @@ nm_device_get_physical_port_id (NMDevice *self) /*****************************************************************************/ +static void +update_connectivity_state (NMDevice *self, NMConnectivityState state) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + /* If the connectivity check is disabled, make an optimistic guess. */ + if (state == NM_CONNECTIVITY_UNKNOWN) { + if (priv->state == NM_DEVICE_STATE_ACTIVATED) { + if (priv->default_route.v4_has || priv->default_route.v6_has) + state = NM_CONNECTIVITY_FULL; + else + state = NM_CONNECTIVITY_LIMITED; + } else { + state = NM_CONNECTIVITY_NONE; + } + } + + if (priv->connectivity_state != state) { +#if WITH_CONCHECK + _LOGD (LOGD_CONCHECK, "state changed from %s to %s", + nm_connectivity_state_to_string (priv->connectivity_state), + nm_connectivity_state_to_string (state)); +#endif + priv->connectivity_state = state; + _notify (self, PROP_CONNECTIVITY); + + if (!ip4_config_merge_and_apply (self, NULL, TRUE)) + _LOGW (LOGD_IP4, "Failed to update IPv4 default route metric"); + if (!ip6_config_merge_and_apply (self, TRUE)) + _LOGW (LOGD_IP6, "Failed to update IPv6 default route metric"); + } +} + +typedef struct { + NMDevice *self; + NMDeviceConnectivityCallback callback; + gpointer user_data; + guint64 seq; +} ConnectivityCheckData; + +static void +concheck_done (ConnectivityCheckData *data) +{ + NMDevice *self = data->self; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + /* The unsolicited connectivity checks don't hook a callback. */ + if (data->callback) + data->callback (data->self, priv->connectivity_state, data->user_data); + g_object_unref (data->self); + g_slice_free (ConnectivityCheckData, data); +} + +#if WITH_CONCHECK +static void +concheck_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) +{ + ConnectivityCheckData *data = user_data; + NMDevice *self = data->self; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnectivity *connectivity = NM_CONNECTIVITY (source_object); + NMConnectivityState state; + GError *error = NULL; + + state = nm_connectivity_check_finish (connectivity, result, &error); + if (error) { + _LOGW (LOGD_DEVICE, "connectivity checking on '%s' failed: %s", + nm_device_get_iface (self), error->message); + g_error_free (error); + } + + if (data->seq == priv->concheck_seq) + update_connectivity_state (data->self, state); + concheck_done (data); +} +#endif /* WITH_CONCHECK */ + +static gboolean +no_concheck (gpointer user_data) +{ + ConnectivityCheckData *data = user_data; + + concheck_done (data); + return G_SOURCE_REMOVE; +} + +void +nm_device_check_connectivity (NMDevice *self, + NMDeviceConnectivityCallback callback, + gpointer user_data) +{ + ConnectivityCheckData *data; +#if WITH_CONCHECK + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); +#endif + + data = g_slice_new0 (ConnectivityCheckData); + data->self = g_object_ref (self); + data->callback = callback; + data->user_data = user_data; + +#if WITH_CONCHECK + if (priv->concheck_periodic_id) { + data->seq = ++priv->concheck_seq; + + /* Kick off a real connectivity check. */ + nm_connectivity_check_async (nm_connectivity_get (), + nm_device_get_iface (self), + concheck_cb, + data); + return; + } +#endif + + /* Fake one. */ + g_idle_add (no_concheck, data); +} + +NMConnectivityState +nm_device_get_connectivity_state (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), NM_CONNECTIVITY_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE (self)->connectivity_state; +} + +#if WITH_CONCHECK +static void +concheck_periodic (NMConnectivity *connectivity, NMDevice *self) +{ + nm_device_check_connectivity (self, NULL, NULL); +} +#endif + +static void +concheck_periodic_update (NMDevice *self) +{ +#if WITH_CONCHECK + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean check_enable; + + check_enable = (priv->state == NM_DEVICE_STATE_ACTIVATED) + && (priv->default_route.v4_has || priv->default_route.v6_has); + + if (check_enable && !priv->concheck_periodic_id) { + /* We just gained a default route. Enable periodic checking. */ + priv->concheck_periodic_id = g_signal_connect (nm_connectivity_get (), + NM_CONNECTIVITY_PERIODIC_CHECK, + G_CALLBACK (concheck_periodic), self); + /* Also kick off a check right away. */ + nm_device_check_connectivity (self, NULL, NULL); + } else if (!check_enable && priv->concheck_periodic_id) { + /* The default route has gone off, and so has connectivity. */ + g_signal_handler_disconnect (nm_connectivity_get (), priv->concheck_periodic_id); + priv->concheck_periodic_id = 0; + update_connectivity_state (self, NM_CONNECTIVITY_NONE); + } +#else + /* update_connectivity_state() figures out how to lie about + * connectivity state if the actual state is not really known. */ + update_connectivity_state (self, NM_CONNECTIVITY_UNKNOWN); +#endif +} + +/*****************************************************************************/ + static SlaveInfo * find_slave_info (NMDevice *self, NMDevice *slave) { @@ -5322,7 +5505,7 @@ ip4_config_merge_and_apply (NMDevice *self, memset (&priv->default_route.v4, 0, sizeof (priv->default_route.v4)); priv->default_route.v4.rt_source = NM_IP_CONFIG_SOURCE_USER; priv->default_route.v4.gateway = gateway; - priv->default_route.v4.metric = default_route_metric; + priv->default_route.v4.metric = default_route_metric + default_route_penalty (self); priv->default_route.v4.mss = nm_ip4_config_get_mss (composite); if (!has_direct_route) { @@ -6069,7 +6252,7 @@ ip6_config_merge_and_apply (NMDevice *self, memset (&priv->default_route.v6, 0, sizeof (priv->default_route.v6)); priv->default_route.v6.rt_source = NM_IP_CONFIG_SOURCE_USER; priv->default_route.v6.gateway = *gateway; - priv->default_route.v6.metric = nm_device_get_ip6_route_metric (self); + priv->default_route.v6.metric = nm_device_get_ip6_route_metric (self) + default_route_penalty (self); priv->default_route.v6.mss = nm_ip6_config_get_mss (composite); if (!has_direct_route) { @@ -9483,6 +9666,8 @@ nm_device_set_ip4_config (NMDevice *self, } nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self); + concheck_periodic_update (self); + if (!nm_device_sys_iface_state_is_external_or_assume (self)) ip4_rp_filter_update (self); @@ -12445,6 +12630,8 @@ _set_state_full (NMDevice *self, if (ip_config_valid (old_state) && !ip_config_valid (state)) notify_ip_properties (self); + concheck_periodic_update (self); + /* Dispose of the cached activation request */ if (req) g_object_unref (req); @@ -13837,6 +14024,9 @@ get_property (GObject *object, guint prop_id, case PROP_RX_BYTES: g_value_set_uint64 (value, priv->stats.rx_bytes); break; + case PROP_CONNECTIVITY: + g_value_set_uint (value, priv->connectivity_state); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -14107,6 +14297,13 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /* Connectivity */ + obj_properties[PROP_CONNECTIVITY] = + g_param_spec_uint (NM_DEVICE_CONNECTIVITY, "", "", + NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); signals[STATE_CHANGED] = diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 073e1fbdf2..eedd2fcd1c 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2013 Red Hat, Inc. + * Copyright (C) 2005 - 2017 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -135,6 +135,8 @@ nm_device_state_reason_check (NMDeviceStateReason reason) #define NM_DEVICE_STATISTICS_TX_BYTES "tx-bytes" #define NM_DEVICE_STATISTICS_RX_BYTES "rx-bytes" +#define NM_DEVICE_CONNECTIVITY "connectivity" + #define NM_TYPE_DEVICE (nm_device_get_type ()) #define NM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice)) #define NM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE, NMDeviceClass)) @@ -689,4 +691,12 @@ gboolean nm_device_hw_addr_get_cloned (NMDevice *self, gboolean *preserve, GError **error); +typedef void (*NMDeviceConnectivityCallback) (NMDevice *self, + NMConnectivityState state, + gpointer user_data); +void nm_device_check_connectivity (NMDevice *self, + NMDeviceConnectivityCallback callback, + gpointer user_data); +NMConnectivityState nm_device_get_connectivity_state (NMDevice *self); + #endif /* __NETWORKMANAGER_DEVICE_H__ */ diff --git a/src/main.c b/src/main.c index 56b14c9144..04c837cb8f 100644 --- a/src/main.c +++ b/src/main.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2004 - 2012 Red Hat, Inc. + * Copyright (C) 2004 - 2017 Red Hat, Inc. * Copyright (C) 2005 - 2008 Novell, Inc. */ @@ -49,6 +49,7 @@ #include "nm-auth-manager.h" #include "nm-core-internal.h" #include "nm-exported-object.h" +#include "nm-connectivity.h" #include "dns/nm-dns-manager.h" #include "systemd/nm-sd.h" @@ -391,6 +392,9 @@ main (int argc, char *argv[]) nm_linux_platform_setup (); NM_UTILS_KEEP_ALIVE (config, NM_PLATFORM_GET, "NMConfig-depends-on-NMPlatform"); +#if WITH_CONCHECK + NM_UTILS_KEEP_ALIVE (nm_manager_get (), nm_connectivity_get (), "NMManager-depends-on-NMConnectivity"); +#endif nm_dispatcher_init (); diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index ce1c25999b..68b23b909f 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -25,37 +25,21 @@ #include "nm-connectivity.h" #include -#if WITH_CONCHECK #include -#endif #include "nm-config.h" -#include "nm-dispatcher.h" #include "NetworkManagerUtils.h" /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE (NMConnectivity, - PROP_URI, - PROP_INTERVAL, - PROP_RESPONSE, - PROP_STATE, -); - typedef struct { char *uri; char *response; guint interval; - gboolean online; /* whether periodic connectivity checking is enabled. */ - -#if WITH_CONCHECK + NMConfig *config; + guint periodic_check_id; CURLM *curl_mhandle; guint curl_timer; - gboolean initial_check_obsoleted; - guint check_id; -#endif - - NMConnectivityState state; } NMConnectivityPrivate; struct _NMConnectivity { @@ -71,21 +55,38 @@ G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT) #define NM_CONNECTIVITY_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMConnectivity, NM_IS_CONNECTIVITY) +NM_DEFINE_SINGLETON_GETTER (NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECTIVITY); + +enum { + PERIODIC_CHECK, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_CONCHECK #define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "connectivity", __VA_ARGS__) +#define _NMLOG2_DOMAIN LOGD_CONCHECK +#define _NMLOG2(level, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled (__level, _NMLOG2_DOMAIN)) { \ + _nm_log (__level, _NMLOG2_DOMAIN, 0, \ + &cb_data->ifspec[3], NULL, \ + "connectivity: (%s) " \ + _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + &cb_data->ifspec[3] \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END + /*****************************************************************************/ -NMConnectivityState -nm_connectivity_get_state (NMConnectivity *connectivity) -{ - g_return_val_if_fail (NM_IS_CONNECTIVITY (connectivity), NM_CONNECTIVITY_UNKNOWN); - - return NM_CONNECTIVITY_GET_PRIVATE (connectivity)->state; -} - NM_UTILS_LOOKUP_STR_DEFINE (nm_connectivity_state_to_string, NMConnectivityState, NM_UTILS_LOOKUP_DEFAULT_WARN ("???"), NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_UNKNOWN, "UNKNOWN"), @@ -95,139 +96,22 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_connectivity_state_to_string, NMConnectivityState NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FULL, "FULL"), ); -static void -update_state (NMConnectivity *self, NMConnectivityState state) -{ - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - - if (priv->state != state) { - _LOGD ("state changed from %s to %s", - nm_connectivity_state_to_string (priv->state), - nm_connectivity_state_to_string (state)); - priv->state = state; - _notify (self, PROP_STATE); - - nm_dispatcher_call_connectivity (state, NULL, NULL, NULL); - } -} - /*****************************************************************************/ -#if WITH_CONCHECK -static void -run_check_complete (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - NMConnectivity *self = NM_CONNECTIVITY (object); - GError *error = NULL; - - nm_connectivity_check_finish (self, result, &error); - if (error) { - _LOGE ("check failed: %s", error->message); - g_error_free (error); - } -} - -static gboolean -run_check (gpointer user_data) -{ - NMConnectivity *self = NM_CONNECTIVITY (user_data); - - nm_connectivity_check_async (self, run_check_complete, NULL); - return TRUE; -} - -static gboolean -idle_start_periodic_checks (gpointer user_data) -{ - NMConnectivity *self = user_data; - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - - priv->check_id = g_timeout_add_seconds (priv->interval, run_check, self); - if (!priv->initial_check_obsoleted) - run_check (self); - - return FALSE; -} -#endif - -static void -_reschedule_periodic_checks (NMConnectivity *self, gboolean force_reschedule) -{ - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - -#if WITH_CONCHECK - if (priv->online && priv->uri && priv->interval) { - if (force_reschedule || !priv->check_id) { - if (priv->check_id) - g_source_remove (priv->check_id); - priv->check_id = g_timeout_add (0, idle_start_periodic_checks, self); - priv->initial_check_obsoleted = FALSE; - } - } else { - nm_clear_g_source (&priv->check_id); - } - if (priv->check_id) - return; -#endif - - /* Either @online is %TRUE but we aren't checking connectivity, or - * @online is %FALSE. Either way we can update our status immediately. - */ - update_state (self, priv->online ? NM_CONNECTIVITY_FULL : NM_CONNECTIVITY_NONE); -} - -void -nm_connectivity_set_online (NMConnectivity *self, - gboolean online) -{ - NMConnectivityPrivate *priv= NM_CONNECTIVITY_GET_PRIVATE (self); - - online = !!online; - if (priv->online != online) { - _LOGD ("set %s", online ? "online" : "offline"); - priv->online = online; - _reschedule_periodic_checks (self, FALSE); - } -} - -/*****************************************************************************/ - -#if WITH_CONCHECK typedef struct { GSimpleAsyncResult *simple; - char *uri; char *response; - guint check_id_when_scheduled; CURL *curl_ehandle; size_t msg_size; char *msg; struct curl_slist *request_headers; guint timeout_id; + char *ifspec; } ConCheckCbData; static void finish_cb_data (ConCheckCbData *cb_data, NMConnectivityState new_state) { - NMConnectivity *self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (cb_data->simple))); - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - - /* Only update the state, if the call was done from external, or if the periodic check - * is still the one that called this async check. */ - if (!cb_data->check_id_when_scheduled || cb_data->check_id_when_scheduled == priv->check_id) { - /* Only update the state, if the URI and response parameters did not change - * since invocation. - * The interval does not matter for exernal calls, and for internal calls - * we don't reach this line if the interval changed. */ - if ( !g_strcmp0 (cb_data->uri, priv->uri) - && !g_strcmp0 (cb_data->response, priv->response)) { - _LOGT ("Update to connectivity state %s", - nm_connectivity_state_to_string (new_state)); - update_state (self, new_state); - } - } - /* Contrary to what cURL manual claim it is *not* safe to remove * the easy handle "at any moment"; specifically not from the * write function. Thus here we just dissociate the cb_data from @@ -239,7 +123,6 @@ finish_cb_data (ConCheckCbData *cb_data, NMConnectivityState new_state) g_simple_async_result_complete (cb_data->simple); g_object_unref (cb_data->simple); curl_slist_free_all (cb_data->request_headers); - g_free (cb_data->uri); g_free (cb_data->response); g_source_remove (cb_data->timeout_id); g_slice_free (ConCheckCbData, cb_data); @@ -254,7 +137,7 @@ curl_check_connectivity (CURLM *mhandle, CURLMcode ret) gint m_left; if (ret != CURLM_OK) - _LOGW ("Connectivity check failed"); + _LOGW ("connectivity check failed"); while ((msg = curl_multi_info_read (mhandle, &m_left))) { if (msg->msg != CURLMSG_DONE) @@ -263,7 +146,7 @@ curl_check_connectivity (CURLM *mhandle, CURLMcode ret) /* Here we have completed a session. Check easy session result. */ eret = curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, &cb_data); if (eret != CURLE_OK) { - _LOGE ("curl cannot extract cb_data for easy handle %p, skipping msg", msg->easy_handle); + _LOG2E ("curl cannot extract cb_data for easy handle %p, skipping msg", msg->easy_handle); continue; } @@ -273,11 +156,11 @@ curl_check_connectivity (CURLM *mhandle, CURLMcode ret) if (msg->data.result == CURLE_OK) { /* If we get here, it means that easy_write_cb() didn't read enough * bytes to be able to do a match. */ - _LOGI ("Check for uri '%s' returned a shorter response than expected '%s'; assuming captive portal.", - cb_data->uri, cb_data->response); + _LOG2I ("response shorter than expected '%s'; assuming captive portal.", + cb_data->response); finish_cb_data (cb_data, NM_CONNECTIVITY_PORTAL); } else { - _LOGD ("Check for uri '%s' failed", cb_data->uri); + _LOG2D ("check failed (%d)", msg->data.result); finish_cb_data (cb_data, NM_CONNECTIVITY_LIMITED); } } @@ -393,7 +276,7 @@ easy_header_cb (char *buffer, size_t size, size_t nitems, void *userdata) if ( len >= sizeof (HEADER_STATUS_ONLINE) - 1 && !g_ascii_strncasecmp (buffer, HEADER_STATUS_ONLINE, sizeof (HEADER_STATUS_ONLINE) - 1)) { - _LOGD ("check for uri '%s' with Status header successful.", cb_data->uri); + _LOG2D ("status header found, check successful"); finish_cb_data (cb_data, NM_CONNECTIVITY_FULL); return 0; } @@ -414,11 +297,11 @@ easy_write_cb (void *buffer, size_t size, size_t nmemb, void *userdata) if (cb_data->msg_size >= strlen (cb_data->response)) { /* We already have enough data -- check response */ if (g_str_has_prefix (cb_data->msg, cb_data->response)) { - _LOGD ("Check for uri '%s' successful.", cb_data->uri); + _LOG2D ("check successful."); finish_cb_data (cb_data, NM_CONNECTIVITY_FULL); } else { - _LOGI ("Check for uri '%s' did not match expected response '%s'; assuming captive portal.", - cb_data->uri, cb_data->response); + _LOG2I ("response did not match expected response '%s'; assuming captive portal.", + cb_data->response); finish_cb_data (cb_data, NM_CONNECTIVITY_PORTAL); } return 0; @@ -435,27 +318,23 @@ timeout_cb (gpointer user_data) NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); CURL *ehandle = cb_data->curl_ehandle; - _LOGI ("Check for uri '%s' timed out.", cb_data->uri); + _LOG2I ("timed out"); finish_cb_data (cb_data, NM_CONNECTIVITY_LIMITED); curl_multi_remove_handle (priv->curl_mhandle, ehandle); curl_easy_cleanup (ehandle); return G_SOURCE_REMOVE; } -#endif - -#define IS_PERIODIC_CHECK(callback) ((callback) == run_check_complete) void nm_connectivity_check_async (NMConnectivity *self, + const char *iface, GAsyncReadyCallback callback, gpointer user_data) { NMConnectivityPrivate *priv; GSimpleAsyncResult *simple; -#if WITH_CONCHECK CURL *ehandle = NULL; -#endif g_return_if_fail (NM_IS_CONNECTIVITY (self)); priv = NM_CONNECTIVITY_GET_PRIVATE (self); @@ -463,7 +342,6 @@ nm_connectivity_check_async (NMConnectivity *self, simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, nm_connectivity_check_async); -#if WITH_CONCHECK if (priv->uri && priv->interval && priv->curl_mhandle) ehandle = curl_easy_init (); @@ -472,16 +350,13 @@ nm_connectivity_check_async (NMConnectivity *self, cb_data->curl_ehandle = ehandle; cb_data->request_headers = curl_slist_append (NULL, "Connection: close"); + cb_data->ifspec = g_strdup_printf ("if!%s", iface); cb_data->simple = simple; - cb_data->uri = g_strdup (priv->uri); if (priv->response) cb_data->response = g_strdup (priv->response); else cb_data->response = g_strdup (NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE); - /* For internal calls (periodic), remember the check-id at time of scheduling. */ - cb_data->check_id_when_scheduled = IS_PERIODIC_CHECK (callback) ? priv->check_id : 0; - curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri); curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb); curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data); @@ -489,23 +364,18 @@ nm_connectivity_check_async (NMConnectivity *self, curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data); curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data); curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->request_headers); + curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec); curl_multi_add_handle (priv->curl_mhandle, ehandle); cb_data->timeout_id = g_timeout_add_seconds (30, timeout_cb, cb_data); - priv->initial_check_obsoleted = TRUE; - - _LOGD ("check: send %s request to '%s'", IS_PERIODIC_CHECK (callback) ? "periodic " : "", priv->uri); + _LOG2D ("sending request to '%s'", priv->uri); return; } else { - g_warn_if_fail (!IS_PERIODIC_CHECK (callback)); - _LOGD ("check: faking request. Connectivity check disabled"); + _LOGD ("(%s) faking request. Connectivity check disabled", iface); } -#else - _LOGD ("check: faking request. Compiled without connectivity-check support"); -#endif - g_simple_async_result_set_op_res_gssize (simple, priv->state); + g_simple_async_result_set_op_res_gssize (simple, NM_CONNECTIVITY_UNKNOWN); g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } @@ -527,117 +397,99 @@ nm_connectivity_check_finish (NMConnectivity *self, /*****************************************************************************/ -static void -get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec) +static gboolean +periodic_check (gpointer user_data) { - NMConnectivity *self = NM_CONNECTIVITY (object); - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - - switch (property_id) { - case PROP_URI: - g_value_set_string (value, priv->uri); - break; - case PROP_INTERVAL: - g_value_set_uint (value, priv->interval); - break; - case PROP_RESPONSE: - if (priv->response) - g_value_set_string (value, priv->response); - else - g_value_set_static_string (value, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE); - break; - case PROP_STATE: - g_value_set_uint (value, priv->state); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } + g_signal_emit (NM_CONNECTIVITY (user_data), signals[PERIODIC_CHECK], 0); + return G_SOURCE_CONTINUE; } static void -set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec) +update_config (NMConnectivity *self, NMConfigData *config_data) { - NMConnectivity *self = NM_CONNECTIVITY (object); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); const char *uri, *response; guint interval; - gboolean changed; + gboolean changed = FALSE; - switch (property_id) { - case PROP_URI: - uri = g_value_get_string (value); - if (uri && !*uri) + /* Set the URI. */ + uri = nm_config_data_get_connectivity_uri (config_data); + if (uri && !*uri) + uri = NULL; + changed = g_strcmp0 (uri, priv->uri) != 0; + if (uri) { + char *scheme = g_uri_parse_scheme (uri); + + if (!scheme) { + _LOGE ("invalid URI '%s' for connectivity check.", uri); uri = NULL; - changed = g_strcmp0 (uri, priv->uri) != 0; -#if WITH_CONCHECK - if (uri) { - char *scheme = g_uri_parse_scheme (uri); + } else if (strcasecmp (scheme, "https") == 0) { + _LOGW ("use of HTTPS for connectivity checking is not reliable and is discouraged (URI: %s)", uri); + } else if (strcasecmp (scheme, "http") != 0) { + _LOGE ("scheme of '%s' uri does't use a scheme that is allowed for connectivity check.", uri); + uri = NULL; + } - if (!scheme) { - _LOGE ("invalid URI '%s' for connectivity check.", uri); - uri = NULL; - } else if (strcasecmp (scheme, "https") == 0) { - _LOGW ("use of HTTPS for connectivity checking is not reliable and is discouraged (URI: %s)", uri); - } else if (strcasecmp (scheme, "http") != 0) { - _LOGE ("scheme of '%s' uri does't use a scheme that is allowed for connectivity check.", uri); - uri = NULL; - } + if (scheme) + g_free (scheme); + } + if (changed) { + g_free (priv->uri); + priv->uri = g_strdup (uri); + } - if (scheme) - g_free (scheme); - } -#endif - if (changed) { - g_free (priv->uri); - priv->uri = g_strdup (uri); - _reschedule_periodic_checks (self, TRUE); - } - break; - case PROP_INTERVAL: - interval = g_value_get_uint (value); - if (priv->interval != interval) { - priv->interval = interval; - _reschedule_periodic_checks (self, TRUE); - } - break; - case PROP_RESPONSE: - response = g_value_get_string (value); - if (g_strcmp0 (response, priv->response) != 0) { - /* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response - * (including "") is accepted. */ - g_free (priv->response); - priv->response = g_strdup (response); - _reschedule_periodic_checks (self, TRUE); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; + /* Set the interval. */ + interval = nm_config_data_get_connectivity_interval (config_data); + if (priv->interval != interval) { + priv->interval = interval; + changed = TRUE; + } + + /* Set the response. */ + response = nm_config_data_get_connectivity_response (config_data); + if (g_strcmp0 (response, priv->response) != 0) { + /* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response + * (including "") is accepted. */ + g_free (priv->response); + priv->response = g_strdup (response); + changed = TRUE; + } + + if (changed) { + nm_clear_g_source (&priv->periodic_check_id); + priv->periodic_check_id = g_timeout_add_seconds (priv->interval, periodic_check, self); } } -/*****************************************************************************/ - +static void +config_changed_cb (NMConfig *config, + NMConfigData *config_data, + NMConfigChangeFlags changes, + NMConfigData *old_data, + NMConnectivity *self) +{ + update_config (self, config_data); +} static void nm_connectivity_init (NMConnectivity *self) { NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); -#if WITH_CONCHECK CURLcode retv; -#endif - priv->state = NM_CONNECTIVITY_NONE; -#if WITH_CONCHECK + priv->config = g_object_ref (nm_config_get ()); + update_config (self, nm_config_get_data (priv->config)); + g_signal_connect (G_OBJECT (priv->config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK (config_changed_cb), + self); + retv = curl_global_init (CURL_GLOBAL_ALL); if (retv == CURLE_OK) priv->curl_mhandle = curl_multi_init (); if (priv->curl_mhandle == NULL) { - _LOGE ("Unable to init cURL, connectivity check will not work"); + _LOGE ("cnable to init cURL, connectivity check will not work"); return; } @@ -646,19 +498,6 @@ nm_connectivity_init (NMConnectivity *self) curl_multi_setopt (priv->curl_mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb); curl_multi_setopt (priv->curl_mhandle, CURLMOPT_TIMERDATA, self); curl_multi_setopt (priv->curl_mhandle, CURLOPT_VERBOSE, 1); -#endif -} - -NMConnectivity * -nm_connectivity_new (const char *uri, - guint interval, - const char *response) -{ - return g_object_new (NM_TYPE_CONNECTIVITY, - NM_CONNECTIVITY_URI, uri, - NM_CONNECTIVITY_INTERVAL, interval, - NM_CONNECTIVITY_RESPONSE, response, - NULL); } static void @@ -670,12 +509,14 @@ dispose (GObject *object) g_clear_pointer (&priv->uri, g_free); g_clear_pointer (&priv->response, g_free); -#if WITH_CONCHECK + if (priv->config) { + g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); + g_clear_object (&priv->config); + } + curl_multi_cleanup (priv->curl_mhandle); curl_global_cleanup (); - - nm_clear_g_source (&priv->check_id); -#endif + nm_clear_g_source (&priv->periodic_check_id); G_OBJECT_CLASS (nm_connectivity_parent_class)->dispose (object); } @@ -685,36 +526,12 @@ nm_connectivity_class_init (NMConnectivityClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->set_property = set_property; - object_class->get_property = get_property; + signals[PERIODIC_CHECK] = + g_signal_new (NM_CONNECTIVITY_PERIODIC_CHECK, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + object_class->dispose = dispose; - - obj_properties[PROP_URI] = - g_param_spec_string (NM_CONNECTIVITY_URI, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_INTERVAL] = - g_param_spec_uint (NM_CONNECTIVITY_INTERVAL, "", "", - 0, G_MAXUINT, NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_RESPONSE] = - g_param_spec_string (NM_CONNECTIVITY_RESPONSE, "", "", - NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_STATE] = - g_param_spec_uint (NM_CONNECTIVITY_STATE, "", "", - NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); } diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h index 6900bd0fa8..0522381a91 100644 --- a/src/nm-connectivity.h +++ b/src/nm-connectivity.h @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2011 Thomas Bechtold + * Copyright (C) 2017 Red Hat, Inc. */ #ifndef __NETWORKMANAGER_CONNECTIVITY_H__ @@ -30,27 +31,18 @@ #define NM_IS_CONNECTIVITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CONNECTIVITY)) #define NM_CONNECTIVITY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONNECTIVITY, NMConnectivityClass)) -#define NM_CONNECTIVITY_URI "uri" -#define NM_CONNECTIVITY_INTERVAL "interval" -#define NM_CONNECTIVITY_RESPONSE "response" -#define NM_CONNECTIVITY_STATE "state" +#define NM_CONNECTIVITY_PERIODIC_CHECK "nm-connectivity-periodic-check" typedef struct _NMConnectivityClass NMConnectivityClass; GType nm_connectivity_get_type (void); +NMConnectivity *nm_connectivity_get (void); + const char *nm_connectivity_state_to_string (NMConnectivityState state); -NMConnectivity *nm_connectivity_new (const char *uri, - guint interval, - const char *response); - -void nm_connectivity_set_online (NMConnectivity *self, - gboolean online); - -NMConnectivityState nm_connectivity_get_state (NMConnectivity *self); - void nm_connectivity_check_async (NMConnectivity *self, + const char *iface, GAsyncReadyCallback callback, gpointer user_data); NMConnectivityState nm_connectivity_check_finish (NMConnectivity *self, diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 1bb26e1c45..0d482e0cad 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2004 - 2012 Red Hat, Inc. + * Copyright (C) 2004 - 2017 Red Hat, Inc. * Copyright (C) 2005 - 2008 Novell, Inc. */ @@ -507,6 +507,7 @@ _dispatcher_call (NMDispatcherAction action, GError *error = NULL; static guint request_counter = 0; guint reqid = ++request_counter; + const char *connectivity_state_string = "UNKNOWN"; if (!dispatcher_proxy) return FALSE; @@ -616,6 +617,10 @@ _dispatcher_call (NMDispatcherAction action, if (!device_dhcp6_props) device_dhcp6_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0)); +#if WITH_CONCHECK + connectivity_state_string = nm_connectivity_state_to_string (connectivity_state); +#endif + /* Send the action to the dispatcher */ if (blocking) { GVariant *ret; @@ -632,7 +637,7 @@ _dispatcher_call (NMDispatcherAction action, &device_ip6_props, device_dhcp4_props, device_dhcp6_props, - nm_connectivity_state_to_string (connectivity_state), + connectivity_state_string, vpn_iface ? vpn_iface : "", &vpn_proxy_props, &vpn_ip4_props, @@ -670,7 +675,7 @@ _dispatcher_call (NMDispatcherAction action, &device_ip6_props, device_dhcp4_props, device_dhcp6_props, - nm_connectivity_state_to_string (connectivity_state), + connectivity_state_string, vpn_iface ? vpn_iface : "", &vpn_proxy_props, &vpn_ip4_props, diff --git a/src/nm-manager.c b/src/nm-manager.c index 5f06f23ff5..e75e499a2e 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2007 - 2009 Novell, Inc. - * Copyright (C) 2007 - 2012 Red Hat, Inc. + * Copyright (C) 2007 - 2017 Red Hat, Inc. */ #include "nm-default.h" @@ -54,6 +54,7 @@ #include "nm-dbus-compat.h" #include "nm-checkpoint.h" #include "nm-checkpoint-manager.h" +#include "nm-dispatcher.h" #include "NetworkManagerUtils.h" #include "introspection/org.freedesktop.NetworkManager.h" @@ -117,7 +118,7 @@ typedef struct { GSList *devices; NMState state; NMConfig *config; - NMConnectivity *connectivity; + NMConnectivityState connectivity_state; NMPolicy *policy; @@ -497,12 +498,6 @@ active_connection_get_by_path (NMManager *manager, const char *path) static void _config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeFlags changes, NMConfigData *old_data, NMManager *self) { - g_object_set (NM_MANAGER_GET_PRIVATE (self)->connectivity, - NM_CONNECTIVITY_URI, nm_config_data_get_connectivity_uri (config_data), - NM_CONNECTIVITY_INTERVAL, nm_config_data_get_connectivity_interval (config_data), - NM_CONNECTIVITY_RESPONSE, nm_config_data_get_connectivity_response (config_data), - NULL); - if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG)) _notify (self, PROP_GLOBAL_DNS_CONFIGURATION); } @@ -770,27 +765,8 @@ set_state (NMManager *self, NMState state) g_signal_emit (self, signals[STATE_CHANGED], 0, priv->state); } -static void -checked_connectivity (GObject *object, GAsyncResult *result, gpointer user_data) -{ - NMManager *manager = user_data; - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - NMConnectivityState connectivity; - - if (priv->state == NM_STATE_CONNECTING || priv->state == NM_STATE_CONNECTED_SITE) { - connectivity = nm_connectivity_check_finish (priv->connectivity, result, NULL); - - if (connectivity == NM_CONNECTIVITY_FULL) - set_state (manager, NM_STATE_CONNECTED_GLOBAL); - - _notify (manager, PROP_CONNECTIVITY); - } - - g_object_unref (manager); -} - static NMState -find_best_device_state (NMManager *manager, gboolean *force_connectivity_check) +find_best_device_state (NMManager *manager) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); NMState best_state = NM_STATE_DISCONNECTED; @@ -804,11 +780,10 @@ find_best_device_state (NMManager *manager, gboolean *force_connectivity_check) case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: if ( nm_active_connection_get_default (ac) || nm_active_connection_get_default6 (ac)) { - if (nm_connectivity_get_state (priv->connectivity) == NM_CONNECTIVITY_FULL) + if (priv->connectivity_state) return NM_STATE_CONNECTED_GLOBAL; best_state = NM_STATE_CONNECTED_SITE; - NM_SET_OUT (force_connectivity_check, TRUE); } else { if (best_state < NM_STATE_CONNECTING) best_state = NM_STATE_CONNECTED_LOCAL; @@ -866,7 +841,6 @@ nm_manager_update_state (NMManager *manager) { NMManagerPrivate *priv; NMState new_state = NM_STATE_DISCONNECTED; - gboolean force_connectivity_check = FALSE; g_return_if_fail (NM_IS_MANAGER (manager)); @@ -875,27 +849,11 @@ nm_manager_update_state (NMManager *manager) if (manager_sleeping (manager)) new_state = NM_STATE_ASLEEP; else - new_state = find_best_device_state (manager, &force_connectivity_check); + new_state = find_best_device_state (manager); - nm_connectivity_set_online (priv->connectivity, new_state >= NM_STATE_CONNECTED_LOCAL); - - if (new_state == NM_STATE_CONNECTED_SITE) { - /* We have a default route, let's see if we can reach the Internet or a - * captive portal. */ - force_connectivity_check = TRUE; - } - - if (new_state == NM_STATE_CONNECTED_LOCAL) { - /* If we just lost a default route, let's retrigger the connectivity check - * so that the connectivity property would be updated to indicate we can't - * reach the Internet anymore. */ - force_connectivity_check = TRUE; - } - - if (force_connectivity_check) { - nm_connectivity_check_async (priv->connectivity, - checked_connectivity, - g_object_ref (manager)); + if ( new_state >= NM_STATE_CONNECTED_LOCAL + && priv->connectivity_state == NM_CONNECTIVITY_FULL) { + new_state = NM_STATE_CONNECTED_GLOBAL; } set_state (manager, new_state); @@ -2008,6 +1966,36 @@ device_realized (NMDevice *device, _notify (self, PROP_DEVICES); } +#if WITH_CONCHECK +static void +device_connectivity_changed (NMDevice *device, + GParamSpec *pspec, + NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMConnectivityState best_state = NM_CONNECTIVITY_UNKNOWN; + NMConnectivityState state; + const GSList *devices; + + for (devices = priv->devices; devices; devices = devices->next) { + state = nm_device_get_connectivity_state (NM_DEVICE (devices->data)); + if (state > best_state) + best_state = state; + } + + if (best_state != priv->connectivity_state) { + priv->connectivity_state = best_state; + + _LOGD (LOGD_CORE, "connectivity checking indicates %s", + nm_connectivity_state_to_string (priv->connectivity_state)); + + nm_manager_update_state (self); + _notify (self, PROP_CONNECTIVITY); + nm_dispatcher_call_connectivity (priv->connectivity_state, NULL, NULL, NULL); + } +} +#endif + static void _device_realize_finish (NMManager *self, NMDevice *device, @@ -2112,6 +2100,12 @@ add_device (NMManager *self, NMDevice *device, GError **error) G_CALLBACK (device_realized), self); +#if WITH_CONCHECK + g_signal_connect (device, "notify::" NM_DEVICE_CONNECTIVITY, + G_CALLBACK (device_connectivity_changed), + self); +#endif + if (priv->startup) { g_signal_connect (device, "notify::" NM_DEVICE_HAS_PENDING_ACTION, G_CALLBACK (device_has_pending_action_changed), @@ -4826,24 +4820,36 @@ impl_manager_get_logging (NMManager *manager, nm_logging_domains_to_string ())); } -static void -connectivity_check_done (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GDBusMethodInvocation *context = user_data; +typedef struct { + guint remaining; + GDBusMethodInvocation *context; NMConnectivityState state; - GError *error = NULL; +} ConnectivityCheckData; - state = nm_connectivity_check_finish (NM_CONNECTIVITY (object), result, &error); - if (error) - g_dbus_method_invocation_take_error (context, error); - else { - g_dbus_method_invocation_return_value (context, - g_variant_new ("(u)", state)); +static void +device_connectivity_done (NMDevice *device, NMConnectivityState state, gpointer user_data) +{ + ConnectivityCheckData *data = user_data; + + data->remaining--; + + /* We check if the state is already FULL so that we can provide the + * response without waiting for slower devices that are not going to + * affect the overall state anyway. */ + + if (data->state != NM_CONNECTIVITY_FULL) { + if (state > data->state) + data->state = state; + + if (data->state == NM_CONNECTIVITY_FULL || !data->remaining) { + g_dbus_method_invocation_return_value (data->context, + g_variant_new ("(u)", data->state)); + } } -} + if (!data->remaining) + g_slice_free (ConnectivityCheckData, data); +} static void check_connectivity_auth_done_cb (NMAuthChain *chain, @@ -4855,6 +4861,8 @@ check_connectivity_auth_done_cb (NMAuthChain *chain, NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GError *error = NULL; NMAuthCallResult result; + ConnectivityCheckData *data; + const GSList *devices; priv->auth_chains = g_slist_remove (priv->auth_chains, chain); @@ -4872,9 +4880,15 @@ check_connectivity_auth_done_cb (NMAuthChain *chain, "Not authorized to recheck connectivity"); } else { /* it's allowed */ - nm_connectivity_check_async (priv->connectivity, - connectivity_check_done, - context); + data = g_slice_new0 (ConnectivityCheckData); + data->context = context; + + for (devices = priv->devices; devices; devices = devices->next) { + data->remaining++; + nm_device_check_connectivity (NM_DEVICE (devices->data), + device_connectivity_done, + data); + } } if (error) @@ -5085,20 +5099,6 @@ handle_firmware_changed (gpointer user_data) return FALSE; } -static void -connectivity_changed (NMConnectivity *connectivity, - GParamSpec *pspec, - gpointer user_data) -{ - NMManager *self = NM_MANAGER (user_data); - - _LOGD (LOGD_CORE, "connectivity checking indicates %s", - nm_connectivity_state_to_string (nm_connectivity_get_state (connectivity))); - - nm_manager_update_state (self); - _notify (self, PROP_CONNECTIVITY); -} - static void firmware_dir_changed (GFileMonitor *monitor, GFile *file, @@ -5906,7 +5906,6 @@ constructed (GObject *object) { NMManager *self = NM_MANAGER (object); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMConfigData *config_data; const NMConfigState *state; G_OBJECT_CLASS (nm_manager_parent_class)->constructed (object); @@ -5949,13 +5948,6 @@ constructed (GObject *object) G_CALLBACK (_config_changed_cb), self); - config_data = nm_config_get_data (priv->config); - priv->connectivity = nm_connectivity_new (nm_config_data_get_connectivity_uri (config_data), - nm_config_data_get_connectivity_interval (config_data), - nm_config_data_get_connectivity_response (config_data)); - g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE, - G_CALLBACK (connectivity_changed), self); - state = nm_config_state_get (priv->config); priv->net_enabled = state->net_enabled; @@ -6111,7 +6103,7 @@ get_property (GObject *object, guint prop_id, nm_utils_g_value_set_object_path_array (value, priv->active_connections, NULL, NULL); break; case PROP_CONNECTIVITY: - g_value_set_uint (value, nm_connectivity_get_state (priv->connectivity)); + g_value_set_uint (value, priv->connectivity_state); break; case PROP_PRIMARY_CONNECTION: nm_utils_g_value_set_object_path (value, priv->primary_connection); @@ -6240,10 +6232,6 @@ dispose (GObject *object) g_signal_handlers_disconnect_by_func (priv->config, _config_changed_cb, manager); g_clear_object (&priv->config); } - if (priv->connectivity) { - g_signal_handlers_disconnect_by_func (priv->connectivity, connectivity_changed, manager); - g_clear_object (&priv->connectivity); - } g_free (priv->hostname);