From dd06e70a33a386b716fedd68393885f45a343b68 Mon Sep 17 00:00:00 2001 From: Christian Persch Date: Wed, 25 Jan 2006 21:52:27 +0000 Subject: libnm-glib be-gone! Use NetworkManager directly via DBUS, by importing the MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2006-01-25 Christian Persch * configure.ac: * data/epiphany.schemas.in: * embed/mozilla/Makefile.am: * embed/mozilla/mozilla-embed-single.cpp: * lib/ephy-prefs.h: * src/Makefile.am: * src/ephy-net-monitor.c: * src/ephy-net-monitor.h: * src/ephy-shell.c: (ephy_shell_sync_network_status), (impl_get_embed_single), (ephy_shell_dispose), (ephy_shell_get_net_monitor), (_ephy_shell_create_instance), (_ephy_shell_startup): * src/ephy-shell.h: libnm-glib be-gone! Use NetworkManager directly via DBUS, by importing the excellent net-monitor extension directly into Epiphany. Code by Jean-François Rameau. --- src/ephy-net-monitor.c | 584 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 584 insertions(+) create mode 100644 src/ephy-net-monitor.c (limited to 'src/ephy-net-monitor.c') diff --git a/src/ephy-net-monitor.c b/src/ephy-net-monitor.c new file mode 100644 index 000000000..0b0dd901c --- /dev/null +++ b/src/ephy-net-monitor.c @@ -0,0 +1,584 @@ +/* + * Copyright (C) 2005, 2006 Jean-François Rameau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Id$ + */ + +#include "config.h" + +#include "ephy-net-monitor.h" + +#include "ephy-dbus.h" +#include "ephy-shell.h" +#include "ephy-session.h" +#include "ephy-embed-single.h" +#include "eel-gconf-extensions.h" +#include "ephy-prefs.h" +#include "ephy-debug.h" + +#include + +#include + +typedef enum +{ + NETWORK_UP, + NETWORK_DOWN +} NetworkStatus; + +#define EPHY_NET_MONITOR_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_NET_MONITOR, EphyNetMonitorPrivate)) + +struct _EphyNetMonitorPrivate +{ + DBusConnection *bus; + guint notify_id; + guint active : 1; + NetworkStatus status; +}; + +enum +{ + PROP_0, + PROP_NETWORK_STATUS +}; + +static GObjectClass *parent_class; + +static void +ephy_net_monitor_set_net_status (EphyNetMonitor *monitor, + NetworkStatus status) +{ + EphyNetMonitorPrivate *priv = monitor->priv; + + LOG ("EphyNetMonitor turning Epiphany to %s mode", + status != NETWORK_DOWN ? "online" : "offline"); + + priv->status = priv->active ? status : NETWORK_UP; + + g_object_notify (G_OBJECT (monitor), "network-status"); +} + +static NetworkStatus +ephy_net_monitor_check_for_active_device (EphyNetMonitor *monitor, + char *all_path[], + int num) +{ + EphyNetMonitorPrivate *priv = monitor->priv; + int i; + DBusMessage * message = NULL; + DBusMessage * reply = NULL; + const char * iface = NULL; + char * op = NULL; + dbus_uint32_t type = 0; + const char * udi = NULL; + dbus_bool_t active = FALSE; + const char * ip4_address = NULL; + const char * broadcast = NULL; + const char * subnetmask = NULL; + const char * hw_addr = NULL; + const char * route = NULL; + const char * primary_dns = NULL; + const char * secondary_dns = NULL; + dbus_uint32_t mode = 0; + dbus_int32_t strength = -1; + char * active_network_path = NULL; + dbus_bool_t link_active = FALSE; + dbus_uint32_t caps = NM_DEVICE_CAP_NONE; + char ** networks = NULL; + int num_networks = 0; + NMActStage act_stage = NM_ACT_STAGE_UNKNOWN; + NetworkStatus status = NETWORK_DOWN; + + for (i = 0; i < num; i++) + { + const char *path = all_path [i]; + DBusError error; + + message = dbus_message_new_method_call (NM_DBUS_SERVICE, + path, + NM_DBUS_INTERFACE_DEVICES, + "getProperties"); + if (message == NULL) + { + g_warning ("EphyNetMonitor: couldn't allocate the dbus message"); + status = NETWORK_UP; + break; + } + + reply = dbus_connection_send_with_reply_and_block (priv->bus, message, -1, NULL); + dbus_message_unref (message); + + if (reply == NULL) + { + g_warning ("EphyNetMonitor: " + "didn't get a reply from NetworkManager for device %s.\n", path); + status = NETWORK_UP; + break; + } + + dbus_error_init (&error); + if (dbus_message_get_args (reply, &error, DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_STRING, &iface, + DBUS_TYPE_UINT32, &type, + DBUS_TYPE_STRING, &udi, + DBUS_TYPE_BOOLEAN,&active, + DBUS_TYPE_UINT32, &act_stage, + DBUS_TYPE_STRING, &ip4_address, + DBUS_TYPE_STRING, &subnetmask, + DBUS_TYPE_STRING, &broadcast, + DBUS_TYPE_STRING, &hw_addr, + DBUS_TYPE_STRING, &route, + DBUS_TYPE_STRING, &primary_dns, + DBUS_TYPE_STRING, &secondary_dns, + DBUS_TYPE_UINT32, &mode, + DBUS_TYPE_INT32, &strength, + DBUS_TYPE_BOOLEAN,&link_active, + DBUS_TYPE_UINT32, &caps, + DBUS_TYPE_STRING, &active_network_path, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &networks, &num_networks, + DBUS_TYPE_INVALID)) + { + dbus_message_unref (reply); + + /* found one active device */ + if (active) + { + status = NETWORK_UP; + break; + } + } + else + { + g_warning ("EphyNetMonitor: " + "unexpected reply from NetworkManager for device %s.\n", path); + if (dbus_error_is_set(&error)) + { + g_warning ("EphyNetMonitor: %s: %s", error.name, error.message); + dbus_error_free(&error); + } + + status = NETWORK_UP; + + break; + } + } + + return active; +} + +/* This is the heart of Net Monitor monitor */ +/* ATM, if there is an active device, we say that network is up: that's all ! */ +static gboolean +ephy_net_monitor_network_status (EphyNetMonitor *monitor) +{ + EphyNetMonitorPrivate *priv = monitor->priv; + DBusMessage *message, *reply; + DBusError error; + NetworkStatus net_status; + + /* ask to Network Manager if there is at least one active device */ + message = dbus_message_new_method_call (NM_DBUS_SERVICE, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "getDevices"); + + if (message == NULL) + { + g_warning ("Couldn't allocate the dbus message"); + /* fallbak: let's Epiphany roll */ + return NETWORK_UP; + } + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (priv->bus, + message, + -1, + &error); + if (dbus_error_is_set (&error)) + { + if (dbus_error_has_name (&error, NM_DBUS_NO_DEVICES_ERROR)) + { + LOG ("EphyNetMonitor: Network Manager says - no available network devices -"); + + net_status = NETWORK_DOWN; + } + else + { + LOG ("EphyNetMonitor can't talk to Network Manager: %s: %s", + error.name, error.message); + + /* fallback */ + net_status = NETWORK_UP; + } + + dbus_error_free(&error); + } + else + { + if (reply == NULL) + { + g_warning("EphyNetMonitor got NULL reply"); + + /* fallback */ + net_status = NETWORK_UP; + } + else + { + /* check if we have at least one active device */ + char **paths = NULL; + int num = -1; + + if (!dbus_message_get_args (reply, + NULL, + DBUS_TYPE_ARRAY, + DBUS_TYPE_OBJECT_PATH, + &paths, + &num, + DBUS_TYPE_INVALID)) + { + g_warning ("EphyNetMonitor: unexpected reply from NetworkManager"); + dbus_message_unref (reply); + net_status = NETWORK_UP; + } + else + { + net_status = ephy_net_monitor_check_for_active_device + (monitor, paths, num); + } + } + } + + if (reply) + { + dbus_message_unref (reply); + } + + if (message) + { + dbus_message_unref (message); + } + + return net_status; +} + +static void +ephy_net_monitor_check_network (EphyNetMonitor *monitor) +{ + NetworkStatus net_status; + + net_status = ephy_net_monitor_network_status (monitor); + + LOG ("EphyNetMonitor guesses the network is %s", + net_status != NETWORK_DOWN ? "up" : "down"); + + ephy_net_monitor_set_net_status (monitor, net_status); +} + +/* Filters all the messages from Network Manager */ +static DBusHandlerResult +filter_func (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + EphyNetMonitor *monitor; + + g_return_val_if_fail (user_data != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + + monitor = EPHY_NET_MONITOR (user_data); + + if (dbus_message_is_signal (message, + NM_DBUS_INTERFACE, + "DeviceNoLongerActive")) + { + LOG ("EphyNetMonitor catches DeviceNoLongerActive signal"); + + ephy_net_monitor_check_network (monitor); + + return DBUS_HANDLER_RESULT_HANDLED; + } + if (dbus_message_is_signal (message, + NM_DBUS_INTERFACE, + "DeviceNowActive")) + { + LOG ("EphyNetMonitor catches DeviceNowActive signal"); + + ephy_net_monitor_set_net_status (monitor, NETWORK_UP); + + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static void +ephy_net_monitor_attach_to_dbus (EphyNetMonitor *monitor) +{ + EphyNetMonitorPrivate *priv = monitor->priv; + DBusError error; + EphyDbus *dbus; + DBusGConnection *g_connection; + + LOG ("EphyNetMonitor is trying to attach to SYSTEM bus"); + + dbus = ephy_dbus_get_default (); + + g_connection = ephy_dbus_get_bus (dbus, EPHY_DBUS_SYSTEM); + g_return_if_fail (g_connection != NULL); + + priv->bus = dbus_g_connection_get_connection (g_connection); + + if (priv->bus != NULL) + { + dbus_connection_add_filter (priv->bus, + filter_func, + monitor, + NULL); + dbus_error_init (&error); + dbus_bus_add_match (priv->bus, + "type='signal',interface='" NM_DBUS_INTERFACE "'", + &error); + if (dbus_error_is_set(&error)) + { + g_warning("EphyNetMonitor cannot register signal handler: %s: %s", + error.name, error.message); + dbus_error_free(&error); + } + LOG ("EphyNetMonitor attached to SYSTEM bus"); + } +} + +static void +connect_to_system_bus_cb (EphyDbus *dbus, + EphyDbusBus kind, + EphyNetMonitor *monitor) +{ + if (kind == EPHY_DBUS_SYSTEM) + { + LOG ("EphyNetMonitor connecting to SYSTEM bus"); + + ephy_net_monitor_attach_to_dbus (monitor); + } +} + +static void +disconnect_from_system_bus_cb (EphyDbus *dbus, + EphyDbusBus kind, + EphyNetMonitor *monitor) +{ + EphyNetMonitorPrivate *priv = monitor->priv; + + if (kind == EPHY_DBUS_SYSTEM) + { + LOG ("EphyNetMonitor disconnected from SYSTEM bus"); + + /* no bus anymore */ + priv->bus = NULL; + } +} + +static void +ephy_net_monitor_startup (EphyNetMonitor *monitor) +{ + EphyDbus *dbus; + + dbus = ephy_dbus_get_default (); + + LOG ("EphyNetMonitor starting up"); + + ephy_net_monitor_attach_to_dbus (monitor); + + /* DBUS may disconnect us at any time. So listen carefully to it */ + g_signal_connect (dbus, "connected", + G_CALLBACK (connect_to_system_bus_cb), monitor); + g_signal_connect (dbus, "disconnected", + G_CALLBACK (disconnect_from_system_bus_cb), monitor); + + /* FIXME what if the system bus isn't available right now? */ + ephy_net_monitor_check_network (monitor); +} + +static void +ephy_net_monitor_shutdown (EphyNetMonitor *monitor) +{ + EphyNetMonitorPrivate *priv = monitor->priv; + EphyDbus *dbus; + + dbus = ephy_dbus_get_default (); + + g_signal_handlers_disconnect_by_func + (dbus, G_CALLBACK (connect_to_system_bus_cb), monitor); + g_signal_handlers_disconnect_by_func + (dbus,G_CALLBACK (disconnect_from_system_bus_cb), monitor); + + priv->bus = NULL; + + LOG ("EphyNetMonitor shutdown"); +} + +static void +notify_network_managed_cb (GConfClient *client, + guint cnxn_id, + GConfEntry *entry, + EphyNetMonitor *monitor) +{ + EphyNetMonitorPrivate *priv = monitor->priv; + GConfValue *value; + gboolean active = FALSE; + + LOG (CONF_NETWORK_MANAGED " key changed"); + + g_assert (entry != NULL); + + value = gconf_entry_get_value (entry); + if (value != NULL && value->type == GCONF_VALUE_BOOL) + { + active = gconf_value_get_bool (value); + } + + priv->active = active; + + g_object_notify (G_OBJECT (monitor), "network-status"); +} + +static void +ephy_net_monitor_init (EphyNetMonitor *monitor) +{ + EphyNetMonitorPrivate *priv; + + priv = monitor->priv = EPHY_NET_MONITOR_GET_PRIVATE (monitor); + + LOG ("EphyNetMonitor initialising"); + + priv->status = NETWORK_UP; + + priv->notify_id = eel_gconf_notification_add + (CONF_NETWORK_MANAGED, + (GConfClientNotifyFunc) notify_network_managed_cb, + monitor); + eel_gconf_notify (CONF_NETWORK_MANAGED); + + ephy_net_monitor_startup (monitor); +} + +static void +ephy_net_monitor_dispose (GObject *object) +{ + EphyNetMonitor *monitor = EPHY_NET_MONITOR (object); + EphyNetMonitorPrivate *priv = monitor->priv; + + LOG ("EphyNetMonitor finalising"); + + ephy_net_monitor_shutdown (monitor); + + if (priv->notify_id != 0) + { + eel_gconf_notification_remove (priv->notify_id); + priv->notify_id = 0; + } + + parent_class->dispose (object); +} + +static void +ephy_net_monitor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EphyNetMonitor *monitor = EPHY_NET_MONITOR (object); + + switch (prop_id) + { + case PROP_NETWORK_STATUS: + g_value_set_boolean (value, ephy_net_monitor_get_net_status (monitor)); + break; + } +} + +static void +ephy_net_monitor_class_init (EphyNetMonitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->dispose = ephy_net_monitor_dispose; + object_class->get_property = ephy_net_monitor_get_property; + +/** + * EphyNetMonitor::network-status: + * + * Whether the network is on-line. + */ + g_object_class_install_property + (object_class, + PROP_NETWORK_STATUS, + g_param_spec_boolean ("network-status", + "network-status", + "network-status", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_type_class_add_private (object_class, sizeof (EphyNetMonitorPrivate)); +} + +/* public API */ + +GType +ephy_net_monitor_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + static const GTypeInfo our_info = + { + sizeof (EphyShellClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) ephy_net_monitor_class_init, + NULL, + NULL, /* class_data */ + sizeof (EphyNetMonitor), + 0, /* n_preallocs */ + (GInstanceInitFunc) ephy_net_monitor_init + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "EphyNetMonitor", + &our_info, 0); + } + + return type; +} + +EphyNetMonitor * +ephy_net_monitor_new (void) +{ + return g_object_new (EPHY_TYPE_NET_MONITOR, NULL); +} + +gboolean +ephy_net_monitor_get_net_status (EphyNetMonitor *monitor) +{ + EphyNetMonitorPrivate *priv; + + g_return_val_if_fail (EPHY_IS_NET_MONITOR (monitor), FALSE); + + priv = monitor->priv; + + return !priv->active || priv->status != NETWORK_DOWN; +} -- cgit v1.2.3