From e4bc5b81dea5c0bfbfb42a929ea273fe19e72d7c Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 28 Aug 2018 13:05:59 +0200 Subject: [PATCH] rfkill: Use GUdev to monitor rfkill device presence This adds the required code to use GUdev to monitor for the existence of the rfkill device. This is relevant as the rfkill device might not exist initially if the rfkill module is not loaded during login. Fixes issue #52 --- meson.build | 1 + plugins/rfkill/meson.build | 7 ++- plugins/rfkill/rfkill-glib.c | 99 +++++++++++++++++++++++++++++++----- 3 files changed, 94 insertions(+), 13 deletions(-) diff --git a/meson.build b/meson.build index c94c4f85..7510f816 100644 --- a/meson.build +++ b/meson.build @@ -182,6 +182,7 @@ enable_rfkill = get_option('rfkill') assert(enable_rfkill or not host_is_linux, 'rfkill is not optional on Linux platforms') if enable_rfkill assert(cc.has_header('linux/rfkill.h'), 'rfkill support requested but RFKill headers not found') + assert(enable_gudev, 'GUdev is required for rfkill support') udev_dir = get_option('udev_dir') if udev_dir == '' diff --git a/plugins/rfkill/meson.build b/plugins/rfkill/meson.build index b85620b4..4d70352a 100644 --- a/plugins/rfkill/meson.build +++ b/plugins/rfkill/meson.build @@ -9,7 +9,12 @@ sources = files( 'main.c' ) -deps = plugins_deps + [gio_unix_dep] +deps = plugins_deps +deps += [ + gio_unix_dep, + gudev_dep, + m_dep +] executable( 'gsd-' + plugin_name, diff --git a/plugins/rfkill/rfkill-glib.c b/plugins/rfkill/rfkill-glib.c index 9862105e..0dc26f92 100644 --- a/plugins/rfkill/rfkill-glib.c +++ b/plugins/rfkill/rfkill-glib.c @@ -38,6 +38,7 @@ #include #include "rfkill-glib.h" +#include enum { CHANGED, @@ -53,11 +54,15 @@ static int signals[LAST_SIGNAL] = { 0 }; struct _CcRfkillGlib { GObject parent; + GUdevClient *udev; + gchar *device_file; + GOutputStream *stream; GIOChannel *channel; guint watch_id; /* rfkill-input inhibitor */ + gboolean noinput; int noinput_fd; /* Pending Bluetooth enablement. @@ -374,12 +379,13 @@ event_cb (GIOChannel *source, static void cc_rfkill_glib_init (CcRfkillGlib *rfkill) { + rfkill->device_file = NULL; rfkill->noinput_fd = -1; } -gboolean -cc_rfkill_glib_open (CcRfkillGlib *rfkill, - GError **error) +static gboolean +_cc_rfkill_glib_open (CcRfkillGlib *rfkill, + GError **error) { int fd; int ret; @@ -387,8 +393,10 @@ cc_rfkill_glib_open (CcRfkillGlib *rfkill, g_return_val_if_fail (CC_RFKILL_IS_GLIB (rfkill), FALSE); g_return_val_if_fail (rfkill->stream == NULL, FALSE); + g_assert (rfkill->device_file); + + fd = open (rfkill->device_file, O_RDWR); - fd = open("/dev/rfkill", O_RDWR); if (fd < 0) { g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), "Could not open RFKILL control device, please verify your installation"); @@ -455,6 +463,60 @@ cc_rfkill_glib_open (CcRfkillGlib *rfkill, return TRUE; } +static void +uevent_cb (GUdevClient *client, + gchar *action, + GUdevDevice *device, + gpointer user_data) +{ + CcRfkillGlib *rfkill = CC_RFKILL_GLIB (user_data); + + if (g_strcmp0 (action, "add") != 0) + return; + + if (g_strcmp0 (g_udev_device_get_name (device), "rfkill") == 0) { + g_autoptr(GError) error = NULL; + + g_debug ("Rfkill device has been created"); + + if (g_udev_device_get_device_file (device)) { + g_clear_pointer (&rfkill->device_file, g_free); + rfkill->device_file = g_strdup (g_udev_device_get_device_file (device)); + } else { + g_warning ("rfkill udev device does not have a device file!"); + } + + if (!_cc_rfkill_glib_open (rfkill, &error)) + g_warning ("Could not open rfkill device: %s", error->message); + else + g_debug ("Opened rfkill device after uevent"); + + g_clear_object (&rfkill->udev); + + /* Sync rfkill input inhibition state*/ + cc_rfkill_glib_set_rfkill_input_inhibited (rfkill, rfkill->noinput); + } +} + +gboolean +cc_rfkill_glib_open (CcRfkillGlib *rfkill, + GError **error) +{ + const char * const subsystems[] = { "misc", NULL }; + GUdevDevice *device; + + rfkill->udev = g_udev_client_new (subsystems); + g_debug ("Setting up uevent listener"); + g_signal_connect (rfkill->udev, "uevent", G_CALLBACK (uevent_cb), rfkill); + + /* Simulate uevent if device already exists. */ + device = g_udev_client_query_by_subsystem_and_name (rfkill->udev, "misc", "rfkill"); + if (device) + uevent_cb (rfkill->udev, "add", device, rfkill); + + return TRUE; +} + #define RFKILL_INPUT_INHIBITED(rfkill) (rfkill->noinput_fd >= 0) gboolean @@ -462,7 +524,7 @@ cc_rfkill_glib_get_rfkill_input_inhibited (CcRfkillGlib *rfkill) { g_return_val_if_fail (CC_RFKILL_IS_GLIB (rfkill), FALSE); - return RFKILL_INPUT_INHIBITED(rfkill); + return rfkill->noinput; } void @@ -471,21 +533,28 @@ cc_rfkill_glib_set_rfkill_input_inhibited (CcRfkillGlib *rfkill, { g_return_if_fail (CC_RFKILL_IS_GLIB (rfkill)); - /* Nothing to do if the states already match. */ - if (RFKILL_INPUT_INHIBITED(rfkill) == inhibit) + /* Shortcut in case we don't have an rfkill device */ + if (!rfkill->stream) { + if (rfkill->noinput == inhibit) + return; + + rfkill->noinput = inhibit; + g_object_notify (G_OBJECT (rfkill), "rfkill-input-inhibited"); + return; + } if (!inhibit && RFKILL_INPUT_INHIBITED(rfkill)) { close (rfkill->noinput_fd); - rfkill->noinput_fd = -1; - g_debug ("Closed rfkill noinput FD."); + + rfkill->noinput_fd = -1; } if (inhibit && !RFKILL_INPUT_INHIBITED(rfkill)) { int fd, res; /* Open write only as we don't want to do any IO to it ever. */ - fd = open ("/dev/rfkill", O_WRONLY); + fd = open (rfkill->device_file, O_WRONLY); if (fd < 0) { if (errno == EACCES) g_warning ("Could not open RFKILL control device, please verify your installation"); @@ -506,7 +575,10 @@ cc_rfkill_glib_set_rfkill_input_inhibited (CcRfkillGlib *rfkill, rfkill->noinput_fd = fd; } - g_object_notify (G_OBJECT (rfkill), "rfkill-input-inhibited"); + if (rfkill->noinput != RFKILL_INPUT_INHIBITED(rfkill)) { + rfkill->noinput = RFKILL_INPUT_INHIBITED(rfkill); + g_object_notify (G_OBJECT (rfkill), "rfkill-input-inhibited"); + } } static void @@ -537,7 +609,7 @@ cc_rfkill_glib_get_property (GObject *object, switch (prop_id) { case PROP_RFKILL_INPUT_INHIBITED: - g_value_set_boolean (value, RFKILL_INPUT_INHIBITED(rfkill)); + g_value_set_boolean (value, rfkill->noinput); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -566,6 +638,9 @@ cc_rfkill_glib_finalize (GObject *object) rfkill->noinput_fd = -1; } + g_clear_pointer (&rfkill->device_file, g_free); + g_clear_object (&rfkill->udev); + G_OBJECT_CLASS(cc_rfkill_glib_parent_class)->finalize(object); } -- 2.23.0.rc1