1 /***************************************************************************
2 * CVSID: $Id$
3 *
4 * device_store.c : Search for .fdi files and merge on match
5 *
6 * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
7 *
8 * Licensed under the Academic Free License version 2.1
9 *
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.
14 *
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.
19 *
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
23 *
24 **************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <dirent.h>
34 #include <expat.h>
35 #include <assert.h>
36 #include <dbus/dbus.h>
37 #include <dbus/dbus-glib.h>
38 #include <math.h>
39
40 #include "hald.h"
41 #include "logger.h"
42 #include "device_info.h"
43 #include "device_store.h"
44 #include "util.h"
45
46 /**
47 * @defgroup DeviceInfo Device Info File Parsing
48 * @ingroup HalDaemon
49 * @brief Parsing of device info files
50 * @{
51 */
52
53
54 /** Maximum nesting depth */
55 #define MAX_DEPTH 32
56
57 /** Maximum amount of CDATA */
58 #define CDATA_BUF_SIZE 1024
59
60 /** Max length of property key */
61 #define MAX_KEY_SIZE 128
62
63 /** Possible elements the parser can process */
64 enum {
65 /** Not processing a known tag */
66 CURELEM_UNKNOWN = -1,
67
68 /** Processing a deviceinfo element */
69 CURELEM_DEVICE_INFO = 0,
70
71 /** Processing a device element */
72 CURELEM_DEVICE = 1,
73
74 /** Processing a match element */
75 CURELEM_MATCH = 2,
76
77 /** Processing a merge element */
78 CURELEM_MERGE = 3,
79
80 /** Processing an append element */
81 CURELEM_APPEND = 4,
82
83 /** Processing a prepend element */
84 CURELEM_PREPEND = 5,
85
86 /** Processing a remove element */
87 CURELEM_REMOVE = 6,
88
89 /** Processing a clear element */
90 CURELEM_CLEAR = 7,
91
92 /** Processing a spawn element */
93 CURELEM_SPAWN = 8
94 };
95
96 /** What and how to merge */
97 enum {
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
109 };
110
111 /** Parsing Context
112 */
113 typedef struct {
114 /** Name of file being parsed */
115 char *file;
116
117 /** Parser object */
118 XML_Parser parser;
119
120 /** Device we are trying to match*/
121 HalDevice *device;
122
123 /** Buffer to put CDATA in */
124 char cdata_buf[CDATA_BUF_SIZE];
125
126 /** Current length of CDATA buffer */
127 int cdata_buf_len;
128
129 /** Current depth we are parsing at */
130 int depth;
131
132 /** Element currently being processed */
133 int curelem;
134
135 /** Stack of elements being processed */
136 int curelem_stack[MAX_DEPTH];
137
138 /** #TRUE if parsing of document have been aborted */
139 dbus_bool_t aborted;
140
141
142 /** Depth of match-fail */
143 int match_depth_first_fail;
144
145 /** #TRUE if all matches on prior depths have been OK */
146 dbus_bool_t match_ok;
147
148
149
150 /** When merging, the key to store the value in */
151 char merge_key[MAX_KEY_SIZE];
152
153 /** Type to merge*/
154 int merge_type;
155
156 /** Set to #TRUE if a device is matched */
157 dbus_bool_t device_matched;
158
159 } ParsingContext;
160
161 /** Resolve a udi-property path as used in .fdi files.
162 *
163 * Examples of udi-property paths:
164 *
165 * info.udi
166 * /org/freedesktop/Hal/devices/computer:kernel.name
167 * @block.storage_device:storage.bus
168 * @block.storage_device:@storage.physical_device:ide.channel
169 *
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.
177 */
178 static gboolean
resolve_udiprop_path(const char * path,const char * source_udi,char * udi_result,size_t udi_result_size,char * prop_result,size_t prop_result_size)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)
182 {
183 int i;
184 gchar **tokens = NULL;
185 gboolean rc;
186
187 rc = FALSE;
188
189 /*HAL_INFO (("Looking at '%s' for udi='%s'", path, source_udi));*/
190
191 /* Split up path into ':' tokens */
192 tokens = g_strsplit (path, ":", 64);
193
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);
198 rc = TRUE;
199 goto out;
200 }
201
202 /* Start with the source udi */
203 strncpy (udi_result, source_udi, udi_result_size);
204
205 for (i = 0; tokens[i] != NULL; i++) {
206 HalDevice *d;
207 gchar *curtoken;
208
209 /*HAL_INFO (("tokens[%d] = '%s'", i, tokens[i]));*/
210
211 d = hal_device_store_find (hald_get_gdl (), udi_result);
212 if (d == NULL)
213 d = hal_device_store_find (hald_get_tdl (), udi_result);
214 if (d == NULL)
215 goto out;
216
217 curtoken = tokens[i];
218
219 /* process all but the last tokens as UDI paths */
220 if (tokens[i+1] == NULL) {
221 strncpy (prop_result, curtoken, prop_result_size);
222 rc = TRUE;
223 goto out;
224 }
225
226
227 /* Check for indirection */
228 if (curtoken[0] == '@') {
229 const char *udiprop;
230 const char *newudi;
231
232 udiprop = curtoken + 1;
233
234 newudi = hal_device_property_get_string (d, udiprop);
235 if (newudi == NULL)
236 goto out;
237
238 /*HAL_INFO (("new_udi = '%s' (from indirection)", newudi));*/
239
240 strncpy (udi_result, newudi, udi_result_size);
241 } else {
242 /*HAL_INFO (("new_udi = '%s'", curtoken));*/
243 strncpy (udi_result, curtoken, udi_result_size);
244 }
245
246 }
247
248 out:
249
250 /*
251 HAL_INFO (("success = '%s'", rc ? "yes" : "no"));
252 HAL_INFO (("udi_result = '%s'", udi_result));
253 HAL_INFO (("prop_result = '%s'", prop_result));
254 */
255
256 g_strfreev (tokens);
257
258 return rc;
259 }
260
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
264 * as a number.
265 *
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.
269 *
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
275 */
276 static gboolean
match_compare_property(HalDevice * d,const char * key,const char * right_side,dbus_int64_t * result)277 match_compare_property (HalDevice *d, const char *key, const char *right_side, dbus_int64_t *result)
278 {
279 gboolean rc;
280 int proptype;
281
282 rc = FALSE;
283
284 if (!hal_device_has_property (d, key))
285 goto out;
286
287 proptype = hal_device_property_get_type (d, key);
288 switch (proptype) {
289 case HAL_PROPERTY_TYPE_STRING:
290 *result = (dbus_int64_t) strcmp (hal_device_property_get_string (d, key), right_side);
291 rc = TRUE;
292 break;
293
294 case HAL_PROPERTY_TYPE_INT32:
295 *result = ((dbus_int64_t) hal_device_property_get_int (d, key)) - strtoll (right_side, NULL, 0);
296 rc = TRUE;
297 break;
298
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));
301 rc = TRUE;
302 break;
303
304 case HAL_PROPERTY_TYPE_DOUBLE:
305 *result = (dbus_int64_t) ceil (hal_device_property_get_double (d, key) - atof (right_side));
306 rc = TRUE;
307 break;
308
309 default:
310 /* explicit fallthrough */
311 case HAL_PROPERTY_TYPE_BOOLEAN:
312 /* explicit blank since this doesn't make sense */
313 break;
314 }
315
316 out:
317 return rc;
318 }
319
320 /** Called when the match element begins.
321 *
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
326 */
327 static dbus_bool_t
handle_match(ParsingContext * pc,const char ** attr)328 handle_match (ParsingContext * pc, const char **attr)
329 {
330 char udi_to_check[256];
331 char prop_to_check[256];
332 const char *key;
333 int num_attrib;
334 HalDevice *d;
335
336 for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++);
337
338 if (num_attrib != 4)
339 return FALSE;
340
341 if (strcmp (attr[0], "key") != 0)
342 return FALSE;
343 key = attr[1];
344
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,
347 pc->device->udi,
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));
351 return FALSE;
352 }
353
354 d = hal_device_store_find (hald_get_gdl (), udi_to_check);
355 if (d == NULL) {
356 d = hal_device_store_find (hald_get_tdl (), udi_to_check);
357 }
358 if (d == NULL) {
359 HAL_ERROR (("Could not find device with udi '%s'", udi_to_check));
360 return FALSE;
361 }
362
363
364 if (strcmp (attr[2], "string") == 0) {
365 const char *value;
366
367 /* match string property */
368
369 value = attr[3];
370
371 /*HAL_INFO(("Checking that key='%s' is a string that "
372 "equals '%s'", key, value)); */
373
374 if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
375 return FALSE;
376
377 if (strcmp (hal_device_property_get_string (d, prop_to_check),
378 value) != 0)
379 return FALSE;
380
381 /*HAL_INFO (("*** string match for key %s", key));*/
382 return TRUE;
383 } else if (strcmp (attr[2], "int") == 0) {
384 dbus_int32_t value;
385
386 /* match integer property */
387 value = strtol (attr[3], NULL, 0);
388
389 /** @todo Check error condition */
390
391 /*HAL_INFO (("Checking that key='%s' is a int that equals %d",
392 key, value));*/
393
394 if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_INT32)
395 return FALSE;
396
397 if (hal_device_property_get_int (d, prop_to_check) != value) {
398 return FALSE;
399 }
400
401 return TRUE;
402 } else if (strcmp (attr[2], "uint64") == 0) {
403 dbus_uint64_t value;
404
405 /* match integer property */
406 value = strtoull (attr[3], NULL, 0);
407
408 /** @todo Check error condition */
409
410 /*HAL_INFO (("Checking that key='%s' is a int that equals %d",
411 key, value));*/
412
413 if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_UINT64)
414 return FALSE;
415
416 if (hal_device_property_get_uint64 (d, prop_to_check) != value) {
417 return FALSE;
418 }
419
420 return TRUE;
421 } else if (strcmp (attr[2], "bool") == 0) {
422 dbus_bool_t value;
423
424 /* match string property */
425
426 if (strcmp (attr[3], "false") == 0)
427 value = FALSE;
428 else if (strcmp (attr[3], "true") == 0)
429 value = TRUE;
430 else
431 return FALSE;
432
433 /*HAL_INFO (("Checking that key='%s' is a bool that equals %s",
434 key, value ? "TRUE" : "FALSE"));*/
435
436 if (hal_device_property_get_type (d, prop_to_check) !=
437 HAL_PROPERTY_TYPE_BOOLEAN)
438 return FALSE;
439
440 if (hal_device_property_get_bool (d, prop_to_check) != value)
441 return FALSE;
442
443 /*HAL_INFO (("*** bool match for key %s", key));*/
444 return TRUE;
445 } else if (strcmp (attr[2], "exists") == 0) {
446 dbus_bool_t should_exist = TRUE;
447
448 if (strcmp (attr[3], "false") == 0)
449 should_exist = FALSE;
450
451 if (should_exist) {
452 if (hal_device_has_property (d, prop_to_check))
453 return TRUE;
454 else
455 return FALSE;
456 } else {
457 if (hal_device_has_property (d, prop_to_check))
458 return FALSE;
459 else
460 return TRUE;
461 }
462 } else if (strcmp (attr[2], "empty") == 0) {
463 int type;
464 dbus_bool_t is_empty = TRUE;
465 dbus_bool_t should_be_empty = TRUE;
466
467
468 if (strcmp (attr[3], "false") == 0)
469 should_be_empty = FALSE;
470
471 type = hal_device_property_get_type (d, prop_to_check);
472 switch (type) {
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)
476 is_empty = FALSE;
477 break;
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))
481 is_empty = FALSE;
482 break;
483 default:
484 /* explicit fallthrough */
485 return FALSE;
486 break;
487 }
488
489 if (should_be_empty) {
490 if (is_empty)
491 return TRUE;
492 else
493 return FALSE;
494 } else {
495 if (is_empty)
496 return FALSE;
497 else
498 return TRUE;
499 }
500 } else if (strcmp (attr[2], "is_ascii") == 0) {
501 dbus_bool_t is_ascii = TRUE;
502 dbus_bool_t should_be_ascii = TRUE;
503 unsigned int i;
504 const char *str;
505
506 if (strcmp (attr[3], "false") == 0)
507 should_be_ascii = FALSE;
508
509 if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
510 return FALSE;
511
512 is_ascii = TRUE;
513
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)
517 is_ascii = FALSE;
518 }
519
520 if (should_be_ascii) {
521 if (is_ascii)
522 return TRUE;
523 else
524 return FALSE;
525 } else {
526 if (is_ascii)
527 return FALSE;
528 else
529 return TRUE;
530 }
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;
535
536 if (strcmp (attr[3], "false") == 0)
537 should_be_absolute_path = FALSE;
538
539 /*HAL_INFO (("d->udi='%s', prop_to_check='%s'", d->udi, prop_to_check));*/
540
541 if (hal_device_property_get_type (d, prop_to_check) != HAL_PROPERTY_TYPE_STRING)
542 return FALSE;
543
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;
548 }
549
550 /*HAL_INFO (("is_absolute=%d, should_be=%d, path='%s'", is_absolute_path, should_be_absolute_path, path));*/
551
552 if (should_be_absolute_path) {
553 if (is_absolute_path)
554 return TRUE;
555 else
556 return FALSE;
557 } else {
558 if (is_absolute_path)
559 return FALSE;
560 else
561 return TRUE;
562 }
563 } else if (strcmp (attr[2], "contains") == 0) {
564 const char *needle;
565 dbus_bool_t contains = FALSE;
566
567 needle = attr[3];
568
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;
572
573 haystack = hal_device_property_get_string (d, prop_to_check);
574 if (needle != NULL && haystack != NULL && strstr (haystack, needle)) {
575 contains = TRUE;
576 }
577
578 }
579 } else if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRLIST &&
580 needle != NULL) {
581 GSList *i;
582 GSList *value;
583
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) {
588 contains = TRUE;
589 break;
590 }
591 }
592 } else {
593 return FALSE;
594 }
595
596 return contains;
597 } else if (strcmp (attr[2], "contains_ncase") == 0) {
598 const char *needle;
599 dbus_bool_t contains_ncase = FALSE;
600
601 needle = attr[3];
602
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;
607
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;
612 }
613
614 g_free (needle_lowercase);
615 g_free (haystack_lowercase);
616 }
617 } else if (hal_device_property_get_type (d, prop_to_check) == HAL_PROPERTY_TYPE_STRLIST &&
618 needle != NULL) {
619 GSList *i;
620 GSList *value;
621
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;
627 break;
628 }
629 }
630 } else {
631 return FALSE;
632 }
633
634 return contains_ncase;
635 } else if (strcmp (attr[2], "compare_lt") == 0) {
636 dbus_int64_t result;
637 if (!match_compare_property (d, prop_to_check, attr[3], &result)) {
638 return FALSE;
639 } else {
640 return result < 0;
641 }
642 } else if (strcmp (attr[2], "compare_le") == 0) {
643 dbus_int64_t result;
644 if (!match_compare_property (d, prop_to_check, attr[3], &result))
645 return FALSE;
646 else
647 return result <= 0;
648 } else if (strcmp (attr[2], "compare_gt") == 0) {
649 dbus_int64_t result;
650 if (!match_compare_property (d, prop_to_check, attr[3], &result))
651 return FALSE;
652 else
653 return result > 0;
654 } else if (strcmp (attr[2], "compare_ge") == 0) {
655 dbus_int64_t result;
656 if (!match_compare_property (d, prop_to_check, attr[3], &result))
657 return FALSE;
658 else
659 return result >= 0;
660 }
661
662 return FALSE;
663 }
664
665
666 /** Called when the merge element begins.
667 *
668 * @param pc Parsing context
669 * @param attr Attribute key/value pairs
670 */
671 static void
handle_merge(ParsingContext * pc,const char ** attr)672 handle_merge (ParsingContext * pc, const char **attr)
673 {
674 int num_attrib;
675
676 pc->merge_type = MERGE_TYPE_UNKNOWN;
677
678
679 for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
680 ;
681 }
682
683 if (num_attrib != 4)
684 return;
685
686 if (strcmp (attr[0], "key") != 0)
687 return;
688 strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
689
690 if (strcmp (attr[2], "type") != 0)
691 return;
692
693 if (strcmp (attr[3], "string") == 0) {
694 /* match string property */
695 pc->merge_type = MERGE_TYPE_STRING;
696 return;
697 } else if (strcmp (attr[3], "bool") == 0) {
698 /* match string property */
699 pc->merge_type = MERGE_TYPE_BOOLEAN;
700 return;
701 } else if (strcmp (attr[3], "int") == 0) {
702 /* match string property */
703 pc->merge_type = MERGE_TYPE_INT32;
704 return;
705 } else if (strcmp (attr[3], "uint64") == 0) {
706 /* match string property */
707 pc->merge_type = MERGE_TYPE_UINT64;
708 return;
709 } else if (strcmp (attr[3], "double") == 0) {
710 /* match string property */
711 pc->merge_type = MERGE_TYPE_DOUBLE;
712 return;
713 } else if (strcmp (attr[3], "strlist") == 0) {
714 /* match string property */
715 pc->merge_type = MERGE_TYPE_STRLIST;
716 return;
717 } else if (strcmp (attr[3], "copy_property") == 0) {
718 /* copy another property */
719 pc->merge_type = MERGE_TYPE_COPY_PROPERTY;
720 return;
721 }
722
723 return;
724 }
725
726 /** Called when the append or prepend element begins.
727 *
728 * @param pc Parsing context
729 * @param attr Attribute key/value pairs
730 */
731 static void
handle_append_prepend(ParsingContext * pc,const char ** attr)732 handle_append_prepend (ParsingContext * pc, const char **attr)
733 {
734 int num_attrib;
735
736 pc->merge_type = MERGE_TYPE_UNKNOWN;
737
738 for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
739 ;
740 }
741
742 if (num_attrib != 4)
743 return;
744
745 if (strcmp (attr[0], "key") != 0)
746 return;
747 strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
748
749 if (strcmp (attr[2], "type") != 0)
750 return;
751
752 if (strcmp (attr[3], "string") == 0) {
753 /* append to a string */
754 pc->merge_type = MERGE_TYPE_STRING;
755 return;
756 } else if (strcmp (attr[3], "strlist") == 0) {
757 /* append to a string list*/
758 pc->merge_type = MERGE_TYPE_STRLIST;
759 return;
760 } else if (strcmp (attr[3], "copy_property") == 0) {
761 /* copy another property */
762 pc->merge_type = MERGE_TYPE_COPY_PROPERTY;
763 return;
764 }
765
766 return;
767 }
768
769
770 /** Called when the spawn element begins.
771 *
772 * @param pc Parsing context
773 * @param attr Attribute key/value pairs
774 */
775 static void
handle_spawn(ParsingContext * pc,const char ** attr)776 handle_spawn (ParsingContext * pc, const char **attr)
777 {
778 int num_attrib;
779
780 pc->merge_type = MERGE_TYPE_UNKNOWN;
781
782 for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
783 ;
784 }
785
786 if (num_attrib != 2)
787 return;
788
789 if (strcmp (attr[0], "udi") != 0)
790 return;
791
792 strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
793
794 pc->merge_type = MERGE_TYPE_SPAWN;
795 return;
796 }
797
798 /** Called when the remove element begins.
799 *
800 * @param pc Parsing context
801 * @param attr Attribute key/value pairs
802 */
803 static void
handle_remove(ParsingContext * pc,const char ** attr)804 handle_remove (ParsingContext * pc, const char **attr)
805 {
806 int num_attrib;
807
808 pc->merge_type = MERGE_TYPE_UNKNOWN;
809
810 for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
811 ;
812 }
813
814 if (num_attrib != 2 && num_attrib != 4)
815 return;
816
817 if (strcmp (attr[0], "key") != 0)
818 return;
819 strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
820
821 if (num_attrib == 4) {
822 if (strcmp (attr[2], "type") != 0)
823 return;
824
825 if (strcmp (attr[3], "strlist") == 0) {
826 /* remove from strlist */
827 pc->merge_type = MERGE_TYPE_STRLIST;
828 return;
829 } else {
830 pc->merge_type = MERGE_TYPE_UNKNOWN;
831 return;
832 }
833 } else {
834 pc->merge_type = MERGE_TYPE_REMOVE;
835 }
836
837 return;
838 }
839
840 /** Called when the clear element begins.
841 *
842 * @param pc Parsing context
843 * @param attr Attribute key/value pairs
844 */
845 static void
handle_clear(ParsingContext * pc,const char ** attr)846 handle_clear (ParsingContext * pc, const char **attr)
847 {
848 int num_attrib;
849
850 pc->merge_type = MERGE_TYPE_UNKNOWN;
851
852 for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
853 ;
854 }
855
856 if (num_attrib != 4)
857 return;
858
859 if (strcmp (attr[0], "key") != 0)
860 return;
861
862
863 if (strcmp (attr[3], "strlist") != 0)
864 return;
865
866 strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
867
868 pc->merge_type = MERGE_TYPE_CLEAR;
869
870 return;
871 }
872
873 /** Abort parsing of document
874 *
875 * @param pc Parsing context
876 */
877 static void
parsing_abort(ParsingContext * pc)878 parsing_abort (ParsingContext * pc)
879 {
880 /* Grr, expat can't abort parsing */
881 HAL_ERROR (("Aborting parsing of document"));
882 pc->aborted = TRUE;
883 }
884
885 /** Called by expat when an element begins.
886 *
887 * @param pc Parsing context
888 * @param el Element name
889 * @param attr Attribute key/value pairs
890 */
891 static void
start(ParsingContext * pc,const char * el,const char ** attr)892 start (ParsingContext * pc, const char *el, const char **attr)
893 {
894 if (pc->aborted)
895 return;
896
897 pc->cdata_buf_len = 0;
898
899 pc->merge_type = MERGE_TYPE_UNKNOWN;
900
901 /*
902 for (i = 0; i < pc->depth; i++)
903 printf(" ");
904
905 printf("%s", el);
906
907 for (i = 0; attr[i]; i += 2) {
908 printf(" %s='%s'", attr[i], attr[i + 1]);
909 }
910
911 printf(" curelem=%d\n", pc->curelem);
912 */
913
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>",
919 pc->file,
920 XML_GetCurrentLineNumber (pc->parser),
921 XML_GetCurrentColumnNumber (pc->parser)));
922 parsing_abort (pc);
923 }
924
925 pc->curelem = CURELEM_MATCH;
926
927 /* don't bother checking if matching at lower depths failed */
928 if (pc->match_ok) {
929 if (!handle_match (pc, attr)) {
930 /* No match */
931 pc->match_depth_first_fail = pc->depth;
932 pc->match_ok = FALSE;
933 }
934 }
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>",
940 pc->file,
941 XML_GetCurrentLineNumber (pc->parser),
942 XML_GetCurrentColumnNumber (pc->parser)));
943 parsing_abort (pc);
944 }
945
946 pc->curelem = CURELEM_MERGE;
947 if (pc->match_ok) {
948 handle_merge (pc, attr);
949 } else {
950 /*HAL_INFO(("No merge!")); */
951 }
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>",
957 pc->file,
958 XML_GetCurrentLineNumber (pc->parser),
959 XML_GetCurrentColumnNumber (pc->parser)));
960 parsing_abort (pc);
961 }
962
963 pc->curelem = CURELEM_APPEND;
964 if (pc->match_ok) {
965 handle_append_prepend (pc, attr);
966 } else {
967 /*HAL_INFO(("No merge!")); */
968 }
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>",
974 pc->file,
975 XML_GetCurrentLineNumber (pc->parser),
976 XML_GetCurrentColumnNumber (pc->parser)));
977 parsing_abort (pc);
978 }
979
980 pc->curelem = CURELEM_PREPEND;
981 if (pc->match_ok) {
982 handle_append_prepend (pc, attr);
983 } else {
984 /*HAL_INFO(("No merge!")); */
985 }
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>",
991 pc->file,
992 XML_GetCurrentLineNumber (pc->parser),
993 XML_GetCurrentColumnNumber (pc->parser)));
994 parsing_abort (pc);
995 }
996
997 pc->curelem = CURELEM_REMOVE;
998 if (pc->match_ok) {
999 handle_remove (pc, attr);
1000 } else {
1001 /*HAL_INFO(("No merge!")); */
1002 }
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>",
1008 pc->file,
1009 XML_GetCurrentLineNumber (pc->parser),
1010 XML_GetCurrentColumnNumber (pc->parser)));
1011 parsing_abort (pc);
1012 }
1013
1014 pc->curelem = CURELEM_CLEAR;
1015 if (pc->match_ok) {
1016 handle_clear (pc, attr);
1017 } else {
1018 /*HAL_INFO(("No merge!")); */
1019 }
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>",
1024 pc->file,
1025 XML_GetCurrentLineNumber (pc->parser),
1026 XML_GetCurrentColumnNumber (pc->parser)));
1027 parsing_abort (pc);
1028 }
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",
1034 pc->file,
1035 XML_GetCurrentLineNumber (pc->parser),
1036 XML_GetCurrentColumnNumber (pc->parser)));
1037 parsing_abort (pc);
1038 }
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 "
1043 "inside <match>",
1044 pc->file,
1045 XML_GetCurrentLineNumber (pc->parser),
1046 XML_GetCurrentColumnNumber (pc->parser)));
1047 parsing_abort (pc);
1048 }
1049
1050 pc->curelem = CURELEM_SPAWN;
1051 if (pc->match_ok) {
1052 handle_spawn (pc, attr);
1053 }
1054
1055 } else {
1056 HAL_ERROR (("%s:%d:%d: Unknown element <%s>",
1057 pc->file,
1058 XML_GetCurrentLineNumber (pc->parser),
1059 XML_GetCurrentColumnNumber (pc->parser), el));
1060 parsing_abort (pc);
1061 }
1062
1063 /* Nasty hack */
1064 assert (pc->depth < MAX_DEPTH);
1065
1066 pc->depth++;
1067
1068 /* store depth */
1069 pc->curelem_stack[pc->depth] = pc->curelem;
1070
1071 }
1072
1073 static void
spawned_device_callouts_add_done(HalDevice * d,gpointer userdata1,gpointer userdata2)1074 spawned_device_callouts_add_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
1075 {
1076 HAL_INFO (("Add callouts completed udi=%s", d->udi));
1077
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);
1081
1082 }
1083
1084 /** Called by expat when an element ends.
1085 *
1086 * @param pc Parsing context
1087 * @param el Element name
1088 */
1089 static void
end(ParsingContext * pc,const char * el)1090 end (ParsingContext * pc, const char *el)
1091 {
1092 if (pc->aborted)
1093 return;
1094
1095 pc->cdata_buf[pc->cdata_buf_len] = '\0';
1096
1097 /* printf(" curelem=%d\n", pc->curelem);*/
1098
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;
1102
1103 switch (pc->merge_type) {
1104 case MERGE_TYPE_STRING:
1105 hal_device_property_set_string (pc->device, pc->merge_key, pc->cdata_buf);
1106 break;
1107
1108 case MERGE_TYPE_STRLIST:
1109 {
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);
1114 }
1115 break;
1116 }
1117
1118 case MERGE_TYPE_INT32:
1119 {
1120 dbus_int32_t value;
1121
1122 /* match integer property */
1123 value = strtol (pc->cdata_buf, NULL, 0);
1124
1125 /** @todo FIXME: Check error condition */
1126
1127 hal_device_property_set_int (pc->device,
1128 pc->merge_key, value);
1129 break;
1130 }
1131
1132 case MERGE_TYPE_UINT64:
1133 {
1134 dbus_uint64_t value;
1135
1136 /* match integer property */
1137 value = strtoull (pc->cdata_buf, NULL, 0);
1138
1139 /** @todo FIXME: Check error condition */
1140
1141 hal_device_property_set_uint64 (pc->device,
1142 pc->merge_key, value);
1143 break;
1144 }
1145
1146 case MERGE_TYPE_BOOLEAN:
1147 hal_device_property_set_bool (pc->device, pc->merge_key,
1148 (strcmp (pc->cdata_buf,
1149 "true") == 0)
1150 ? TRUE : FALSE);
1151 break;
1152
1153 case MERGE_TYPE_DOUBLE:
1154 hal_device_property_set_double (pc->device, pc->merge_key,
1155 atof (pc->cdata_buf));
1156 break;
1157
1158 case MERGE_TYPE_COPY_PROPERTY:
1159 {
1160 char udi_to_merge_from[256];
1161 char prop_to_merge[256];
1162
1163 /* Resolve key paths like 'someudi/foo/bar/baz:prop.name'
1164 * '@prop.here.is.an.udi:with.prop.name'
1165 */
1166 if (!resolve_udiprop_path (pc->cdata_buf,
1167 pc->device->udi,
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));
1171 } else {
1172 HalDevice *d;
1173
1174 d = hal_device_store_find (hald_get_gdl (), udi_to_merge_from);
1175 if (d == NULL) {
1176 d = hal_device_store_find (hald_get_tdl (), udi_to_merge_from);
1177 }
1178 if (d == NULL) {
1179 HAL_ERROR (("Could not find device with udi '%s'", udi_to_merge_from));
1180 } else {
1181 hal_device_copy_property (d, prop_to_merge, pc->device, pc->merge_key);
1182 }
1183 }
1184 break;
1185 }
1186
1187 default:
1188 HAL_ERROR (("Unknown merge_type=%d='%c'",
1189 pc->merge_type, pc->merge_type));
1190 break;
1191 }
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)) {
1196 char buf[256];
1197 char buf2[256];
1198
1199 /* As soon as we are appending, we have matched the device... */
1200 pc->device_matched = TRUE;
1201
1202 if (pc->merge_type == MERGE_TYPE_STRLIST) {
1203 hal_device_property_strlist_append (pc->device, pc->merge_key, pc->cdata_buf);
1204 } else {
1205 const char *existing_string;
1206
1207 switch (pc->merge_type) {
1208 case MERGE_TYPE_STRING:
1209 strncpy (buf, pc->cdata_buf, sizeof (buf));
1210 break;
1211
1212 case MERGE_TYPE_COPY_PROPERTY:
1213 hal_device_property_get_as_string (pc->device, pc->cdata_buf, buf, sizeof (buf));
1214 break;
1215
1216 default:
1217 HAL_ERROR (("Unknown merge_type=%d='%c'", pc->merge_type, pc->merge_type));
1218 break;
1219 }
1220
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));
1225 } else {
1226 strncpy (buf2, buf, sizeof (buf2));
1227 }
1228 hal_device_property_set_string (pc->device, pc->merge_key, buf2);
1229 }
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)) {
1234 char buf[256];
1235 char buf2[256];
1236
1237 /* As soon as we are prepending, we have matched the device... */
1238 pc->device_matched = TRUE;
1239
1240 if (pc->merge_type == MERGE_TYPE_STRLIST) {
1241 hal_device_property_strlist_prepend (pc->device, pc->merge_key, pc->cdata_buf);
1242 } else {
1243 const char *existing_string;
1244
1245 switch (pc->merge_type) {
1246 case MERGE_TYPE_STRING:
1247 strncpy (buf, pc->cdata_buf, sizeof (buf));
1248 break;
1249
1250 case MERGE_TYPE_COPY_PROPERTY:
1251 hal_device_property_get_as_string (pc->device, pc->cdata_buf, buf, sizeof (buf));
1252 break;
1253
1254 default:
1255 HAL_ERROR (("Unknown merge_type=%d='%c'", pc->merge_type, pc->merge_type));
1256 break;
1257 }
1258
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));
1263 } else {
1264 strncpy (buf2, buf, sizeof (buf2));
1265 }
1266 hal_device_property_set_string (pc->device, pc->merge_key, buf2);
1267 }
1268 } else if (pc->curelem == CURELEM_REMOVE && pc->match_ok) {
1269
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);
1273 } else {
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);
1277 }
1278 }
1279 } else if (pc->merge_type == MERGE_TYPE_SPAWN) {
1280 HalDevice *spawned;
1281
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);
1285
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));
1289
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);
1295
1296 hal_device_store_add (hald_get_tdl (), spawned);
1297
1298 di_search_and_merge (spawned, DEVICE_INFO_TYPE_INFORMATION);
1299 di_search_and_merge (spawned, DEVICE_INFO_TYPE_POLICY);
1300
1301 hal_util_callout_device_add (spawned, spawned_device_callouts_add_done, NULL, NULL);
1302 }
1303
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);
1307 }
1308 }
1309
1310
1311 pc->cdata_buf_len = 0;
1312 pc->depth--;
1313
1314 /* maintain curelem */
1315 pc->curelem = pc->curelem_stack[pc->depth];
1316
1317 /* maintain pc->match_ok */
1318 if (pc->depth <= pc->match_depth_first_fail)
1319 pc->match_ok = TRUE;
1320 }
1321
1322 /** Called when there is CDATA
1323 *
1324 * @param pc Parsing context
1325 * @param s Pointer to data
1326 * @param len Length of data
1327 */
1328 static void
cdata(ParsingContext * pc,const char * s,int len)1329 cdata (ParsingContext * pc, const char *s, int len)
1330 {
1331 int bytes_left;
1332 int bytes_to_copy;
1333
1334 if (pc->aborted)
1335 return;
1336
1337 bytes_left = CDATA_BUF_SIZE - pc->cdata_buf_len;
1338 if (len > bytes_left) {
1339 HAL_ERROR (("CDATA in element larger than %d",
1340 CDATA_BUF_SIZE));
1341 }
1342
1343 bytes_to_copy = len;
1344 if (bytes_to_copy > bytes_left)
1345 bytes_to_copy = bytes_left;
1346
1347 if (bytes_to_copy > 0)
1348 memcpy (pc->cdata_buf + pc->cdata_buf_len, s,
1349 bytes_to_copy);
1350
1351 pc->cdata_buf_len += bytes_to_copy;
1352 }
1353
1354
1355 /** Process a device information info file.
1356 *
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
1361 * was merged
1362 */
1363 static dbus_bool_t
process_fdi_file(const char * dir,const char * filename,HalDevice * device)1364 process_fdi_file (const char *dir, const char *filename,
1365 HalDevice * device)
1366 {
1367 int rc;
1368 char buf[512];
1369 FILE *file;
1370 int filesize;
1371 size_t read;
1372 char *filebuf;
1373 dbus_bool_t device_matched;
1374 XML_Parser parser;
1375 ParsingContext *parsing_context;
1376
1377 file = NULL;
1378 filebuf = NULL;
1379 parser = NULL;
1380 parsing_context = NULL;
1381
1382 device_matched = FALSE;
1383
1384 snprintf (buf, sizeof (buf), "%s/%s", dir, filename);
1385
1386 /*HAL_INFO(("analyzing file %s", buf));*/
1387
1388 /* open file and read it into a buffer; it's a small file... */
1389 file = fopen (buf, "r");
1390 if (file == NULL) {
1391 HAL_ERROR (("Could not open file %s", buf));
1392 goto out;
1393 }
1394
1395 fseek (file, 0L, SEEK_END);
1396 filesize = (int) ftell (file);
1397 rewind (file);
1398 filebuf = (char *) malloc (filesize);
1399 if (filebuf == NULL) {
1400 HAL_ERROR (("Could not allocate %d bytes for file %s", filesize, buf));
1401 goto out;
1402 }
1403 read = fread (filebuf, sizeof (char), filesize, file);
1404
1405 /* initialize parsing context */
1406 parsing_context =
1407 (ParsingContext *) malloc (sizeof (ParsingContext));
1408 if (parsing_context == NULL) {
1409 HAL_ERROR (("Could not allocate parsing context"));
1410 goto out;
1411 }
1412
1413 /* TODO: reuse parser
1414 */
1415 parser = XML_ParserCreate (NULL);
1416 if (parser == NULL) {
1417 HAL_ERROR (("Could not allocate XML parser"));
1418 goto out;
1419 }
1420
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;
1430
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);
1437
1438 rc = XML_Parse (parser, filebuf, filesize, 1);
1439 /*printf("XML_Parse rc=%d\r\n", rc); */
1440
1441 if (rc == 0) {
1442 /* error parsing document */
1443 HAL_ERROR (("Error parsing XML document %s at line %d, "
1444 "column %d : %s",
1445 buf,
1446 XML_GetCurrentLineNumber (parser),
1447 XML_GetCurrentColumnNumber (parser),
1448 XML_ErrorString (XML_GetErrorCode (parser))));
1449 device_matched = FALSE;
1450 } else {
1451 /* document parsed ok */
1452 device_matched = parsing_context->device_matched;
1453 }
1454
1455 out:
1456 if (filebuf != NULL)
1457 free (filebuf);
1458 if (file != NULL)
1459 fclose (file);
1460 if (parser != NULL)
1461 XML_ParserFree (parser);
1462 if (parsing_context != NULL)
1463 free (parsing_context);
1464
1465 return device_matched;
1466 }
1467
1468
1469
1470 static int
1471 #ifdef __GLIBC__
my_alphasort(const void * a,const void * b)1472 my_alphasort(const void *a, const void *b)
1473 #else
1474 my_alphasort(const struct dirent **a, const struct dirent **b)
1475 #endif
1476 {
1477 return -alphasort (a, b);
1478 }
1479
1480
1481 /** Scan all directories and subdirectories in the given directory and
1482 * process each *.fdi file
1483 *
1484 * @param d Device to merge information into
1485 * @return #TRUE if information was merged
1486 */
1487 static dbus_bool_t
scan_fdi_files(const char * dir,HalDevice * d)1488 scan_fdi_files (const char *dir, HalDevice * d)
1489 {
1490 int i;
1491 int num_entries;
1492 dbus_bool_t found_fdi_file;
1493 struct dirent **name_list;
1494
1495 found_fdi_file = 0;
1496
1497 /*HAL_INFO(("scan_fdi_files: Processing dir '%s'", dir));*/
1498
1499 num_entries = scandir (dir, &name_list, 0, my_alphasort);
1500 if (num_entries == -1) {
1501 return FALSE;
1502 }
1503
1504 for (i = num_entries - 1; i >= 0; i--) {
1505 int len;
1506 char *filename;
1507 gchar *full_path;
1508
1509 filename = name_list[i]->d_name;
1510 len = strlen (filename);
1511
1512 full_path = g_strdup_printf ("%s/%s", dir, filename);
1513 /*HAL_INFO (("Full path = %s", full_path));*/
1514
1515 /* Mmm, d_type can be DT_UNKNOWN, use glib to determine
1516 * the type
1517 */
1518 if (g_file_test (full_path, (G_FILE_TEST_IS_REGULAR))) {
1519 /* regular file */
1520
1521 if (len >= 5 &&
1522 filename[len - 4] == '.' &&
1523 filename[len - 3] == 'f' &&
1524 filename[len - 2] == 'd' &&
1525 filename[len - 1] == 'i') {
1526 /*HAL_INFO (("scan_fdi_files: Processing file '%s'", filename));*/
1527 found_fdi_file = process_fdi_file (dir, filename, d);
1528 if (found_fdi_file) {
1529 HAL_INFO (("*** Matched file %s/%s", dir, filename));
1530 /*break;*/
1531 }
1532 }
1533
1534 } else if (g_file_test (full_path, (G_FILE_TEST_IS_DIR))
1535 && strcmp (filename, ".") != 0
1536 && strcmp (filename, "..") != 0) {
1537 int num_bytes;
1538 char *dirname;
1539
1540 /* Directory; do the recursion thingy but not
1541 * for . and ..
1542 */
1543
1544 num_bytes = len + strlen (dir) + 1 + 1;
1545 dirname = (char *) malloc (num_bytes);
1546 if (dirname == NULL) {
1547 HAL_ERROR (("couldn't allocated %d bytes",
1548 num_bytes));
1549 break;
1550 }
1551
1552 snprintf (dirname, num_bytes, "%s/%s", dir,
1553 filename);
1554 found_fdi_file = scan_fdi_files (dirname, d);
1555 free (dirname);
1556 /*
1557 if (found_fdi_file)
1558 break;
1559 */
1560 }
1561
1562 g_free (full_path);
1563
1564 free (name_list[i]);
1565 }
1566
1567 for (; i >= 0; i--) {
1568 free (name_list[i]);
1569 }
1570
1571 free (name_list);
1572
1573 return found_fdi_file;
1574 }
1575
1576 /** Search the device info file repository for a .fdi file to merge
1577 * more information into the device object.
1578 *
1579 * @param d Device to merge information into
1580 * @return #TRUE if information was merged
1581 */
1582 dbus_bool_t
di_search_and_merge(HalDevice * d,DeviceInfoType type)1583 di_search_and_merge (HalDevice *d, DeviceInfoType type)
1584 {
1585 static gboolean have_checked_hal_fdi_source = FALSE;
1586 static char *hal_fdi_source_preprobe = NULL;
1587 static char *hal_fdi_source_information = NULL;
1588 static char *hal_fdi_source_policy = NULL;
1589 dbus_bool_t ret;
1590 char *s1;
1591 char *s2;
1592
1593 ret = FALSE;
1594
1595 if (!have_checked_hal_fdi_source) {
1596 hal_fdi_source_preprobe = getenv ("HAL_FDI_SOURCE_PREPROBE");
1597 hal_fdi_source_information = getenv ("HAL_FDI_SOURCE_INFORMATION");
1598 hal_fdi_source_policy = getenv ("HAL_FDI_SOURCE_POLICY");
1599 have_checked_hal_fdi_source = TRUE;
1600 }
1601
1602 switch (type) {
1603 case DEVICE_INFO_TYPE_PREPROBE:
1604 if (hal_fdi_source_preprobe != NULL) {
1605 s1 = hal_fdi_source_preprobe;
1606 s2 = NULL;
1607 } else {
1608 s1 = PACKAGE_DATA_DIR "/hal/fdi/preprobe";
1609 s2 = PACKAGE_SYSCONF_DIR "/hal/fdi/preprobe";
1610 }
1611 break;
1612
1613 case DEVICE_INFO_TYPE_INFORMATION:
1614 if (hal_fdi_source_information != NULL) {
1615 s1 = hal_fdi_source_information;
1616 s2 = NULL;
1617 } else {
1618 s1 = PACKAGE_DATA_DIR "/hal/fdi/information";
1619 s2 = PACKAGE_SYSCONF_DIR "/hal/fdi/information";
1620 }
1621 break;
1622
1623 case DEVICE_INFO_TYPE_POLICY:
1624 if (hal_fdi_source_policy != NULL) {
1625 s1 = hal_fdi_source_policy;
1626 s2 = NULL;
1627 } else {
1628 s1 = PACKAGE_DATA_DIR "/hal/fdi/policy";
1629 s2 = PACKAGE_SYSCONF_DIR "/hal/fdi/policy";
1630 }
1631 break;
1632
1633 default:
1634 s1 = NULL;
1635 s2 = NULL;
1636 HAL_ERROR (("Bogus device information type %d", type));
1637 break;
1638 }
1639
1640 if (s1 != NULL)
1641 ret = scan_fdi_files (s1, d) || ret;
1642 if (s2 != NULL)
1643 ret = scan_fdi_files (s2, d) || ret;
1644
1645 return ret;
1646 }
1647
1648 /** @} */
1649