mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-13 17:30:33 +01:00
rfkill: rework to handle new dynamic HAL killswitches
Requires HAL 0.5.12 as of 2008-11-19 and kernel 2.6.27 or later; if any dynamic killswitches are found, polled killswitches are ignored. This is half the fix; the other half is to do something intelligent with the rfkill state instead of taking the wifi devices down.
This commit is contained in:
parent
2445f78e07
commit
ed274df7c7
3 changed files with 252 additions and 105 deletions
|
|
@ -39,19 +39,41 @@
|
|||
|
||||
#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;
|
||||
gboolean rfkilled; /* Authoritative rfkill state */
|
||||
|
||||
/* Killswitch handling */
|
||||
GSList *killswitch_list;
|
||||
/* 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 poll_rfkilled;
|
||||
guint32 pending_polls;
|
||||
GSList *poll_proxies;
|
||||
|
||||
gboolean disposed;
|
||||
} NMHalManagerPrivate;
|
||||
|
|
@ -280,6 +302,75 @@ device_new_capability (LibHalContext *ctx, const char *udi, const char *capabili
|
|||
g_signal_emit (self, signals[UDI_ADDED], 0, udi, creator->device_type_name, creator->creator_fn);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -317,52 +408,52 @@ add_initial_devices (NMHalManager *self)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
killswitch_poll_cleanup (NMHalManager *self)
|
||||
{
|
||||
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
|
||||
|
||||
if (priv->poll_proxies) {
|
||||
g_slist_foreach (priv->poll_proxies, (GFunc) g_object_unref, NULL);
|
||||
g_slist_free (priv->poll_proxies);
|
||||
priv->poll_proxies = NULL;
|
||||
}
|
||||
|
||||
priv->pending_polls = 0;
|
||||
priv->poll_rfkilled = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
killswitch_getpower_done (gpointer user_data)
|
||||
{
|
||||
NMHalManager *self = NM_HAL_MANAGER (user_data);
|
||||
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
|
||||
Killswitch *ks = user_data;
|
||||
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (ks->self);
|
||||
GSList *iter;
|
||||
RfKillState new_state = RFKILL_UNBLOCKED;
|
||||
|
||||
priv->pending_polls--;
|
||||
|
||||
if (priv->pending_polls > 0)
|
||||
return;
|
||||
|
||||
if (priv->poll_rfkilled != priv->rfkilled) {
|
||||
priv->rfkilled = priv->poll_rfkilled;
|
||||
g_signal_emit (self, signals[RFKILL_CHANGED], 0, priv->rfkilled);
|
||||
if (ks->proxy) {
|
||||
g_object_unref (ks->proxy);
|
||||
ks->proxy = NULL;
|
||||
}
|
||||
|
||||
killswitch_poll_cleanup (self);
|
||||
/* 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,
|
||||
self);
|
||||
poll_killswitches,
|
||||
ks->self);
|
||||
}
|
||||
|
||||
static void
|
||||
killswitch_getpower_reply (DBusGProxy *proxy,
|
||||
DBusGProxyCall *call_id,
|
||||
gpointer user_data)
|
||||
DBusGProxyCall *call_id,
|
||||
gpointer user_data)
|
||||
{
|
||||
NMHalManager *self = NM_HAL_MANAGER (user_data);
|
||||
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
|
||||
Killswitch *ks = user_data;
|
||||
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (ks->self);
|
||||
int power = 1;
|
||||
GError *err = NULL;
|
||||
|
||||
|
|
@ -370,7 +461,7 @@ killswitch_getpower_reply (DBusGProxy *proxy,
|
|||
G_TYPE_INT, &power,
|
||||
G_TYPE_INVALID)) {
|
||||
if (power == 0)
|
||||
priv->poll_rfkilled = TRUE;
|
||||
ks->state = RFKILL_HARD_BLOCKED;
|
||||
} else {
|
||||
if (err->message) {
|
||||
/* Only print the error if we haven't seen it before */
|
||||
|
|
@ -392,7 +483,7 @@ killswitch_getpower_reply (DBusGProxy *proxy,
|
|||
if (strstr (err->message, "Did not receive a reply")) {
|
||||
nm_warning ("HAL did not reply to killswitch power request;"
|
||||
" assuming radio is blocked.");
|
||||
priv->poll_rfkilled = TRUE;
|
||||
ks->state = RFKILL_HARD_BLOCKED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -400,69 +491,56 @@ killswitch_getpower_reply (DBusGProxy *proxy,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poll_one_killswitch (gpointer data, gpointer user_data)
|
||||
{
|
||||
NMHalManager *self = NM_HAL_MANAGER (user_data);
|
||||
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
|
||||
DBusGProxy *proxy;
|
||||
|
||||
proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
|
||||
"org.freedesktop.Hal",
|
||||
(char *) data,
|
||||
"org.freedesktop.Hal.Device.KillSwitch");
|
||||
|
||||
dbus_g_proxy_begin_call (proxy, "GetPower",
|
||||
killswitch_getpower_reply,
|
||||
self,
|
||||
killswitch_getpower_done,
|
||||
G_TYPE_INVALID);
|
||||
priv->pending_polls++;
|
||||
priv->poll_proxies = g_slist_prepend (priv->poll_proxies, proxy);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
poll_killswitches (gpointer user_data)
|
||||
{
|
||||
NMHalManager *self = NM_HAL_MANAGER (user_data);
|
||||
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
|
||||
GSList *iter;
|
||||
|
||||
killswitch_poll_cleanup (self);
|
||||
for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) {
|
||||
Killswitch *ks = iter->data;
|
||||
|
||||
g_slist_foreach (priv->killswitch_list, poll_one_killswitch, self);
|
||||
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 void
|
||||
add_killswitch_device (NMHalManager *self, const char *udi)
|
||||
static Killswitch *
|
||||
killswitch_new (const char *udi,
|
||||
int state,
|
||||
gboolean polled,
|
||||
NMHalManager *self)
|
||||
{
|
||||
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
|
||||
char *type;
|
||||
GSList *iter;
|
||||
Killswitch *ks;
|
||||
|
||||
type = libhal_device_get_property_string (priv->hal_ctx, udi, "killswitch.type", NULL);
|
||||
if (!type)
|
||||
return;
|
||||
ks = g_malloc0 (sizeof (Killswitch));
|
||||
ks->udi = g_strdup (udi);
|
||||
ks->state = state;
|
||||
ks->self = self;
|
||||
ks->polled = polled;
|
||||
|
||||
if (strcmp (type, "wlan"))
|
||||
goto out;
|
||||
return ks;
|
||||
}
|
||||
|
||||
/* see if it's already in the list */
|
||||
for (iter = priv->killswitch_list; iter; iter = iter->next) {
|
||||
const char *list_udi = (const char *) iter->data;
|
||||
if (!strcmp (list_udi, udi))
|
||||
goto out;
|
||||
}
|
||||
static void
|
||||
killswitch_free (gpointer user_data)
|
||||
{
|
||||
Killswitch *ks = user_data;
|
||||
|
||||
/* Poll switches if this is the first switch we've found */
|
||||
if (!priv->killswitch_list)
|
||||
priv->killswitch_poll_id = g_idle_add (poll_killswitches, self);
|
||||
|
||||
priv->killswitch_list = g_slist_append (priv->killswitch_list, g_strdup (udi));
|
||||
nm_info ("Found radio killswitch %s", udi);
|
||||
|
||||
out:
|
||||
libhal_free_string (type);
|
||||
if (ks->proxy)
|
||||
g_object_unref (ks->proxy);
|
||||
g_free (ks->udi);
|
||||
g_free (ks);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -470,9 +548,9 @@ add_killswitch_devices (NMHalManager *self)
|
|||
{
|
||||
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
|
||||
char **udis;
|
||||
int num_udis;
|
||||
int i;
|
||||
DBusError err;
|
||||
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);
|
||||
|
|
@ -485,9 +563,72 @@ add_killswitch_devices (NMHalManager *self)
|
|||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_udis; i++)
|
||||
add_killswitch_device (self, udis[i]);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
|
@ -520,6 +661,7 @@ 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)) {
|
||||
|
|
@ -550,12 +692,11 @@ hal_deinit (NMHalManager *self)
|
|||
g_source_remove (priv->killswitch_poll_id);
|
||||
priv->killswitch_poll_id = 0;
|
||||
}
|
||||
killswitch_poll_cleanup (self);
|
||||
|
||||
if (priv->killswitch_list) {
|
||||
g_slist_foreach (priv->killswitch_list, (GFunc) g_free, NULL);
|
||||
g_slist_free (priv->killswitch_list);
|
||||
priv->killswitch_list = NULL;
|
||||
if (priv->killswitches) {
|
||||
g_slist_foreach (priv->killswitches, (GFunc) killswitch_free, NULL);
|
||||
g_slist_free (priv->killswitches);
|
||||
priv->killswitches = NULL;
|
||||
}
|
||||
|
||||
if (!priv->hal_ctx)
|
||||
|
|
@ -666,7 +807,7 @@ nm_hal_manager_init (NMHalManager *self)
|
|||
{
|
||||
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
|
||||
|
||||
priv->rfkilled = FALSE;
|
||||
priv->rfkill_state = RFKILL_UNBLOCKED;
|
||||
|
||||
priv->dbus_mgr = nm_dbus_manager_get ();
|
||||
|
||||
|
|
@ -763,9 +904,9 @@ nm_hal_manager_class_init (NMHalManagerClass *klass)
|
|||
G_SIGNAL_RUN_FIRST,
|
||||
G_STRUCT_OFFSET (NMHalManagerClass, rfkill_changed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__BOOLEAN,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_BOOLEAN);
|
||||
G_TYPE_UINT);
|
||||
|
||||
signals[HAL_REAPPEARED] =
|
||||
g_signal_new ("hal-reappeared",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@
|
|||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
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 ())
|
||||
|
|
@ -53,7 +59,7 @@ typedef struct {
|
|||
|
||||
void (*udi_removed) (NMHalManager *manager, const char *udi);
|
||||
|
||||
void (*rfkill_changed) (NMHalManager *manager, gboolean hw_enabled);
|
||||
void (*rfkill_changed) (NMHalManager *manager, RfKillState state);
|
||||
|
||||
void (*hal_reappeared) (NMHalManager *manager);
|
||||
} NMHalManagerClass;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ static void hal_manager_udi_removed_cb (NMHalManager *hal_mgr,
|
|||
gpointer user_data);
|
||||
|
||||
static void hal_manager_rfkill_changed_cb (NMHalManager *hal_mgr,
|
||||
gboolean rfkilled,
|
||||
RfKillState state,
|
||||
gpointer user_data);
|
||||
|
||||
static void hal_manager_hal_reappeared_cb (NMHalManager *hal_mgr,
|
||||
|
|
@ -1782,12 +1782,12 @@ hal_manager_udi_removed_cb (NMHalManager *manager,
|
|||
|
||||
static void
|
||||
hal_manager_rfkill_changed_cb (NMHalManager *hal_mgr,
|
||||
gboolean rfkilled,
|
||||
RfKillState state,
|
||||
gpointer user_data)
|
||||
{
|
||||
NMManager *self = NM_MANAGER (user_data);
|
||||
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
|
||||
gboolean enabled = !rfkilled;
|
||||
gboolean enabled = (state == RFKILL_UNBLOCKED);
|
||||
|
||||
if (priv->wireless_hw_enabled != enabled) {
|
||||
nm_info ("Wireless now %s by radio killswitch", enabled ? "enabled" : "disabled");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue