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 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 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 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 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 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 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 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 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 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 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 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 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 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 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__ 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 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 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