mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-25 04:50:07 +01:00
merge: branch 'lr/device-conn'
https://bugzilla.gnome.org/show_bug.cgi?id=780448
This commit is contained in:
commit
29af644f9a
8 changed files with 440 additions and 423 deletions
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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] =
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
178
src/nm-manager.c
178
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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue