[glib] add GNetworkMonitor, for... monitoring the network
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] add GNetworkMonitor, for... monitoring the network
- Date: Mon, 14 Nov 2011 19:00:25 +0000 (UTC)
commit fe5ba0f291c11a22fbfe812d1c8315837fa14177
Author: Dan Winship <danw gnome org>
Date: Sun Jun 12 15:59:36 2011 -0400
add GNetworkMonitor, for... monitoring the network
Add GNetworkMonitor and its associated extension point, provide a base
implementation that always claims the network is available, and a
netlink-based implementation built on top of that that actually tracks
the network state.
https://bugzilla.gnome.org/show_bug.cgi?id=620932
configure.ac | 4 +
docs/reference/gio/gio-docs.xml | 5 +-
docs/reference/gio/gio-sections.txt | 20 ++
gio/Makefile.am | 11 +
gio/gio.h | 1 +
gio/gio.symbols | 10 +
gio/giomodule.c | 11 +
gio/giotypes.h | 1 +
gio/gnetworkmonitor.c | 261 ++++++++++++++++++++
gio/gnetworkmonitor.h | 89 +++++++
gio/gnetworkmonitorbase.c | 412 +++++++++++++++++++++++++++++++
gio/gnetworkmonitorbase.h | 66 +++++
gio/gnetworkmonitornetlink.c | 465 +++++++++++++++++++++++++++++++++++
gio/gnetworkmonitornetlink.h | 57 +++++
gio/tests/.gitignore | 1 +
gio/tests/Makefile.am | 3 +
gio/tests/network-monitor.c | 463 ++++++++++++++++++++++++++++++++++
17 files changed, 1878 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index eeb74e8..82f5499 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1109,6 +1109,10 @@ if test $glib_native_win32 = no; then
fi
AC_SUBST(NETWORK_LIBS)
+AC_CHECK_HEADER([linux/netlink.h],
+ [AC_DEFINE(HAVE_NETLINK, 1, [We have AF_NETLINK sockets])])
+AM_CONDITIONAL(HAVE_NETLINK, [test "$ac_cv_header_linux_netlink_h" = "yes"])
+
case $host in
*-*-solaris* )
AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, Needed to get declarations for msg_control and msg_controllen on Solaris)
diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml
index fd408aa..00ed715 100644
--- a/docs/reference/gio/gio-docs.xml
+++ b/docs/reference/gio/gio-docs.xml
@@ -104,7 +104,7 @@
<xi:include href="xml/gasyncinitable.xml"/>
</chapter>
<chapter id="networking">
- <title>Lowlevel network support</title>
+ <title>Low-level network support</title>
<xi:include href="xml/gsocket.xml"/>
<xi:include href="xml/ginetaddress.xml"/>
<xi:include href="xml/ginetaddressmask.xml"/>
@@ -120,7 +120,7 @@
<xi:include href="xml/gproxyaddress.xml"/>
</chapter>
<chapter id="highlevel-socket">
- <title>Highlevel network functionallity</title>
+ <title>High-level network functionallity</title>
<xi:include href="xml/gsocketclient.xml"/>
<xi:include href="xml/gsocketconnection.xml"/>
<xi:include href="xml/gunixconnection.xml"/>
@@ -129,6 +129,7 @@
<xi:include href="xml/gsocketlistener.xml"/>
<xi:include href="xml/gsocketservice.xml"/>
<xi:include href="xml/gthreadedsocketservice.xml"/>
+ <xi:include href="xml/gnetworkmonitor.xml"/>
</chapter>
<chapter id="tls">
<title>TLS (SSL) support</title>
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 33287c0..72b1ffb 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -3460,3 +3460,23 @@ G_DBUS_OBJECT_MANAGER_SERVER_GET_CLASS
<SUBSECTION Private>
GDBusObjectManagerServerPrivate
</SECTION>
+
+<SECTION>
+<FILE>gnetworkmonitor</FILE>
+<TITLE>GNetworkMonitor</TITLE>
+GNetworkMonitor
+GNetworkMonitorInterface
+G_NETWORK_MONITOR_EXTENSION_POINT_NAME
+g_network_monitor_get_default
+g_network_monitor_get_network_available
+g_network_monitor_can_reach
+g_network_monitor_can_reach_async
+g_network_monitor_can_reach_finish
+<SUBSECTION Standard>
+g_network_monitor_get_type
+G_TYPE_NETWORK_MONITOR
+G_NETWORK_MONITOR
+G_IS_NETWORK_MONITOR
+G_NETWORK_MONITOR_GET_INTERFACE
+</SECTION>
+
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 249cc7b..7e819a1 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -232,6 +232,13 @@ giounixinclude_HEADERS = \
gunixoutputstream.h \
gunixsocketaddress.h \
$(NULL)
+
+if HAVE_NETLINK
+unix_sources += \
+ gnetworkmonitornetlink.c \
+ gnetworkmonitornetlink.h \
+ $(NULL)
+endif
endif
win32_actual_sources = \
@@ -332,6 +339,9 @@ libgio_2_0_la_SOURCES = \
gnativevolumemonitor.h \
gnetworkaddress.c \
gnetworkingprivate.h \
+ gnetworkmonitor.c \
+ gnetworkmonitorbase.c \
+ gnetworkmonitorbase.h \
gnetworkservice.c \
goutputstream.c \
gpermission.c \
@@ -498,6 +508,7 @@ gio_headers = \
gmountoperation.h \
gnativevolumemonitor.h \
gnetworkaddress.h \
+ gnetworkmonitor.h \
gnetworkservice.h \
goutputstream.h \
gpermission.h \
diff --git a/gio/gio.h b/gio/gio.h
index ab65b1b..042c938 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -92,6 +92,7 @@
#include <gio/gmountoperation.h>
#include <gio/gnativevolumemonitor.h>
#include <gio/gnetworkaddress.h>
+#include <gio/gnetworkmonitor.h>
#include <gio/gnetworkservice.h>
#include <gio/goutputstream.h>
#include <gio/gpermission.h>
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 9ac949f..d861395 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1564,3 +1564,13 @@ g_dbus_object_manager_server_set_connection
g_dbus_object_manager_server_get_type
g_dbus_object_manager_server_new
g_dbus_object_manager_server_unexport
+g_network_monitor_can_reach
+g_network_monitor_can_reach_async
+g_network_monitor_can_reach_finish
+g_network_monitor_get_default
+g_network_monitor_get_network_available
+g_network_monitor_get_type
+g_network_monitor_base_add_network
+g_network_monitor_base_get_type
+g_network_monitor_base_remove_network
+g_network_monitor_base_set_networks
diff --git a/gio/giomodule.c b/gio/giomodule.c
index eac317a..efd8388 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -771,6 +771,10 @@ extern GType _g_winhttp_vfs_get_type (void);
extern GType _g_dummy_proxy_resolver_get_type (void);
extern GType _g_dummy_tls_backend_get_type (void);
+extern GType g_network_monitor_base_get_type (void);
+#ifdef HAVE_NETLINK
+extern GType _g_network_monitor_netlink_get_type (void);
+#endif
#ifdef G_PLATFORM_WIN32
@@ -850,6 +854,9 @@ _g_io_modules_ensure_extension_points_registered (void)
ep = g_io_extension_point_register (G_TLS_BACKEND_EXTENSION_POINT_NAME);
g_io_extension_point_set_required_type (ep, G_TYPE_TLS_BACKEND);
+
+ ep = g_io_extension_point_register (G_NETWORK_MONITOR_EXTENSION_POINT_NAME);
+ g_io_extension_point_set_required_type (ep, G_TYPE_NETWORK_MONITOR);
}
G_UNLOCK (registered_extensions);
@@ -921,6 +928,10 @@ _g_io_modules_ensure_loaded (void)
_g_socks4_proxy_get_type ();
_g_socks5_proxy_get_type ();
_g_dummy_tls_backend_get_type ();
+ g_network_monitor_base_get_type ();
+#ifdef HAVE_NETLINK
+ _g_network_monitor_netlink_get_type ();
+#endif
}
G_UNLOCK (loaded_dirs);
diff --git a/gio/giotypes.h b/gio/giotypes.h
index 6081322..4077dbb 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -123,6 +123,7 @@ typedef struct _GMemoryOutputStream GMemoryOutputStream;
typedef struct _GMount GMount; /* Dummy typedef */
typedef struct _GMountOperation GMountOperation;
typedef struct _GNetworkAddress GNetworkAddress;
+typedef struct _GNetworkMonitor GNetworkMonitor;
typedef struct _GNetworkService GNetworkService;
typedef struct _GOutputStream GOutputStream;
typedef struct _GIOStream GIOStream;
diff --git a/gio/gnetworkmonitor.c b/gio/gnetworkmonitor.c
new file mode 100644
index 0000000..33e9704
--- /dev/null
+++ b/gio/gnetworkmonitor.c
@@ -0,0 +1,261 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "glib.h"
+#include "glibintl.h"
+
+#include "gnetworkmonitor.h"
+#include "ginetaddress.h"
+#include "ginetsocketaddress.h"
+#include "ginitable.h"
+#include "gioenumtypes.h"
+#include "giomodule-priv.h"
+#include "gsimpleasyncresult.h"
+
+/**
+ * SECTION:gnetworkmonitor
+ * @title: GNetworkMonitor
+ * @short_description: Network status monitor
+ * @include: gio/gio.h
+ */
+
+/**
+ * GNetworkMonitor:
+ *
+ * #GNetworkMonitor monitors the status of network connections and
+ * indicates when a possibly-user-visible change has occurred.
+ *
+ * Since: 2.32
+ */
+
+G_DEFINE_INTERFACE_WITH_CODE (GNetworkMonitor, g_network_monitor, G_TYPE_OBJECT,
+ g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_INITABLE);)
+
+
+enum {
+ NETWORK_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/**
+ * g_network_monitor_get_default:
+ *
+ * Gets the default #GNetworkMonitor for the system.
+ *
+ * Returns: (transfer none): a #GNetworkMonitor
+ *
+ * Since: 2.32
+ */
+GNetworkMonitor *
+g_network_monitor_get_default (void)
+{
+ return _g_io_module_get_default (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
+ "GIO_USE_NETWORK_MONITOR",
+ NULL);
+}
+
+/**
+ * g_network_monitor_get_network_available:
+ * @monitor: the #GNetworkMonitor
+ *
+ * Checks if the network is available. "Available" here means that the
+ * system has a default route available for at least one of IPv4 or
+ * IPv6. It does not necessarily imply that the public Internet is
+ * reachable. See #GNetworkMonitor:network-available for more details.
+ *
+ * Return value: whether the network is available
+ *
+ * Since: 2.32
+ */
+gboolean
+g_network_monitor_get_network_available (GNetworkMonitor *monitor)
+{
+ gboolean available = FALSE;
+
+ g_object_get (G_OBJECT (monitor), "network-available", &available, NULL);
+ return available;
+}
+
+/**
+ * g_network_monitor_can_reach:
+ * @monitor: a #GNetworkMonitor
+ * @connectable: a #GSocketConnectable
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Attempts to determine whether or not the host pointed to by
+ * @connectable can be reached, without actually trying to connect to
+ * it.
+ *
+ * This may return %TRUE even when #GNetworkMonitor:network-available
+ * is %FALSE, if, for example, @monitor can determine that
+ * @connectable refers to a host on a local network.
+ *
+ * If @monitor believes that an attempt to connect to @connectable
+ * will succeed, it will return %TRUE. Otherwise, it will return
+ * %FALSE and set @error to an appropriate error (such as
+ * %G_IO_ERROR_HOST_UNREACHABLE).
+ *
+ * Note that although this does not attempt to connect to
+ * @connectable, it may still block for a brief period of time (eg,
+ * trying to do multicast DNS on the local network), so if you do not
+ * want to block, you should use g_network_monitor_can_reach_async().
+ *
+ * Return value: %TRUE if @connectable is reachable, %FALSE if not.
+ *
+ * Since: 2.32
+ */
+gboolean
+g_network_monitor_can_reach (GNetworkMonitor *monitor,
+ GSocketConnectable *connectable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GNetworkMonitorInterface *iface;
+
+ iface = G_NETWORK_MONITOR_GET_INTERFACE (monitor);
+ return iface->can_reach (monitor, connectable, cancellable, error);
+}
+
+static void
+g_network_monitor_real_can_reach_async (GNetworkMonitor *monitor,
+ GSocketConnectable *connectable,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ GError *error = NULL;
+
+ simple = g_simple_async_result_new (G_OBJECT (monitor),
+ callback, user_data,
+ g_network_monitor_real_can_reach_async);
+ if (g_network_monitor_can_reach (monitor, connectable, cancellable, &error))
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ else
+ g_simple_async_result_take_error (simple, error);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+}
+
+void
+g_network_monitor_can_reach_async (GNetworkMonitor *monitor,
+ GSocketConnectable *connectable,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GNetworkMonitorInterface *iface;
+
+ iface = G_NETWORK_MONITOR_GET_INTERFACE (monitor);
+ iface->can_reach_async (monitor, connectable, cancellable, callback, user_data);
+}
+
+static gboolean
+g_network_monitor_real_can_reach_finish (GNetworkMonitor *monitor,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (monitor), g_network_monitor_real_can_reach_async), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+ else
+ return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+gboolean
+g_network_monitor_can_reach_finish (GNetworkMonitor *monitor,
+ GAsyncResult *result,
+ GError **error)
+{
+ GNetworkMonitorInterface *iface;
+
+ iface = G_NETWORK_MONITOR_GET_INTERFACE (monitor);
+ return iface->can_reach_finish (monitor, result, error);
+}
+
+static void
+g_network_monitor_default_init (GNetworkMonitorInterface *iface)
+{
+ iface->can_reach_async = g_network_monitor_real_can_reach_async;
+ iface->can_reach_finish = g_network_monitor_real_can_reach_finish;
+
+ /**
+ * GNetworkMonitor::network-changed:
+ * @monitor: a #GNetworkMonitor
+ * @available: the current value of #GNetworkMonitor:network-available
+ *
+ * Emitted when the network configuration changes. If @available is
+ * %TRUE, then some hosts may be reachable that were not reachable
+ * before, while others that were reachable before may no longer be
+ * reachable. If @available is %FALSE, then no remote hosts are
+ * reachable.
+ *
+ * Since: 2.32
+ */
+ signals[NETWORK_CHANGED] =
+ g_signal_new (I_("network-changed"),
+ G_TYPE_NETWORK_MONITOR,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GNetworkMonitorInterface, network_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1,
+ G_TYPE_BOOLEAN);
+
+ /**
+ * GNetworkMonitor:network-available:
+ *
+ * Whether the network is considered available. That is, whether the
+ * system has a default route for at least one of IPv4 or IPv6.
+ *
+ * Real-world networks are of course much more complicated than
+ * this; the machine may be connected to a wifi hotspot that
+ * requires payment before allowing traffic through, or may be
+ * connected to a functioning router that has lost its own upstream
+ * connectivity. Some hosts might only be accessible when a VPN is
+ * active. Other hosts might only be accessible when the VPN is
+ * <emphasis>not</emphasis> active. Thus, it is best to use
+ * g_network_monitor_can_reach() or
+ * g_network_monitor_can_reach_async() to test for reachability on a
+ * host-by-host basis. (On the other hand, when the property is
+ * %FALSE, the application can reasonably expect that no remote
+ * hosts at all are reachable, and should indicate this to the user
+ * in its UI.)
+ *
+ * See also #GNetworkMonitor::network-changed.
+ *
+ * Since: 2.32
+ */
+ g_object_interface_install_property (iface,
+ g_param_spec_boolean ("network-available",
+ P_("Network available"),
+ P_("Whether the network is available"),
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/gio/gnetworkmonitor.h b/gio/gnetworkmonitor.h
new file mode 100644
index 0000000..e525293
--- /dev/null
+++ b/gio/gnetworkmonitor.h
@@ -0,0 +1,89 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_NETWORK_MONITOR_H__
+#define __G_NETWORK_MONITOR_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+/**
+ * G_NETWORK_MONITOR_EXTENSION_POINT_NAME:
+ *
+ * Extension point for network status monitoring functionality.
+ * See <link linkend="extending-gio">Extending GIO</link>.
+ *
+ * Since: 2.30
+ */
+#define G_NETWORK_MONITOR_EXTENSION_POINT_NAME "gio-network-monitor"
+
+#define G_TYPE_NETWORK_MONITOR (g_network_monitor_get_type ())
+#define G_NETWORK_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_NETWORK_MONITOR, GNetworkMonitor))
+#define G_IS_NETWORK_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_NETWORK_MONITOR))
+#define G_NETWORK_MONITOR_GET_INTERFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_NETWORK_MONITOR, GNetworkMonitorInterface))
+
+typedef struct _GNetworkMonitorInterface GNetworkMonitorInterface;
+
+struct _GNetworkMonitorInterface {
+ GTypeInterface g_iface;
+
+ void (*network_changed) (GNetworkMonitor *monitor,
+ gboolean available);
+
+ gboolean (*can_reach) (GNetworkMonitor *monitor,
+ GSocketConnectable *connectable,
+ GCancellable *cancellable,
+ GError **error);
+ void (*can_reach_async) (GNetworkMonitor *monitor,
+ GSocketConnectable *connectable,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*can_reach_finish) (GNetworkMonitor *monitor,
+ GAsyncResult *result,
+ GError **error);
+};
+
+GType g_network_monitor_get_type (void) G_GNUC_CONST;
+GNetworkMonitor *g_network_monitor_get_default (void);
+
+gboolean g_network_monitor_get_network_available (GNetworkMonitor *monitor);
+
+gboolean g_network_monitor_can_reach (GNetworkMonitor *monitor,
+ GSocketConnectable *connectable,
+ GCancellable *cancellable,
+ GError **error);
+void g_network_monitor_can_reach_async (GNetworkMonitor *monitor,
+ GSocketConnectable *connectable,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean g_network_monitor_can_reach_finish (GNetworkMonitor *monitor,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __G_NETWORK_MONITOR_H__ */
diff --git a/gio/gnetworkmonitorbase.c b/gio/gnetworkmonitorbase.c
new file mode 100644
index 0000000..2ac4115
--- /dev/null
+++ b/gio/gnetworkmonitorbase.c
@@ -0,0 +1,412 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gnetworkmonitorbase.h"
+#include "ginetaddress.h"
+#include "ginetaddressmask.h"
+#include "ginetsocketaddress.h"
+#include "ginitable.h"
+#include "gioerror.h"
+#include "giomodule-priv.h"
+#include "gnetworkmonitor.h"
+#include "gsocketaddressenumerator.h"
+#include "gsocketconnectable.h"
+#include "glibintl.h"
+
+static void g_network_monitor_base_iface_init (GNetworkMonitorInterface *iface);
+static void g_network_monitor_base_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorBase, g_network_monitor_base, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_network_monitor_base_initable_iface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
+ g_network_monitor_base_iface_init)
+ _g_io_modules_ensure_extension_points_registered ();
+ g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
+ g_define_type_id,
+ "base",
+ 0))
+
+enum
+{
+ PROP_0,
+
+ PROP_NETWORK_AVAILABLE
+};
+
+struct _GNetworkMonitorBasePrivate
+{
+ GPtrArray *networks;
+ gboolean have_ipv4_default_route;
+ gboolean have_ipv6_default_route;
+ gboolean is_available;
+
+ GMainContext *context;
+ GSource *network_changed_source;
+ gboolean initializing;
+};
+
+static guint network_changed_signal = 0;
+
+static void queue_network_changed (GNetworkMonitorBase *monitor);
+
+static void
+g_network_monitor_base_init (GNetworkMonitorBase *monitor)
+{
+ monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
+ G_TYPE_NETWORK_MONITOR_BASE,
+ GNetworkMonitorBasePrivate);
+
+ monitor->priv->networks = g_ptr_array_new_with_free_func (g_object_unref);
+ monitor->priv->context = g_main_context_get_thread_default ();
+ if (monitor->priv->context)
+ g_main_context_ref (monitor->priv->context);
+
+ monitor->priv->initializing = TRUE;
+ queue_network_changed (monitor);
+}
+
+static void
+g_network_monitor_base_constructed (GObject *object)
+{
+ GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
+
+ if (G_OBJECT_TYPE (monitor) == G_TYPE_NETWORK_MONITOR_BASE)
+ {
+ GInetAddressMask *mask;
+
+ /* We're the dumb base class, not a smarter subclass. So just
+ * assume that the network is available.
+ */
+ mask = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
+ g_network_monitor_base_add_network (monitor, mask);
+ g_object_unref (mask);
+
+ mask = g_inet_address_mask_new_from_string ("::/0", NULL);
+ g_network_monitor_base_add_network (monitor, mask);
+ g_object_unref (mask);
+ }
+}
+
+static void
+g_network_monitor_base_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
+
+ switch (prop_id)
+ {
+ case PROP_NETWORK_AVAILABLE:
+ g_value_set_boolean (value, monitor->priv->is_available);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+
+}
+
+static void
+g_network_monitor_base_finalize (GObject *object)
+{
+ GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
+
+ g_ptr_array_free (monitor->priv->networks, TRUE);
+ if (monitor->priv->network_changed_source)
+ {
+ g_source_destroy (monitor->priv->network_changed_source);
+ g_source_unref (monitor->priv->network_changed_source);
+ }
+ if (monitor->priv->context)
+ g_main_context_unref (monitor->priv->context);
+
+ G_OBJECT_CLASS (g_network_monitor_base_parent_class)->finalize (object);
+}
+
+static void
+g_network_monitor_base_class_init (GNetworkMonitorBaseClass *monitor_class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (monitor_class);
+
+ g_type_class_add_private (monitor_class, sizeof (GNetworkMonitorBasePrivate));
+
+ gobject_class->constructed = g_network_monitor_base_constructed;
+ gobject_class->get_property = g_network_monitor_base_get_property;
+ gobject_class->finalize = g_network_monitor_base_finalize;
+
+ g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
+}
+
+static gboolean
+g_network_monitor_base_can_reach (GNetworkMonitor *monitor,
+ GSocketConnectable *connectable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GNetworkMonitorBasePrivate *priv = G_NETWORK_MONITOR_BASE (monitor)->priv;
+ GSocketAddressEnumerator *enumerator;
+ GSocketAddress *addr;
+
+ if (priv->have_ipv4_default_route &&
+ priv->have_ipv6_default_route)
+ return TRUE;
+
+ if (priv->networks->len == 0)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
+ _("Network unreachable"));
+ return FALSE;
+ }
+
+ enumerator = g_socket_connectable_proxy_enumerate (connectable);
+ addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
+ if (!addr)
+ {
+ /* Either the user cancelled, or DNS resolution failed */
+ g_object_unref (enumerator);
+ return FALSE;
+ }
+
+ while (addr)
+ {
+ if (G_IS_INET_SOCKET_ADDRESS (addr))
+ {
+ GInetAddress *iaddr;
+ int i;
+
+ iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
+ for (i = 0; i < priv->networks->len; i++)
+ {
+ if (g_inet_address_mask_matches (priv->networks->pdata[i], iaddr))
+ {
+ g_object_unref (addr);
+ g_object_unref (enumerator);
+ return TRUE;
+ }
+ }
+ }
+
+ g_object_unref (addr);
+ addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
+ }
+ g_object_unref (enumerator);
+
+ if (error && !*error)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
+ _("Host unreachable"));
+ }
+ return FALSE;
+}
+
+static void
+g_network_monitor_base_iface_init (GNetworkMonitorInterface *monitor_iface)
+{
+ monitor_iface->can_reach = g_network_monitor_base_can_reach;
+
+ network_changed_signal = g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR);
+}
+
+static gboolean
+g_network_monitor_base_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return TRUE;
+}
+
+static void
+g_network_monitor_base_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = g_network_monitor_base_initable_init;
+}
+
+static gboolean
+emit_network_changed (gpointer user_data)
+{
+ GNetworkMonitorBase *monitor = user_data;
+ gboolean is_available;
+
+ g_object_ref (monitor);
+
+ if (monitor->priv->initializing)
+ monitor->priv->initializing = FALSE;
+ else
+ {
+ is_available = (monitor->priv->have_ipv4_default_route ||
+ monitor->priv->have_ipv6_default_route);
+ if (monitor->priv->is_available != is_available)
+ {
+ monitor->priv->is_available = is_available;
+ g_object_notify (G_OBJECT (monitor), "network-available");
+ }
+
+ g_signal_emit (monitor, network_changed_signal, 0, is_available);
+ }
+
+ g_source_unref (monitor->priv->network_changed_source);
+ monitor->priv->network_changed_source = NULL;
+
+ g_object_unref (monitor);
+ return FALSE;
+}
+
+static void
+queue_network_changed (GNetworkMonitorBase *monitor)
+{
+ if (!monitor->priv->network_changed_source)
+ {
+ GSource *source;
+
+ source = g_idle_source_new ();
+ /* Use G_PRIORITY_HIGH_IDLE priority so that multiple
+ * network-change-related notifications coming in at
+ * G_PRIORITY_DEFAULT will get coalesced into one signal
+ * emission.
+ */
+ g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
+ g_source_set_callback (source, emit_network_changed, monitor, NULL);
+ g_source_attach (source, monitor->priv->context);
+ monitor->priv->network_changed_source = source;
+ }
+
+ /* Normally we wait to update is_available until we emit the signal,
+ * to keep things consistent. But when we're first creating the
+ * object, we want it to be correct right away.
+ */
+ if (monitor->priv->initializing)
+ {
+ monitor->priv->is_available = (monitor->priv->have_ipv4_default_route ||
+ monitor->priv->have_ipv6_default_route);
+ }
+}
+
+/**
+ * g_network_monitor_base_add_network:
+ * @monitor: the #GNetworkMonitorBase
+ * @network: a #GInetAddressMask
+ *
+ * Adds @network to @monitor's list of available networks.
+ */
+void
+g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
+ GInetAddressMask *network)
+{
+ int i;
+
+ for (i = 0; i < monitor->priv->networks->len; i++)
+ {
+ if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
+ return;
+ }
+
+ g_ptr_array_add (monitor->priv->networks, g_object_ref (network));
+ if (g_inet_address_mask_get_length (network) == 0)
+ {
+ switch (g_inet_address_mask_get_family (network))
+ {
+ case G_SOCKET_FAMILY_IPV4:
+ monitor->priv->have_ipv4_default_route = TRUE;
+ break;
+ case G_SOCKET_FAMILY_IPV6:
+ monitor->priv->have_ipv6_default_route = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Don't emit network-changed when multicast-link-local routing
+ * changes. This rather arbitrary decision is mostly because it
+ * seems to change quite often...
+ */
+ if (g_inet_address_get_is_mc_link_local (g_inet_address_mask_get_address (network)))
+ return;
+
+ queue_network_changed (monitor);
+}
+
+/**
+ * g_network_monitor_base_remove_network:
+ * @monitor: the #GNetworkMonitorBase
+ * @network: a #GInetAddressMask
+ *
+ * Removes @network from @monitor's list of available networks.
+ */
+void
+g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
+ GInetAddressMask *network)
+{
+ int i;
+
+ for (i = 0; i < monitor->priv->networks->len; i++)
+ {
+ if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
+ {
+ g_ptr_array_remove_index_fast (monitor->priv->networks, i);
+
+ if (g_inet_address_mask_get_length (network) == 0)
+ {
+ switch (g_inet_address_mask_get_family (network))
+ {
+ case G_SOCKET_FAMILY_IPV4:
+ monitor->priv->have_ipv4_default_route = FALSE;
+ break;
+ case G_SOCKET_FAMILY_IPV6:
+ monitor->priv->have_ipv6_default_route = FALSE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ queue_network_changed (monitor);
+ return;
+ }
+ }
+}
+
+/**
+ * g_network_monitor_base_set_networks:
+ * @monitor: the #GNetworkMonitorBase
+ * @networks: (array length=length): an array of #GInetAddressMask
+ * @length: length of @networks
+ *
+ * Drops @monitor's current list of available networks and replaces
+ * it with @networks.
+ */
+void
+g_network_monitor_base_set_networks (GNetworkMonitorBase *monitor,
+ GInetAddressMask **networks,
+ gint length)
+{
+ int i;
+
+ g_ptr_array_set_size (monitor->priv->networks, 0);
+ monitor->priv->have_ipv4_default_route = FALSE;
+ monitor->priv->have_ipv6_default_route = FALSE;
+
+ for (i = 0; i < length; i++)
+ g_network_monitor_base_add_network (monitor, networks[i]);
+}
diff --git a/gio/gnetworkmonitorbase.h b/gio/gnetworkmonitorbase.h
new file mode 100644
index 0000000..47ef336
--- /dev/null
+++ b/gio/gnetworkmonitorbase.h
@@ -0,0 +1,66 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_NETWORK_MONITOR_BASE_H__
+#define __G_NETWORK_MONITOR_BASE_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_NETWORK_MONITOR_BASE (g_network_monitor_base_get_type ())
+#define G_NETWORK_MONITOR_BASE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_NETWORK_MONITOR_BASE, GNetworkMonitorBase))
+#define G_NETWORK_MONITOR_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_NETWORK_MONITOR_BASE, GNetworkMonitorBaseClass))
+#define G_IS_NETWORK_MONITOR_BASE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_NETWORK_MONITOR_BASE))
+#define G_IS_NETWORK_MONITOR_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_NETWORK_MONITOR_BASE))
+#define G_NETWORK_MONITOR_BASE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_NETWORK_MONITOR_BASE, GNetworkMonitorBaseClass))
+
+typedef struct _GNetworkMonitorBase GNetworkMonitorBase;
+typedef struct _GNetworkMonitorBaseClass GNetworkMonitorBaseClass;
+typedef struct _GNetworkMonitorBasePrivate GNetworkMonitorBasePrivate;
+
+struct _GNetworkMonitorBase {
+ GObject parent_instance;
+
+ GNetworkMonitorBasePrivate *priv;
+};
+
+struct _GNetworkMonitorBaseClass {
+ GObjectClass parent_class;
+
+ /*< private >*/
+ /* Padding for future expansion */
+ gpointer padding[8];
+};
+
+GType g_network_monitor_base_get_type (void);
+
+/*< protected >*/
+void g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
+ GInetAddressMask *network);
+void g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
+ GInetAddressMask *network);
+void g_network_monitor_base_set_networks (GNetworkMonitorBase *monitor,
+ GInetAddressMask **networks,
+ gint length);
+
+G_END_DECLS
+
+#endif /* __G_NETWORK_MONITOR_BASE_H__ */
diff --git a/gio/gnetworkmonitornetlink.c b/gio/gnetworkmonitornetlink.c
new file mode 100644
index 0000000..da60b8a
--- /dev/null
+++ b/gio/gnetworkmonitornetlink.c
@@ -0,0 +1,465 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include "gnetworkmonitornetlink.h"
+#include "gcredentials.h"
+#include "ginetaddressmask.h"
+#include "ginitable.h"
+#include "giomodule-priv.h"
+#include "glibintl.h"
+#include "gnetworkingprivate.h"
+#include "gnetworkmonitor.h"
+#include "gsocket.h"
+#include "gunixcredentialsmessage.h"
+
+static void g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *iface);
+static void g_network_monitor_netlink_initable_iface_init (GInitableIface *iface);
+
+#define g_network_monitor_netlink_get_type _g_network_monitor_netlink_get_type
+G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE,
+ G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
+ g_network_monitor_netlink_iface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_network_monitor_netlink_initable_iface_init)
+ _g_io_modules_ensure_extension_points_registered ();
+ g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
+ g_define_type_id,
+ "netlink",
+ 20))
+
+struct _GNetworkMonitorNetlinkPrivate
+{
+ GSocket *sock;
+ GSource *source, *dump_source;
+
+ GPtrArray *dump_networks;
+};
+
+static gboolean read_netlink_messages (GSocket *socket,
+ GIOCondition condition,
+ gpointer user_data);
+static gboolean request_dump (GNetworkMonitorNetlink *nl,
+ GError **error);
+
+static void
+g_network_monitor_netlink_init (GNetworkMonitorNetlink *nl)
+{
+ nl->priv = G_TYPE_INSTANCE_GET_PRIVATE (nl,
+ G_TYPE_NETWORK_MONITOR_NETLINK,
+ GNetworkMonitorNetlinkPrivate);
+}
+
+
+static gboolean
+g_network_monitor_netlink_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable);
+ gint sockfd, val;
+ struct sockaddr_nl snl;
+
+ /* We create the socket the old-school way because sockaddr_netlink
+ * can't be represented as a GSocketAddress
+ */
+ sockfd = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sockfd == -1)
+ {
+ int errsv = errno;
+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+ _("Could not create netlink socket: %s"),
+ g_strerror (errno));
+ return FALSE;
+ }
+
+ snl.nl_family = AF_NETLINK;
+ snl.nl_pid = snl.nl_pad = 0;
+ snl.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
+ if (bind (sockfd, (struct sockaddr *)&snl, sizeof (snl)) != 0)
+ {
+ int errsv = errno;
+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+ _("Could not bind netlink socket: %s"),
+ g_strerror (errno));
+ close (sockfd);
+ return FALSE;
+ }
+
+ val = 1;
+ if (setsockopt (sockfd, SOL_SOCKET, SO_PASSCRED, &val, sizeof (val)) != 0)
+ {
+ int errsv = errno;
+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+ _("Could not set options on netlink socket: %s"),
+ g_strerror (errno));
+ close (sockfd);
+ return FALSE;
+ }
+
+ nl->priv->sock = g_socket_new_from_fd (sockfd, error);
+ if (error)
+ {
+ g_prefix_error (error, "%s", _("Could not wrap netlink socket: "));
+ close (sockfd);
+ return FALSE;
+ }
+
+ /* Request the current state */
+ if (!request_dump (nl, error))
+ return FALSE;
+
+ /* And read responses; since we haven't yet marked the socket
+ * non-blocking, each call will block until a message is received.
+ */
+ while (nl->priv->dump_networks)
+ {
+ if (!read_netlink_messages (NULL, G_IO_IN, nl))
+ break;
+ }
+
+ g_socket_set_blocking (nl->priv->sock, FALSE);
+ nl->priv->source = g_socket_create_source (nl->priv->sock, G_IO_IN, NULL);
+ g_source_set_callback (nl->priv->source,
+ (GSourceFunc) read_netlink_messages, nl, NULL);
+ g_source_attach (nl->priv->source,
+ g_main_context_get_thread_default ());
+
+ return TRUE;
+}
+
+static gboolean
+request_dump (GNetworkMonitorNetlink *nl,
+ GError **error)
+{
+ struct nlmsghdr *n;
+ struct rtgenmsg *gen;
+ gchar buf[NLMSG_SPACE (sizeof (*gen))];
+
+ memset (buf, 0, sizeof (buf));
+ n = (struct nlmsghdr*) buf;
+ n->nlmsg_len = NLMSG_LENGTH (sizeof (*gen));
+ n->nlmsg_type = RTM_GETROUTE;
+ n->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ n->nlmsg_pid = 0;
+ gen = NLMSG_DATA (n);
+ gen->rtgen_family = AF_UNSPEC;
+
+ if (g_socket_send (nl->priv->sock, buf, sizeof (buf),
+ NULL, error) < 0)
+ {
+ g_prefix_error (error, "%s", _("Could not send netlink request: "));
+ return FALSE;
+ }
+
+ nl->priv->dump_networks = g_ptr_array_new_with_free_func (g_object_unref);
+ return TRUE;
+}
+
+static gboolean
+timeout_request_dump (gpointer user_data)
+{
+ GNetworkMonitorNetlink *nl = user_data;
+
+ g_source_destroy (nl->priv->dump_source);
+ g_source_unref (nl->priv->dump_source);
+ nl->priv->dump_source = NULL;
+
+ request_dump (nl, NULL);
+
+ return FALSE;
+}
+
+static void
+queue_request_dump (GNetworkMonitorNetlink *nl)
+{
+ if (nl->priv->dump_networks)
+ return;
+
+ if (nl->priv->dump_source)
+ {
+ g_source_destroy (nl->priv->dump_source);
+ g_source_unref (nl->priv->dump_source);
+ }
+
+ nl->priv->dump_source = g_timeout_source_new (1000);
+ g_source_set_callback (nl->priv->dump_source,
+ (GSourceFunc) timeout_request_dump, nl, NULL);
+ g_source_attach (nl->priv->dump_source,
+ g_main_context_get_thread_default ());
+}
+
+static void
+add_network (GNetworkMonitorNetlink *nl,
+ GSocketFamily family,
+ gint dest_len,
+ guint8 *dest,
+ guint8 *gateway)
+{
+ GInetAddress *dest_addr;
+ GInetAddressMask *network;
+
+ if (dest)
+ dest_addr = g_inet_address_new_from_bytes (dest, family);
+ else
+ dest_addr = g_inet_address_new_any (family);
+ network = g_inet_address_mask_new (dest_addr, dest_len, NULL);
+ g_object_unref (dest_addr);
+ g_return_if_fail (network != NULL);
+
+ if (nl->priv->dump_networks)
+ g_ptr_array_add (nl->priv->dump_networks, network);
+ else
+ {
+ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), network);
+ g_object_unref (network);
+ }
+}
+
+static void
+remove_network (GNetworkMonitorNetlink *nl,
+ GSocketFamily family,
+ gint dest_len,
+ guint8 *dest,
+ guint8 *gateway)
+{
+ GInetAddress *dest_addr;
+ GInetAddressMask *network;
+
+ if (dest)
+ dest_addr = g_inet_address_new_from_bytes (dest, family);
+ else
+ dest_addr = g_inet_address_new_any (family);
+ network = g_inet_address_mask_new (dest_addr, dest_len, NULL);
+ g_object_unref (dest_addr);
+ g_return_if_fail (network != NULL);
+
+ if (nl->priv->dump_networks)
+ {
+ GInetAddressMask **dump_networks = (GInetAddressMask **)nl->priv->dump_networks->pdata;
+ int i;
+
+ for (i = 0; i < nl->priv->dump_networks->len; i++)
+ {
+ if (g_inet_address_mask_equal (network, dump_networks[i]))
+ g_ptr_array_remove_index_fast (nl->priv->dump_networks, i--);
+ }
+ g_object_unref (network);
+ }
+ else
+ {
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (nl), network);
+ g_object_unref (network);
+ }
+}
+
+static void
+finish_dump (GNetworkMonitorNetlink *nl)
+{
+ g_network_monitor_base_set_networks (G_NETWORK_MONITOR_BASE (nl),
+ (GInetAddressMask **)nl->priv->dump_networks->pdata,
+ nl->priv->dump_networks->len);
+ g_ptr_array_free (nl->priv->dump_networks, FALSE);
+ nl->priv->dump_networks = NULL;
+}
+
+static gboolean
+read_netlink_messages (GSocket *socket,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ GNetworkMonitorNetlink *nl = user_data;
+ GInputVector iv;
+ gsize len;
+ GSocketControlMessage **cmsgs = NULL;
+ gint num_cmsgs = 0, i, flags;
+ GError *error = NULL;
+ GCredentials *creds;
+ uid_t sender;
+ struct nlmsghdr *msg;
+ struct rtmsg *rtmsg;
+ struct rtattr *attr;
+ gsize attrlen;
+ guint8 *dest, *gateway;
+ gboolean retval = TRUE;
+
+ iv.buffer = NULL;
+ iv.size = 0;
+
+ flags = MSG_PEEK | MSG_TRUNC;
+ len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
+ NULL, NULL, &flags, NULL, &error);
+ if (len < 0)
+ {
+ g_warning ("Error on netlink socket: %s", error->message);
+ g_error_free (error);
+ if (nl->priv->dump_networks)
+ finish_dump (nl);
+ return FALSE;
+ }
+
+ iv.buffer = g_malloc (len);
+ iv.size = len;
+ len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
+ &cmsgs, &num_cmsgs, NULL, NULL, &error);
+ if (len < 0)
+ {
+ g_warning ("Error on netlink socket: %s", error->message);
+ g_error_free (error);
+ if (nl->priv->dump_networks)
+ finish_dump (nl);
+ return FALSE;
+ }
+
+ if (num_cmsgs != 1 || !G_IS_UNIX_CREDENTIALS_MESSAGE (cmsgs[0]))
+ goto done;
+
+ creds = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (cmsgs[0]));
+ sender = g_credentials_get_unix_user (creds, NULL);
+ if (sender != 0)
+ goto done;
+
+ msg = (struct nlmsghdr *) iv.buffer;
+ for (; len > 0; msg = NLMSG_NEXT (msg, len))
+ {
+ if (!NLMSG_OK (msg, (size_t) len))
+ {
+ g_warning ("netlink message was truncated; shouldn't happen...");
+ retval = FALSE;
+ goto done;
+ }
+
+ switch (msg->nlmsg_type)
+ {
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ rtmsg = NLMSG_DATA (msg);
+
+ if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6)
+ continue;
+ if (rtmsg->rtm_type == RTN_UNREACHABLE)
+ continue;
+
+ attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg));
+ attr = RTM_RTA (rtmsg);
+ dest = gateway = NULL;
+ while (RTA_OK (attr, attrlen))
+ {
+ if (attr->rta_type == RTA_DST)
+ dest = RTA_DATA (attr);
+ else if (attr->rta_type == RTA_GATEWAY)
+ gateway = RTA_DATA (attr);
+ attr = RTA_NEXT (attr, attrlen);
+ }
+
+ if (dest || gateway)
+ {
+ if (msg->nlmsg_type == RTM_NEWROUTE)
+ add_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest, gateway);
+ else
+ remove_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest, gateway);
+ queue_request_dump (nl);
+ }
+ break;
+
+ case NLMSG_DONE:
+ finish_dump (nl);
+ goto done;
+
+ case NLMSG_ERROR:
+ {
+ struct nlmsgerr *e = NLMSG_DATA (msg);
+
+ g_warning ("netlink error: %s", g_strerror (-e->error));
+ }
+ retval = FALSE;
+ goto done;
+
+ default:
+ g_warning ("unexpected netlink message %d", msg->nlmsg_type);
+ retval = FALSE;
+ goto done;
+ }
+ }
+
+ done:
+ for (i = 0; i < num_cmsgs; i++)
+ g_object_unref (cmsgs[i]);
+ g_free (cmsgs);
+
+ g_free (iv.buffer);
+
+ if (!retval && nl->priv->dump_networks)
+ finish_dump (nl);
+ return retval;
+}
+
+static void
+g_network_monitor_netlink_finalize (GObject *object)
+{
+ GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (object);
+
+ if (nl->priv->sock)
+ {
+ g_socket_close (nl->priv->sock, NULL);
+ g_object_unref (nl->priv->sock);
+ }
+
+ if (nl->priv->source)
+ {
+ g_source_destroy (nl->priv->source);
+ g_source_unref (nl->priv->source);
+ }
+
+ if (nl->priv->dump_source)
+ {
+ g_source_destroy (nl->priv->dump_source);
+ g_source_unref (nl->priv->dump_source);
+ }
+
+ G_OBJECT_CLASS (g_network_monitor_netlink_parent_class)->finalize (object);
+}
+
+static void
+g_network_monitor_netlink_class_init (GNetworkMonitorNetlinkClass *nl_class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
+
+ g_type_class_add_private (nl_class, sizeof (GNetworkMonitorNetlinkPrivate));
+
+ gobject_class->finalize = g_network_monitor_netlink_finalize;
+}
+
+static void
+g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *monitor_iface)
+{
+}
+
+static void
+g_network_monitor_netlink_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = g_network_monitor_netlink_initable_init;
+}
diff --git a/gio/gnetworkmonitornetlink.h b/gio/gnetworkmonitornetlink.h
new file mode 100644
index 0000000..f6fa22a
--- /dev/null
+++ b/gio/gnetworkmonitornetlink.h
@@ -0,0 +1,57 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_NETWORK_MONITOR_NETLINK_H__
+#define __G_NETWORK_MONITOR_NETLINK_H__
+
+#include "gnetworkmonitorbase.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_NETWORK_MONITOR_NETLINK (_g_network_monitor_netlink_get_type ())
+#define G_NETWORK_MONITOR_NETLINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_NETWORK_MONITOR_NETLINK, GNetworkMonitorNetlink))
+#define G_NETWORK_MONITOR_NETLINK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_NETWORK_MONITOR_NETLINK, GNetworkMonitorNetlinkClass))
+#define G_IS_NETWORK_MONITOR_NETLINK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_NETWORK_MONITOR_NETLINK))
+#define G_IS_NETWORK_MONITOR_NETLINK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_NETWORK_MONITOR_NETLINK))
+#define G_NETWORK_MONITOR_NETLINK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_NETWORK_MONITOR_NETLINK, GNetworkMonitorNetlinkClass))
+
+typedef struct _GNetworkMonitorNetlink GNetworkMonitorNetlink;
+typedef struct _GNetworkMonitorNetlinkClass GNetworkMonitorNetlinkClass;
+typedef struct _GNetworkMonitorNetlinkPrivate GNetworkMonitorNetlinkPrivate;
+
+struct _GNetworkMonitorNetlink {
+ GNetworkMonitorBase parent_instance;
+
+ GNetworkMonitorNetlinkPrivate *priv;
+};
+
+struct _GNetworkMonitorNetlinkClass {
+ GNetworkMonitorBaseClass parent_class;
+
+ /*< private >*/
+ /* Padding for future expansion */
+ gpointer padding[8];
+};
+
+GType _g_network_monitor_netlink_get_type (void);
+
+G_END_DECLS
+
+#endif /* __G_NETWORK_MONITOR_NETLINK_H__ */
diff --git a/gio/tests/.gitignore b/gio/tests/.gitignore
index 8fdd74f..e3961af 100644
--- a/gio/tests/.gitignore
+++ b/gio/tests/.gitignore
@@ -70,6 +70,7 @@ live-g-file
memory-input-stream
memory-output-stream
mimeapps
+network-monitor
network-address
org.gtk.test.enums.xml
pollable
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 2091366..8d23c7a 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -56,6 +56,7 @@ TEST_PROGS += \
tls-interaction \
cancellable \
vfs \
+ network-monitor \
$(NULL)
if OS_UNIX
@@ -492,6 +493,8 @@ cancellable_LDADD = $(progs_ldadd)
vfs_LDADD = $(progs_ldadd)
+network_monitor_LDADD = $(progs_ldadd)
+
# -----------------------------------------------------------------------------
if OS_UNIX
diff --git a/gio/tests/network-monitor.c b/gio/tests/network-monitor.c
new file mode 100644
index 0000000..0ba9ec4
--- /dev/null
+++ b/gio/tests/network-monitor.c
@@ -0,0 +1,463 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gio.h"
+
+/* hack */
+#define GIO_COMPILATION
+#include "gnetworkmonitorbase.h"
+
+#include <string.h>
+
+/* Test data; the GInetAddresses and GInetAddressMasks get filled in
+ * in main(). Each address in a TestAddress matches the mask in its
+ * corresponding TestMask, and none of them match any of the other
+ * masks. The addresses in unmatched don't match any of the masks.
+ */
+
+typedef struct {
+ const char *string;
+ GInetAddress *address;
+} TestAddress;
+
+typedef struct {
+ const char *mask_string;
+ GInetAddressMask *mask;
+ TestAddress *addresses;
+} TestMask;
+
+TestAddress net127addrs[] = {
+ { "127.0.0.1", NULL },
+ { "127.0.0.2", NULL },
+ { "127.0.0.255", NULL },
+ { "127.0.1.0", NULL },
+ { "127.0.255.0", NULL },
+ { "127.0.255.0", NULL },
+ { "127.255.255.255", NULL },
+ { NULL, NULL }
+};
+TestMask net127 = { "127.0.0.0/8", NULL, net127addrs };
+
+TestAddress net10addrs[] = {
+ { "10.0.0.1", NULL },
+ { "10.0.0.2", NULL },
+ { "10.0.0.255", NULL },
+ { NULL, NULL }
+};
+TestMask net10 = { "10.0.0.0/24", NULL, net10addrs };
+
+TestAddress net192addrs[] = {
+ { "192.168.0.1", NULL },
+ { "192.168.0.2", NULL },
+ { "192.168.0.255", NULL },
+ { "192.168.1.0", NULL },
+ { "192.168.15.0", NULL },
+ { NULL, NULL }
+};
+TestMask net192 = { "192.168.0.0/20", NULL, net192addrs };
+
+TestAddress netlocal6addrs[] = {
+ { "::1", NULL },
+ { NULL, NULL }
+};
+TestMask netlocal6 = { "::1/128", NULL, netlocal6addrs };
+
+TestAddress netfe80addrs[] = {
+ { "fe80::", NULL },
+ { "fe80::1", NULL },
+ { "fe80::21b:77ff:fea2:972a", NULL },
+ { NULL, NULL }
+};
+TestMask netfe80 = { "fe80::/64", NULL, netfe80addrs };
+
+TestAddress unmatched[] = {
+ { "10.0.1.0", NULL },
+ { "10.0.255.0", NULL },
+ { "10.255.255.255", NULL },
+ { "192.168.16.0", NULL },
+ { "192.168.255.0", NULL },
+ { "192.169.0.0", NULL },
+ { "192.255.255.255", NULL },
+ { "::2", NULL },
+ { "1::1", NULL },
+ { "fe80::1:0:0:0:0", NULL },
+ { "fe80:8000::0:0:0:0", NULL },
+ { NULL, NULL }
+};
+
+GInetAddressMask *ip4_default, *ip6_default;
+
+static void
+notify_handler (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ gboolean *emitted = user_data;
+
+ *emitted = TRUE;
+}
+
+static void
+network_changed_handler (GNetworkMonitor *monitor,
+ gboolean available,
+ gpointer user_data)
+{
+ gboolean *emitted = user_data;
+
+ *emitted = TRUE;
+}
+
+static void
+assert_signals (GNetworkMonitor *monitor,
+ gboolean should_emit_notify,
+ gboolean should_emit_network_changed,
+ gboolean expected_network_available)
+{
+ gboolean emitted_notify = FALSE, emitted_network_changed = FALSE;
+ guint h1, h2;
+
+ h1 = g_signal_connect (monitor, "notify::network-available",
+ G_CALLBACK (notify_handler),
+ &emitted_notify);
+ h2 = g_signal_connect (monitor, "network-changed",
+ G_CALLBACK (network_changed_handler),
+ &emitted_network_changed);
+
+ g_main_context_iteration (NULL, FALSE);
+
+ g_signal_handler_disconnect (monitor, h1);
+ g_signal_handler_disconnect (monitor, h2);
+
+ g_assert (emitted_notify == should_emit_notify);
+ g_assert (emitted_network_changed == should_emit_network_changed);
+
+ g_assert (g_network_monitor_get_network_available (monitor) == expected_network_available);
+}
+
+static void
+run_tests (GNetworkMonitor *monitor,
+ TestAddress *addresses,
+ gboolean should_be_reachable)
+{
+ GError *error = NULL;
+ int i;
+ gboolean reachable;
+ GSocketAddress *sockaddr;
+
+ for (i = 0; addresses[i].address; i++)
+ {
+ sockaddr = g_inet_socket_address_new (addresses[i].address, 0);
+ reachable = g_network_monitor_can_reach (monitor,
+ G_SOCKET_CONNECTABLE (sockaddr),
+ NULL, &error);
+ g_object_unref (sockaddr);
+ g_assert_cmpint (reachable, ==, should_be_reachable);
+ if (should_be_reachable)
+ g_assert_no_error (error);
+ else
+ {
+ g_assert (error != NULL);
+ g_clear_error (&error);
+ }
+ }
+}
+
+static void
+test_default (void)
+{
+ GNetworkMonitor *monitor;
+ GError *error = NULL;
+
+ monitor = g_initable_new (G_TYPE_NETWORK_MONITOR_BASE, NULL, &error, NULL);
+ g_assert_no_error (error);
+
+ /* In the default configuration, all addresses are reachable */
+ run_tests (monitor, net127.addresses, TRUE);
+ run_tests (monitor, net10.addresses, TRUE);
+ run_tests (monitor, net192.addresses, TRUE);
+ run_tests (monitor, netlocal6.addresses, TRUE);
+ run_tests (monitor, netfe80.addresses, TRUE);
+ run_tests (monitor, unmatched, TRUE);
+
+ assert_signals (monitor, FALSE, FALSE, TRUE);
+}
+
+static void
+test_remove_default (void)
+{
+ GNetworkMonitor *monitor;
+ GError *error = NULL;
+
+ monitor = g_initable_new (G_TYPE_NETWORK_MONITOR_BASE, NULL, &error, NULL);
+ g_assert_no_error (error);
+ assert_signals (monitor, FALSE, FALSE, TRUE);
+
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
+ ip4_default);
+ assert_signals (monitor, FALSE, TRUE, TRUE);
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
+ ip6_default);
+ assert_signals (monitor, TRUE, TRUE, FALSE);
+
+ /* Now nothing should be reachable */
+ run_tests (monitor, net127.addresses, FALSE);
+ run_tests (monitor, net10.addresses, FALSE);
+ run_tests (monitor, net192.addresses, FALSE);
+ run_tests (monitor, netlocal6.addresses, FALSE);
+ run_tests (monitor, netfe80.addresses, FALSE);
+ run_tests (monitor, unmatched, FALSE);
+}
+
+static void
+test_add_networks (void)
+{
+ GNetworkMonitor *monitor;
+ GError *error = NULL;
+
+ monitor = g_initable_new (G_TYPE_NETWORK_MONITOR_BASE, NULL, &error, NULL);
+ g_assert_no_error (error);
+ assert_signals (monitor, FALSE, FALSE, TRUE);
+
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
+ ip4_default);
+ assert_signals (monitor, FALSE, TRUE, TRUE);
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
+ ip6_default);
+ assert_signals (monitor, TRUE, TRUE, FALSE);
+
+ /* Now add the masks one by one */
+
+ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
+ net127.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+
+ run_tests (monitor, net127.addresses, TRUE);
+ run_tests (monitor, net10.addresses, FALSE);
+ run_tests (monitor, net192.addresses, FALSE);
+ run_tests (monitor, netlocal6.addresses, FALSE);
+ run_tests (monitor, netfe80.addresses, FALSE);
+ run_tests (monitor, unmatched, FALSE);
+
+ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
+ net10.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ run_tests (monitor, net127.addresses, TRUE);
+ run_tests (monitor, net10.addresses, TRUE);
+ run_tests (monitor, net192.addresses, FALSE);
+ run_tests (monitor, netlocal6.addresses, FALSE);
+ run_tests (monitor, netfe80.addresses, FALSE);
+ run_tests (monitor, unmatched, FALSE);
+
+ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
+ net192.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ run_tests (monitor, net127.addresses, TRUE);
+ run_tests (monitor, net10.addresses, TRUE);
+ run_tests (monitor, net192.addresses, TRUE);
+ run_tests (monitor, netlocal6.addresses, FALSE);
+ run_tests (monitor, netfe80.addresses, FALSE);
+ run_tests (monitor, unmatched, FALSE);
+
+ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
+ netlocal6.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ run_tests (monitor, net127.addresses, TRUE);
+ run_tests (monitor, net10.addresses, TRUE);
+ run_tests (monitor, net192.addresses, TRUE);
+ run_tests (monitor, netlocal6.addresses, TRUE);
+ run_tests (monitor, netfe80.addresses, FALSE);
+ run_tests (monitor, unmatched, FALSE);
+
+ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
+ netfe80.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ run_tests (monitor, net127.addresses, TRUE);
+ run_tests (monitor, net10.addresses, TRUE);
+ run_tests (monitor, net192.addresses, TRUE);
+ run_tests (monitor, netlocal6.addresses, TRUE);
+ run_tests (monitor, netfe80.addresses, TRUE);
+ run_tests (monitor, unmatched, FALSE);
+}
+
+static void
+test_remove_networks (void)
+{
+ GNetworkMonitor *monitor;
+ GError *error = NULL;
+
+ monitor = g_initable_new (G_TYPE_NETWORK_MONITOR_BASE, NULL, &error, NULL);
+ g_assert_no_error (error);
+ assert_signals (monitor, FALSE, FALSE, TRUE);
+
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
+ ip4_default);
+ assert_signals (monitor, FALSE, TRUE, TRUE);
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
+ ip6_default);
+ assert_signals (monitor, TRUE, TRUE, FALSE);
+
+ /* First add them */
+ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
+ net127.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
+ net10.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
+ net192.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
+ netlocal6.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
+ netfe80.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+
+ run_tests (monitor, net127.addresses, TRUE);
+ run_tests (monitor, net10.addresses, TRUE);
+ run_tests (monitor, net192.addresses, TRUE);
+ run_tests (monitor, netlocal6.addresses, TRUE);
+ run_tests (monitor, netfe80.addresses, TRUE);
+ run_tests (monitor, unmatched, FALSE);
+
+ /* Now remove them one by one */
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
+ net127.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ run_tests (monitor, net127.addresses, FALSE);
+ run_tests (monitor, net10.addresses, TRUE);
+ run_tests (monitor, net192.addresses, TRUE);
+ run_tests (monitor, netlocal6.addresses, TRUE);
+ run_tests (monitor, netfe80.addresses, TRUE);
+ run_tests (monitor, unmatched, FALSE);
+
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
+ net10.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ run_tests (monitor, net127.addresses, FALSE);
+ run_tests (monitor, net10.addresses, FALSE);
+ run_tests (monitor, net192.addresses, TRUE);
+ run_tests (monitor, netlocal6.addresses, TRUE);
+ run_tests (monitor, netfe80.addresses, TRUE);
+ run_tests (monitor, unmatched, FALSE);
+
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
+ net192.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ run_tests (monitor, net127.addresses, FALSE);
+ run_tests (monitor, net10.addresses, FALSE);
+ run_tests (monitor, net192.addresses, FALSE);
+ run_tests (monitor, netlocal6.addresses, TRUE);
+ run_tests (monitor, netfe80.addresses, TRUE);
+ run_tests (monitor, unmatched, FALSE);
+
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
+ netlocal6.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ run_tests (monitor, net127.addresses, FALSE);
+ run_tests (monitor, net10.addresses, FALSE);
+ run_tests (monitor, net192.addresses, FALSE);
+ run_tests (monitor, netlocal6.addresses, FALSE);
+ run_tests (monitor, netfe80.addresses, TRUE);
+ run_tests (monitor, unmatched, FALSE);
+
+ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
+ netfe80.mask);
+ assert_signals (monitor, FALSE, TRUE, FALSE);
+ run_tests (monitor, net127.addresses, FALSE);
+ run_tests (monitor, net10.addresses, FALSE);
+ run_tests (monitor, net192.addresses, FALSE);
+ run_tests (monitor, netlocal6.addresses, FALSE);
+ run_tests (monitor, netfe80.addresses, FALSE);
+ run_tests (monitor, unmatched, FALSE);
+}
+
+
+static void
+init_test (TestMask *test)
+{
+ GError *error = NULL;
+ int i;
+
+ test->mask = g_inet_address_mask_new_from_string (test->mask_string, &error);
+ g_assert_no_error (error);
+
+ for (i = 0; test->addresses[i].string; i++)
+ {
+ test->addresses[i].address = g_inet_address_new_from_string (test->addresses[i].string);
+ if (strchr (test->addresses[i].string, ':'))
+ g_assert_cmpint (g_inet_address_get_family (test->addresses[i].address), ==, G_SOCKET_FAMILY_IPV6);
+ else
+ g_assert_cmpint (g_inet_address_get_family (test->addresses[i].address), ==, G_SOCKET_FAMILY_IPV4);
+ }
+}
+
+static void
+watch_network_changed (GNetworkMonitor *monitor,
+ gboolean available,
+ gpointer user_data)
+{
+ g_print ("Network is %s\n", available ? "up" : "down");
+}
+
+static void
+do_watch_network (void)
+{
+ GNetworkMonitor *monitor = g_network_monitor_get_default ();
+ GMainLoop *loop;
+
+ g_print ("Monitoring via %s\n", g_type_name_from_instance ((GTypeInstance *) monitor));
+
+ g_signal_connect (monitor, "network-changed",
+ G_CALLBACK (watch_network_changed), NULL);
+ watch_network_changed (monitor, g_network_monitor_get_network_available (monitor), NULL);
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+}
+
+int
+main (int argc, char **argv)
+{
+ g_type_init ();
+
+ if (argc == 2 && !strcmp (argv[1], "--watch"))
+ {
+ do_watch_network ();
+ return 0;
+ }
+
+ g_test_init (&argc, &argv, NULL);
+
+ init_test (&net127);
+ init_test (&net10);
+ init_test (&net192);
+ init_test (&netlocal6);
+ init_test (&netfe80);
+ ip4_default = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
+ ip6_default = g_inet_address_mask_new_from_string ("::/0", NULL);
+
+ g_test_add_func ("/network-monitor/default", test_default);
+ g_test_add_func ("/network-monitor/remove_default", test_remove_default);
+ g_test_add_func ("/network-monitor/add_networks", test_add_networks);
+ g_test_add_func ("/network-monitor/remove_networks", test_remove_networks);
+
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]