Blob Blame History Raw
From e4bc5b81dea5c0bfbfb42a929ea273fe19e72d7c Mon Sep 17 00:00:00 2001
From: Benjamin Berg <bberg@redhat.com>
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 <gio/gunixoutputstream.h>
 
 #include "rfkill-glib.h"
+#include <gudev/gudev.h>
 
 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