[PATCH 5/5] convert linux addon-input into a singleton addon

Rob Taylor rob.taylor at codethink.co.uk
Thu Jun 7 04:01:59 PDT 2007


Converts hald-addon-input to a singleton addon, making use of the new additions
to libhal api.  This uses the glib mainloop for listening to input events and
the dbus connection. Adds dependancy on glib and dbus-glib.
---
 fdi/policy/10osvendor/10-input-policy.fdi |    8 +-
 hald/linux/addons/Makefile.am             |    2 +-
 hald/linux/addons/addon-input.c           |  283 ++++++++++++++++++++++------
 3 files changed, 227 insertions(+), 66 deletions(-)

diff --git a/fdi/policy/10osvendor/10-input-policy.fdi b/fdi/policy/10osvendor/10-input-policy.fdi
index be4c235..8aecb81 100644
--- a/fdi/policy/10osvendor/10-input-policy.fdi
+++ b/fdi/policy/10osvendor/10-input-policy.fdi
@@ -5,13 +5,13 @@
   <device>
     <match key="info.capabilities" contains="input">
       <match key="info.capabilities" contains="button">
-	<match key="info.addons" contains_not="hald-addon-input">
-	  <append key="info.addons" type="strlist">hald-addon-input</append>
+	<match key="info.addons.singleton" contains_not="hald-addon-input">
+	  <append key="info.addons.singleton" type="strlist">hald-addon-input</append>
         </match>
       </match>
       <match key="info.capabilities" contains="input.keys">
-	<match key="info.addons" contains_not="hald-addon-input">
-	  <append key="info.addons" type="strlist">hald-addon-input</append>
+	<match key="info.addons.singleton" contains_not="hald-addon-input">
+	  <append key="info.addons.singleton" type="strlist">hald-addon-input</append>
         </match>
         <match key="info.capabilities" contains_not="button">
 	  <append key="info.capabilities" type="strlist">button</append>
diff --git a/hald/linux/addons/Makefile.am b/hald/linux/addons/Makefile.am
index 6a6a268..8546f02 100644
--- a/hald/linux/addons/Makefile.am
+++ b/hald/linux/addons/Makefile.am
@@ -79,7 +79,7 @@ hald_addon_hid_ups_SOURCES = addon-hid-ups.c ../../logger.c ../../util_helper.c
 hald_addon_hid_ups_LDADD = $(top_builddir)/libhal/libhal.la @GLIB_LIBS@
 
 hald_addon_input_SOURCES = addon-input.c ../../logger.c ../../util_helper.c
-hald_addon_input_LDADD = $(top_builddir)/libhal/libhal.la
+hald_addon_input_LDADD = $(top_builddir)/libhal/libhal.la @GLIB_LIBS@
 
 hald_addon_pmu_SOURCES = addon-pmu.c ../../logger.c ../../util_helper.c
 hald_addon_pmu_LDADD = $(top_builddir)/libhal/libhal.la
diff --git a/hald/linux/addons/addon-input.c b/hald/linux/addons/addon-input.c
index 6f3167f..45b1e61 100644
--- a/hald/linux/addons/addon-input.c
+++ b/hald/linux/addons/addon-input.c
@@ -6,6 +6,7 @@
  * Copyright (C) 2005 David Zeuthen, <david at fubar.dk>
  * Copyright (C) 2005 Ryan Lortie <desrt at desrt.ca>
  * Copyright (C) 2006 Matthew Garrett <mjg59 at srcf.ucam.org>
+ * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor at codethink.co.uk>
  *
  * 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
@@ -32,16 +33,19 @@
 #include <unistd.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
 #include <linux/input.h>
+#include <glib/gmain.h>
+#include <glib/gprintf.h>
+#include <dbus/dbus-glib-lowlevel.h>
 
 #include "libhal/libhal.h"
 
 #include "../../logger.h"
 #include "../../util_helper.h"
 
