core: make connectivity checking per-device

This moves tracking of connectivity to NMDevice and makes the NMManager
negotiate the best of known connectivity states of devices. The NMConnectivity
singleton handles its own configuration and scheduling of the permission
checks, but otherwise greatly simplifies it.

This will be useful to determine correct metrics for multiple default routes
depending on actual internet connectivity.

The per-device connection checks is not yet exposed on the D-Bus, since they
probably should be per-address-family as well.
This commit is contained in:
Lubomir Rintel 2017-03-20 13:36:00 +00:00
parent 4ec7dd987e
commit 9d43869e47
8 changed files with 402 additions and 420 deletions

View file

@ -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 \

View file

@ -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;
@ -1697,6 +1703,167 @@ 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);
}
}
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. */
update_connectivity_state (self, NM_CONNECTIVITY_NONE);
g_signal_handler_disconnect (nm_connectivity_get (), priv->concheck_periodic_id);
priv->concheck_periodic_id = 0;
}
#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)
{
@ -9483,6 +9650,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 +12614,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 +14008,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 +14281,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] =

View file

@ -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__ */

View file

@ -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 ();

View file

@ -25,37 +25,21 @@
#include "nm-connectivity.h"
#include <string.h>
#if WITH_CONCHECK
#include <curl/curl.h>
#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);
}

View file

@ -16,6 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de>
* 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,

View file

@ -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,

View file

@ -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,41 @@ device_realized (NMDevice *device,
_notify (self, PROP_DEVICES);
}
#if WITH_CONCHECK
static void
nm_manager_update_connectivity_state (NMManager *self)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
NMConnectivityState state = NM_CONNECTIVITY_UNKNOWN;
NMDevice *device;
if (priv->primary_connection) {
device = nm_active_connection_get_device (priv->primary_connection);
if (device)
state = nm_device_get_connectivity_state (device);
}
if (state != priv->connectivity_state) {
priv->connectivity_state = 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);
}
}
static void
device_connectivity_changed (NMDevice *device,
GParamSpec *pspec,
NMManager *self)
{
nm_manager_update_connectivity_state (self);
}
#endif
static void
_device_realize_finish (NMManager *self,
NMDevice *device,
@ -2112,6 +2105,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),
@ -4827,24 +4826,14 @@ impl_manager_get_logging (NMManager *manager,
}
static void
connectivity_check_done (GObject *object,
GAsyncResult *result,
gpointer user_data)
device_connectivity_done (NMDevice *device, NMConnectivityState state, gpointer user_data)
{
GDBusMethodInvocation *context = user_data;
NMConnectivityState state;
GError *error = NULL;
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));
}
g_dbus_method_invocation_return_value (context,
g_variant_new ("(u)", state));
}
static void
check_connectivity_auth_done_cb (NMAuthChain *chain,
GError *auth_error,
@ -4872,9 +4861,14 @@ 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);
if (priv->primary_connection) {
nm_device_check_connectivity (nm_active_connection_get_device (priv->primary_connection),
device_connectivity_done,
context);
} else {
g_dbus_method_invocation_return_value (context,
g_variant_new ("(u)", priv->connectivity_state));
}
}
if (error)
@ -5085,20 +5079,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,
@ -5173,6 +5153,7 @@ policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user
_notify (self, PROP_PRIMARY_CONNECTION);
_notify (self, PROP_PRIMARY_CONNECTION_TYPE);
nm_manager_update_metered (self);
nm_manager_update_connectivity_state (self);
}
}
@ -5906,7 +5887,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 +5929,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 +6084,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 +6213,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);