From 485a6900fcd481f0899e4a775c96d5a34b62cb90 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 3 Dec 2014 18:36:25 +0100 Subject: [PATCH] gio: Add GNetworkMonitor impl based on NetworkManager Which implements the new GNetworkConnectivity property. https://bugzilla.gnome.org/show_bug.cgi?id=664562 --- gio/Makefile.am | 2 + gio/giomodule.c | 2 + gio/gnetworkmonitornm.c | 307 ++++++++++++++++++++++++++++++++++++++++ gio/gnetworkmonitornm.h | 53 +++++++ 4 files changed, 364 insertions(+) create mode 100644 gio/gnetworkmonitornm.c create mode 100644 gio/gnetworkmonitornm.h diff --git a/gio/Makefile.am b/gio/Makefile.am index e993e2fef..a4cf8232f 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -291,6 +291,8 @@ if HAVE_NETLINK unix_sources += \ gnetworkmonitornetlink.c \ gnetworkmonitornetlink.h \ + gnetworkmonitornm.c \ + gnetworkmonitornm.h \ $(NULL) endif endif diff --git a/gio/giomodule.c b/gio/giomodule.c index 510f652d8..256cb8311 100644 --- a/gio/giomodule.c +++ b/gio/giomodule.c @@ -891,6 +891,7 @@ 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); +extern GType _g_network_monitor_nm_get_type (void); #endif #ifdef G_OS_UNIX @@ -1092,6 +1093,7 @@ _g_io_modules_ensure_loaded (void) g_type_ensure (g_network_monitor_base_get_type ()); #ifdef HAVE_NETLINK g_type_ensure (_g_network_monitor_netlink_get_type ()); + g_type_ensure (_g_network_monitor_nm_get_type ()); #endif } diff --git a/gio/gnetworkmonitornm.c b/gio/gnetworkmonitornm.c new file mode 100644 index 000000000..02ff8804c --- /dev/null +++ b/gio/gnetworkmonitornm.c @@ -0,0 +1,307 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright 2014 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, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "gnetworkmonitornm.h" +#include "gioerror.h" +#include "ginitable.h" +#include "giomodule-priv.h" +#include "glibintl.h" +#include "glib/gstdio.h" +#include "gnetworkingprivate.h" +#include "gnetworkmonitor.h" +#include "gdbusproxy.h" + +static void g_network_monitor_nm_iface_init (GNetworkMonitorInterface *iface); +static void g_network_monitor_nm_initable_iface_init (GInitableIface *iface); + +enum +{ + PROP_0, + + PROP_NETWORK_AVAILABLE, + PROP_CONNECTIVITY +}; + +typedef enum { + NM_CONNECTIVITY_UNKNOWN, + NM_CONNECTIVITY_NONE, + NM_CONNECTIVITY_PORTAL, + NM_CONNECTIVITY_LIMITED, + NM_CONNECTIVITY_FULL +} NMConnectivityState; + +struct _GNetworkMonitorNMPrivate +{ + GDBusProxy *proxy; + + GNetworkConnectivity connectivity; + gboolean network_available; +}; + +#define g_network_monitor_nm_get_type _g_network_monitor_nm_get_type +G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNM, g_network_monitor_nm, G_TYPE_NETWORK_MONITOR_NETLINK, + G_ADD_PRIVATE (GNetworkMonitorNM) + G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR, + g_network_monitor_nm_iface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + g_network_monitor_nm_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, + "networkmanager", + 30)) + +static void +g_network_monitor_nm_init (GNetworkMonitorNM *nm) +{ + nm->priv = g_network_monitor_nm_get_instance_private (nm); +} + +static void +g_network_monitor_nm_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GNetworkMonitorNM *nm = G_NETWORK_MONITOR_NM (object); + + switch (prop_id) + { + case PROP_NETWORK_AVAILABLE: + g_value_set_boolean (value, nm->priv->network_available); + break; + + case PROP_CONNECTIVITY: + g_value_set_enum (value, nm->priv->connectivity); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GNetworkConnectivity +nm_conn_to_g_conn (int nm_state) +{ + switch (nm_state) + { + case NM_CONNECTIVITY_UNKNOWN: + return G_NETWORK_CONNECTIVITY_LOCAL; + case NM_CONNECTIVITY_NONE: + return G_NETWORK_CONNECTIVITY_LOCAL; + case NM_CONNECTIVITY_PORTAL: + return G_NETWORK_CONNECTIVITY_PORTAL; + case NM_CONNECTIVITY_LIMITED: + return G_NETWORK_CONNECTIVITY_LIMITED; + case NM_CONNECTIVITY_FULL: + return G_NETWORK_CONNECTIVITY_FULL; + default: + g_warning ("Unknown NM connectivity state %d", nm_state); + return G_NETWORK_CONNECTIVITY_LOCAL; + } +} + +static void +sync_properties (GNetworkMonitorNM *nm, + gboolean emit_signals) +{ + GVariant *v; + NMConnectivityState nm_connectivity; + gboolean new_network_available; + GNetworkConnectivity new_connectivity; + + v = g_dbus_proxy_get_cached_property (nm->priv->proxy, "Connectivity"); + nm_connectivity = g_variant_get_uint32 (v); + g_variant_unref (v); + + if (nm_connectivity == NM_CONNECTIVITY_NONE) + { + new_network_available = FALSE; + new_connectivity = G_NETWORK_CONNECTIVITY_LOCAL; + } + else + { + new_network_available = TRUE; + new_connectivity = nm_conn_to_g_conn (nm_connectivity); + } + + if (!emit_signals) + { + nm->priv->network_available = new_network_available; + nm->priv->connectivity = new_connectivity; + return; + } + + if (new_network_available != nm->priv->network_available) + { + nm->priv->network_available = new_network_available; + g_object_notify (G_OBJECT (nm), "network-available"); + } + if (new_connectivity != nm->priv->connectivity) + { + nm->priv->connectivity = new_connectivity; + g_object_notify (G_OBJECT (nm), "connectivity"); + } +} + +static void +update_cached_property (GDBusProxy *proxy, + const char *property_name, + GVariantDict *dict) +{ + GVariant *v; + + v = g_variant_dict_lookup_value (dict, property_name, NULL); + if (!v) + return; + g_dbus_proxy_set_cached_property (proxy, property_name, v); +} + +static void +proxy_signal_cb (GDBusProxy *proxy, + gchar *sender_name, + gchar *signal_name, + GVariant *parameters, + GNetworkMonitorNM *nm) +{ + GVariant *asv; + GVariantDict *dict; + + if (g_strcmp0 (signal_name, "PropertiesChanged") != 0) + return; + + g_variant_get (parameters, "(@a{sv})", &asv); + if (!asv) + return; + + dict = g_variant_dict_new (asv); + if (!dict) + { + g_warning ("Failed to handle PropertiesChanged signal from NetworkManager"); + return; + } + + update_cached_property (nm->priv->proxy, "Connectivity", dict); + + g_variant_dict_unref (dict); + + sync_properties (nm, TRUE); +} + +static gboolean +has_property (GDBusProxy *proxy, + const char *property_name) +{ + char **props; + guint i; + gboolean prop_found = FALSE; + + props = g_dbus_proxy_get_cached_property_names (proxy); + for (i = 0; props[i] != NULL; i++) + { + if (g_str_equal (props[i], property_name)) + { + prop_found = TRUE; + break; + } + } + + g_strfreev (props); + return prop_found; +} + +static gboolean +g_network_monitor_nm_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GNetworkMonitorNM *nm = G_NETWORK_MONITOR_NM (initable); + GDBusProxy *proxy; + GInitableIface *parent_iface; + + parent_iface = g_type_interface_peek_parent (G_NETWORK_MONITOR_NM_GET_INITABLE_IFACE (initable)); + if (!parent_iface->init (initable, cancellable, error)) + return FALSE; + + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + NULL, + "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + "org.freedesktop.NetworkManager", + cancellable, + error); + if (!proxy) + return FALSE; + + /* Verify it has the PrimaryConnection and Connectivity properties */ + if (!has_property (proxy, "Connectivity")) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("NetworkManager version too old")); + g_object_unref (proxy); + return FALSE; + } + + g_signal_connect (G_OBJECT (proxy), "g-signal", + G_CALLBACK (proxy_signal_cb), nm); + nm->priv->proxy = proxy; + sync_properties (nm, FALSE); + + return TRUE; +} + +static void +g_network_monitor_nm_finalize (GObject *object) +{ + GNetworkMonitorNM *nm = G_NETWORK_MONITOR_NM (object); + + g_clear_object (&nm->priv->proxy); + + G_OBJECT_CLASS (g_network_monitor_nm_parent_class)->finalize (object); +} + +static void +g_network_monitor_nm_class_init (GNetworkMonitorNMClass *nl_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class); + + gobject_class->finalize = g_network_monitor_nm_finalize; + gobject_class->get_property = g_network_monitor_nm_get_property; + + g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available"); + g_object_class_override_property (gobject_class, PROP_CONNECTIVITY, "connectivity"); +} + +static void +g_network_monitor_nm_iface_init (GNetworkMonitorInterface *monitor_iface) +{ +} + +static void +g_network_monitor_nm_initable_iface_init (GInitableIface *iface) +{ + iface->init = g_network_monitor_nm_initable_init; +} diff --git a/gio/gnetworkmonitornm.h b/gio/gnetworkmonitornm.h new file mode 100644 index 000000000..7a4bbe4c5 --- /dev/null +++ b/gio/gnetworkmonitornm.h @@ -0,0 +1,53 @@ +/* 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, see . + */ + +#ifndef __G_NETWORK_MONITOR_NM_H__ +#define __G_NETWORK_MONITOR_NM_H__ + +#include "gnetworkmonitornetlink.h" + +G_BEGIN_DECLS + +#define G_TYPE_NETWORK_MONITOR_NM (_g_network_monitor_nm_get_type ()) +#define G_NETWORK_MONITOR_NM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_NETWORK_MONITOR_NM, GNetworkMonitorNM)) +#define G_NETWORK_MONITOR_NM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_NETWORK_MONITOR_NM, GNetworkMonitorNMClass)) +#define G_IS_NETWORK_MONITOR_NM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_NETWORK_MONITOR_NM)) +#define G_IS_NETWORK_MONITOR_NM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_NETWORK_MONITOR_NM)) +#define G_NETWORK_MONITOR_NM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_NETWORK_MONITOR_NM, GNetworkMonitorNMClass)) +#define G_NETWORK_MONITOR_NM_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable)) + + +typedef struct _GNetworkMonitorNM GNetworkMonitorNM; +typedef struct _GNetworkMonitorNMClass GNetworkMonitorNMClass; +typedef struct _GNetworkMonitorNMPrivate GNetworkMonitorNMPrivate; + +struct _GNetworkMonitorNM { + GNetworkMonitorNetlink parent_instance; + + GNetworkMonitorNMPrivate *priv; +}; + +struct _GNetworkMonitorNMClass { + GNetworkMonitorNetlinkClass parent_class; +}; + +GType _g_network_monitor_nm_get_type (void); + +G_END_DECLS + +#endif /* __G_NETWORK_MONITOR_NM_H__ */