#include #include #include #include "hald-fdi.h" #include "hald-device.h" static void hald_fdi_class_init (HaldFdiClass *klass); static void hald_fdi_init (HaldFdi *fdi); static void hald_fdi_finalize (GObject *object); #define HALD_FDI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), HALD_TYPE_FDI, HaldFdiPrivate)) #define BYTES_PER_READ 4096 struct HaldFdiPrivate { GHashTable *hash; GNode *root; GNode *current; GNode *parent; GMarkupParser *m_parser; GMarkupParseContext *context; gboolean ignore_device; const char *current_file; }; typedef enum { HALD_FDI_LINK_MATCH, HALD_FDI_LINK_MERGE, HALD_FDI_LINK_APPEND, HALD_FDI_LINK_PREPEND, HALD_FDI_LINK_REMOVE, HALD_FDI_LINK_SPAWN, HALD_FDI_LINK_LAST } HaldFdiLinkType; typedef struct { HaldFdiLinkType type; char *match_type; char *match_value; char *value_type; char *value_data; char *data; } HaldFdiLink; enum { CREATE_DEVICE, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0, }; static gpointer hald_fdi_object = NULL; G_DEFINE_TYPE (HaldFdi, hald_fdi, G_TYPE_OBJECT) /** * parser_error_print: * * Error out on the invalid fdi entry **/ static void parser_error_print (HaldFdi *fdi, const char *data) { gint line_number = 0; gint char_number = 0; g_markup_parse_context_get_position (fdi->priv->context, &line_number, &char_number); g_error ("invalid entry: %s on %s (ln:%i, ch:%i)", data, fdi->priv->current_file, line_number, char_number); } /** * parser_start_node_cb: * * Called for open tags **/ static void parser_start_node_cb (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error) { HaldFdiLink *link; HaldFdi *fdi; fdi = HALD_FDI (user_data); /* stuff to ignore */ if (strcmp (element_name, "deviceinfo") == 0) { return; } /* check to see if we should ignore this device */ if (strcmp (element_name, "device") == 0) { if (attribute_values[0] == NULL) { fdi->priv->ignore_device = FALSE; return; } fdi->priv->ignore_device = TRUE; /* look this up in the hash to see if we should load this device */ if (g_hash_table_lookup (fdi->priv->hash, attribute_values[0])) { fdi->priv->ignore_device = FALSE; } return; } /* are we ignoring this device? */ if (fdi->priv->ignore_device) { return; } link = g_new0 (HaldFdiLink, 1); fdi->priv->current = g_node_new (link); g_node_append (fdi->priv->parent, fdi->priv->current); link->match_type = g_strdup (attribute_names[0]); link->match_value = g_strdup (attribute_values[0]); link->value_type = g_strdup (attribute_names[1]); link->value_data = g_strdup (attribute_values[1]); link->data = NULL; if (strcmp (element_name, "match") == 0) { link->type = HALD_FDI_LINK_MATCH; fdi->priv->parent = fdi->priv->current; } else if (strcmp (element_name, "merge") == 0) { link->type = HALD_FDI_LINK_MERGE; } else if (strcmp (element_name, "append") == 0) { link->type = HALD_FDI_LINK_APPEND; } else if (strcmp (element_name, "prepend") == 0) { link->type = HALD_FDI_LINK_PREPEND; } else if (strcmp (element_name, "remove") == 0) { link->type = HALD_FDI_LINK_REMOVE; } else if (strcmp (element_name, "spawn") == 0) { link->type = HALD_FDI_LINK_SPAWN; } else { parser_error_print (fdi, element_name); } } /** * parser_end_node_cb: * * Called for close tags **/ static void parser_end_node_cb (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { HaldFdi *fdi; fdi = HALD_FDI (user_data); /* are we ignoring this device? */ if (fdi->priv->ignore_device) { return; } /* stuff to ignore */ if (strcmp (element_name, "deviceinfo") == 0) { return; } /* go down a level */ if (strcmp (element_name, "match") == 0) { fdi->priv->parent = fdi->priv->parent->parent; } } /** * parser_text_cb: * * Called for character data **/ static void parser_text_cb (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { HaldFdi *fdi; fdi = HALD_FDI (user_data); /* are we ignoring this device? */ if (fdi->priv->ignore_device) { return; } /* we have not started known tags yet */ if (fdi->priv->current == NULL) { return; } char *newtext = g_strdup (text); newtext = g_strstrip (newtext); HaldFdiLink *link; link = fdi->priv->current->data; if (strlen (newtext) != 0) { link->data = g_strdup (newtext); } /* valid? */ g_free (newtext); } /** * parser_error_cb: **/ static void parser_error_cb (GMarkupParseContext *context, GError *error, gpointer user_data) { HaldFdi *fdi; fdi = HALD_FDI (user_data); g_markup_parse_context_free (fdi->priv->context); fdi->priv->context = NULL; } /** * hald_fdi_parse_file: **/ gboolean hald_fdi_parse_file (HaldFdi *fdi, const gchar *path, GError **error) { GIOChannel *io; gchar buf[BYTES_PER_READ]; gboolean result = TRUE; fdi->priv->m_parser = g_new0 (GMarkupParser, 1); fdi->priv->m_parser->start_element = parser_start_node_cb; fdi->priv->m_parser->end_element = parser_end_node_cb; fdi->priv->m_parser->error = parser_error_cb; fdi->priv->m_parser->text = parser_text_cb; fdi->priv->context = g_markup_parse_context_new (fdi->priv->m_parser, 0, fdi, NULL); fdi->priv->parent = fdi->priv->root; fdi->priv->current_file = path; /* Parse the string */ io = g_io_channel_new_file (path, "r", error); if (!io) { result = FALSE; goto exit; } while (TRUE) { GIOStatus io_status; gsize bytes_read; io_status = g_io_channel_read_chars (io, buf, BYTES_PER_READ, &bytes_read, error); if (io_status == G_IO_STATUS_ERROR) { result = FALSE; goto exit; } if (io_status != G_IO_STATUS_NORMAL) break; g_markup_parse_context_parse (fdi->priv->context, buf, bytes_read, error); if (error != NULL && *error != NULL) { result = FALSE; goto exit; } if (bytes_read < BYTES_PER_READ) { break; } } g_io_channel_shutdown (io, FALSE, NULL); exit: g_markup_parse_context_free (fdi->priv->context); g_free (fdi->priv->m_parser); return result; } /** * hald_fdi_scan_directory: **/ gboolean hald_fdi_scan_directory (HaldFdi *fdi, const char *location) { g_return_val_if_fail (HALD_IS_FDI (fdi), FALSE); g_return_val_if_fail (location != NULL, FALSE); GDir *dir; const char *name; char *full_path; dir = g_dir_open (location, 0, NULL); if (dir == NULL) { g_error ("directory open failed: %s", location); return FALSE; } while (TRUE) { name = g_dir_read_name (dir); /* end of list? */ if (name == NULL) { break; } full_path = g_strdup_printf ("%s/%s", location, name); /* recurse into directory structure */ if (g_file_test (full_path, (G_FILE_TEST_IS_REGULAR))) { /* only process files that are *.fdi */ if (g_str_has_suffix (name, ".fdi")) { hald_fdi_parse_file (fdi, full_path, NULL); } } else if (g_file_test (full_path, (G_FILE_TEST_IS_DIR))) { hald_fdi_scan_directory (fdi, full_path); } g_free (full_path); } g_dir_close (dir); return TRUE; } /** * hald_fdi_print_tree_recurse: **/ static void hald_fdi_print_tree_recurse (HaldFdi *fdi, GNode *node) { int i; HaldFdiLink *link; if (node == NULL) { return; } for (i=0; idata; if (link) { if (link->type == HALD_FDI_LINK_MATCH) { g_print (" %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data); } else if (link->type == HALD_FDI_LINK_MERGE) { g_print (" %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data); } else if (link->type == HALD_FDI_LINK_APPEND) { g_print (" %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data); } else if (link->type == HALD_FDI_LINK_PREPEND) { g_print (" %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data); } else if (link->type == HALD_FDI_LINK_REMOVE) { g_print (" %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data); } else if (link->type == HALD_FDI_LINK_SPAWN) { g_print (" %s (%s) = %s (%s) [%s]", link->match_value, link->match_type, link->value_data, link->value_type, link->data); } else { g_error ("unknown type, must be an error"); } } g_print ("\n"); hald_fdi_print_tree_recurse (fdi, node->children); hald_fdi_print_tree_recurse (fdi, node->next); } /** * hald_fdi_print_tree: **/ gboolean hald_fdi_print_tree (HaldFdi *fdi) { g_return_val_if_fail (HALD_IS_FDI (fdi), FALSE); hald_fdi_print_tree_recurse (fdi, fdi->priv->root->children); return TRUE; } /** * hald_fdi_add_allowed_device: **/ gboolean hald_fdi_add_allowed_device (HaldFdi *fdi, const char *devicename) { g_return_val_if_fail (HALD_IS_FDI (fdi), FALSE); g_return_val_if_fail (devicename != NULL, FALSE); g_hash_table_insert (fdi->priv->hash, g_strdup (devicename), GINT_TO_POINTER (TRUE)); return TRUE; } /** * hald_get_gvalue_from_type: **/ static GValue * hald_get_gvalue_from_type (const char *type, const char *value) { GValue *unit; unit = g_new0 (GValue, 1); if (strcmp (type, "bool") == 0) { g_value_init (unit, G_TYPE_BOOLEAN); if (strcmp (value, "true") == 0) { g_value_set_boolean (unit, TRUE); } else { g_value_set_boolean (unit, TRUE); } } else if (strcmp (type, "string") == 0) { g_value_init (unit, G_TYPE_STRING); g_value_set_string (unit, value); } else if (strcmp (type, "int") == 0) { g_value_init (unit, G_TYPE_INT); g_value_set_int (unit, atoi (value)); } else if (strcmp (type, "float") == 0) { g_value_init (unit, G_TYPE_FLOAT); g_value_set_float (unit, strtod (value, NULL)); } else { g_debug ("unknown value type: %s", type); unit = NULL; } return unit; } /** * hald_g_value_equal: * * how is this not in glib? **/ static gboolean hald_g_value_equal (GValue *one, GValue *two) { if (G_VALUE_TYPE (one) != G_VALUE_TYPE (two)) { g_error ("cannot compare types"); } GType type = G_VALUE_TYPE (one); if (type == G_TYPE_STRING) { const char *o = g_value_get_string (one); const char *t = g_value_get_string (two); if (o == NULL || t == NULL) return FALSE; return (strcmp (o, t) == 0); } else if (type == G_TYPE_INT) { int o = g_value_get_int (one); int t = g_value_get_int (two); return (o == t); } else if (type == G_TYPE_FLOAT) { float o = g_value_get_float (one); float t = g_value_get_float (two); return (o == t); } g_error ("type not supported"); return FALSE; } /** * hald_fdi_merge_item_merge: * * Merges information from the fdi entry into the device **/ static gboolean hald_fdi_merge_item_merge (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link) { GValue *new; gboolean ret; new = hald_get_gvalue_from_type (link->value_data, link->data); if (new == NULL) { return FALSE; } ret = hald_device_set_property (device, link->match_value, new, NULL); g_free (new); return ret; } /** * hald_fdi_merge_item_match: * * Find out if a device matches a rule **/ static gboolean hald_fdi_merge_item_match (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link) { GValue *new1 = NULL; GValue *new2 = NULL; /* convert the fdi value to a GValue */ /* TODO: do this in parsing */ new1 = hald_get_gvalue_from_type (link->value_type, link->value_data); if (new1 == NULL) { return FALSE; } hald_device_get_property (device, link->match_value, &new2, NULL); if (new2 == NULL) { return FALSE; } return hald_g_value_equal (new1, new2); } /** * hald_fdi_merge_item_spawn: * * Creates a new device. We have to emit this back to the manager class as we * have no reference to manager here. **/ static gboolean hald_fdi_merge_item_spawn (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link) { const char *udi = link->match_value; g_signal_emit (fdi, signals [CREATE_DEVICE], 0, udi); return TRUE; } /** * hald_fdi_merge_item_append: * * Appends an item onto a string list **/ static gboolean hald_fdi_merge_item_append (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link) { const char *key = link->match_value; return hald_device_string_list_append (device, key, link->data, NULL); } /** * hald_fdi_merge_item_prepend: * * Prepends an item onto a string list **/ static gboolean hald_fdi_merge_item_prepend (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link) { const char *key = link->match_value; return hald_device_string_list_prepend (device, key, link->data, NULL); } /** * hald_fdi_merge_item_remove: * * Removes a property **/ static gboolean hald_fdi_merge_item_remove (HaldFdi *fdi, HaldDevice *device, HaldFdiLink *link) { const char *key = link->match_value; return hald_device_remove_property (device, key, NULL); } /** * hald_fdi_merge_recurse: **/ static void hald_fdi_merge_recurse (HaldFdi *fdi, HaldDevice *device, GNode *node) { HaldFdiLink *link; gboolean cont; if (node == NULL) { return; } link = node->data; cont = TRUE; /* if we pass in root directly, then the first link is NULL */ if (link) { if (link->type == HALD_FDI_LINK_MATCH) { cont = hald_fdi_merge_item_match (fdi, device, link); } else if (link->type == HALD_FDI_LINK_MERGE) { hald_fdi_merge_item_merge (fdi, device, link); } else if (link->type == HALD_FDI_LINK_SPAWN) { hald_fdi_merge_item_spawn (fdi, device, link); } else if (link->type == HALD_FDI_LINK_APPEND) { hald_fdi_merge_item_append (fdi, device, link); } else if (link->type == HALD_FDI_LINK_PREPEND) { hald_fdi_merge_item_prepend (fdi, device, link); } else if (link->type == HALD_FDI_LINK_REMOVE) { hald_fdi_merge_item_remove (fdi, device, link); } else { g_warning ("no action!"); } } /* only do children if the match was okay */ if (cont) { hald_fdi_merge_recurse (fdi, device, node->children); } hald_fdi_merge_recurse (fdi, device, node->next); } /** * hald_fdi_merge_recurse: * * We have device, try and match the cached fdi stuff **/ gboolean hald_fdi_merge (HaldFdi *fdi, HaldDevice *device) { g_return_val_if_fail (HALD_IS_FDI (fdi), FALSE); g_return_val_if_fail (HALD_IS_DEVICE (device), FALSE); hald_fdi_merge_recurse (fdi, device, fdi->priv->root->children); return TRUE; } /** * hald_fdi_class_init: * @klass: The HaldFdiClass **/ static void hald_fdi_class_init (HaldFdiClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = hald_fdi_finalize; g_type_class_add_private (klass, sizeof (HaldFdiPrivate)); signals [CREATE_DEVICE] = g_signal_new ("create-device", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (HaldFdiClass, create_device), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); } /** * hald_fdi_init: * @fdi: This fdi class instance **/ static void hald_fdi_init (HaldFdi *fdi) { fdi->priv = HALD_FDI_GET_PRIVATE (fdi); fdi->priv->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); fdi->priv->root = g_node_new (NULL); } /** * hald_fdi_free_item_recurse: * * Free all the text data for each node **/ static void hald_fdi_free_item_recurse (GNode *node) { HaldFdiLink *link; if (node == NULL) { return; } link = node->data; /* if we pass in root directly, then the first link is NULL */ if (link) { if (link->match_type) { g_free (link->match_type); } if (link->match_value) { g_free (link->match_value); } if (link->value_type) { g_free (link->value_type); } if (link->value_data) { g_free (link->value_data); } if (link->data) { g_free (link->data); } } hald_fdi_free_item_recurse (node->children); hald_fdi_free_item_recurse (node->next); } /** * hald_fdi_finalize: * @object: The object to finalize * * Finalise the fdi, by unref'ing all the depending modules. **/ static void hald_fdi_finalize (GObject *object) { HaldFdi *fdi; g_return_if_fail (object != NULL); g_return_if_fail (HALD_IS_FDI (object)); fdi = HALD_FDI (object); g_return_if_fail (fdi->priv != NULL); /* free all the node data */ hald_fdi_free_item_recurse (fdi->priv->root->children); g_hash_table_destroy (fdi->priv->hash); G_OBJECT_CLASS (hald_fdi_parent_class)->finalize (object); } /** * hald_fdi_new: * * Return value: a new HaldFdi object. **/ HaldFdi * hald_fdi_new (void) { if (hald_fdi_object) { g_object_ref (hald_fdi_object); } else { hald_fdi_object = g_object_new (HALD_TYPE_FDI, NULL); g_object_add_weak_pointer (hald_fdi_object, (gpointer *) &hald_fdi_object); } return HALD_FDI (hald_fdi_object); }