00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #ifdef HAVE_CONFIG_H
00027 # include <config.h>
00028 #endif
00029
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <dirent.h>
00034 #include <expat.h>
00035 #include <assert.h>
00036 #include <dbus/dbus.h>
00037 #include <dbus/dbus-glib.h>
00038
00039 #include <libhal/libhal.h>
00040
00041 #include "logger.h"
00042 #include "device_info.h"
00043
00053 #define MAX_DEPTH 32
00054
00056 #define CDATA_BUF_SIZE 1024
00057
00059 #define MAX_KEY_SIZE 128
00060
00062 enum {
00064 CURELEM_UNKNOWN = -1,
00065
00067 CURELEM_DEVICE_INFO = 0,
00068
00070 CURELEM_DEVICE = 1,
00071
00073 CURELEM_MATCH = 2,
00074
00076 CURELEM_MERGE = 3,
00077 };
00078
00081 typedef struct {
00083 char *file;
00084
00086 XML_Parser parser;
00087
00089 HalDevice *device;
00090
00092 char cdata_buf[CDATA_BUF_SIZE];
00093
00095 int cdata_buf_len;
00096
00098 int depth;
00099
00101 int curelem;
00102
00104 int curelem_stack[MAX_DEPTH];
00105
00107 dbus_bool_t aborted;
00108
00109
00111 int match_depth_first_fail;
00112
00114 dbus_bool_t match_ok;
00115
00116
00117
00119 char merge_key[MAX_KEY_SIZE];
00120
00122 int merge_type;
00123
00125 dbus_bool_t device_matched;
00126
00127 } ParsingContext;
00128
00136 static dbus_bool_t
00137 handle_match (ParsingContext * pc, const char **attr)
00138 {
00139 const char *key;
00140 int num_attrib;
00141
00142 for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++);
00143
00144 if (num_attrib != 4)
00145 return FALSE;
00146
00147 if (strcmp (attr[0], "key") != 0)
00148 return FALSE;
00149 key = attr[1];
00150
00151 if (strcmp (attr[2], "string") == 0) {
00152 const char *value;
00153
00154
00155
00156 value = attr[3];
00157
00158
00159
00160
00161 if (ds_property_get_type (pc->device, key) != DBUS_TYPE_STRING)
00162 return FALSE;
00163
00164 if (strcmp
00165 (ds_property_get_string (pc->device, key), value) != 0)
00166 return FALSE;
00167
00168 HAL_INFO (("*** string match for key %s", key));
00169 return TRUE;
00170 } else if (strcmp (attr[2], "int") == 0) {
00171 dbus_int32_t value;
00172
00173
00174 value = strtol (attr[3], NULL, 0);
00175
00178 HAL_INFO (("Checking that key='%s' is a int that equals %d",
00179 key, value));
00180
00181 if (ds_property_get_type (pc->device, key) != DBUS_TYPE_INT32)
00182 return FALSE;
00183
00184 if (ds_property_get_int (pc->device, key) != value) {
00185 return FALSE;
00186 }
00187
00188 return TRUE;
00189 } else if (strcmp (attr[2], "bool") == 0) {
00190 dbus_bool_t value;
00191
00192
00193
00194 if (strcmp (attr[3], "false") == 0)
00195 value = FALSE;
00196 else if (strcmp (attr[3], "true") == 0)
00197 value = TRUE;
00198 else
00199 return FALSE;
00200
00201 HAL_INFO (("Checking that key='%s' is a bool that equals %s",
00202 key, value ? "TRUE" : "FALSE"));
00203
00204 if (ds_property_get_type (pc->device, key) !=
00205 DBUS_TYPE_BOOLEAN)
00206 return FALSE;
00207
00208 if (ds_property_get_bool (pc->device, key) != value)
00209 return FALSE;
00210
00211 HAL_INFO (("*** bool match for key %s", key));
00212 return TRUE;
00213 }
00214
00215 return FALSE;
00216 }
00217
00218
00224 static void
00225 handle_merge (ParsingContext * pc, const char **attr)
00226 {
00227 int num_attrib;
00228
00229 for (num_attrib = 0; attr[num_attrib] != NULL; num_attrib++) {
00230 ;
00231 }
00232
00233 if (num_attrib != 4)
00234 return;
00235
00236 if (strcmp (attr[0], "key") != 0)
00237 return;
00238 strncpy (pc->merge_key, attr[1], MAX_KEY_SIZE);
00239
00240 if (strcmp (attr[2], "type") != 0)
00241 return;
00242
00243 if (strcmp (attr[3], "string") == 0) {
00244
00245 pc->merge_type = DBUS_TYPE_STRING;
00246 return;
00247 } else if (strcmp (attr[3], "bool") == 0) {
00248
00249 pc->merge_type = DBUS_TYPE_BOOLEAN;
00250 return;
00251 } else if (strcmp (attr[3], "int") == 0) {
00252
00253 pc->merge_type = DBUS_TYPE_INT32;
00254 return;
00255 } else if (strcmp (attr[3], "double") == 0) {
00256
00257 pc->merge_type = DBUS_TYPE_DOUBLE;
00258 return;
00259 }
00260
00261 return;
00262 }
00263
00268 static void
00269 parsing_abort (ParsingContext * pc)
00270 {
00271
00272 HAL_ERROR (("Aborting parsing of document"));
00273 pc->aborted = TRUE;
00274 }
00275
00282 static void
00283 start (ParsingContext * pc, const char *el, const char **attr)
00284 {
00285 int i;
00286
00287 if (pc->aborted)
00288 return;
00289
00290 pc->cdata_buf_len = 0;
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305 if (strcmp (el, "match") == 0) {
00306 if (pc->curelem != CURELEM_DEVICE
00307 && pc->curelem != CURELEM_MATCH) {
00308 HAL_ERROR (("%s:%d:%d: Element <match> can only be "
00309 "inside <device> and <match>",
00310 pc->file,
00311 XML_GetCurrentLineNumber (pc->parser),
00312 XML_GetCurrentColumnNumber (pc->parser)));
00313 parsing_abort (pc);
00314 }
00315
00316 pc->curelem = CURELEM_MATCH;
00317
00318 if (pc->match_ok) {
00319 if (!handle_match (pc, attr)) {
00320
00321 pc->match_depth_first_fail = pc->depth;
00322 pc->match_ok = FALSE;
00323 }
00324 }
00325 } else if (strcmp (el, "merge") == 0) {
00326 if (pc->curelem != CURELEM_DEVICE
00327 && pc->curelem != CURELEM_MATCH) {
00328 HAL_ERROR (("%s:%d:%d: Element <merge> can only be "
00329 "inside <device> and <match>",
00330 pc->file,
00331 XML_GetCurrentLineNumber (pc->parser),
00332 XML_GetCurrentColumnNumber (pc->parser)));
00333 parsing_abort (pc);
00334 }
00335
00336 pc->curelem = CURELEM_MERGE;
00337 if (pc->match_ok) {
00338 handle_merge (pc, attr);
00339 } else {
00340
00341 }
00342 } else if (strcmp (el, "device") == 0) {
00343 if (pc->curelem != CURELEM_DEVICE_INFO) {
00344 HAL_ERROR (("%s:%d:%d: Element <device> can only be "
00345 "inside <deviceinfo>",
00346 pc->file,
00347 XML_GetCurrentLineNumber (pc->parser),
00348 XML_GetCurrentColumnNumber (pc->parser)));
00349 parsing_abort (pc);
00350 }
00351 pc->curelem = CURELEM_DEVICE;
00352 } else if (strcmp (el, "deviceinfo") == 0) {
00353 if (pc->curelem != CURELEM_UNKNOWN) {
00354 HAL_ERROR (("%s:%d:%d: Element <deviceinfo> must be "
00355 "a top-level element",
00356 pc->file,
00357 XML_GetCurrentLineNumber (pc->parser),
00358 XML_GetCurrentColumnNumber (pc->parser)));
00359 parsing_abort (pc);
00360 }
00361 pc->curelem = CURELEM_DEVICE_INFO;
00362 } else {
00363 HAL_ERROR (("%s:%d:%d: Unknown element <%s>",
00364 pc->file,
00365 XML_GetCurrentLineNumber (pc->parser),
00366 XML_GetCurrentColumnNumber (pc->parser), el));
00367 parsing_abort (pc);
00368 }
00369
00370
00371 assert (pc->depth < MAX_DEPTH);
00372
00373 pc->depth++;
00374
00375
00376 pc->curelem_stack[pc->depth] = pc->curelem;
00377
00378 }
00379
00385 static void
00386 end (ParsingContext * pc, const char *el)
00387 {
00388 if (pc->aborted)
00389 return;
00390
00391 pc->cdata_buf[pc->cdata_buf_len] = '\0';
00392
00393
00394
00395 if (pc->curelem == CURELEM_MERGE && pc->match_ok) {
00396
00397 pc->device_matched = TRUE;
00398
00399 switch (pc->merge_type) {
00400 case DBUS_TYPE_STRING:
00401 ds_property_set_string (pc->device, pc->merge_key,
00402 pc->cdata_buf);
00403 break;
00404
00405
00406 case DBUS_TYPE_INT32:
00407 {
00408 dbus_int32_t value;
00409
00410
00411 value = strtol (pc->cdata_buf, NULL, 0);
00412
00415 ds_property_set_int (pc->device,
00416 pc->merge_key, value);
00417 break;
00418 }
00419
00420 case DBUS_TYPE_BOOLEAN:
00421 ds_property_set_bool (pc->device, pc->merge_key,
00422 (strcmp (pc->cdata_buf,
00423 "true") == 0)
00424 ? TRUE : FALSE);
00425 break;
00426
00427 case DBUS_TYPE_DOUBLE:
00428 ds_property_set_double (pc->device, pc->merge_key,
00429 atof (pc->cdata_buf));
00430 break;
00431
00432 default:
00433 HAL_ERROR (("Unknown merge_type=%d='%c'",
00434 pc->merge_type, pc->merge_type));
00435 break;
00436 }
00437 }
00438
00439
00440 pc->cdata_buf_len = 0;
00441 pc->depth--;
00442
00443
00444 pc->curelem = pc->curelem_stack[pc->depth];
00445
00446
00447 if (pc->depth < pc->match_depth_first_fail)
00448 pc->match_ok = TRUE;
00449 }
00450
00457 static void
00458 cdata (ParsingContext * pc, const char *s, int len)
00459 {
00460 int bytes_left;
00461 int bytes_to_copy;
00462
00463 if (pc->aborted)
00464 return;
00465
00466 bytes_left = CDATA_BUF_SIZE - pc->cdata_buf_len;
00467 if (len > bytes_left) {
00468 HAL_ERROR (("CDATA in element larger than %d",
00469 CDATA_BUF_SIZE));
00470 }
00471
00472 bytes_to_copy = len;
00473 if (bytes_to_copy > bytes_left)
00474 bytes_to_copy = bytes_left;
00475
00476 if (bytes_to_copy > 0)
00477 memcpy (pc->cdata_buf + pc->cdata_buf_len, s,
00478 bytes_to_copy);
00479
00480 pc->cdata_buf_len += bytes_to_copy;
00481 }
00482
00483
00492 static dbus_bool_t
00493 process_fdi_file (const char *dir, const char *filename,
00494 HalDevice * device)
00495 {
00496 int rc;
00497 char buf[512];
00498 FILE *file;
00499 int filesize;
00500 char *filebuf;
00501 dbus_bool_t device_matched;
00502 XML_Parser parser;
00503 ParsingContext *parsing_context;
00504
00505 snprintf (buf, 511, "%s/%s", dir, filename);
00506
00507
00508
00509
00510 file = fopen (buf, "r");
00511 if (file == NULL) {
00512 perror ("fopen");
00513 return FALSE;
00514 }
00515
00516 fseek (file, 0L, SEEK_END);
00517 filesize = (int) ftell (file);
00518 rewind (file);
00519 filebuf = (char *) malloc (filesize);
00520 if (filebuf == NULL) {
00521 perror ("malloc");
00522 fclose (file);
00523 return FALSE;
00524 }
00525 fread (filebuf, sizeof (char), filesize, file);
00526
00527
00528
00529
00530
00531 parser = XML_ParserCreate (NULL);
00532
00533
00534 parsing_context =
00535 (ParsingContext *) malloc (sizeof (ParsingContext));
00536 if (parsing_context == NULL) {
00537 perror ("malloc");
00538 return FALSE;
00539 }
00540 parsing_context->depth = 0;
00541 parsing_context->device_matched = FALSE;
00542 parsing_context->match_ok = TRUE;
00543 parsing_context->curelem = CURELEM_UNKNOWN;
00544 parsing_context->aborted = FALSE;
00545 parsing_context->file = buf;
00546 parsing_context->parser = parser;
00547 parsing_context->device = device;
00548
00549
00550 XML_SetElementHandler (parser,
00551 (XML_StartElementHandler) start,
00552 (XML_EndElementHandler) end);
00553 XML_SetCharacterDataHandler (parser,
00554 (XML_CharacterDataHandler) cdata);
00555 XML_SetUserData (parser, parsing_context);
00556
00557 rc = XML_Parse (parser, filebuf, filesize, 1);
00558
00559
00560 if (rc == 0) {
00561
00562 HAL_ERROR (("Error parsing XML document %s at line %d, "
00563 "column %d : %s",
00564 buf,
00565 XML_GetCurrentLineNumber (parser),
00566 XML_GetCurrentColumnNumber (parser),
00567 XML_ErrorString (XML_GetErrorCode (parser))));
00568 device_matched = FALSE;
00569 } else {
00570
00571 device_matched = parsing_context->device_matched;
00572 }
00573
00574 free (filebuf);
00575 fclose (file);
00576 free (parsing_context);
00577
00578 return device_matched;
00579 }
00580
00581
00582
00589 static int
00590 scan_fdi_files (const char *dir, HalDevice * d)
00591 {
00592 int i;
00593 int num_entries;
00594 dbus_bool_t found_fdi_file;
00595 struct dirent **name_list;
00596
00597 found_fdi_file = 0;
00598
00599
00600
00601 num_entries = scandir (dir, &name_list, 0, alphasort);
00602 if (num_entries == -1) {
00603 perror ("scandir");
00604 return FALSE;
00605 }
00606
00607 for (i = num_entries - 1; i >= 0; i--) {
00608 int len;
00609 char *filename;
00610
00611 filename = name_list[i]->d_name;
00612 len = strlen (filename);
00613
00614 if (name_list[i]->d_type == DT_REG) {
00615
00616
00617 if (len >= 5 &&
00618 filename[len - 4] == '.' &&
00619 filename[len - 3] == 'f' &&
00620 filename[len - 2] == 'd' &&
00621 filename[len - 1] == 'i') {
00622 HAL_INFO (("scan_fdi_files: Processing "
00623 "file '%s'", filename));
00624 found_fdi_file =
00625 process_fdi_file (dir, filename, d);
00626 if (found_fdi_file) {
00627 HAL_INFO (("*** Matched file %s/%s",
00628 dir, filename));
00629 break;
00630 }
00631 }
00632
00633 } else if (name_list[i]->d_type == DT_DIR &&
00634 strcmp (filename, ".") != 0
00635 && strcmp (filename, "..") != 0) {
00636 int num_bytes;
00637 char *dirname;
00638
00639
00640
00641
00642
00643 num_bytes = len + strlen (dir) + 1 + 1;
00644 dirname = (char *) malloc (num_bytes);
00645 if (dirname == NULL) {
00646 HAL_ERROR (("couldn't allocated %d bytes",
00647 num_bytes));
00648 break;
00649 }
00650
00651 snprintf (dirname, num_bytes, "%s/%s", dir,
00652 filename);
00653 found_fdi_file = scan_fdi_files (dirname, d);
00654 free (dirname);
00655 if (found_fdi_file)
00656 break;
00657 }
00658
00659 free (name_list[i]);
00660 }
00661
00662 for (; i >= 0; i--) {
00663 free (name_list[i]);
00664 }
00665
00666 free (name_list);
00667
00668 return found_fdi_file;
00669 }
00670
00671
00678 dbus_bool_t
00679 di_search_and_merge (HalDevice * d)
00680 {
00681 return scan_fdi_files (PACKAGE_DATA_DIR "/hal/fdi", d);
00682 }
00683