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
00063 {
00065 CURELEM_UNKNOWN = -1,
00066
00068 CURELEM_DEVICE_INFO = 0,
00069
00071 CURELEM_DEVICE = 1,
00072
00074 CURELEM_MATCH = 2,
00075
00077 CURELEM_MERGE = 3,
00078 };
00079
00082 typedef struct
00083 {
00085 char* file;
00086
00088 XML_Parser parser;
00089
00091 HalDevice* device;
00092
00094 char cdata_buf[CDATA_BUF_SIZE];
00095
00097 int cdata_buf_len;
00098
00100 int depth;
00101
00103 int curelem;
00104
00106 int curelem_stack[MAX_DEPTH];
00107
00109 dbus_bool_t aborted;
00110
00111
00113 int match_depth_first_fail;
00114
00116 dbus_bool_t match_ok;
00117
00118
00119
00121 char merge_key[MAX_KEY_SIZE];
00122
00124 int merge_type;
00125
00127 dbus_bool_t device_matched;
00128
00129 } ParsingContext;
00130
00138 static dbus_bool_t handle_match(ParsingContext* pc, const char** attr)
00139 {
00140 const char* key;
00141 int num_attrib;
00142
00143 for(num_attrib=0; attr[num_attrib]!=NULL; num_attrib++)
00144 ;
00145
00146 if( num_attrib!=4 )
00147 return FALSE;
00148
00149 if( strcmp(attr[0], "key")!=0 )
00150 return FALSE;
00151 key = attr[1];
00152
00153 if( strcmp(attr[2], "string")==0 )
00154 {
00155 const char* value;
00156
00157
00158
00159 value = attr[3];
00160
00161
00162
00163
00164 if( ds_property_get_type(pc->device, key)!=DBUS_TYPE_STRING )
00165 return FALSE;
00166
00167 if( strcmp(ds_property_get_string(pc->device, key), value)!=0 )
00168 return FALSE;
00169
00170 HAL_INFO(("*** string match for key %s", key));
00171 return TRUE;
00172 }
00173 else if( strcmp(attr[2], "int")==0 )
00174 {
00175 dbus_int32_t value;
00176
00177
00178 value = strtol(attr[3], NULL, 0);
00179
00182 HAL_INFO(("Checking that key='%s' is a int that equals %d",
00183 key, value));
00184
00185 if( ds_property_get_type(pc->device, key)!=DBUS_TYPE_INT32 )
00186 return FALSE;
00187
00188 if( ds_property_get_int(pc->device, key)!=value )
00189 {
00190 return FALSE;
00191 }
00192
00193 return TRUE;
00194 }
00195 else if( strcmp(attr[2], "bool")==0 )
00196 {
00197 dbus_bool_t value;
00198
00199
00200
00201 if( strcmp(attr[3], "false")==0 )
00202 value=FALSE;
00203 else if( strcmp(attr[3], "true")==0 )
00204 value=TRUE;
00205 else
00206 return FALSE;
00207
00208 HAL_INFO(("Checking that key='%s' is a bool that equals %s",
00209 key, value ? "TRUE" : "FALSE"));
00210
00211 if( ds_property_get_type(pc->device, key)!=DBUS_TYPE_BOOLEAN )
00212 return FALSE;
00213
00214 if( ds_property_get_bool(pc->device, key)!=value )
00215 return FALSE;
00216
00217 HAL_INFO(("*** bool match for key %s", key));
00218 return TRUE;
00219 }
00220
00221 return FALSE;
00222 }
00223
00224
00230 static void handle_merge(ParsingContext* pc, const char** attr)
00231 {
00232 int num_attrib;
00233
00234 for(num_attrib=0; attr[num_attrib]!=NULL; num_attrib++)
00235 ;
00236
00237 if( num_attrib!=4 )
00238 return;
00239
00240 if( strcmp(attr[0], "key")!=0 )
00241 return;
00242 strncpy(pc->merge_key, attr[1], MAX_KEY_SIZE);
00243
00244 if( strcmp(attr[2], "type")!=0 )
00245 return;
00246
00247 if( strcmp(attr[3], "string")==0 )
00248 {
00249
00250 pc->merge_type = DBUS_TYPE_STRING;
00251 return;
00252 }
00253 else if( strcmp(attr[3], "bool")==0 )
00254 {
00255
00256 pc->merge_type = DBUS_TYPE_BOOLEAN;
00257 return;
00258 }
00259 else if( strcmp(attr[3], "int")==0 )
00260 {
00261
00262 pc->merge_type = DBUS_TYPE_INT32;
00263 return;
00264 }
00265 else if( strcmp(attr[3], "double")==0 )
00266 {
00267
00268 pc->merge_type = DBUS_TYPE_DOUBLE;
00269 return;
00270 }
00271
00272 return;
00273 }
00274
00279 static void parsing_abort(ParsingContext* pc)
00280 {
00281
00282 HAL_ERROR(("Aborting parsing of document"));
00283 pc->aborted = TRUE;
00284 }
00285
00292 static void start(ParsingContext *pc, const char *el, const char **attr)
00293 {
00294 int i;
00295
00296 if( pc->aborted )
00297 return;
00298
00299 pc->cdata_buf_len = 0;
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314 if( strcmp(el, "match")==0 )
00315 {
00316 if( pc->curelem!=CURELEM_DEVICE && pc->curelem!=CURELEM_MATCH )
00317 {
00318 HAL_ERROR(("%s:%d:%d: Element <match> can only be inside "
00319 "<device> and <match>",
00320 pc->file,
00321 XML_GetCurrentLineNumber(pc->parser),
00322 XML_GetCurrentColumnNumber(pc->parser)));
00323 parsing_abort(pc);
00324 }
00325
00326 pc->curelem = CURELEM_MATCH;
00327
00328 if( pc->match_ok )
00329 {
00330 if( !handle_match(pc, attr) )
00331 {
00332
00333 pc->match_depth_first_fail = pc->depth;
00334 pc->match_ok = FALSE;
00335 }
00336 }
00337 }
00338 else if( strcmp(el, "merge")==0 )
00339 {
00340 if( pc->curelem!=CURELEM_DEVICE && pc->curelem!=CURELEM_MATCH )
00341 {
00342 HAL_ERROR(("%s:%d:%d: Element <merge> can only be inside "
00343 "<device> and <match>",
00344 pc->file,
00345 XML_GetCurrentLineNumber(pc->parser),
00346 XML_GetCurrentColumnNumber(pc->parser)));
00347 parsing_abort(pc);
00348 }
00349
00350 pc->curelem = CURELEM_MERGE;
00351 if( pc->match_ok )
00352 {
00353 handle_merge(pc, attr);
00354 }
00355 else
00356 {
00357
00358 }
00359 }
00360 else if( strcmp(el, "device")==0 )
00361 {
00362 if( pc->curelem!=CURELEM_DEVICE_INFO )
00363 {
00364 HAL_ERROR(("%s:%d:%d: Element <device> can only be inside "
00365 "<deviceinfo>",
00366 pc->file,
00367 XML_GetCurrentLineNumber(pc->parser),
00368 XML_GetCurrentColumnNumber(pc->parser)));
00369 parsing_abort(pc);
00370 }
00371 pc->curelem = CURELEM_DEVICE;
00372 }
00373 else if( strcmp(el, "deviceinfo")==0 )
00374 {
00375 if( pc->curelem!=CURELEM_UNKNOWN )
00376 {
00377 HAL_ERROR(("%s:%d:%d: Element <deviceinfo> must be a top-level "
00378 "element",
00379 pc->file,
00380 XML_GetCurrentLineNumber(pc->parser),
00381 XML_GetCurrentColumnNumber(pc->parser)));
00382 parsing_abort(pc);
00383 }
00384 pc->curelem = CURELEM_DEVICE_INFO;
00385 }
00386 else
00387 {
00388 HAL_ERROR(("%s:%d:%d: Unknown element <%s>",
00389 pc->file,
00390 XML_GetCurrentLineNumber(pc->parser),
00391 XML_GetCurrentColumnNumber(pc->parser), el));
00392 parsing_abort(pc);
00393 }
00394
00395
00396 assert( pc->depth<MAX_DEPTH );
00397
00398 pc->depth++;
00399
00400
00401 pc->curelem_stack[pc->depth] = pc->curelem;
00402
00403 }
00404
00410 static void end(ParsingContext* pc, const char* el)
00411 {
00412 if( pc->aborted )
00413 return;
00414
00415 pc->cdata_buf[pc->cdata_buf_len]='\0';
00416
00417
00418
00419 if( pc->curelem==CURELEM_MERGE && pc->match_ok )
00420 {
00421
00422 pc->device_matched = TRUE;
00423
00424 switch( pc->merge_type )
00425 {
00426 case DBUS_TYPE_STRING:
00427 ds_property_set_string(pc->device, pc->merge_key, pc->cdata_buf);
00428 break;
00429
00430
00431 case DBUS_TYPE_INT32:
00432 {
00433 dbus_int32_t value;
00434
00435
00436 value = strtol(pc->cdata_buf, NULL, 0);
00437
00440 ds_property_set_int(pc->device, pc->merge_key, value);
00441 break;
00442 }
00443
00444 case DBUS_TYPE_BOOLEAN:
00445 ds_property_set_bool(
00446 pc->device, pc->merge_key,
00447 (strcmp(pc->cdata_buf, "true")==0) ? TRUE : FALSE);
00448 break;
00449
00450 case DBUS_TYPE_DOUBLE:
00451 ds_property_set_double(pc->device, pc->merge_key,
00452 atof(pc->cdata_buf));
00453 break;
00454
00455 default:
00456 HAL_ERROR(("Unknown merge_type=%d='%c'",
00457 pc->merge_type, pc->merge_type));
00458 break;
00459 }
00460 }
00461
00462
00463 pc->cdata_buf_len = 0;
00464 pc->depth--;
00465
00466
00467 pc->curelem = pc->curelem_stack[pc->depth];
00468
00469
00470 if( pc->depth < pc->match_depth_first_fail )
00471 pc->match_ok=TRUE;
00472 }
00473
00480 static void cdata(ParsingContext *pc, const char* s, int len)
00481 {
00482 int bytes_left;
00483 int bytes_to_copy;
00484
00485 if( pc->aborted )
00486 return;
00487
00488 bytes_left = CDATA_BUF_SIZE - pc->cdata_buf_len;
00489 if( len>bytes_left )
00490 {
00491 HAL_ERROR(("CDATA in element larger than %d", CDATA_BUF_SIZE));
00492 }
00493
00494 bytes_to_copy = len;
00495 if( bytes_to_copy>bytes_left )
00496 bytes_to_copy = bytes_left;
00497
00498 if( bytes_to_copy>0 )
00499 memcpy(pc->cdata_buf + pc->cdata_buf_len, s, bytes_to_copy);
00500
00501 pc->cdata_buf_len += bytes_to_copy;
00502 }
00503
00504
00513 static dbus_bool_t process_fdi_file(const char* dir, const char* filename,
00514 HalDevice* device)
00515 {
00516 int rc;
00517 char buf[512];
00518 FILE* file;
00519 int filesize;
00520 char* filebuf;
00521 dbus_bool_t device_matched;
00522 XML_Parser parser;
00523 ParsingContext* parsing_context;
00524
00525 snprintf(buf, 511, "%s/%s", dir, filename);
00526
00527
00528
00529
00530 file = fopen(buf, "r");
00531 if( file==NULL )
00532 {
00533 perror("fopen");
00534 return FALSE;
00535 }
00536
00537 fseek(file, 0L, SEEK_END);
00538 filesize = (int) ftell(file);
00539 rewind(file);
00540 filebuf = (char*) malloc(filesize);
00541 if( filebuf==NULL )
00542 {
00543 perror("malloc");
00544 fclose(file);
00545 return FALSE;
00546 }
00547 fread(filebuf, sizeof(char), filesize, file);
00548
00549
00550
00551
00552 parser = XML_ParserCreate(NULL);
00553
00554
00555 parsing_context = (ParsingContext*) malloc(sizeof(ParsingContext));
00556 if( parsing_context==NULL )
00557 {
00558 perror("malloc");
00559 return FALSE;
00560 }
00561 parsing_context->depth = 0;
00562 parsing_context->device_matched = FALSE;
00563 parsing_context->match_ok = TRUE;
00564 parsing_context->curelem = CURELEM_UNKNOWN;
00565 parsing_context->aborted = FALSE;
00566 parsing_context->file = buf;
00567 parsing_context->parser = parser;
00568 parsing_context->device = device;
00569
00570
00571 XML_SetElementHandler(parser,
00572 (XML_StartElementHandler) start,
00573 (XML_EndElementHandler) end);
00574 XML_SetCharacterDataHandler(parser,
00575 (XML_CharacterDataHandler) cdata);
00576 XML_SetUserData(parser, parsing_context);
00577
00578 rc = XML_Parse(parser, filebuf, filesize, 1);
00579
00580
00581 if( rc==0 )
00582 {
00583
00584 HAL_ERROR(("Error parsing XML document %s at line %d, column %d : %s",
00585 buf,
00586 XML_GetCurrentLineNumber(parser),
00587 XML_GetCurrentColumnNumber(parser),
00588 XML_ErrorString(XML_GetErrorCode(parser)) ));
00589 device_matched = FALSE;
00590 }
00591 else
00592 {
00593
00594 device_matched = parsing_context->device_matched;
00595 }
00596
00597 free(filebuf);
00598 fclose(file);
00599 free(parsing_context);
00600
00601 return device_matched;
00602 }
00603
00604
00605
00612 static int scan_fdi_files(const char* dir, HalDevice* d)
00613 {
00614 int i;
00615 int num_entries;
00616 dbus_bool_t found_fdi_file;
00617 struct dirent **name_list;
00618
00619 found_fdi_file = 0;
00620
00621
00622
00623 num_entries = scandir(dir, &name_list, 0, alphasort);
00624 if( num_entries==-1 )
00625 {
00626 perror("scandir");
00627 return FALSE;
00628 }
00629
00630 for(i=num_entries-1; i>=0; i--)
00631 {
00632 int len;
00633 char* filename;
00634
00635 filename = name_list[i]->d_name;
00636 len = strlen(filename);
00637
00638 if( name_list[i]->d_type==DT_REG )
00639 {
00640
00641
00642 if( len>=5 &&
00643 filename[len-4]=='.' &&
00644 filename[len-3]=='f' &&
00645 filename[len-2]=='d' &&
00646 filename[len-1]=='i' )
00647 {
00648 HAL_INFO(("scan_fdi_files: Processing file '%s'", filename));
00649 found_fdi_file = process_fdi_file(dir, filename, d);
00650 if( found_fdi_file )
00651 {
00652 HAL_INFO(("*** Matched file %s/%s", dir, filename));
00653 break;
00654 }
00655 }
00656
00657 }
00658 else if( name_list[i]->d_type==DT_DIR &&
00659 strcmp(filename, ".")!=0 && strcmp(filename, "..")!=0 )
00660 {
00661 int num_bytes;
00662 char* dirname;
00663
00664
00665
00666 num_bytes = len + strlen(dir) + 1 + 1;
00667 dirname = (char*) malloc(num_bytes);
00668 if( dirname==NULL )
00669 {
00670 HAL_ERROR(("couldn't allocated %d bytes", num_bytes));
00671 break;
00672 }
00673
00674 snprintf(dirname, num_bytes, "%s/%s", dir, filename);
00675 found_fdi_file = scan_fdi_files(dirname, d);
00676 free(dirname);
00677 if( found_fdi_file )
00678 break;
00679 }
00680
00681 free(name_list[i]);
00682 }
00683
00684 for( ;i>=0; i--)
00685 {
00686 free(name_list[i]);
00687 }
00688
00689 free(name_list);
00690
00691 return found_fdi_file;
00692 }
00693
00694
00701 dbus_bool_t di_search_and_merge(HalDevice* d)
00702 {
00703 return scan_fdi_files(PACKAGE_DATA_DIR "/hal/fdi", d);
00704 }
00705