1 /***************************************************************************
4 * device_store.c : Search for .fdi files and merge on match
6 * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
8 * Licensed under the Academic Free License version 2.1
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 **************************************************************************/
36 #include <dbus/dbus.h>
37 #include <dbus/dbus-glib.h>
42 #include "device_info.h"
43 #include "device_store.h"
47 * @defgroup DeviceInfo Device Info File Parsing
49 * @brief Parsing of device info files
54 /** Maximum nesting depth */
57 /** Maximum amount of CDATA */
58 #define CDATA_BUF_SIZE 1024
60 /** Max length of property key */
61 #define MAX_KEY_SIZE 128
63 /** Possible elements the parser can process */
65 /** Not processing a known tag */
68 /** Processing a deviceinfo element */
69 CURELEM_DEVICE_INFO
= 0,
71 /** Processing a device element */
74 /** Processing a match element */
77 /** Processing a merge element */
80 /** Processing an append element */
83 /** Processing a prepend element */
86 /** Processing a remove element */
89 /** Processing a clear element */
92 /** Processing a spawn element */
96 /** What and how to merge */
98 MERGE_TYPE_UNKNOWN
= 0,
99 MERGE_TYPE_STRING
= 1,
100 MERGE_TYPE_BOOLEAN
= 2,
101 MERGE_TYPE_INT32
= 3,
102 MERGE_TYPE_UINT64
= 4,
103 MERGE_TYPE_DOUBLE
= 5,
104 MERGE_TYPE_COPY_PROPERTY
= 6,
105 MERGE_TYPE_STRLIST
= 7,
106 MERGE_TYPE_REMOVE
= 8,
107 MERGE_TYPE_CLEAR
= 9,
108 MERGE_TYPE_SPAWN
= 10
114 /** Name of file being parsed */
120 /** Device we are trying to match*/
123 /** Buffer to put CDATA in */
124 char cdata_buf
[CDATA_BUF_SIZE
];
126 /** Current length of CDATA buffer */
129 /** Current depth we are parsing at */
132 /** Element currently being processed */
135 /** Stack of elements being processed */
136 int curelem_stack
[MAX_DEPTH
];
138 /** #TRUE if parsing of document have been aborted */
142 /** Depth of match-fail */
143 int match_depth_first_fail
;
145 /** #TRUE if all matches on prior depths have been OK */
146 dbus_bool_t match_ok
;
150 /** When merging, the key to store the value in */
151 char merge_key
[MAX_KEY_SIZE
];
156 /** Set to #TRUE if a device is matched */
157 dbus_bool_t device_matched
;
161 /** Resolve a udi-property path as used in .fdi files.
163 * Examples of udi-property paths:
166 * /org/freedesktop/Hal/devices/computer:kernel.name
167 * @block.storage_device:storage.bus
168 * @block.storage_device:@storage.physical_device:ide.channel
170 * @param source_udi UDI of source device
171 * @param path The given path
172 * @param udi_result Where to store the resulting UDI
173 * @param udi_result_size Size of UDI string
174 * @param prop_result Where to store the resulting property name
175 * @param prop_result_size Size of property string
176 * @return TRUE if and only if the path resolved.
179 resolve_udiprop_path (const char *path
, const char *source_udi
,
180 char *udi_result
, size_t udi_result_size
,
181 char *prop_result
, size_t prop_result_size
)
184 gchar
**tokens
= NULL
;
189 /*HAL_INFO (("Looking at '%s' for udi='%s'", path, source_udi));*/
191 /* Split up path into ':' tokens */
192 tokens
= g_strsplit (path
, ":", 64);
194 /* Detect trivial property access, e.g. path='foo.bar' */
195 if (tokens
== NULL
|| tokens
[0] == NULL
|| tokens
[1] == NULL
) {
196 strncpy (udi_result
, source_udi
, udi_result_size
);
197 strncpy (prop_result
, path
, prop_result_size
);
202 /* Start with the source udi */
203 strncpy (udi_result
, source_udi
, udi_result_size
);
205 for (i
= 0; tokens
[i
] != NULL
; i
++) {
209 /*HAL_INFO (("tokens[%d] = '%s'", i, tokens[i]));*/
211 d
= hal_device_store_find (hald_get_gdl (), udi_result
);
213 d
= hal_device_store_find (hald_get_tdl (), udi_result
);
217 curtoken
= tokens
[i
];
219 /* process all but the last tokens as UDI paths */
220 if (tokens
[i
+1] == NULL
) {
221 strncpy (prop_result
, curtoken
, prop_result_size
);
227 /* Check for indirection */
228 if (curtoken
[0] == '@') {
232 udiprop
= curtoken
+ 1;
234 newudi
= hal_device_property_get_string (d
, udiprop
);
238 /*HAL_INFO (("new_udi = '%s' (from indirection)", newudi));*/
240 strncpy (udi_result
, newudi
, udi_result_size
);
242 /*HAL_INFO (("new_udi = '%s'", curtoken));*/
243 strncpy (udi_result
, curtoken
, udi_result_size
);
251 HAL_INFO (("success = '%s'", rc ? "yes" : "no"));
252 HAL_INFO (("udi_result = '%s'", udi_result));
253 HAL_INFO (("prop_result = '%s'", prop_result));
261 /* Compare the value of a property on a hal device object against a string value
262 * and return the result. Note that this works for several types, e.g. both strings
263 * and integers - in the latter case the given right side string will be interpreted
266 * The comparison might not make sense if you are comparing a property which is an integer
267 * against a string in which case this function returns FALSE. Also, if the property doesn't
268 * exist this function will also return FALSE.
270 * @param d hal device object
271 * @param key Key of the property to compare
272 * @param right_side Value to compare against
273 * @param result Pointer to where to store result
274 * @return TRUE if, and only if, the comparison could take place
277 match_compare_property (HalDevice
*d
, const char *key
, const char *right_side
, dbus_int64_t
*result
)
284 if (!hal_device_has_property (d
, key
))
287 proptype
= hal_device_property_get_type (d
, key
);
289 case HAL_PROPERTY_TYPE_STRING
:
290 *result
= (dbus_int64_t
) strcmp (hal_device_property_get_string (d
, key
), right_side
);
294 case HAL_PROPERTY_TYPE_INT32
:
295 *result
= ((dbus_int64_t
) hal_device_property_get_int (d
, key
)) - strtoll (right_side
, NULL
, 0);
299 case HAL_PROPERTY_TYPE_UINT64
:
300 *result
= ((dbus_int64_t
) hal_device_property_get_uint64 (d
, key
)) - ((dbus_int64_t
) strtoll (right_side
, NULL
, 0));
304 case HAL_PROPERTY_TYPE_DOUBLE
:
305 *result
= (dbus_int64_t
) ceil (hal_device_property_get_double (d
, key
) - atof (right_side
));
310 /* explicit fallthrough */
311 case HAL_PROPERTY_TYPE_BOOLEAN
:
312 /* explicit blank since this doesn't make sense */
320 /** Called when the match element begins.
322 * @param pc Parsing context
323 * @param attr Attribute key/value pairs
324 * @return #FALSE if the device in question didn't
325 * match the data in the attributes
328 handle_match (ParsingContext
* pc
, const char **attr
)
330 char udi_to_check
[256];
331 char prop_to_check
[256];
336 for (num_attrib
= 0; attr
[num_attrib
] != NULL
; num_attrib
++);
341 if (strcmp (attr
[0], "key") != 0)
345 /* Resolve key paths like 'someudi/foo/bar/baz:prop.name' '@prop.here.is.an.udi:with.prop.name' */
346 if (!resolve_udiprop_path (key
,
348 udi_to_check
, sizeof (udi_to_check
),
349 prop_to_check
, sizeof (prop_to_check
))) {
350 HAL_ERROR (("Could not resolve keypath '%s' on udi '%s'", key
, pc
->device
->udi
));
354 d
= hal_device_store_find (hald_get_gdl (), udi_to_check
);
356 d
= hal_device_store_find (hald_get_tdl (), udi_to_check
);
359 HAL_ERROR (("Could not find device with udi '%s'", udi_to_check
));
364 if (strcmp (attr
[2], "string") == 0) {
367 /* match string property */
371 /*HAL_INFO(("Checking that key='%s' is a string that "
372 "equals '%s'", key, value)); */
374 if (hal_device_property_get_type (d
, prop_to_check
) != HAL_PROPERTY_TYPE_STRING
)
377 if (strcmp (hal_device_property_get_string (d
, prop_to_check
),
381 /*HAL_INFO (("*** string match for key %s", key));*/
383 } else if (strcmp (attr
[2], "int") == 0) {
386 /* match integer property */
387 value
= strtol (attr
[3], NULL
, 0);
389 /** @todo Check error condition */
391 /*HAL_INFO (("Checking that key='%s' is a int that equals %d",
394 if (hal_device_property_get_type (d
, prop_to_check
) != HAL_PROPERTY_TYPE_INT32
)
397 if (hal_device_property_get_int (d
, prop_to_check
) != value
) {
402 } else if (strcmp (attr
[2], "uint64") == 0) {
405 /* match integer property */
406 value
= strtoull (attr
[3], NULL
, 0);
408 /** @todo Check error condition */
410 /*HAL_INFO (("Checking that key='%s' is a int that equals %d",
413 if (hal_device_property_get_type (d
, prop_to_check
) != HAL_PROPERTY_TYPE_UINT64
)
416 if (hal_device_property_get_uint64 (d
, prop_to_check
) != value
) {
421 } else if (strcmp (attr
[2], "bool") == 0) {
424 /* match string property */
426 if (strcmp (attr
[3], "false") == 0)
428 else if (strcmp (attr
[3], "true") == 0)
433 /*HAL_INFO (("Checking that key='%s' is a bool that equals %s",
434 key, value ? "TRUE" : "FALSE"));*/
436 if (hal_device_property_get_type (d
, prop_to_check
) !=
437 HAL_PROPERTY_TYPE_BOOLEAN
)
440 if (hal_device_property_get_bool (d
, prop_to_check
) != value
)
443 /*HAL_INFO (("*** bool match for key %s", key));*/
445 } else if (strcmp (attr
[2], "exists") == 0) {
446 dbus_bool_t should_exist
= TRUE
;
448 if (strcmp (attr
[3], "false") == 0)
449 should_exist
= FALSE
;
452 if (hal_device_has_property (d
, prop_to_check
))
457 if (hal_device_has_property (d
, prop_to_check
))
462 } else if (strcmp (attr
[2], "empty") == 0) {
464 dbus_bool_t is_empty
= TRUE
;
465 dbus_bool_t should_be_empty
= TRUE
;
468 if (strcmp (attr
[3], "false") == 0)
469 should_be_empty
= FALSE
;
471 type
= hal_device_property_get_type (d
, prop_to_check
);
473 case HAL_PROPERTY_TYPE_STRING
:
474 if (hal_device_has_property (d
, prop_to_check
))
475 if (strlen (hal_device_property_get_string (d
, prop_to_check
)) > 0)
478 case HAL_PROPERTY_TYPE_STRLIST
:
479 if (hal_device_has_property (d
, prop_to_check
))
480 if (!hal_device_property_strlist_is_empty(d
, prop_to_check
))
484 /* explicit fallthrough */
489 if (should_be_empty
) {
500 } else if (strcmp (attr
[2], "is_ascii") == 0) {
501 dbus_bool_t is_ascii
= TRUE
;
502 dbus_bool_t should_be_ascii
= TRUE
;
506 if (strcmp (attr
[3], "false") == 0)
507 should_be_ascii
= FALSE
;
509 if (hal_device_property_get_type (d
, prop_to_check
) != HAL_PROPERTY_TYPE_STRING
)
514 str
= hal_device_property_get_string (d
, prop_to_check
);
515 for (i
= 0; str
[i
] != '\0'; i
++) {
516 if (((unsigned char) str
[i
]) > 0x7f)
520 if (should_be_ascii
) {
531 } else if (strcmp (attr
[2], "is_absolute_path") == 0) {
532 const char *path
= NULL
;
533 dbus_bool_t is_absolute_path
= FALSE
;
534 dbus_bool_t should_be_absolute_path
= TRUE
;
536 if (strcmp (attr
[3], "false") == 0)
537 should_be_absolute_path
= FALSE
;
539 /*HAL_INFO (("d->udi='%s', prop_to_check='%s'", d->udi, prop_to_check));*/
541 if (hal_device_property_get_type (d
, prop_to_check
) != HAL_PROPERTY_TYPE_STRING
)
544 if (hal_device_has_property (d
, prop_to_check
)) {
545 path
= hal_device_property_get_string (d
, prop_to_check
);
546 if (g_path_is_absolute (path
))
547 is_absolute_path
= TRUE
;
550 /*HAL_INFO (("is_absolute=%d, should_be=%d, path='%s'", is_absolute_path, should_be_absolute_path, path));*/
552 if (should_be_absolute_path
) {
553 if (is_absolute_path
)
558 if (is_absolute_path
)
563 } else if (strcmp (attr
[2], "contains") == 0) {
565 dbus_bool_t contains
= FALSE
;
569 if (hal_device_property_get_type (d
, prop_to_check
) == HAL_PROPERTY_TYPE_STRING
) {
570 if (hal_device_has_property (d
, prop_to_check
)) {
571 const char *haystack
;
573 haystack
= hal_device_property_get_string (d
, prop_to_check
);
574 if (needle
!= NULL
&& haystack
!= NULL
&& strstr (haystack
, needle
)) {
579 } else if (hal_device_property_get_type (d
, prop_to_check
) == HAL_PROPERTY_TYPE_STRLIST
&&
584 value
= hal_device_property_get_strlist (d
, prop_to_check
);
585 for (i
= value
; i
!= NULL
; i
= g_slist_next (i
)) {
586 const char *str
= i
->data
;
587 if (strcmp (str
, needle
) == 0) {
597 } else if (strcmp (attr
[2], "contains_ncase") == 0) {
599 dbus_bool_t contains_ncase
= FALSE
;
603 if (hal_device_property_get_type (d
, prop_to_check
) == HAL_PROPERTY_TYPE_STRING
) {
604 if (hal_device_has_property (d
, prop_to_check
)) {
605 char *needle_lowercase
;
606 char *haystack_lowercase
;
608 needle_lowercase
= g_utf8_strdown (needle
, -1);
609 haystack_lowercase
= g_utf8_strdown (hal_device_property_get_string (d
, prop_to_check
), -1);
610 if (needle_lowercase
!= NULL
&& haystack_lowercase
!= NULL
&& strstr (haystack_lowercase
, needle_lowercase
)) {
611 contains_ncase
= TRUE
;
614 g_free (needle_lowercase
);
615 g_free (haystack_lowercase
);
617 } else if (hal_device_property_get_type (d
, prop_to_check
) == HAL_PROPERTY_TYPE_STRLIST
&&
622 value
= hal_device_property_get_strlist (d
, prop_to_check
);
623 for (i
= value
; i
!= NULL
; i
= g_slist_next (i
)) {
624 const char *str
= i
->data
;
625 if (g_ascii_strcasecmp (str
, needle
) == 0) {
626 contains_ncase
= TRUE
;
634 return contains_ncase
;
635 } else if (strcmp (attr
[2], "compare_lt") == 0) {
637 if (!match_compare_property (d
, prop_to_check
, attr
[3], &result
)) {
642 } else if (strcmp (attr
[2], "compare_le") == 0) {
644 if (!match_compare_property (d
, prop_to_check
, attr
[3], &result
))
648 } else if (strcmp (attr
[2], "compare_gt") == 0) {
650 if (!match_compare_property (d
, prop_to_check
, attr
[3], &result
))
654 } else if (strcmp (attr
[2], "compare_ge") == 0) {
656 if (!match_compare_property (d
, prop_to_check
, attr
[3], &result
))
666 /** Called when the merge element begins.
668 * @param pc Parsing context
669 * @param attr Attribute key/value pairs
672 handle_merge (ParsingContext
* pc
, const char **attr
)
676 pc
->merge_type
= MERGE_TYPE_UNKNOWN
;
679 for (num_attrib
= 0; attr
[num_attrib
] != NULL
; num_attrib
++) {
686 if (strcmp (attr
[0], "key") != 0)
688 strncpy (pc
->merge_key
, attr
[1], MAX_KEY_SIZE
);
690 if (strcmp (attr
[2], "type") != 0)
693 if (strcmp (attr
[3], "string") == 0) {
694 /* match string property */
695 pc
->merge_type
= MERGE_TYPE_STRING
;
697 } else if (strcmp (attr
[3], "bool") == 0) {
698 /* match string property */
699 pc
->merge_type
= MERGE_TYPE_BOOLEAN
;
701 } else if (strcmp (attr
[3], "int") == 0) {
702 /* match string property */
703 pc
->merge_type
= MERGE_TYPE_INT32
;
705 } else if (strcmp (attr
[3], "uint64") == 0) {
706 /* match string property */
707 pc
->merge_type
= MERGE_TYPE_UINT64
;
709 } else if (strcmp (attr
[3], "double") == 0) {
710 /* match string property */
711 pc
->merge_type
= MERGE_TYPE_DOUBLE
;
713 } else if (strcmp (attr
[3], "strlist") == 0) {
714 /* match string property */
715 pc
->merge_type
= MERGE_TYPE_STRLIST
;
717 } else if (strcmp (attr
[3], "copy_property") == 0) {
718 /* copy another property */
719 pc
->merge_type
= MERGE_TYPE_COPY_PROPERTY
;
726 /** Called when the append or prepend element begins.
728 * @param pc Parsing context
729 * @param attr Attribute key/value pairs
732 handle_append_prepend (ParsingContext
* pc
, const char **attr
)
736 pc
->merge_type
= MERGE_TYPE_UNKNOWN
;
738 for (num_attrib
= 0; attr
[num_attrib
] != NULL
; num_attrib
++) {
745 if (strcmp (attr
[0], "key") != 0)
747 strncpy (pc
->merge_key
, attr
[1], MAX_KEY_SIZE
);
749 if (strcmp (attr
[2], "type") != 0)
752 if (strcmp (attr
[3], "string") == 0) {
753 /* append to a string */
754 pc
->merge_type
= MERGE_TYPE_STRING
;
756 } else if (strcmp (attr
[3], "strlist") == 0) {
757 /* append to a string list*/
758 pc
->merge_type
= MERGE_TYPE_STRLIST
;
760 } else if (strcmp (attr
[3], "copy_property") == 0) {
761 /* copy another property */
762 pc
->merge_type
= MERGE_TYPE_COPY_PROPERTY
;
770 /** Called when the spawn element begins.
772 * @param pc Parsing context
773 * @param attr Attribute key/value pairs
776 handle_spawn (ParsingContext
* pc
, const char **attr
)
780 pc
->merge_type
= MERGE_TYPE_UNKNOWN
;
782 for (num_attrib
= 0; attr
[num_attrib
] != NULL
; num_attrib
++) {
789 if (strcmp (attr
[0], "udi") != 0)
792 strncpy (pc
->merge_key
, attr
[1], MAX_KEY_SIZE
);
794 pc
->merge_type
= MERGE_TYPE_SPAWN
;
798 /** Called when the remove element begins.
800 * @param pc Parsing context
801 * @param attr Attribute key/value pairs
804 handle_remove (ParsingContext
* pc
, const char **attr
)
808 pc
->merge_type
= MERGE_TYPE_UNKNOWN
;
810 for (num_attrib
= 0; attr
[num_attrib
] != NULL
; num_attrib
++) {
814 if (num_attrib
!= 2 && num_attrib
!= 4)
817 if (strcmp (attr
[0], "key") != 0)
819 strncpy (pc
->merge_key
, attr
[1], MAX_KEY_SIZE
);
821 if (num_attrib
== 4) {
822 if (strcmp (attr
[2], "type") != 0)
825 if (strcmp (attr
[3], "strlist") == 0) {
826 /* remove from strlist */
827 pc
->merge_type
= MERGE_TYPE_STRLIST
;
830 pc
->merge_type
= MERGE_TYPE_UNKNOWN
;
834 pc
->merge_type
= MERGE_TYPE_REMOVE
;
840 /** Called when the clear element begins.
842 * @param pc Parsing context
843 * @param attr Attribute key/value pairs
846 handle_clear (ParsingContext
* pc
, const char **attr
)
850 pc
->merge_type
= MERGE_TYPE_UNKNOWN
;
852 for (num_attrib
= 0; attr
[num_attrib
] != NULL
; num_attrib
++) {
859 if (strcmp (attr
[0], "key") != 0)
863 if (strcmp (attr
[3], "strlist") != 0)
866 strncpy (pc
->merge_key
, attr
[1], MAX_KEY_SIZE
);
868 pc
->merge_type
= MERGE_TYPE_CLEAR
;
873 /** Abort parsing of document
875 * @param pc Parsing context
878 parsing_abort (ParsingContext
* pc
)
880 /* Grr, expat can't abort parsing */
881 HAL_ERROR (("Aborting parsing of document"));
885 /** Called by expat when an element begins.
887 * @param pc Parsing context
888 * @param el Element name
889 * @param attr Attribute key/value pairs
892 start (ParsingContext
* pc
, const char *el
, const char **attr
)
897 pc
->cdata_buf_len
= 0;
899 pc
->merge_type
= MERGE_TYPE_UNKNOWN
;
902 for (i = 0; i < pc->depth; i++)
907 for (i = 0; attr[i]; i += 2) {
908 printf(" %s='%s'", attr[i], attr[i + 1]);
911 printf(" curelem=%d\n", pc->curelem);
914 if (strcmp (el
, "match") == 0) {
915 if (pc
->curelem
!= CURELEM_DEVICE
916 && pc
->curelem
!= CURELEM_MATCH
) {
917 HAL_ERROR (("%s:%d:%d: Element <match> can only be "
918 "inside <device> and <match>",
920 XML_GetCurrentLineNumber (pc
->parser
),
921 XML_GetCurrentColumnNumber (pc
->parser
)));
925 pc
->curelem
= CURELEM_MATCH
;
927 /* don't bother checking if matching at lower depths failed */
929 if (!handle_match (pc
, attr
)) {
931 pc
->match_depth_first_fail
= pc
->depth
;
932 pc
->match_ok
= FALSE
;
935 } else if (strcmp (el
, "merge") == 0) {
936 if (pc
->curelem
!= CURELEM_DEVICE
937 && pc
->curelem
!= CURELEM_MATCH
) {
938 HAL_ERROR (("%s:%d:%d: Element <merge> can only be "
939 "inside <device> and <match>",
941 XML_GetCurrentLineNumber (pc
->parser
),
942 XML_GetCurrentColumnNumber (pc
->parser
)));
946 pc
->curelem
= CURELEM_MERGE
;
948 handle_merge (pc
, attr
);
950 /*HAL_INFO(("No merge!")); */
952 } else if (strcmp (el
, "append") == 0) {
953 if (pc
->curelem
!= CURELEM_DEVICE
954 && pc
->curelem
!= CURELEM_MATCH
) {
955 HAL_ERROR (("%s:%d:%d: Element <append> can only be "
956 "inside <device> and <match>",
958 XML_GetCurrentLineNumber (pc
->parser
),
959 XML_GetCurrentColumnNumber (pc
->parser
)));
963 pc
->curelem
= CURELEM_APPEND
;
965 handle_append_prepend (pc
, attr
);
967 /*HAL_INFO(("No merge!")); */
969 } else if (strcmp (el
, "prepend") == 0) {
970 if (pc
->curelem
!= CURELEM_DEVICE
971 && pc
->curelem
!= CURELEM_MATCH
) {
972 HAL_ERROR (("%s:%d:%d: Element <prepend> can only be "
973 "inside <device> and <match>",
975 XML_GetCurrentLineNumber (pc
->parser
),
976 XML_GetCurrentColumnNumber (pc
->parser
)));
980 pc
->curelem
= CURELEM_PREPEND
;
982 handle_append_prepend (pc
, attr
);
984 /*HAL_INFO(("No merge!")); */
986 } else if (strcmp (el
, "remove") == 0) {
987 if (pc
->curelem
!= CURELEM_DEVICE
988 && pc
->curelem
!= CURELEM_MATCH
) {
989 HAL_ERROR (("%s:%d:%d: Element <remove> can only be "
990 "inside <device> and <match>",
992 XML_GetCurrentLineNumber (pc
->parser
),
993 XML_GetCurrentColumnNumber (pc
->parser
)));
997 pc
->curelem
= CURELEM_REMOVE
;
999 handle_remove (pc
, attr
);
1001 /*HAL_INFO(("No merge!")); */
1003 } else if (strcmp (el
, "clear") == 0) {
1004 if (pc
->curelem
!= CURELEM_DEVICE
1005 && pc
->curelem
!= CURELEM_MATCH
) {
1006 HAL_ERROR (("%s:%d:%d: Element <remove> can only be "
1007 "inside <device> and <match>",
1009 XML_GetCurrentLineNumber (pc
->parser
),
1010 XML_GetCurrentColumnNumber (pc
->parser
)));
1014 pc
->curelem
= CURELEM_CLEAR
;
1016 handle_clear (pc
, attr
);
1018 /*HAL_INFO(("No merge!")); */
1020 } else if (strcmp (el
, "device") == 0) {
1021 if (pc
->curelem
!= CURELEM_DEVICE_INFO
) {
1022 HAL_ERROR (("%s:%d:%d: Element <device> can only be "
1023 "inside <deviceinfo>",
1025 XML_GetCurrentLineNumber (pc
->parser
),
1026 XML_GetCurrentColumnNumber (pc
->parser
)));
1029 pc
->curelem
= CURELEM_DEVICE
;
1030 } else if (strcmp (el
, "deviceinfo") == 0) {
1031 if (pc
->curelem
!= CURELEM_UNKNOWN
) {
1032 HAL_ERROR (("%s:%d:%d: Element <deviceinfo> must be "
1033 "a top-level element",
1035 XML_GetCurrentLineNumber (pc
->parser
),
1036 XML_GetCurrentColumnNumber (pc
->parser
)));
1039 pc
->curelem
= CURELEM_DEVICE_INFO
;
1040 } else if (strcmp (el
, "spawn") == 0) {
1041 if (pc
->curelem
!= CURELEM_MATCH
) {
1042 HAL_ERROR (("%s:%d:%d: Element <spawn> can only be "
1045 XML_GetCurrentLineNumber (pc
->parser
),
1046 XML_GetCurrentColumnNumber (pc
->parser
)));
1050 pc
->curelem
= CURELEM_SPAWN
;
1052 handle_spawn (pc
, attr
);
1056 HAL_ERROR (("%s:%d:%d: Unknown element <%s>",
1058 XML_GetCurrentLineNumber (pc
->parser
),
1059 XML_GetCurrentColumnNumber (pc
->parser
), el
));
1064 assert (pc
->depth
< MAX_DEPTH
);
1069 pc
->curelem_stack
[pc
->depth
] = pc
->curelem
;
1074 spawned_device_callouts_add_done (HalDevice
*d
, gpointer userdata1
, gpointer userdata2
)
1076 HAL_INFO (("Add callouts completed udi=%s", d
->udi
));
1078 /* Move from temporary to global device store */
1079 hal_device_store_remove (hald_get_tdl (), d
);
1080 hal_device_store_add (hald_get_gdl (), d
);
1084 /** Called by expat when an element ends.
1086 * @param pc Parsing context
1087 * @param el Element name
1090 end (ParsingContext
* pc
, const char *el
)
1095 pc
->cdata_buf
[pc
->cdata_buf_len
] = '\0';
1097 /* printf(" curelem=%d\n", pc->curelem);*/
1099 if (pc
->curelem
== CURELEM_MERGE
&& pc
->match_ok
) {
1100 /* As soon as we are merging, we have matched the device... */
1101 pc
->device_matched
= TRUE
;
1103 switch (pc
->merge_type
) {
1104 case MERGE_TYPE_STRING
:
1105 hal_device_property_set_string (pc
->device
, pc
->merge_key
, pc
->cdata_buf
);
1108 case MERGE_TYPE_STRLIST
:
1110 int type
= hal_device_property_get_type (pc
->device
, pc
->merge_key
);
1111 if (type
== HAL_PROPERTY_TYPE_STRLIST
|| type
== HAL_PROPERTY_TYPE_INVALID
) {
1112 hal_device_property_remove (pc
->device
, pc
->merge_key
);
1113 hal_device_property_strlist_append (pc
->device
, pc
->merge_key
, pc
->cdata_buf
);
1118 case MERGE_TYPE_INT32
:
1122 /* match integer property */
1123 value
= strtol (pc
->cdata_buf
, NULL
, 0);
1125 /** @todo FIXME: Check error condition */
1127 hal_device_property_set_int (pc
->device
,
1128 pc
->merge_key
, value
);
1132 case MERGE_TYPE_UINT64
:
1134 dbus_uint64_t value
;
1136 /* match integer property */
1137 value
= strtoull (pc
->cdata_buf
, NULL
, 0);
1139 /** @todo FIXME: Check error condition */
1141 hal_device_property_set_uint64 (pc
->device
,
1142 pc
->merge_key
, value
);
1146 case MERGE_TYPE_BOOLEAN
:
1147 hal_device_property_set_bool (pc
->device
, pc
->merge_key
,
1148 (strcmp (pc
->cdata_buf
,
1153 case MERGE_TYPE_DOUBLE
:
1154 hal_device_property_set_double (pc
->device
, pc
->merge_key
,
1155 atof (pc
->cdata_buf
));
1158 case MERGE_TYPE_COPY_PROPERTY
:
1160 char udi_to_merge_from
[256];
1161 char prop_to_merge
[256];
1163 /* Resolve key paths like 'someudi/foo/bar/baz:prop.name'
1164 * '@prop.here.is.an.udi:with.prop.name'
1166 if (!resolve_udiprop_path (pc
->cdata_buf
,
1168 udi_to_merge_from
, sizeof (udi_to_merge_from
),
1169 prop_to_merge
, sizeof (prop_to_merge
))) {
1170 HAL_ERROR (("Could not resolve keypath '%s' on udi '%s'", pc
->cdata_buf
, pc
->device
->udi
));
1174 d
= hal_device_store_find (hald_get_gdl (), udi_to_merge_from
);
1176 d
= hal_device_store_find (hald_get_tdl (), udi_to_merge_from
);
1179 HAL_ERROR (("Could not find device with udi '%s'", udi_to_merge_from
));
1181 hal_device_copy_property (d
, prop_to_merge
, pc
->device
, pc
->merge_key
);
1188 HAL_ERROR (("Unknown merge_type=%d='%c'",
1189 pc
->merge_type
, pc
->merge_type
));
1192 } else if (pc
->curelem
== CURELEM_APPEND
&& pc
->match_ok
&&
1193 (hal_device_property_get_type (pc
->device
, pc
->merge_key
) == HAL_PROPERTY_TYPE_STRING
||
1194 hal_device_property_get_type (pc
->device
, pc
->merge_key
) == HAL_PROPERTY_TYPE_STRLIST
||
1195 hal_device_property_get_type (pc
->device
, pc
->merge_key
) == HAL_PROPERTY_TYPE_INVALID
)) {
1199 /* As soon as we are appending, we have matched the device... */
1200 pc
->device_matched
= TRUE
;
1202 if (pc
->merge_type
== MERGE_TYPE_STRLIST
) {
1203 hal_device_property_strlist_append (pc
->device
, pc
->merge_key
, pc
->cdata_buf
);
1205 const char *existing_string
;
1207 switch (pc
->merge_type
) {
1208 case MERGE_TYPE_STRING
:
1209 strncpy (buf
, pc
->cdata_buf
, sizeof (buf
));
1212 case MERGE_TYPE_COPY_PROPERTY
:
1213 hal_device_property_get_as_string (pc
->device
, pc
->cdata_buf
, buf
, sizeof (buf
));
1217 HAL_ERROR (("Unknown merge_type=%d='%c'", pc
->merge_type
, pc
->merge_type
));
1221 existing_string
= hal_device_property_get_string (pc
->device
, pc
->merge_key
);
1222 if (existing_string
!= NULL
) {
1223 strncpy (buf2
, existing_string
, sizeof (buf2
));
1224 strncat (buf2
, buf
, sizeof (buf2
) - strlen(buf2
));
1226 strncpy (buf2
, buf
, sizeof (buf2
));
1228 hal_device_property_set_string (pc
->device
, pc
->merge_key
, buf2
);
1230 } else if (pc
->curelem
== CURELEM_PREPEND
&& pc
->match_ok
&&
1231 (hal_device_property_get_type (pc
->device
, pc
->merge_key
) == HAL_PROPERTY_TYPE_STRING
||
1232 hal_device_property_get_type (pc
->device
, pc
->merge_key
) == HAL_PROPERTY_TYPE_STRLIST
||
1233 hal_device_property_get_type (pc
->device
, pc
->merge_key
) == HAL_PROPERTY_TYPE_INVALID
)) {
1237 /* As soon as we are prepending, we have matched the device... */
1238 pc
->device_matched
= TRUE
;
1240 if (pc
->merge_type
== MERGE_TYPE_STRLIST
) {
1241 hal_device_property_strlist_prepend (pc
->device
, pc
->merge_key
, pc
->cdata_buf
);
1243 const char *existing_string
;
1245 switch (pc
->merge_type
) {
1246 case MERGE_TYPE_STRING
:
1247 strncpy (buf
, pc
->cdata_buf
, sizeof (buf
));
1250 case MERGE_TYPE_COPY_PROPERTY
:
1251 hal_device_property_get_as_string (pc
->device
, pc
->cdata_buf
, buf
, sizeof (buf
));
1255 HAL_ERROR (("Unknown merge_type=%d='%c'", pc
->merge_type
, pc
->merge_type
));
1259 existing_string
= hal_device_property_get_string (pc
->device
, pc
->merge_key
);
1260 if (existing_string
!= NULL
) {
1261 strncpy (buf2
, buf
, sizeof (buf2
));
1262 strncat (buf2
, existing_string
, sizeof (buf2
) - strlen(buf2
));
1264 strncpy (buf2
, buf
, sizeof (buf2
));
1266 hal_device_property_set_string (pc
->device
, pc
->merge_key
, buf2
);
1268 } else if (pc
->curelem
== CURELEM_REMOVE
&& pc
->match_ok
) {
1270 if (pc
->merge_type
== MERGE_TYPE_STRLIST
) {
1271 /* covers <remove key="foobar" type="strlist">blah</remove> */
1272 hal_device_property_strlist_remove (pc
->device
, pc
->merge_key
, pc
->cdata_buf
);
1274 /* only allow <remove key="foobar"/>, not <remove key="foobar">blah</remove> */
1275 if (strlen (pc
->cdata_buf
) == 0) {
1276 hal_device_property_remove (pc
->device
, pc
->merge_key
);
1279 } else if (pc
->merge_type
== MERGE_TYPE_SPAWN
) {
1282 spawned
= hal_device_store_find (hald_get_gdl (), pc
->merge_key
);
1283 if (spawned
== NULL
)
1284 spawned
= hal_device_store_find (hald_get_tdl (), pc
->merge_key
);
1286 if (spawned
== NULL
) {
1287 HAL_INFO (("Spawning new device object '%s' caused by <spawn> on udi '%s'",
1288 pc
->merge_key
, pc
->device
->udi
));
1290 spawned
= hal_device_new ();
1291 hal_device_property_set_string (spawned
, "info.bus", "unknown");
1292 hal_device_property_set_string (spawned
, "info.udi", pc
->merge_key
);
1293 hal_device_property_set_string (spawned
, "info.parent", pc
->device
->udi
);
1294 hal_device_set_udi (spawned
, pc
->merge_key
);
1296 hal_device_store_add (hald_get_tdl (), spawned
);
1298 di_search_and_merge (spawned
, DEVICE_INFO_TYPE_INFORMATION
);
1299 di_search_and_merge (spawned
, DEVICE_INFO_TYPE_POLICY
);
1301 hal_util_callout_device_add (spawned
, spawned_device_callouts_add_done
, NULL
, NULL
);
1304 } else if (pc
->curelem
== CURELEM_CLEAR
&& pc
->match_ok
) {
1305 if (pc
->merge_type
== MERGE_TYPE_CLEAR
) {
1306 hal_device_property_strlist_clear (pc
->device
, pc
->merge_key
);
1311 pc
->cdata_buf_len
= 0;
1314 /* maintain curelem */
1315 pc
->curelem
= pc
->curelem_stack
[pc
->depth
];
1317 /* maintain pc->match_ok */
1318 if (pc
->depth
<= pc
->match_depth_first_fail
)
1319 pc
->match_ok
= TRUE
;
1322 /** Called when there is CDATA
1324 * @param pc Parsing context
1325 * @param s Pointer to data
1326 * @param len Length of data
1329 cdata (ParsingContext
* pc
, const char *s
, int len
)
1337 bytes_left
= CDATA_BUF_SIZE
- pc
->cdata_buf_len
;
1338 if (len
> bytes_left
) {
1339 HAL_ERROR (("CDATA in element larger than %d",
1343 bytes_to_copy
= len
;
1344 if (bytes_to_copy
> bytes_left
)
1345 bytes_to_copy
= bytes_left
;
1347 if (bytes_to_copy
> 0)
1348 memcpy (pc
->cdata_buf
+ pc
->cdata_buf_len
, s
,
1351 pc
->cdata_buf_len
+= bytes_to_copy
;
1355 /** Process a device information info file.
1357 * @param dir Directory file resides in
1358 * @param filename File name
1359 * @param device Device to match on
1360 * @return #TRUE if file matched device and information
1364 process_fdi_file (const char *dir
, const char *filename
,
1373 dbus_bool_t device_matched
;
1375 ParsingContext
*parsing_context
;
1380 parsing_context
= NULL
;
1382 device_matched
= FALSE
;
1384 snprintf (buf
, sizeof (buf
), "%s/%s", dir
, filename
);
1386 /*HAL_INFO(("analyzing file %s", buf));*/
1388 /* open file and read it into a buffer; it's a small file... */
1389 file
= fopen (buf
, "r");
1391 HAL_ERROR (("Could not open file %s", buf
));
1395 fseek (file
, 0L, SEEK_END
);
1396 filesize
= (int) ftell (file
);
1398 filebuf
= (char *) malloc (filesize
);
1399 if (filebuf
== NULL
) {
1400 HAL_ERROR (("Could not allocate %d bytes for file %s", filesize
, buf
));
1403 read
= fread (filebuf
, sizeof (char), filesize
, file
);
1405 /* initialize parsing context */
1407 (ParsingContext
*) malloc (sizeof (ParsingContext
));
1408 if (parsing_context
== NULL
) {
1409 HAL_ERROR (("Could not allocate parsing context"));
1413 /* TODO: reuse parser
1415 parser
= XML_ParserCreate (NULL
);
1416 if (parser
== NULL
) {
1417 HAL_ERROR (("Could not allocate XML parser"));
1421 parsing_context
->depth
= 0;
1422 parsing_context
->device_matched
= FALSE
;
1423 parsing_context
->match_ok
= TRUE
;
1424 parsing_context
->curelem
= CURELEM_UNKNOWN
;
1425 parsing_context
->aborted
= FALSE
;
1426 parsing_context
->file
= buf
;
1427 parsing_context
->parser
= parser
;
1428 parsing_context
->device
= device
;
1429 parsing_context
->match_depth_first_fail
= -1;
1431 XML_SetElementHandler (parser
,
1432 (XML_StartElementHandler
) start
,
1433 (XML_EndElementHandler
) end
);
1434 XML_SetCharacterDataHandler (parser
,
1435 (XML_CharacterDataHandler
) cdata
);
1436 XML_SetUserData (parser
, parsing_context
);
1438 rc
= XML_Parse (parser
, filebuf
, filesize
, 1);
1439 /*printf("XML_Parse rc=%d\r\n", rc); */
1442 /* error parsing document */
1443 HAL_ERROR (("Error parsing XML document %s at line %d, "
1446 XML_GetCurrentLineNumber (parser
),
1447 XML_GetCurrentColumnNumber (parser
),
1448 XML_ErrorString (XML_GetErrorCode (parser
))));
1449 device_matched
= FALSE
;
1451 /* document parsed ok */
1452 device_matched
= parsing_context
->device_matched
;
1460 XML_ParserFree (parser
);
1461 free(parsing_context
);
1463 return device_matched
;
1470 my_alphasort(const void *a
, const void *b
)
1472 my_alphasort(const struct dirent
**a
, const struct dirent
**b
)
1475 return -alphasort (a
, b
);
1479 /** Scan all directories and subdirectories in the given directory and
1480 * process each *.fdi file
1482 * @param d Device to merge information into
1483 * @return #TRUE if information was merged
1486 scan_fdi_files (const char *dir
, HalDevice
* d
)
1490 dbus_bool_t found_fdi_file
;
1491 struct dirent
**name_list
;
1495 /*HAL_INFO(("scan_fdi_files: Processing dir '%s'", dir));*/
1497 num_entries
= scandir (dir
, &name_list
, 0, my_alphasort
);
1498 if (num_entries
== -1) {
1502 for (i
= num_entries
- 1; i
>= 0; i
--) {
1507 filename
= name_list
[i
]->d_name
;
1508 len
= strlen (filename
);
1510 full_path
= g_strdup_printf ("%s/%s", dir
, filename
);
1511 /*HAL_INFO (("Full path = %s", full_path));*/
1513 /* Mmm, d_type can be DT_UNKNOWN, use glib to determine
1516 if (g_file_test (full_path
, (G_FILE_TEST_IS_REGULAR
))) {
1520 filename
[len
- 4] == '.' &&
1521 filename
[len
- 3] == 'f' &&
1522 filename
[len
- 2] == 'd' &&
1523 filename
[len
- 1] == 'i') {
1524 /*HAL_INFO (("scan_fdi_files: Processing file '%s'", filename));*/
1525 found_fdi_file
= process_fdi_file (dir
, filename
, d
);
1526 if (found_fdi_file
) {
1527 HAL_INFO (("*** Matched file %s/%s", dir
, filename
));
1532 } else if (g_file_test (full_path
, (G_FILE_TEST_IS_DIR
))
1533 && strcmp (filename
, ".") != 0
1534 && strcmp (filename
, "..") != 0) {
1538 /* Directory; do the recursion thingy but not
1542 num_bytes
= len
+ strlen (dir
) + 1 + 1;
1543 dirname
= (char *) malloc (num_bytes
);
1544 if (dirname
== NULL
) {
1545 HAL_ERROR (("couldn't allocated %d bytes",
1550 snprintf (dirname
, num_bytes
, "%s/%s", dir
,
1552 found_fdi_file
= scan_fdi_files (dirname
, d
);
1562 free (name_list
[i
]);
1565 for (; i
>= 0; i
--) {
1566 free (name_list
[i
]);
1571 return found_fdi_file
;
1574 /** Search the device info file repository for a .fdi file to merge
1575 * more information into the device object.
1577 * @param d Device to merge information into
1578 * @return #TRUE if information was merged
1581 di_search_and_merge (HalDevice
*d
, DeviceInfoType type
)
1583 static gboolean have_checked_hal_fdi_source
= FALSE
;
1584 static char *hal_fdi_source_preprobe
= NULL
;
1585 static char *hal_fdi_source_information
= NULL
;
1586 static char *hal_fdi_source_policy
= NULL
;
1594 if (!have_checked_hal_fdi_source
) {
1595 hal_fdi_source_preprobe
= getenv ("HAL_FDI_SOURCE_PREPROBE");
1596 hal_fdi_source_information
= getenv ("HAL_FDI_SOURCE_INFORMATION");
1597 hal_fdi_source_policy
= getenv ("HAL_FDI_SOURCE_POLICY");
1598 have_checked_hal_fdi_source
= TRUE
;
1602 case DEVICE_INFO_TYPE_PREPROBE
:
1603 if (hal_fdi_source_preprobe
!= NULL
) {
1604 s1
= hal_fdi_source_preprobe
;
1608 s1
= PACKAGE_OLD_DATA_DIR
"/hal/fdi/preprobe";
1609 s2
= PACKAGE_DATA_DIR
"/hal/fdi/preprobe";
1610 s3
= PACKAGE_SYSCONF_DIR
"/hal/fdi/preprobe";
1614 case DEVICE_INFO_TYPE_INFORMATION
:
1615 if (hal_fdi_source_information
!= NULL
) {
1616 s1
= hal_fdi_source_information
;
1620 s1
= PACKAGE_OLD_DATA_DIR
"/hal/fdi/information";
1621 s2
= PACKAGE_DATA_DIR
"/hal/fdi/information";
1622 s3
= PACKAGE_SYSCONF_DIR
"/hal/fdi/information";
1626 case DEVICE_INFO_TYPE_POLICY
:
1627 if (hal_fdi_source_policy
!= NULL
) {
1628 s1
= hal_fdi_source_policy
;
1632 s1
= PACKAGE_OLD_DATA_DIR
"/hal/fdi/policy";
1633 s2
= PACKAGE_DATA_DIR
"/hal/fdi/policy";
1634 s3
= PACKAGE_SYSCONF_DIR
"/hal/fdi/policy";
1642 HAL_ERROR (("Bogus device information type %d", type
));
1647 ret
= scan_fdi_files (s1
, d
) || ret
;
1649 ret
= scan_fdi_files (s2
, d
) || ret
;
1651 ret
= scan_fdi_files (s3
, d
) || ret
;