# device-xml.c

Go to the documentation of this file.

    00001
    00010 /* $Progeny$
    00011  *
    00012  * AUTHOR: John R. Daily <jdaily@progeny.com>
    00013  *
    00014  * Copyright 2002 Progeny Linux Systems, Inc.
    00015  * Copyright 2002 Hewlett-Packard Company
    00016  *
    00017  * Permission is hereby granted, free of charge, to any person obtaining a
    00018  * copy of this software and associated documentation files (the "Software"),
    00019  * to deal in the Software without restriction, including without limitation
    00020  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    00021  * and/or sell copies of the Software, and to permit persons to whom the
    00022  * Software is furnished to do so, subject to the following conditions:
    00023  *
    00024  * The above copyright notice and this permission notice shall be included in
    00025  * all copies or substantial portions of the Software.
    00026  *
    00027  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    00028  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    00029  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
    00030  * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    00031  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    00032  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    00033  * DEALINGS IN THE SOFTWARE.
    00034  */
    00035
    00036 /*
    00037  * This is slightly different than busclass-xml.c and vendor-xml.c.
    00038  * With those, the data that interests us is actually in the
    00039  * XML files, so the structures built by those modules are the final
    00040  * structures.
    00041
    00042  * With the devices, the list we build here is only an intermediate
    00043  * place to store the discover_device structures.  When we probe the
    00044  * bus, we will move the discover_device structures for which
    00045  * we have found corresponding hardware from this list to the final
    00046  * one. (That action happens in the modules for the individual buses,
    00047  * such as pci.c.)
    00048
    00049  * Whereas busclass-xml.c and vendor-xml.c create pci_busclasses and
    00050  * pci_vendors lists, respectively, this module creates
    00051  * pci_devices_xml.  Objects are moved from this list to the
    00052  * pci_devices list in pci.c, and the rest are freed.
    00053  */
    00054
    00055 #include "config.h"
    00056
    00057 #include <sys/types.h>
    00058
    00059 #include <assert.h>
    00060 #include <ctype.h>
    00061 #include <limits.h>
    00062 #include <stdbool.h>
    00063 #include <string.h>
    00064 #include <errno.h>
    00065
    00066 #include <expat.h>
    00067
    00068 #include <discover/discover.h>
    00069 #include <discover/discover-xml.h>
    00070
    00071 #include <discover/load-url.h>
    00072 #include <discover/device.h>
    00073 #include <discover/utils.h>
    00074 #include <discover/stack.h>
    00075
    00077 #define IDLEN 5
    00078
    00079 static discover_device_t *devices_xml[BUS_COUNT];
    00080
    00082 enum state { START, FINISH, DEVICE, DATA };
    00083 struct context {
    00084     enum state state;
    00085
    00086     discover_xml_busclass_t *busclasses;
    00087     discover_xml_vendor_t *vendors;
    00088
    00089     /* Device stuff */
    00090     discover_device_t **dhead;
    00091     discover_device_t *dtail;
    00092
    00093     /* data stuff */
    00094     discover_xml_stack *stack;
    00095 /*
    00096     discover_data_t *data_current;
    00097     discover_data_t *data_parent;
    00098 */
    00099     int nesting;
    00100     int last_nesting;
    00101
    00102     int unknown_level; /* How deep are we into unknown XML tags? */
    00103 };
    00104
    00105 static char *known_device_elements[] = {
    00106     "data",
    00107     "device",
    00108     "device_list",
    00109     NULL
    00110 };
    00111
    00112 static void
    00113 get_data_failure_handler(discover_error_t **status, char *url)
    00114 {
    00115     char *errmsg;
    00116     static int maxurlsize = 1024; /* If your URL is longer than this,
    00117                                      you've got problems */
    00118
    00119     if((*status)->code == DISCOVER_EIO) {
    00120         errmsg = _discover_xmalloc(maxurlsize + 1);
    00121         snprintf(errmsg, maxurlsize, "Resource not found: %s", url);
    00122         (*status)->create_message(status, errmsg);
    00123         free(errmsg);
    00124     } else {
    00125         if (errno) {
    00126             errmsg = _discover_xmalloc(maxurlsize + 1);
    00127             snprintf(errmsg, maxurlsize, "Problem loading resource: %s: %s",
    00128                      strerror(errno), url);
    00129             (*status)->create_message(status, errmsg);
    00130             free(errmsg);
    00131         } else {
    00132             (*status)->create_message(status,
    00133                     "Unknown failure from system-dependent libraries");
    00134         }
    00135     }
    00136 }
    00137
    00138 static bool
    00139 unknown_device_element(const XML_Char * const tag)
    00140 {
    00141     int i;
    00142     for (i = 0; known_device_elements[i] != NULL; i++) {
    00143         if (strcmp(tag, known_device_elements[i]) == 0)
    00144             return false;
    00145     }
    00146     return true;
    00147 }
    00148
    00149 static void
    00150 create_device(struct context *context, const XML_Char *attrs[])
    00151 {
    00152     int i;
    00153     char *busclass, *model_id, *model_name, *vendor_id, *vendor_name;
    00154     discover_device_t *new_device;
    00155
    00156     assert(context != NULL);
    00157     assert(attrs != NULL);
    00158
    00159     busclass = model_id = model_name = vendor_id = NULL;
    00160     for (i = 0; attrs[i]; i += 2) {
    00161         if (strcmp(attrs[i], "busclass") == 0) {
    00162             busclass = (char *)attrs[i + 1];
    00163
    00164         } else if (strcmp(attrs[i], "model") == 0) {
    00165             model_id = (char *)attrs[i + 1];
    00166
    00167         } else if (strcmp(attrs[i], "model_name") == 0) {
    00168             model_name = (char *)attrs[i + 1];
    00169
    00170         } else if (strcmp(attrs[i], "vendor") == 0) {
    00171             vendor_id = (char *)attrs[i + 1];
    00172         }
    00173     }
    00174
    00175     assert(model_id != NULL);
    00176     assert(model_name != NULL);
    00177     assert(vendor_id != NULL);
    00178
    00179     vendor_name = discover_xml_vendor_id2name(context->vendors, vendor_id);
    00180     assert(vendor_name != NULL);
    00181
    00182     context->stack = discover_xml_stack_new();
    00183
    00184     new_device = discover_device_new();
    00185     new_device->busclasses = context->busclasses;
    00186     new_device->vendors = context->vendors;
    00187     if (busclass) {
    00188         new_device->busclass = _discover_xstrdup(busclass);
    00189     } else {
    00190         new_device->busclass = NULL;
    00191     }
    00192     new_device->model_id = _discover_xstrdup(model_id);
    00193     new_device->model_name = _discover_xstrdup(model_name);
    00194     new_device->vendor_id = _discover_xstrdup(vendor_id);
    00195     new_device->vendor_name = _discover_xstrdup(vendor_name);
    00196     new_device->data = NULL;
    00197     new_device->next = NULL;
    00198
    00199     if (*(context->dhead)) {
    00200         context->dtail->next = new_device;
    00201         context->dtail = new_device;
    00202     } else {
    00203         *(context->dhead) = new_device;
    00204         context->dtail = new_device;
    00205     }
    00206 }
    00207
    00208 static void
    00209 create_data(struct context *context, const XML_Char *attrs[])
    00210 {
    00211     discover_data_t *new_data, *current_data;
    00212     discover_xml_stack *stack;
    00213     int i;
    00214     char *discover_class, *version;
    00215
    00216     assert(context != NULL);
    00217     assert(attrs != NULL);
    00218
    00219     new_data = discover_data_new();
    00220     new_data->text = NULL;
    00221     new_data->next = new_data->prev = new_data->child = NULL;
    00222
    00223
    00224     discover_class = version = NULL;
    00225     for (i = 0; attrs[i]; i += 2) {
    00226         if (strcmp(attrs[i], "class") == 0) {
    00227             discover_class = (char *)attrs[i + 1];
    00228         } else if (strcmp(attrs[i], "version") == 0) {
    00229             version = (char *)attrs[i + 1];
    00230         }
    00231     }
    00232
    00233     assert(discover_class != NULL);
    00234
    00235     new_data->discover_class = _discover_xstrdup(discover_class);
    00236     if (version) {
    00237         new_data->version = _discover_xstrdup(version);
    00238     }
    00239
    00240     stack = context->stack;
    00241
    00242     assert(stack != NULL);
    00243
    00244     current_data = discover_xml_stack_get(stack);
    00245
    00246     if(current_data) { /* The first time through this, we have no data. */
    00247         /* First element of the list */
    00248         if(stack->depth > context->nesting) {
    00249             discover_xml_stack_pop(&stack);
    00250             //current_data = discover_xml_stack_pop(&stack);
    00251             new_data->prev = current_data;
    00252             new_data->prev->next = new_data;
    00253             if(context->nesting) {
    00254                 new_data->parent =
    00255                     discover_xml_stack_getbynum(stack, context->nesting);
    00256             }
    00257         } else {
    00258             /* Brand new child */
    00259             new_data->parent = current_data;
    00260             new_data->parent->child = new_data;
    00261         }
    00262     }
    00263
    00264     discover_xml_stack_push(&stack, new_data);
    00265     context->stack = stack;
    00266 }
    00267
    00268 static void
    00269 start_element(void *ctx, const XML_Char *name, const XML_Char *attrs[])
    00270 {
    00271     struct context *context = ctx;
    00272
    00273     assert(context != NULL);
    00274     assert(name != NULL);
    00275     assert(attrs != NULL);
    00276
    00277
    00278     if (unknown_device_element(name)) {
    00279         context->unknown_level++;
    00280         return;
    00281     }
    00282
    00283     if (context->unknown_level > 0) {
    00284         return;
    00285     }
    00286
    00287     switch (context->state) {
    00288     case FINISH:
    00289         return;
    00290
    00291     case START:
    00292         if (strcmp(name, "device") == 0) {
    00293             context->state = DEVICE;
    00294             create_device(context, attrs);
    00295         }
    00296         break;
    00297
    00298     case DEVICE:
    00299         if (strcmp(name, "data") == 0) {
    00300             context->nesting = context->last_nesting = 0;
    00301             context->state = DATA;
    00302         }
    00303         /* Falls through */
    00304
    00305     case DATA:
    00306         if (strcmp(name, "data") == 0) {
    00307             create_data(context, attrs);
    00308             context->last_nesting = context->nesting;
    00309             context->nesting++;
    00310         }
    00311         break;
    00312     }
    00313 }
    00314
    00315 static void
    00316 end_element(void *ctx, const XML_Char *name)
    00317 {
    00318     struct context *context = ctx;
    00319     discover_device_t *device;
    00320     discover_data_t *current_data;
    00321     discover_xml_stack *stack;
    00322
    00323
    00324     assert(context != NULL);
    00325     assert(name != NULL);
    00326
    00327
    00328     if (unknown_device_element(name)) {
    00329         context->unknown_level--;
    00330         return;
    00331     }
    00332
    00333     if (context->unknown_level > 0) {
    00334         return;
    00335     }
    00336
    00337     switch (context->state) {
    00338     case FINISH:
    00339     case START:
    00340         break;
    00341
    00342     case DEVICE:
    00343         context->state = START;
    00344         device = context->dtail;
    00345         stack = context->stack;
    00346         current_data = discover_xml_stack_get(stack);
    00347         device->data = discover_data_get_first(current_data);
    00348
    00349         while (discover_xml_stack_pop(&(context->stack))) ;
    00350         discover_xml_stack_destroy(context->stack);
    00351         context->stack = NULL;
    00352
    00353         break;
    00354
    00355     case DATA:
    00356         context->nesting--;
    00357         stack = context->stack;
    00358         if((context->nesting + 2) <= stack->depth) {
    00359             while((context->nesting + 1 < stack->depth) &&
    00360                     stack->depth > 1) {
    00361                 discover_xml_stack_pop(&stack);
    00362             }
    00363             context->stack = stack;
    00364         }
    00365
    00366         if (context->nesting == 0) {
    00367             context->state = DEVICE;
    00368         }
    00369     }
    00370 }
    00371
    00372 static void
    00373 cdata(void *ctx, const XML_Char *data, int len)
    00374 {
    00375     struct context *context = ctx;
    00376     size_t old_len;
    00377     discover_data_t *current_data;
    00378
    00379     assert(context != NULL);
    00380     assert(data != NULL);
    00381
    00382     if (context->state == DATA
    00383         && context->nesting > context->last_nesting) {
    00384         assert(context->stack != NULL);
    00385         current_data = context->stack->data;
    00386         assert(current_data != NULL);
    00387
    00388         if (!current_data->text) {
    00389             old_len = 0;
    00390             current_data->text = _discover_xmalloc(1);
    00391             current_data->text[0] = '\0';
    00392         } else {
    00393             old_len = strlen(current_data->text);
    00394         }
    00395         current_data->text
    00396             = _discover_xrealloc(current_data->text,
    00397                                  old_len + len + 1);
    00398         strncat(current_data->text,
    00399                 (const char *)data,
    00400                 (unsigned int)len);
    00401     }
    00402 }
    00403
    00418 /* Sshh!  Don't tell, but this doesn't actually do any merging at all.
    00419  * Instead, it simply inserts newer entries at the front of the list,
    00420  * meaning that device info found later supersedes info found earlier.
    00421  * This gives the illusion of merging, but potentially wastes memory
    00422  * with duplicates.
    00423  */
    00424 void
    00425 discover_xml_merge_device_url(discover_device_t **dlist, char *url,
    00426                               discover_xml_busclass_t *busclasses,
    00427                               discover_xml_vendor_t *vendors,
    00428                               discover_error_t *status)
    00429 {
    00430     XML_Parser parser;
    00431     struct context context;
    00432
    00433     assert(url != NULL);
    00434     assert(busclasses != NULL);
    00435     assert(vendors != NULL);
    00436     assert(status != NULL);
    00437
    00438     context.state = START;
    00439     context.dhead = dlist;
    00440     if (*(context.dhead)) {
    00441         discover_device_t *next = *(context.dhead);
    00442         while(next->next != NULL) {
    00443             next = next->next;
    00444         }
    00445         context.dtail = next;
    00446     } else {
    00447         context.dtail = NULL;
    00448     }
    00449
    00450     context.busclasses = busclasses;
    00451     context.vendors = vendors;
    00452     context.unknown_level = 0;
    00453     context.stack = NULL;
    00454
    00455     parser = XML_ParserCreate(NULL);
    00456     XML_SetElementHandler(parser, start_element, end_element);
    00457     XML_SetCharacterDataHandler(parser, cdata);
    00458     XML_SetUserData(parser, &context);
    00459
    00460     if (!_discover_load_url(url, parser)) {
    00461         XML_ParserFree(parser);
    00462         status->code = DISCOVER_EIO;
    00463         return;
    00464     }
    00465
    00466     if (!XML_Parse(parser, "", 0, 1)) {
    00467         XML_ParserFree(parser);
    00468         status->code = DISCOVER_EXML;
    00469         return;
    00470     }
    00471
    00472     XML_ParserFree(parser);
    00473
    00474     return;
    00475 }
    00476
    00483 discover_device_t *
    00484 discover_xml_get_devices(discover_bus_t bus, discover_error_t *status)
    00485 {
    00486     discover_xml_url_t *urls, *i;
    00487     char *url;
    00488     discover_xml_busclass_t *busclasses;
    00489     discover_xml_vendor_t *vendors;
    00490
    00491     assert(status != NULL);
    00492
    00493     status->code = 0;
    00494
    00495     if (!devices_xml[bus]) {
    00496         urls = discover_xml_get_data_urls(bus, DEVICE, status);
    00497         if (status->code != 0) {
    00498             return NULL;
    00499         }
    00500
    00501         busclasses = discover_xml_get_busclasses(bus, status);
    00502         if (status->code != 0) {
    00503             return NULL;
    00504         }
    00505
    00506         vendors = discover_xml_get_vendors(bus, status);
    00507         if (status->code != 0) {
    00508             return NULL;
    00509         }
    00510
    00511         for (i = urls;
    00512              i;
    00513              i = discover_xml_url_get_next(i)) {
    00514             url = discover_xml_url_get_url(i);
    00515             discover_xml_merge_device_url(&(devices_xml[bus]), url,
    00516                                           busclasses, vendors, status);
    00517             if (status->code != 0) {
    00518                 get_data_failure_handler(&status, url);
    00519             }
    00520         }
    00521     }
    00522
    00523
    00524     return devices_xml[bus];
    00525 }
    00526
    00530 void
    00531 discover_xml_free_devices(void)
    00532 {
    00533     int i;
    00534     for (i = 0; i < BUS_COUNT; i++) {
    00535         discover_device_free(devices_xml[i], 1);
    00536         devices_xml[i] = NULL;
    00537     }
    00538 }
    00539
    00549 discover_device_t *
    00550 discover_xml_find_device(discover_device_t *xml_devices,
    00551                          char *target_vendor, char *target_model,
    00552                          discover_error_t *status)
    00553 {
    00554     discover_device_t *device;
    00555
    00556     assert(target_vendor || target_model);
    00557
    00558     for (device = xml_devices;
    00559          device;
    00560          device = device->next) {
    00561         if (target_vendor && target_model) {
    00562             if (strcmp(device->model_id, target_model) == 0
    00563                 && strcmp(device->vendor_id, target_vendor) == 0) {
    00564                 break;
    00565             }
    00566         } else if (target_vendor) {
    00567             if (strcmp(device->vendor_id, target_vendor) == 0) {
    00568                 break;
    00569             }
    00570         } else {
    00571             /* We only have target_model. */
    00572             if (strcmp(device->model_id, target_model) == 0) {
    00573                 break;
    00574             }
    00575         }
    00576     }
    00577
    00578     return device;
    00579 }
    00580
    00593 discover_device_t *
    00594 discover_xml_find_next_device(discover_device_t *xml_devices,
    00595                          char *target_vendor, char *target_model,
    00596                          discover_error_t *status)
    00597 {
    00598     return discover_xml_find_device(xml_devices->next,
    00599                                     target_vendor, target_model,
    00600                                     status);
    00601 }
    00602
    00603
    00613 discover_device_t *
    00614 discover_xml_get_matching_devices(discover_device_t *xml_devices,
    00615                                   char *target_vendor, char *target_model,
    00616                                   discover_error_t *status)
    00617 {
    00618     discover_device_t *device, *last, *copy;
    00619     discover_device_t *head_device = NULL;
    00620
    00621     device = discover_xml_find_device(xml_devices, target_vendor,
    00622                                       target_model, status);
    00623     last = NULL;
    00624
    00625     while(device) {
    00626         copy = discover_device_new();
    00627         discover_device_copy(device, copy);
    00628         copy->next = NULL;
    00629         copy->extra = NULL;
    00630
    00631         if (last) {
    00632             last->extra = copy;
    00633         } else {
    00634             head_device = copy;
    00635         }
    00636
    00637         last = copy;
    00638
    00639         device = discover_xml_find_next_device(device, target_vendor,
    00640                                                target_model, status);
    00641     }
    00642
    00643     return head_device;
    00644 }
    00645
    00648 /*
    00649  * Local variables:
    00650  * c-file-style: "progeny"
    00651  * indent-tabs-mode: nil
    00652  * End:
    00653  */
    00654 /* vim: set cin fo=tcroq sw=4 et sts=4 tw=75: */