-static char *udi;
-static int button_state = FALSE;
-static int button_has_state = FALSE;
 
 static char *key_name[KEY_MAX + 1] = {
 	[0 ... KEY_MAX] = NULL,
@@ -179,23 +183,58 @@ static char *key_name[KEY_MAX + 1] = {
 #define LONG(x) ((x)/BITS_PER_LONG)
 #define test_bit(bit, array)    ((array[LONG(bit)] >> OFF(bit)) & 1)
 
-static void
-main_loop (LibHalContext *ctx, FILE* eventfp)
+typedef struct _InputData InputData;
+struct _InputData
 {
-	DBusError error;
 	struct input_event event;
+	gsize offset;
+	gboolean button_has_state;
+	gboolean button_state;
+	char udi[1];			/*variable size*/
+};
 
+static LibHalContext *ctx = NULL;
+static GMainLoop *gmain = NULL;
+static GHashTable *inputs = NULL;
+static GList *devices = NULL;
 
-	while (fread (&event, sizeof(event), 1, eventfp)) {
+static gboolean
+event_io (GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+	InputData *input_data = (InputData*) data;
+	DBusError error;
+	GError *gerror = NULL;
+	gsize read_bytes;
+
+	if (condition & (G_IO_HUP | G_IO_ERR))
+		return FALSE;
+
+	/** tbh, we can probably assume every time we read we have a whole
+	 * event availiable, but hey..*/
+	while (g_io_channel_read_chars (channel,
+			((gchar*)&input_data->event) + input_data->offset,
+			sizeof(struct input_event) - input_data->offset,
+			&read_bytes, &gerror) == G_IO_STATUS_NORMAL) {
+
+		if (input_data->offset + read_bytes < sizeof (struct input_event)) {
+			input_data->offset = input_data->offset + read_bytes;
+			HAL_DEBUG (("incomplete read"));
+			return TRUE;
+		} else {
+			input_data->offset = 0;
+		}
 
-		if (button_has_state && event.type == EV_SW) {
+		if (input_data->button_has_state &&
+		    input_data->event.type == EV_SW) {
 			char *name = NULL;
 
-			HAL_INFO (("%s: event.value=%d ; event.code=%d (0x%02x)", 
-				   udi, event.value, event.code, event.code));
+			HAL_INFO (("%s: event.value=%d ; event.code=%d (0x%02x)",
+				   input_data->udi, input_data->event.value,
+				   input_data->event.code,
+				   input_data->event.code));
 
 
-			switch (event.code) {
+			switch (input_data->event.code) {
 			case SW_LID:
 				name = "lid";
 				break;
@@ -225,100 +264,222 @@ main_loop (LibHalContext *ctx, FILE* eventfp)
 				 * 19:08:26.960 [I] event.value=0 ; event.code=0 (0x00)
 				 */
 
-				if (ioctl (fileno (eventfp), EVIOCGSW(sizeof (bitmask)), bitmask) < 0) {
+				if (ioctl (g_io_channel_unix_get_fd(channel), EVIOCGSW(sizeof (bitmask)), bitmask) < 0) {
 					HAL_DEBUG (("ioctl EVIOCGSW failed"));
 				} else {
-					int new_state = test_bit (event.code, bitmask);
-					if (new_state != button_state) {
-						button_state = new_state;
-						
+					int new_state = test_bit (input_data->event.code, bitmask);
+					if (new_state != input_data->button_state) {
+						input_data->button_state = new_state;
+
 						dbus_error_init (&error);
-						libhal_device_set_property_bool (ctx, udi, "button.state.value", 
-										 button_state, &error);
-						
+						libhal_device_set_property_bool (ctx, input_data->udi, "button.state.value",
+										 input_data->button_state, &error);
+
 						dbus_error_init (&error);
-						libhal_device_emit_condition (ctx, udi, 
+						libhal_device_emit_condition (ctx, input_data->udi,
 									      "ButtonPressed",
 									      name,
 									      &error);
+						dbus_error_free (&error);
 					}
 				}
 			}
-		} else if (event.type == EV_KEY && key_name[event.code] != NULL && event.value == 1) {
+		} else if (input_data->event.type == EV_KEY && key_name[input_data->event.code] != NULL && input_data->event.value == 1) {
 			dbus_error_init (&error);
-			libhal_device_emit_condition (ctx, udi, 
+			libhal_device_emit_condition (ctx, input_data->udi,
 						      "ButtonPressed",
-						      key_name[event.code],
+						      key_name[input_data->event.code],
 						      &error);
+			dbus_error_free (&error);
 		}
 	}
+
+	return TRUE;
+}
+
+static void
+destroy_data (InputData *data)
+{
+	HAL_DEBUG (("Removing '%s'", data->udi));
+	g_hash_table_remove (inputs, data->udi);
+
+	g_free (data);
+
+	if (g_hash_table_size (inputs) == 0) {
+		HAL_INFO(("no more devices, exiting"));
+		g_main_loop_quit (gmain);
+	}
+}
+
+
+static void
+update_proc_title ()
+{
+	GList *lp;
+	gchar *new_command_line, *p;
+	gint len = 0;
+
+	for (lp = devices; lp; lp = lp->next)
+		len = len + strlen (lp->data) + 1;
 	
-	dbus_error_free (&error);
+	len += strlen ("hald-addon-input: Listening on");
+
+	new_command_line = g_malloc (len + 1);
+	p = new_command_line;
+	p += g_sprintf (new_command_line, "hald-addon-input: Listening on");
+
+	for (lp = devices; lp; lp = lp->next)
+	{
+		p += g_sprintf (p, " %s", (gchar*) lp->data);
+	}
+
+	hal_set_proc_title (new_command_line);
+	g_free (new_command_line);
+}
+
+static void
+add_device (LibHalContext *ctx,
+	    const char *udi,
+	    const LibHalPropertySet *properties)
+{
+	int eventfp;
+	GIOChannel *channel;
+	InputData *data;
+	int len = strlen (udi);
+	const char* device_file;
+
+	data = (InputData*) g_malloc (sizeof (InputData) + len);
+
+	memcpy (&(data->udi), udi, len+1);
+
+	if ((device_file = libhal_ps_get_string (properties, "input.device")) == NULL) {
+		HAL_ERROR(("%s has no property input.device", udi));
+		return;
+	}
+
+	/* button_has_state will be false if the key isn't available*/
+	data->button_has_state = libhal_ps_get_bool (properties, "button.has_state");
+	if (data->button_has_state)
+		data->button_state = libhal_ps_get_bool (properties, "button.state.value");
+
+	data->offset = 0;
+	eventfp = open(device_file, O_RDONLY | O_NONBLOCK);
+	if (!eventfp) {
+		HAL_ERROR(("Unable to open %s for reading", device_file));
+		return;
+	}
+
+
+	HAL_DEBUG (("%s: Listening on %s", udi, device_file));
+
+	devices = g_list_prepend (devices, g_strdup (device_file));
+	update_proc_title ();
+
+	channel = g_io_channel_unix_new (eventfp);
+	g_io_channel_set_encoding (channel, NULL, NULL);
+
+	g_hash_table_insert (inputs, data->udi, channel);
+	g_io_add_watch_full (channel,
+			     G_PRIORITY_DEFAULT, G_IO_IN | G_IO_ERR | G_IO_HUP,
+			     event_io, data, (GDestroyNotify) destroy_data);
+}
+
+
+static void
+remove_device (LibHalContext *ctx,
+	    const char *udi,
+	    const LibHalPropertySet *properties)
+{
+
+	GIOChannel *channel;
+	const gchar *device_file;
+	GList *lp;
+
+	channel = g_hash_table_lookup (inputs, udi);
+
+	HAL_DEBUG (("Removing channel for '%s'", udi));
+
+	if (G_UNLIKELY (!channel)) {
+		HAL_ERROR(("DeviceRemove called for unknown device: '%s'", udi));
+		return;
+	}
+
+	g_io_channel_unref (channel);
+
+	if ((device_file = libhal_ps_get_string (properties, "input.device")) == NULL) {
+		HAL_ERROR(("%s has no property input.device", udi));
+		return;
+	}
+
+	lp = g_list_find_custom (devices, device_file, (GCompareFunc) strcmp);
+	if (lp) {
+		devices = g_list_remove_link (devices, lp);
+		g_free (lp->data);
+		g_list_free_1 (lp);
+		update_proc_title ();
+	}
 }
 
 int
 main (int argc, char **argv)
 {
-	LibHalContext *ctx = NULL;
+	DBusConnection *dbus_connection;
 	DBusError error;
-	char *device_file;
-	FILE *eventfp;
-	char *s;
+	const char *commandline;
 
 	hal_set_proc_title_init (argc, argv);
 
 	/* setup_logger (); */
 
 	dbus_error_init (&error);
-
-	if ((udi = getenv ("UDI")) == NULL)
+	if ((ctx = libhal_ctx_init_direct (&error)) == NULL) {
+		HAL_WARNING (("Unable to init libhal context"));
 		goto out;
-	
-	if ((device_file = getenv ("HAL_PROP_INPUT_DEVICE")) == NULL)
+	}
+
+	if ((dbus_connection = libhal_ctx_get_dbus_connection(ctx)) == NULL) {
+		HAL_WARNING (("Cannot get DBus connection"));
 		goto out;
+	}
 
-	s = getenv ("HAL_PROP_BUTTON_HAS_STATE");
-	if (s != NULL && strcmp (s, "true") == 0) {
-		button_has_state = TRUE;
-		if ((s = getenv ("HAL_PROP_BUTTON_STATE_VALUE")) == NULL)
-			goto out;
-		button_state = (strcmp (s, "true") == 0);
+	if ((commandline = getenv ("SINGLETON_COMMAND_LINE")) == NULL) {
+		HAL_WARNING (("SINGLETON_COMMAND_LINE not set"));
+		goto out;
 	}
 
-	if ((ctx = libhal_ctx_init_direct (&error)) == NULL)
-                goto out;
+	libhal_ctx_set_singleton_device_added (ctx, add_device);
+	libhal_ctx_set_singleton_device_removed (ctx, remove_device);
+
+	dbus_connection_setup_with_g_main (dbus_connection, NULL);
+	dbus_connection_set_exit_on_disconnect (dbus_connection, 0);
 
 	dbus_error_init (&error);
-	if (!libhal_device_addon_is_ready (ctx, udi, &error)) {
+
+	if (!libhal_device_singleton_addon_is_ready (ctx, commandline, &error)) {
 		goto out;
 	}
 
-	eventfp = fopen(device_file, "r");	
-
-	if (!eventfp)
-		goto out;
+/*
+ * We should do real privilage dropping here, or we can do drop_privialages
+ * if haldaemon user can read input.
+ * drop_privileges (0);
+*/
 
-	drop_privileges (0);
+	inputs = g_hash_table_new (g_str_hash, g_str_equal);
 
-	hal_set_proc_title ("hald-addon-input: listening on %s", device_file);
+	gmain = g_main_loop_new (NULL, FALSE);
+	g_main_loop_run (gmain);
 
-	while (1)
-	{
-		main_loop (ctx, eventfp);
-		
-		/* If main_loop exits sleep for 5s and try to reconnect (again). */
-		sleep (5);
-	}
-	
 	return 0;
 
- out:
+out:
+	HAL_DEBUG (("An error occured, exiting cleanly"));
 	if (ctx != NULL) {
-                dbus_error_init (&error);
-                libhal_ctx_shutdown (ctx, &error);
-                libhal_ctx_free (ctx);
-        }
-	
+		dbus_error_init (&error);
+		libhal_ctx_shutdown (ctx, &error);
+		libhal_ctx_free (ctx);
+	}
+
 	return 0;
 }
 
-- 
1.5.2-rc3.GIT


--------------020002090005010409010607--


More information about the hal mailing list