From f8026260c68bf70d3f1db09ab704689f0a67c51f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 5 Jun 2009 01:55:02 -0400 Subject: [PATCH] rfkill: port rfkill to libgudev --- configure.ac | 4 + src/Makefile.am | 4 + src/nm-hal-manager.c | 370 +----------------------------------------- src/nm-hal-manager.h | 9 - src/nm-manager.c | 76 +++++++-- src/nm-udev-manager.c | 305 ++++++++++++++++++++++++++++++++++ src/nm-udev-manager.h | 59 +++++++ 7 files changed, 432 insertions(+), 395 deletions(-) create mode 100644 src/nm-udev-manager.c create mode 100644 src/nm-udev-manager.h diff --git a/configure.ac b/configure.ac index 446e0aa0a6..a83ac28c91 100644 --- a/configure.ac +++ b/configure.ac @@ -189,6 +189,10 @@ PKG_CHECK_MODULES(GMODULE, gmodule-2.0) AC_SUBST(GMODULE_CFLAGS) AC_SUBST(GMODULE_LIBS) +PKG_CHECK_MODULES(GUDEV, gudev-1.0) +AC_SUBST(GUDEV_CFLAGS) +AC_SUBST(GUDEV_LIBS) + PKG_CHECK_EXISTS(gio-2.0,[have_gio=yes],[have_gio=no]) if test x"$have_gio" = "xno"; then AC_DEFINE([NO_GIO],[1],[Define if you don't have GIO]) diff --git a/src/Makefile.am b/src/Makefile.am index 6a561c11bf..2f218bd7df 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -73,6 +73,8 @@ NetworkManager_SOURCES = \ nm-dbus-manager.c \ nm-hal-manager.c \ nm-hal-manager.h \ + nm-udev-manager.c \ + nm-udev-manager.h \ nm-hostname-provider.c \ nm-hostname-provider.h \ nm-ip4-config.c \ @@ -145,6 +147,7 @@ NetworkManager_CPPFLAGS = \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ $(HAL_CFLAGS) \ + $(GUDEV_CFLAGS) \ $(OPENSSL_CFLAGS) \ $(LIBNL_CFLAGS) \ -DG_DISABLE_DEPRECATED \ @@ -162,6 +165,7 @@ NetworkManager_LDADD = \ $(DBUS_LIBS) \ $(GLIB_LIBS) \ $(HAL_LIBS) \ + $(GUDEV_LIBS) \ $(LIBNL_LIBS) \ $(top_builddir)/marshallers/libmarshallers.la \ ./named-manager/libnamed-manager.la \ diff --git a/src/nm-hal-manager.c b/src/nm-hal-manager.c index 408ee02f87..8b211e125e 100644 --- a/src/nm-hal-manager.c +++ b/src/nm-hal-manager.c @@ -34,47 +34,13 @@ #include "nm-device-wifi.h" #include "nm-device-ethernet.h" -/* Killswitch poll frequency in seconds */ -#define RFKILL_POLL_FREQUENCY 6 - #define HAL_DBUS_SERVICE "org.freedesktop.Hal" -typedef struct { - char *udi; - gboolean polled; - RfKillState state; - - /* For polling */ - DBusGProxy *proxy; - - NMHalManager *self; -} Killswitch; - typedef struct { LibHalContext *hal_ctx; NMDBusManager *dbus_mgr; GSList *device_creators; - /* Authoritative rfkill state (RFKILL_* enum) - */ - RfKillState rfkill_state; - - /* Killswitch handling: - * There are two types of killswitches: - * a) old-style: require polling - * b) new-style: requires hal 0.5.12 as of 2008-11-19, and 2.6.27 kernel - * or later; emit PropertyChanged for the 'state' property when the - * rfkill status changes - * - * If new-style switches are found, they are used. Otherwise, old-style - * switches are used. - */ - GSList *killswitches; - - /* Old-style killswitch polling stuff */ - guint32 killswitch_poll_id; - char *kswitch_err; - gboolean disposed; } NMHalManagerPrivate; @@ -85,7 +51,6 @@ G_DEFINE_TYPE (NMHalManager, nm_hal_manager, G_TYPE_OBJECT) enum { UDI_ADDED, UDI_REMOVED, - RFKILL_CHANGED, HAL_REAPPEARED, LAST_SIGNAL @@ -94,8 +59,6 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -static gboolean poll_killswitches (gpointer user_data); - /* Device creators */ typedef struct { @@ -351,75 +314,6 @@ device_new_capability (LibHalContext *ctx, const char *udi, const char *capabili emit_udi_added (self, udi, creator); } -static RfKillState -hal_to_nm_rfkill_state (int hal_state) -{ - switch (hal_state) { - case 0: - return RFKILL_SOFT_BLOCKED; - case 2: - return RFKILL_HARD_BLOCKED; - case 1: - default: - return RFKILL_UNBLOCKED; - } -} - -static void -device_property_changed (LibHalContext *ctx, - const char *udi, - const char *key, - dbus_bool_t is_removed, - dbus_bool_t is_added) -{ - NMHalManager *self = NM_HAL_MANAGER (libhal_ctx_get_user_data (ctx)); - NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self); - GSList *iter; - gboolean found = FALSE; - DBusError error; - int state; - RfKillState new_state = RFKILL_UNBLOCKED; - - /* Ignore event if it's not a killswitch state change */ - if (strcmp (key, "killswitch.state") || is_removed) - return; - - /* Check all killswitches, and if any switch is blocked, the new rfkill - * state becomes blocked. - */ - for (iter = priv->killswitches; iter; iter = iter->next) { - Killswitch *ks = iter->data; - - if (!strcmp (ks->udi, udi)) { - found = TRUE; - - /* Get switch state */ - dbus_error_init (&error); - state = libhal_device_get_property_int (ctx, ks->udi, "killswitch.state", &error); - if (dbus_error_is_set (&error)) { - nm_warning ("(%s) Error reading killswitch state: %s.", - ks->udi, - error.message ? error.message : "unknown"); - dbus_error_free (&error); - } else - ks->state = hal_to_nm_rfkill_state (state); - } - - /* If any switch is blocked, overall state is blocked */ - if (ks->state > new_state) - new_state = ks->state; - } - - /* Notify of new rfkill state change; but only if the killswitch which - * this event is for was one we care about - */ - if (found && (new_state != priv->rfkill_state)) { - priv->rfkill_state = new_state; - g_signal_emit (self, signals[RFKILL_CHANGED], 0, priv->rfkill_state); - } -} - - static void add_initial_devices (NMHalManager *self) { @@ -456,230 +350,6 @@ add_initial_devices (NMHalManager *self) } } -static void -killswitch_getpower_done (gpointer user_data) -{ - Killswitch *ks = user_data; - NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (ks->self); - GSList *iter; - RfKillState new_state = RFKILL_UNBLOCKED; - - if (ks->proxy) { - g_object_unref (ks->proxy); - ks->proxy = NULL; - } - - /* Check all killswitches, and if any switch is blocked, the new rfkill - * state becomes blocked. But emit final state until the last killswitch - * has been updated. - */ - for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) { - Killswitch *candidate = iter->data; - - /* If any GetPower call has yet to complete; don't emit final state */ - if (candidate->proxy) - return; - - if (candidate->state > new_state) - new_state = candidate->state; - } - - if (new_state != priv->rfkill_state) { - priv->rfkill_state = new_state; - g_signal_emit (ks->self, signals[RFKILL_CHANGED], 0, priv->rfkill_state); - } - - /* Schedule next poll */ - priv->killswitch_poll_id = g_timeout_add_seconds (RFKILL_POLL_FREQUENCY, - poll_killswitches, - ks->self); -} - -static void -killswitch_getpower_reply (DBusGProxy *proxy, - DBusGProxyCall *call_id, - gpointer user_data) -{ - Killswitch *ks = user_data; - NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (ks->self); - int power = 1; - GError *err = NULL; - - if (dbus_g_proxy_end_call (proxy, call_id, &err, - G_TYPE_INT, &power, - G_TYPE_INVALID)) { - if (power == 0) - ks->state = RFKILL_HARD_BLOCKED; - } else { - if (err->message) { - /* Only print the error if we haven't seen it before */ - if (!priv->kswitch_err || strcmp (priv->kswitch_err, err->message) != 0) { - nm_warning ("Error getting killswitch power: %s.", err->message); - g_free (priv->kswitch_err); - priv->kswitch_err = g_strdup (err->message); - - /* If there was an error talking to HAL, treat that as rfkilled. - * See rh #448889. On some Dell laptops, dellWirelessCtl - * may not be present, but HAL still advertises a killswitch, - * and calls to GetPower() will fail. Thus we cannot assume - * that a failure of GetPower() automatically means the wireless - * is rfkilled, because in this situation NM would never bring - * the radio up. Only assume failures between NM and HAL should - * block the radio, not failures of the HAL killswitch callout - * itself. - */ - if (strstr (err->message, "Did not receive a reply")) { - nm_warning ("HAL did not reply to killswitch power request;" - " assuming radio is blocked."); - ks->state = RFKILL_HARD_BLOCKED; - } - } - } - g_error_free (err); - } -} - -static gboolean -poll_killswitches (gpointer user_data) -{ - NMHalManager *self = NM_HAL_MANAGER (user_data); - NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self); - GSList *iter; - - for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) { - Killswitch *ks = iter->data; - - ks->state = RFKILL_UNBLOCKED; - ks->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), - "org.freedesktop.Hal", - ks->udi, - "org.freedesktop.Hal.Device.KillSwitch"); - dbus_g_proxy_begin_call (ks->proxy, "GetPower", - killswitch_getpower_reply, - ks, - killswitch_getpower_done, - G_TYPE_INVALID); - } - return FALSE; -} - -static Killswitch * -killswitch_new (const char *udi, - int state, - gboolean polled, - NMHalManager *self) -{ - Killswitch *ks; - - ks = g_malloc0 (sizeof (Killswitch)); - ks->udi = g_strdup (udi); - ks->state = state; - ks->self = self; - ks->polled = polled; - - return ks; -} - -static void -killswitch_free (gpointer user_data) -{ - Killswitch *ks = user_data; - - if (ks->proxy) - g_object_unref (ks->proxy); - g_free (ks->udi); - g_free (ks); -} - -static void -add_killswitch_devices (NMHalManager *self) -{ - NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self); - char **udis; - int num_udis, i; - DBusError err; - GSList *polled = NULL, *active = NULL, *iter; - - dbus_error_init (&err); - udis = libhal_find_device_by_capability (priv->hal_ctx, "killswitch", &num_udis, &err); - if (!udis) - return; - - if (dbus_error_is_set (&err)) { - nm_warning ("Could not find killswitch devices: %s", err.message); - dbus_error_free (&err); - return; - } - - /* filter switches we care about */ - for (i = 0; i < num_udis; i++) { - Killswitch *ks; - char *type; - int state; - gboolean found = FALSE; - - type = libhal_device_get_property_string (priv->hal_ctx, udis[i], "killswitch.type", NULL); - if (!type) - continue; - - /* Only care about WLAN for now */ - if (strcmp (type, "wlan")) { - libhal_free_string (type); - continue; - } - - /* see if it's already in the list */ - for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) { - ks = iter->data; - if (!strcmp (udis[i], ks->udi)) { - found = TRUE; - break; - } - } - if (found) - continue; - - dbus_error_init (&err); - state = libhal_device_get_property_int (priv->hal_ctx, udis[i], "killswitch.state", &err); - if (dbus_error_is_set (&err)) { - dbus_error_free (&err); - nm_info ("Found radio killswitch %s (polled)", udis[i]); - ks = killswitch_new (udis[i], RFKILL_UNBLOCKED, TRUE, self); - polled = g_slist_append (polled, ks); - } else { - nm_info ("Found radio killswitch %s (monitored)", udis[i]); - ks = killswitch_new (udis[i], hal_to_nm_rfkill_state (state), FALSE, self); - active = g_slist_append (active, ks); - } - } - - /* Active killswitches are used in preference to polled killswitches */ - if (active) { - for (iter = active; iter; iter = g_slist_next (iter)) - priv->killswitches = g_slist_append (priv->killswitches, iter->data); - - if (priv->killswitches) - nm_info ("Watching killswitches for radio status"); - - /* Dispose of any polled killswitches found */ - g_slist_foreach (polled, (GFunc) killswitch_free, NULL); - } else { - for (iter = polled; iter; iter = g_slist_next (iter)) - priv->killswitches = g_slist_append (priv->killswitches, iter->data); - - /* Poll switches if this is the first switch we've found */ - if (priv->killswitches) { - if (!priv->killswitch_poll_id) - priv->killswitch_poll_id = g_idle_add (poll_killswitches, self); - nm_info ("Polling killswitches for radio status"); - } - } - - g_slist_free (active); - g_slist_free (polled); - libhal_free_string_array (udis); -} - static gboolean hal_init (NMHalManager *self) { @@ -709,7 +379,6 @@ hal_init (NMHalManager *self) libhal_ctx_set_device_added (priv->hal_ctx, device_added); libhal_ctx_set_device_removed (priv->hal_ctx, device_removed); libhal_ctx_set_device_new_capability (priv->hal_ctx, device_new_capability); - libhal_ctx_set_device_property_modified (priv->hal_ctx, device_property_changed); libhal_device_property_watch_all (priv->hal_ctx, &error); if (dbus_error_is_set (&error)) { @@ -736,17 +405,6 @@ hal_deinit (NMHalManager *self) NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self); DBusError error; - if (priv->killswitch_poll_id) { - g_source_remove (priv->killswitch_poll_id); - priv->killswitch_poll_id = 0; - } - - if (priv->killswitches) { - g_slist_foreach (priv->killswitches, (GFunc) killswitch_free, NULL); - g_slist_free (priv->killswitches); - priv->killswitches = NULL; - } - if (!priv->hal_ctx) return; @@ -814,10 +472,8 @@ nm_hal_manager_query_devices (NMHalManager *self) NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self); /* Find hardware we care about */ - if (priv->hal_ctx) { - add_killswitch_devices (self); + if (priv->hal_ctx) add_initial_devices (self); - } } gboolean @@ -855,8 +511,6 @@ nm_hal_manager_init (NMHalManager *self) { NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self); - priv->rfkill_state = RFKILL_UNBLOCKED; - priv->dbus_mgr = nm_dbus_manager_get (); register_built_in_creators (self); @@ -903,17 +557,6 @@ dispose (GObject *object) G_OBJECT_CLASS (nm_hal_manager_parent_class)->dispose (object); } -static void -finalize (GObject *object) -{ - NMHalManager *self = NM_HAL_MANAGER (object); - NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self); - - g_free (priv->kswitch_err); - - G_OBJECT_CLASS (nm_hal_manager_parent_class)->finalize (object); -} - static void nm_hal_manager_class_init (NMHalManagerClass *klass) { @@ -923,7 +566,6 @@ nm_hal_manager_class_init (NMHalManagerClass *klass) /* virtual methods */ object_class->dispose = dispose; - object_class->finalize = finalize; /* Signals */ signals[UDI_ADDED] = @@ -946,16 +588,6 @@ nm_hal_manager_class_init (NMHalManagerClass *klass) G_TYPE_NONE, 1, G_TYPE_STRING); - signals[RFKILL_CHANGED] = - g_signal_new ("rfkill-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMHalManagerClass, rfkill_changed), - NULL, NULL, - g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, 1, - G_TYPE_UINT); - signals[HAL_REAPPEARED] = g_signal_new ("hal-reappeared", G_OBJECT_CLASS_TYPE (object_class), diff --git a/src/nm-hal-manager.h b/src/nm-hal-manager.h index 319aad3045..9c32cdcde9 100644 --- a/src/nm-hal-manager.h +++ b/src/nm-hal-manager.h @@ -25,12 +25,6 @@ #include #include -typedef enum { - RFKILL_UNBLOCKED = 0, - RFKILL_SOFT_BLOCKED = 1, - RFKILL_HARD_BLOCKED = 2 -} RfKillState; - G_BEGIN_DECLS #define NM_TYPE_HAL_MANAGER (nm_hal_manager_get_type ()) @@ -61,15 +55,12 @@ typedef struct { void (*udi_removed) (NMHalManager *manager, const char *udi); - void (*rfkill_changed) (NMHalManager *manager, RfKillState state); - void (*hal_reappeared) (NMHalManager *manager); } NMHalManagerClass; GType nm_hal_manager_get_type (void); NMHalManager *nm_hal_manager_new (void); -gboolean nm_hal_manager_get_rfkilled (NMHalManager *manager); void nm_hal_manager_query_devices (NMHalManager *manager); gboolean nm_hal_manager_udi_exists (NMHalManager *manager, const char *udi); diff --git a/src/nm-manager.c b/src/nm-manager.c index 2ffa8af644..95c388c743 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -44,6 +44,7 @@ #include "nm-marshal.h" #include "nm-dbus-glib-types.h" #include "nm-hal-manager.h" +#include "nm-udev-manager.h" #include "nm-hostname-provider.h" #include "nm-bluez-manager.h" #include "nm-bluez-common.h" @@ -93,9 +94,9 @@ static void hal_manager_udi_removed_cb (NMHalManager *hal_mgr, const char *udi, gpointer user_data); -static void hal_manager_rfkill_changed_cb (NMHalManager *hal_mgr, - RfKillState state, - gpointer user_data); +static void udev_manager_rfkill_changed_cb (NMHalManager *hal_mgr, + RfKillState state, + gpointer user_data); static void hal_manager_hal_reappeared_cb (NMHalManager *hal_mgr, gpointer user_data); @@ -140,6 +141,7 @@ typedef struct { NMDBusManager *dbus_mgr; NMHalManager *hal_mgr; + NMUdevManager *udev_mgr; NMBluezManager *bluez_mgr; GHashTable *user_connections; @@ -1578,6 +1580,7 @@ nm_manager_get (void) { static NMManager *singleton = NULL; NMManagerPrivate *priv; + gboolean enabled; if (singleton) return g_object_ref (singleton); @@ -1611,16 +1614,38 @@ nm_manager_get (void) G_CALLBACK (hal_manager_udi_removed_cb), singleton); - g_signal_connect (priv->hal_mgr, - "rfkill-changed", - G_CALLBACK (hal_manager_rfkill_changed_cb), - singleton); - g_signal_connect (priv->hal_mgr, "hal-reappeared", G_CALLBACK (hal_manager_hal_reappeared_cb), singleton); + priv->udev_mgr = nm_udev_manager_new (); + g_signal_connect (priv->udev_mgr, + "rfkill-changed", + G_CALLBACK (udev_manager_rfkill_changed_cb), + singleton); + + switch (nm_udev_manager_get_rfkill_state (priv->udev_mgr)) { + case RFKILL_UNBLOCKED: + priv->wireless_enabled = TRUE; + priv->wireless_hw_enabled = TRUE; + break; + case RFKILL_SOFT_BLOCKED: + priv->wireless_enabled = FALSE; + priv->wireless_hw_enabled = TRUE; + break; + case RFKILL_HARD_BLOCKED: + priv->wireless_enabled = FALSE; + priv->wireless_hw_enabled = FALSE; + break; + default: + break; + } + + enabled = (priv->wireless_enabled && priv->wireless_hw_enabled); + nm_info ("Wireless now %s by radio killswitch", enabled ? "enabled" : "disabled"); + manager_set_wireless_enabled (singleton, enabled); + priv->bluez_mgr = nm_bluez_manager_get (); g_signal_connect (priv->bluez_mgr, @@ -2040,21 +2065,38 @@ hal_manager_udi_removed_cb (NMHalManager *manager, } static void -hal_manager_rfkill_changed_cb (NMHalManager *hal_mgr, - RfKillState state, - gpointer user_data) +udev_manager_rfkill_changed_cb (NMHalManager *hal_mgr, + RfKillState state, + gpointer user_data) { NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - gboolean enabled = (state == RFKILL_UNBLOCKED); + gboolean new_we = TRUE, new_whe = TRUE; - if (priv->wireless_hw_enabled != enabled) { - nm_info ("Wireless now %s by radio killswitch", enabled ? "enabled" : "disabled"); - priv->wireless_hw_enabled = enabled; + switch (state) { + case RFKILL_UNBLOCKED: + new_we = TRUE; + new_whe = TRUE; + break; + case RFKILL_SOFT_BLOCKED: + new_we = FALSE; + new_whe = TRUE; + break; + case RFKILL_HARD_BLOCKED: + new_we = FALSE; + new_whe = FALSE; + break; + default: + break; + } + + nm_info ("Wireless now %s by radio killswitch", + (new_we && new_whe) ? "enabled" : "disabled"); + if (new_whe != priv->wireless_hw_enabled) { + priv->wireless_hw_enabled = new_whe; g_object_notify (G_OBJECT (self), NM_MANAGER_WIRELESS_HARDWARE_ENABLED); - - manager_set_wireless_enabled (self, enabled); } + manager_set_wireless_enabled (self, new_we); } static void diff --git a/src/nm-udev-manager.c b/src/nm-udev-manager.c new file mode 100644 index 0000000000..feafa11c14 --- /dev/null +++ b/src/nm-udev-manager.c @@ -0,0 +1,305 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 Red Hat, Inc. + */ + +#include +#include +#include + +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include + +#include "nm-udev-manager.h" +#include "nm-marshal.h" +#include "nm-utils.h" + +typedef struct { + GUdevClient *client; + + /* Authoritative rfkill state (RFKILL_* enum) + */ + RfKillState rfkill_state; + GSList *killswitches; + + gboolean disposed; +} NMUdevManagerPrivate; + +#define NM_UDEV_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_UDEV_MANAGER, NMUdevManagerPrivate)) + +G_DEFINE_TYPE (NMUdevManager, nm_udev_manager, G_TYPE_OBJECT) + +enum { + RFKILL_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +typedef struct { + char *name; + guint64 seqnum; + char *path; + char *driver; + gint state; +} Killswitch; + +RfKillState +nm_udev_manager_get_rfkill_state (NMUdevManager *self) +{ + g_return_val_if_fail (self != NULL, RFKILL_UNBLOCKED); + + return NM_UDEV_MANAGER_GET_PRIVATE (self)->rfkill_state; +} + +static Killswitch * +killswitch_new (GUdevDevice *device) +{ + Killswitch *ks; + GUdevDevice *parent = NULL; + const char *driver; + + ks = g_malloc0 (sizeof (Killswitch)); + ks->name = g_strdup (g_udev_device_get_name (device)); + ks->seqnum = g_udev_device_get_seqnum (device); + ks->path = g_strdup (g_udev_device_get_sysfs_path (device)); + + driver = g_udev_device_get_property (device, "DRIVER"); + if (!driver) { + parent = g_udev_device_get_parent (device); + if (parent) + driver = g_udev_device_get_property (parent, "DRIVER"); + } + if (driver) + ks->driver = g_strdup (driver); + + if (parent) + g_object_unref (parent); + + return ks; +} + +static void +killswitch_destroy (Killswitch *ks) +{ + g_return_if_fail (ks != NULL); + + g_free (ks->name); + g_free (ks->path); + g_free (ks->driver); + memset (ks, 0, sizeof (Killswitch)); + g_free (ks); +} + +NMUdevManager * +nm_udev_manager_new (void) +{ + return NM_UDEV_MANAGER (g_object_new (NM_TYPE_UDEV_MANAGER, NULL)); +} + +static RfKillState +sysfs_state_to_nm_state (gint sysfs_state) +{ + switch (sysfs_state) { + case 0: + return RFKILL_HARD_BLOCKED; + case 1: + return RFKILL_UNBLOCKED; + case 2: + return RFKILL_SOFT_BLOCKED; + default: + g_warning ("%s: Unhandled rfkill state %d", __func__, sysfs_state); + break; + } + return RFKILL_UNBLOCKED; +} + +static void +recheck_killswitches (NMUdevManager *self) +{ + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + GSList *iter; + RfKillState poll_state = RFKILL_UNBLOCKED; + + for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) { + Killswitch *ks = iter->data; + GUdevDevice *device; + RfKillState dev_state; + + device = g_udev_client_query_by_subsystem_and_name (priv->client, "rfkill", ks->name); + if (!device) + continue; + + dev_state = sysfs_state_to_nm_state (g_udev_device_get_property_as_int (device, "RFKILL_STATE")); + if (dev_state > poll_state) + poll_state = dev_state; + + g_object_unref (device); + } + + if (poll_state != priv->rfkill_state) { + priv->rfkill_state = poll_state; + g_signal_emit (self, signals[RFKILL_CHANGED], 0, priv->rfkill_state); + } +} + +static Killswitch * +killswitch_find_by_name (NMUdevManager *self, const char *name) +{ + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + GSList *iter; + + g_return_val_if_fail (name != NULL, NULL); + + for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) { + Killswitch *candidate = iter->data; + + if (!strcmp (name, candidate->name)) + return candidate; + } + return NULL; +} + +static void +add_one_killswitch (NMUdevManager *self, GUdevDevice *device) +{ + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + const char *type; + Killswitch *ks; + + type = g_udev_device_get_property (device, "RFKILL_TYPE"); + if (!type || strcmp (type, "wlan")) + return; + + ks = killswitch_new (device); + priv->killswitches = g_slist_prepend (priv->killswitches, ks); + + nm_info ("Found radio killswitch %s (at %s) (driver %s)", + ks->name, + ks->path, + ks->driver ? ks->driver : ""); +} + +static void +handle_uevent (GUdevClient *client, + const char *action, + GUdevDevice *device, + gpointer user_data) +{ + NMUdevManager *self = NM_UDEV_MANAGER (user_data); + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + const char *name, *subsys; + + g_return_if_fail (action != NULL); + + name = g_udev_device_get_name (device); + g_return_if_fail (name != NULL); + + /* A bit paranoid */ + subsys = g_udev_device_get_subsystem (device); + g_return_if_fail (subsys && !strcmp (subsys, "rfkill")); + + if (!strcmp (action, "add")) { + if (!killswitch_find_by_name (self, name)) + add_one_killswitch (self, device); + } else if (!strcmp (action, "remove")) { + GSList *iter; + + for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) { + Killswitch *ks = iter->data; + + if (!strcmp (ks->name, name)) { + nm_info ("Radio killswitch %s disappeared", ks->path); + priv->killswitches = g_slist_remove (priv->killswitches, iter); + killswitch_destroy (iter->data); + g_slist_free (iter); + break; + } + } + } + + // FIXME: check sequence #s to ensure events don't arrive out of order + + recheck_killswitches (self); +} + +static void +nm_udev_manager_init (NMUdevManager *self) +{ + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + const char *subsys[2] = { "rfkill", NULL }; + GList *switches, *iter; + + priv->rfkill_state = RFKILL_UNBLOCKED; + priv->client = g_udev_client_new (subsys); + g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self); + + switches = g_udev_client_query_by_subsystem (priv->client, "rfkill"); + for (iter = switches; iter; iter = g_list_next (iter)) { + add_one_killswitch (self, G_UDEV_DEVICE (iter->data)); + g_object_unref (G_UDEV_DEVICE (iter->data)); + } + g_list_free (switches); + + recheck_killswitches (self); +} + +static void +dispose (GObject *object) +{ + NMUdevManager *self = NM_UDEV_MANAGER (object); + NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self); + + if (priv->disposed) { + G_OBJECT_CLASS (nm_udev_manager_parent_class)->dispose (object); + return; + } + priv->disposed = TRUE; + + g_object_unref (priv->client); + + g_slist_foreach (priv->killswitches, (GFunc) killswitch_destroy, NULL); + g_slist_free (priv->killswitches); + + G_OBJECT_CLASS (nm_udev_manager_parent_class)->dispose (object); +} + +static void +nm_udev_manager_class_init (NMUdevManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMUdevManagerPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + + /* Signals */ + signals[RFKILL_CHANGED] = + g_signal_new ("rfkill-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMUdevManagerClass, rfkill_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); +} + diff --git a/src/nm-udev-manager.h b/src/nm-udev-manager.h new file mode 100644 index 0000000000..dd4dbf1591 --- /dev/null +++ b/src/nm-udev-manager.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2008 Red Hat, Inc. + */ + +#ifndef NM_UDEV_MANAGER_H +#define NM_UDEV_MANAGER_H + +#include +#include + +typedef enum { + RFKILL_UNBLOCKED = 0, + RFKILL_SOFT_BLOCKED = 1, + RFKILL_HARD_BLOCKED = 2 +} RfKillState; + +G_BEGIN_DECLS + +#define NM_TYPE_UDEV_MANAGER (nm_udev_manager_get_type ()) +#define NM_UDEV_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_UDEV_MANAGER, NMUdevManager)) +#define NM_UDEV_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_UDEV_MANAGER, NMUdevManagerClass)) +#define NM_IS_UDEV_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_UDEV_MANAGER)) +#define NM_IS_UDEV_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_UDEV_MANAGER)) +#define NM_UDEV_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_UDEV_MANAGER, NMUdevManagerClass)) + +typedef struct { + GObject parent; +} NMUdevManager; + +typedef struct { + GObjectClass parent; + + /* Virtual functions */ + void (*rfkill_changed) (NMUdevManager *manager, RfKillState state); +} NMUdevManagerClass; + +GType nm_udev_manager_get_type (void); + +NMUdevManager *nm_udev_manager_new (void); +RfKillState nm_udev_manager_get_rfkill_state (NMUdevManager *manager); + +#endif /* NM_UDEV_MANAGER_H */