1/* 2 * XML DRI client-side driver configuration 3 * Copyright (C) 2003 Felix Kuehling 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included 13 * in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * FELIX KUEHLING, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 21 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 * 23 */ 24/** 25 * \file xmlconfig.c 26 * \brief Driver-independent client-side part of the XML configuration 27 * \author Felix Kuehling 28 */ 29 30#include "xmlconfig.h" 31#include <limits.h> 32#include <stdarg.h> 33#include <stdbool.h> 34#include <stdint.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38#include <assert.h> 39#if WITH_XMLCONFIG 40#include <expat.h> 41#include <unistd.h> 42#include <errno.h> 43#include <dirent.h> 44#include <sys/stat.h> 45#endif 46#ifdef NO_REGEX 47typedef int regex_t; 48#define REG_EXTENDED 0 49#define REG_NOSUB 0 50#define REG_NOMATCH 1 51static inline int regcomp(regex_t *r, const char *s, int f) { return 0; } 52static inline int regexec(regex_t *r, const char *s, int n, void *p, int f) { return REG_NOMATCH; } 53static inline void regfree(regex_t* r) {} 54#else 55#include <regex.h> 56#endif 57#include <fcntl.h> 58#include <math.h> 59#include "strndup.h" 60#include "u_process.h" 61#include "os_file.h" 62 63/* For systems like Hurd */ 64#ifndef PATH_MAX 65#define PATH_MAX 4096 66#endif 67 68static bool 69be_verbose(void) 70{ 71 const char *s = getenv("MESA_DEBUG"); 72 if (!s) 73 return true; 74 75 return strstr(s, "silent") == NULL; 76} 77 78/** \brief Locale-independent integer parser. 79 * 80 * Works similar to strtol. Leading space is NOT skipped. The input 81 * number may have an optional sign. Radix is specified by base. If 82 * base is 0 then decimal is assumed unless the input number is 83 * prefixed by 0x or 0X for hexadecimal or 0 for octal. After 84 * returning tail points to the first character that is not part of 85 * the integer number. If no number was found then tail points to the 86 * start of the input string. */ 87static int 88strToI(const char *string, const char **tail, int base) 89{ 90 int radix = base == 0 ? 10 : base; 91 int result = 0; 92 int sign = 1; 93 bool numberFound = false; 94 const char *start = string; 95 96 assert(radix >= 2 && radix <= 36); 97 98 if (*string == '-') { 99 sign = -1; 100 string++; 101 } else if (*string == '+') 102 string++; 103 if (base == 0 && *string == '0') { 104 numberFound = true; 105 if (*(string+1) == 'x' || *(string+1) == 'X') { 106 radix = 16; 107 string += 2; 108 } else { 109 radix = 8; 110 string++; 111 } 112 } 113 do { 114 int digit = -1; 115 if (radix <= 10) { 116 if (*string >= '0' && *string < '0' + radix) 117 digit = *string - '0'; 118 } else { 119 if (*string >= '0' && *string <= '9') 120 digit = *string - '0'; 121 else if (*string >= 'a' && *string < 'a' + radix - 10) 122 digit = *string - 'a' + 10; 123 else if (*string >= 'A' && *string < 'A' + radix - 10) 124 digit = *string - 'A' + 10; 125 } 126 if (digit != -1) { 127 numberFound = true; 128 result = radix*result + digit; 129 string++; 130 } else 131 break; 132 } while (true); 133 *tail = numberFound ? string : start; 134 return sign * result; 135} 136 137/** \brief Locale-independent floating-point parser. 138 * 139 * Works similar to strtod. Leading space is NOT skipped. The input 140 * number may have an optional sign. '.' is interpreted as decimal 141 * point and may occur at most once. Optionally the number may end in 142 * [eE]<exponent>, where <exponent> is an integer as recognized by 143 * strToI. In that case the result is number * 10^exponent. After 144 * returning tail points to the first character that is not part of 145 * the floating point number. If no number was found then tail points 146 * to the start of the input string. 147 * 148 * Uses two passes for maximum accuracy. */ 149static float 150strToF(const char *string, const char **tail) 151{ 152 int nDigits = 0, pointPos, exponent; 153 float sign = 1.0f, result = 0.0f, scale; 154 const char *start = string, *numStart; 155 156 /* sign */ 157 if (*string == '-') { 158 sign = -1.0f; 159 string++; 160 } else if (*string == '+') 161 string++; 162 163 /* first pass: determine position of decimal point, number of 164 * digits, exponent and the end of the number. */ 165 numStart = string; 166 while (*string >= '0' && *string <= '9') { 167 string++; 168 nDigits++; 169 } 170 pointPos = nDigits; 171 if (*string == '.') { 172 string++; 173 while (*string >= '0' && *string <= '9') { 174 string++; 175 nDigits++; 176 } 177 } 178 if (nDigits == 0) { 179 /* no digits, no number */ 180 *tail = start; 181 return 0.0f; 182 } 183 *tail = string; 184 if (*string == 'e' || *string == 'E') { 185 const char *expTail; 186 exponent = strToI(string+1, &expTail, 10); 187 if (expTail == string+1) 188 exponent = 0; 189 else 190 *tail = expTail; 191 } else 192 exponent = 0; 193 string = numStart; 194 195 /* scale of the first digit */ 196 scale = sign * (float)pow(10.0, (double)(pointPos-1 + exponent)); 197 198 /* second pass: parse digits */ 199 do { 200 if (*string != '.') { 201 assert(*string >= '0' && *string <= '9'); 202 result += scale * (float)(*string - '0'); 203 scale *= 0.1f; 204 nDigits--; 205 } 206 string++; 207 } while (nDigits > 0); 208 209 return result; 210} 211 212/** \brief Parse a value of a given type. */ 213static unsigned char 214parseValue(driOptionValue *v, driOptionType type, const char *string) 215{ 216 const char *tail = NULL; 217 /* skip leading white-space */ 218 string += strspn(string, " \f\n\r\t\v"); 219 switch (type) { 220 case DRI_BOOL: 221 if (!strcmp(string, "false")) { 222 v->_bool = false; 223 tail = string + 5; 224 } else if (!strcmp(string, "true")) { 225 v->_bool = true; 226 tail = string + 4; 227 } 228 else 229 return false; 230 break; 231 case DRI_ENUM: /* enum is just a special integer */ 232 case DRI_INT: 233 v->_int = strToI(string, &tail, 0); 234 break; 235 case DRI_FLOAT: 236 v->_float = strToF(string, &tail); 237 break; 238 case DRI_STRING: 239 free(v->_string); 240 v->_string = strndup(string, STRING_CONF_MAXLEN); 241 return true; 242 case DRI_SECTION: 243 unreachable("shouldn't be parsing values in section declarations"); 244 } 245 246 if (tail == string) 247 return false; /* empty string (or containing only white-space) */ 248 /* skip trailing white space */ 249 if (*tail) 250 tail += strspn(tail, " \f\n\r\t\v"); 251 if (*tail) 252 return false; /* something left over that is not part of value */ 253 254 return true; 255} 256 257/** \brief Find an option in an option cache with the name as key */ 258static uint32_t 259findOption(const driOptionCache *cache, const char *name) 260{ 261 uint32_t len = strlen(name); 262 uint32_t size = 1 << cache->tableSize, mask = size - 1; 263 uint32_t hash = 0; 264 uint32_t i, shift; 265 266 /* compute a hash from the variable length name */ 267 for (i = 0, shift = 0; i < len; ++i, shift = (shift+8) & 31) 268 hash += (uint32_t)name[i] << shift; 269 hash *= hash; 270 hash = (hash >> (16-cache->tableSize/2)) & mask; 271 272 /* this is just the starting point of the linear search for the option */ 273 for (i = 0; i < size; ++i, hash = (hash+1) & mask) { 274 /* if we hit an empty entry then the option is not defined (yet) */ 275 if (cache->info[hash].name == NULL) 276 break; 277 else if (!strcmp(name, cache->info[hash].name)) 278 break; 279 } 280 /* this assertion fails if the hash table is full */ 281 assert (i < size); 282 283 return hash; 284} 285 286/** \brief Like strdup with error checking. */ 287#define XSTRDUP(dest,source) do { \ 288 if (!(dest = strdup(source))) { \ 289 fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); \ 290 abort(); \ 291 } \ 292 } while (0) 293 294/** \brief Check if a value is in info->range. */ 295UNUSED static bool 296checkValue(const driOptionValue *v, const driOptionInfo *info) 297{ 298 switch (info->type) { 299 case DRI_ENUM: /* enum is just a special integer */ 300 case DRI_INT: 301 return (info->range.start._int == info->range.end._int || 302 (v->_int >= info->range.start._int && 303 v->_int <= info->range.end._int)); 304 305 case DRI_FLOAT: 306 return (info->range.start._float == info->range.end._float || 307 (v->_float >= info->range.start._float && 308 v->_float <= info->range.end._float)); 309 310 default: 311 return true; 312 } 313} 314 315void 316driParseOptionInfo(driOptionCache *info, 317 const driOptionDescription *configOptions, 318 unsigned numOptions) 319{ 320 /* Make the hash table big enough to fit more than the maximum number of 321 * config options we've ever seen in a driver. 322 */ 323 info->tableSize = 7; 324 info->info = calloc((size_t)1 << info->tableSize, sizeof(driOptionInfo)); 325 info->values = calloc((size_t)1 << info->tableSize, sizeof(driOptionValue)); 326 if (info->info == NULL || info->values == NULL) { 327 fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); 328 abort(); 329 } 330 331 UNUSED bool in_section = false; 332 for (int o = 0; o < numOptions; o++) { 333 const driOptionDescription *opt = &configOptions[o]; 334 335 if (opt->info.type == DRI_SECTION) { 336 in_section = true; 337 continue; 338 } 339 340 /* for driconf xml generation, options must always be preceded by a 341 * DRI_CONF_SECTION 342 */ 343 assert(in_section); 344 345 const char *name = opt->info.name; 346 int i = findOption(info, name); 347 driOptionInfo *optinfo = &info->info[i]; 348 driOptionValue *optval = &info->values[i]; 349 350 assert(!optinfo->name); /* No duplicate options in your list. */ 351 352 optinfo->type = opt->info.type; 353 optinfo->range = opt->info.range; 354 XSTRDUP(optinfo->name, name); 355 356 switch (opt->info.type) { 357 case DRI_BOOL: 358 optval->_bool = opt->value._bool; 359 break; 360 361 case DRI_INT: 362 case DRI_ENUM: 363 optval->_int = opt->value._int; 364 break; 365 366 case DRI_FLOAT: 367 optval->_float = opt->value._float; 368 break; 369 370 case DRI_STRING: 371 XSTRDUP(optval->_string, opt->value._string); 372 break; 373 374 case DRI_SECTION: 375 unreachable("handled above"); 376 } 377 378 /* Built-in default values should always be valid. */ 379 assert(checkValue(optval, optinfo)); 380 381 char *envVal = getenv(name); 382 if (envVal != NULL) { 383 driOptionValue v; 384 385 /* make sure the value is initialized to something sensible */ 386 v._string = NULL; 387 388 if (parseValue(&v, opt->info.type, envVal) && 389 checkValue(&v, optinfo)) { 390 /* don't use XML_WARNING, we want the user to see this! */ 391 if (be_verbose()) { 392 fprintf(stderr, 393 "ATTENTION: default value of option %s overridden by environment.\n", 394 name); 395 } 396 *optval = v; 397 } else { 398 fprintf(stderr, "illegal environment value for %s: \"%s\". Ignoring.\n", 399 name, envVal); 400 } 401 } 402 } 403} 404 405char * 406driGetOptionsXml(const driOptionDescription *configOptions, unsigned numOptions) 407{ 408 char *str = ralloc_strdup(NULL, 409 "<?xml version=\"1.0\" standalone=\"yes\"?>\n" \ 410 "<!DOCTYPE driinfo [\n" \ 411 " <!ELEMENT driinfo (section*)>\n" \ 412 " <!ELEMENT section (description+, option+)>\n" \ 413 " <!ELEMENT description (enum*)>\n" \ 414 " <!ATTLIST description lang CDATA #FIXED \"en\"\n" \ 415 " text CDATA #REQUIRED>\n" \ 416 " <!ELEMENT option (description+)>\n" \ 417 " <!ATTLIST option name CDATA #REQUIRED\n" \ 418 " type (bool|enum|int|float) #REQUIRED\n" \ 419 " default CDATA #REQUIRED\n" \ 420 " valid CDATA #IMPLIED>\n" \ 421 " <!ELEMENT enum EMPTY>\n" \ 422 " <!ATTLIST enum value CDATA #REQUIRED\n" \ 423 " text CDATA #REQUIRED>\n" \ 424 "]>" \ 425 "<driinfo>\n"); 426 427 bool in_section = false; 428 for (int o = 0; o < numOptions; o++) { 429 const driOptionDescription *opt = &configOptions[o]; 430 431 const char *name = opt->info.name; 432 const char *types[] = { 433 [DRI_BOOL] = "bool", 434 [DRI_INT] = "int", 435 [DRI_FLOAT] = "float", 436 [DRI_ENUM] = "enum", 437 [DRI_STRING] = "string", 438 }; 439 440 if (opt->info.type == DRI_SECTION) { 441 if (in_section) 442 ralloc_asprintf_append(&str, " </section>\n"); 443 444 ralloc_asprintf_append(&str, 445 " <section>\n" 446 " <description lang=\"en\" text=\"%s\"/>\n", 447 opt->desc); 448 449 in_section = true; 450 continue; 451 } 452 453 ralloc_asprintf_append(&str, 454 " <option name=\"%s\" type=\"%s\" default=\"", 455 name, 456 types[opt->info.type]); 457 458 switch (opt->info.type) { 459 case DRI_BOOL: 460 ralloc_asprintf_append(&str, opt->value._bool ? "true" : "false"); 461 break; 462 463 case DRI_INT: 464 case DRI_ENUM: 465 ralloc_asprintf_append(&str, "%d", opt->value._int); 466 break; 467 468 case DRI_FLOAT: 469 ralloc_asprintf_append(&str, "%f", opt->value._float); 470 break; 471 472 case DRI_STRING: 473 ralloc_asprintf_append(&str, "%s", opt->value._string); 474 break; 475 476 case DRI_SECTION: 477 unreachable("handled above"); 478 break; 479 } 480 ralloc_asprintf_append(&str, "\""); 481 482 483 switch (opt->info.type) { 484 case DRI_INT: 485 case DRI_ENUM: 486 if (opt->info.range.start._int < opt->info.range.end._int) { 487 ralloc_asprintf_append(&str, " valid=\"%d:%d\"", 488 opt->info.range.start._int, 489 opt->info.range.end._int); 490 } 491 break; 492 493 case DRI_FLOAT: 494 if (opt->info.range.start._float < opt->info.range.end._float) { 495 ralloc_asprintf_append(&str, " valid=\"%f:%f\"", 496 opt->info.range.start._float, 497 opt->info.range.end._float); 498 } 499 break; 500 501 default: 502 break; 503 } 504 505 ralloc_asprintf_append(&str, ">\n"); /* end of <option> */ 506 507 508 ralloc_asprintf_append(&str, " <description lang=\"en\" text=\"%s\"%s>\n", 509 opt->desc, opt->info.type != DRI_ENUM ? "/" : ""); 510 511 if (opt->info.type == DRI_ENUM) { 512 for (int i = 0; i < ARRAY_SIZE(opt->enums) && opt->enums[i].desc; i++) { 513 ralloc_asprintf_append(&str, " <enum value=\"%d\" text=\"%s\"/>\n", 514 opt->enums[i].value, opt->enums[i].desc); 515 } 516 ralloc_asprintf_append(&str, " </description>\n"); 517 } 518 519 ralloc_asprintf_append(&str, " </option>\n"); 520 } 521 522 assert(in_section); 523 ralloc_asprintf_append(&str, " </section>\n"); 524 525 ralloc_asprintf_append(&str, "</driinfo>\n"); 526 527 char *output = strdup(str); 528 ralloc_free(str); 529 530 return output; 531} 532 533/** 534 * Print message to \c stderr if the \c LIBGL_DEBUG environment variable 535 * is set. 536 * 537 * Is called from the drivers. 538 * 539 * \param f \c printf like format string. 540 */ 541static void 542__driUtilMessage(const char *f, ...) 543{ 544 va_list args; 545 const char *libgl_debug; 546 547 libgl_debug=getenv("LIBGL_DEBUG"); 548 if (libgl_debug && !strstr(libgl_debug, "quiet")) { 549 fprintf(stderr, "libGL: "); 550 va_start(args, f); 551 vfprintf(stderr, f, args); 552 va_end(args); 553 fprintf(stderr, "\n"); 554 } 555} 556 557/* We don't have real line/column # info in static-config case: */ 558#if !WITH_XML_CONFIG 559# define XML_GetCurrentLineNumber(p) -1 560# define XML_GetCurrentColumnNumber(p) -1 561#endif 562 563/** \brief Output a warning message. */ 564#define XML_WARNING1(msg) do { \ 565 __driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \ 566 (int) XML_GetCurrentLineNumber(data->parser), \ 567 (int) XML_GetCurrentColumnNumber(data->parser)); \ 568 } while (0) 569#define XML_WARNING(msg, ...) do { \ 570 __driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \ 571 (int) XML_GetCurrentLineNumber(data->parser), \ 572 (int) XML_GetCurrentColumnNumber(data->parser), \ 573 ##__VA_ARGS__); \ 574 } while (0) 575/** \brief Output an error message. */ 576#define XML_ERROR1(msg) do { \ 577 __driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \ 578 (int) XML_GetCurrentLineNumber(data->parser), \ 579 (int) XML_GetCurrentColumnNumber(data->parser)); \ 580 } while (0) 581#define XML_ERROR(msg, ...) do { \ 582 __driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \ 583 (int) XML_GetCurrentLineNumber(data->parser), \ 584 (int) XML_GetCurrentColumnNumber(data->parser), \ 585 ##__VA_ARGS__); \ 586 } while (0) 587 588/** \brief Parser context for configuration files. */ 589struct OptConfData { 590 const char *name; 591#if WITH_XMLCONFIG 592 XML_Parser parser; 593#endif 594 driOptionCache *cache; 595 int screenNum; 596 const char *driverName, *execName; 597 const char *kernelDriverName; 598 const char *deviceName; 599 const char *engineName; 600 const char *applicationName; 601 uint32_t engineVersion; 602 uint32_t applicationVersion; 603 uint32_t ignoringDevice; 604 uint32_t ignoringApp; 605 uint32_t inDriConf; 606 uint32_t inDevice; 607 uint32_t inApp; 608 uint32_t inOption; 609}; 610 611/** \brief Parse a list of ranges of type info->type. */ 612static unsigned char 613parseRange(driOptionInfo *info, const char *string) 614{ 615 char *cp; 616 617 XSTRDUP(cp, string); 618 619 char *sep; 620 sep = strchr(cp, ':'); 621 if (!sep) { 622 free(cp); 623 return false; 624 } 625 626 *sep = '\0'; 627 if (!parseValue(&info->range.start, info->type, cp) || 628 !parseValue(&info->range.end, info->type, sep+1)) { 629 free(cp); 630 return false; 631 } 632 if (info->type == DRI_INT && 633 info->range.start._int >= info->range.end._int) { 634 free(cp); 635 return false; 636 } 637 if (info->type == DRI_FLOAT && 638 info->range.start._float >= info->range.end._float) { 639 free(cp); 640 return false; 641 } 642 643 free(cp); 644 return true; 645} 646 647/** \brief Parse attributes of a device element. */ 648static void 649parseDeviceAttr(struct OptConfData *data, const char **attr) 650{ 651 uint32_t i; 652 const char *driver = NULL, *screen = NULL, *kernel = NULL, *device = NULL; 653 for (i = 0; attr[i]; i += 2) { 654 if (!strcmp(attr[i], "driver")) driver = attr[i+1]; 655 else if (!strcmp(attr[i], "screen")) screen = attr[i+1]; 656 else if (!strcmp(attr[i], "kernel_driver")) kernel = attr[i+1]; 657 else if (!strcmp(attr[i], "device")) device = attr[i+1]; 658 else XML_WARNING("unknown device attribute: %s.", attr[i]); 659 } 660 if (driver && strcmp(driver, data->driverName)) 661 data->ignoringDevice = data->inDevice; 662 else if (kernel && (!data->kernelDriverName || 663 strcmp(kernel, data->kernelDriverName))) 664 data->ignoringDevice = data->inDevice; 665 else if (device && (!data->deviceName || 666 strcmp(device, data->deviceName))) 667 data->ignoringDevice = data->inDevice; 668 else if (screen) { 669 driOptionValue screenNum; 670 if (!parseValue(&screenNum, DRI_INT, screen)) 671 XML_WARNING("illegal screen number: %s.", screen); 672 else if (screenNum._int != data->screenNum) 673 data->ignoringDevice = data->inDevice; 674 } 675} 676 677/** \brief Parse attributes of an application element. */ 678static void 679parseAppAttr(struct OptConfData *data, const char **attr) 680{ 681 uint32_t i; 682 const char *exec = NULL; 683 const char *sha1 = NULL; 684 const char *exec_regexp = NULL; 685 const char *application_name_match = NULL; 686 const char *application_versions = NULL; 687 driOptionInfo version_range = { 688 .type = DRI_INT, 689 }; 690 691 for (i = 0; attr[i]; i += 2) { 692 if (!strcmp(attr[i], "name")) /* not needed here */; 693 else if (!strcmp(attr[i], "executable")) exec = attr[i+1]; 694 else if (!strcmp(attr[i], "executable_regexp")) exec_regexp = attr[i+1]; 695 else if (!strcmp(attr[i], "sha1")) sha1 = attr[i+1]; 696 else if (!strcmp(attr[i], "application_name_match")) 697 application_name_match = attr[i+1]; 698 else if (!strcmp(attr[i], "application_versions")) 699 application_versions = attr[i+1]; 700 else XML_WARNING("unknown application attribute: %s.", attr[i]); 701 } 702 if (exec && strcmp(exec, data->execName)) { 703 data->ignoringApp = data->inApp; 704 } else if (exec_regexp) { 705 regex_t re; 706 707 if (regcomp(&re, exec_regexp, REG_EXTENDED|REG_NOSUB) == 0) { 708 if (regexec(&re, data->execName, 0, NULL, 0) == REG_NOMATCH) 709 data->ignoringApp = data->inApp; 710 regfree(&re); 711 } else 712 XML_WARNING("Invalid executable_regexp=\"%s\".", exec_regexp); 713 } else if (sha1) { 714 /* SHA1_DIGEST_STRING_LENGTH includes terminating null byte */ 715 if (strlen(sha1) != (SHA1_DIGEST_STRING_LENGTH - 1)) { 716 XML_WARNING("Incorrect sha1 application attribute"); 717 data->ignoringApp = data->inApp; 718 } else { 719 size_t len; 720 char* content; 721 char path[PATH_MAX]; 722 if (util_get_process_exec_path(path, ARRAY_SIZE(path)) > 0 && 723 (content = os_read_file(path, &len))) { 724 uint8_t sha1x[SHA1_DIGEST_LENGTH]; 725 char sha1s[SHA1_DIGEST_STRING_LENGTH]; 726 _mesa_sha1_compute(content, len, sha1x); 727 _mesa_sha1_format((char*) sha1s, sha1x); 728 free(content); 729 730 if (strcmp(sha1, sha1s)) { 731 data->ignoringApp = data->inApp; 732 } 733 } else { 734 data->ignoringApp = data->inApp; 735 } 736 } 737 } else if (application_name_match) { 738 regex_t re; 739 740 if (regcomp(&re, application_name_match, REG_EXTENDED|REG_NOSUB) == 0) { 741 if (regexec(&re, data->applicationName, 0, NULL, 0) == REG_NOMATCH) 742 data->ignoringApp = data->inApp; 743 regfree(&re); 744 } else 745 XML_WARNING("Invalid application_name_match=\"%s\".", application_name_match); 746 } 747 if (application_versions) { 748 driOptionValue v = { ._int = data->applicationVersion }; 749 if (parseRange(&version_range, application_versions)) { 750 if (!checkValue(&v, &version_range)) 751 data->ignoringApp = data->inApp; 752 } else { 753 XML_WARNING("Failed to parse application_versions range=\"%s\".", 754 application_versions); 755 } 756 } 757} 758 759/** \brief Parse attributes of an application element. */ 760static void 761parseEngineAttr(struct OptConfData *data, const char **attr) 762{ 763 uint32_t i; 764 const char *engine_name_match = NULL, *engine_versions = NULL; 765 driOptionInfo version_range = { 766 .type = DRI_INT, 767 }; 768 for (i = 0; attr[i]; i += 2) { 769 if (!strcmp(attr[i], "name")) /* not needed here */; 770 else if (!strcmp(attr[i], "engine_name_match")) engine_name_match = attr[i+1]; 771 else if (!strcmp(attr[i], "engine_versions")) engine_versions = attr[i+1]; 772 else XML_WARNING("unknown application attribute: %s.", attr[i]); 773 } 774 if (engine_name_match) { 775 regex_t re; 776 777 if (regcomp(&re, engine_name_match, REG_EXTENDED|REG_NOSUB) == 0) { 778 if (regexec(&re, data->engineName, 0, NULL, 0) == REG_NOMATCH) 779 data->ignoringApp = data->inApp; 780 regfree(&re); 781 } else 782 XML_WARNING("Invalid engine_name_match=\"%s\".", engine_name_match); 783 } 784 if (engine_versions) { 785 driOptionValue v = { ._int = data->engineVersion }; 786 if (parseRange(&version_range, engine_versions)) { 787 if (!checkValue(&v, &version_range)) 788 data->ignoringApp = data->inApp; 789 } else { 790 XML_WARNING("Failed to parse engine_versions range=\"%s\".", 791 engine_versions); 792 } 793 } 794} 795 796/** \brief Parse attributes of an option element. */ 797static void 798parseOptConfAttr(struct OptConfData *data, const char **attr) 799{ 800 uint32_t i; 801 const char *name = NULL, *value = NULL; 802 for (i = 0; attr[i]; i += 2) { 803 if (!strcmp(attr[i], "name")) name = attr[i+1]; 804 else if (!strcmp(attr[i], "value")) value = attr[i+1]; 805 else XML_WARNING("unknown option attribute: %s.", attr[i]); 806 } 807 if (!name) XML_WARNING1("name attribute missing in option."); 808 if (!value) XML_WARNING1("value attribute missing in option."); 809 if (name && value) { 810 driOptionCache *cache = data->cache; 811 uint32_t opt = findOption(cache, name); 812 if (cache->info[opt].name == NULL) 813 /* don't use XML_WARNING, drirc defines options for all drivers, 814 * but not all drivers support them */ 815 return; 816 else if (getenv(cache->info[opt].name)) { 817 /* don't use XML_WARNING, we want the user to see this! */ 818 if (be_verbose()) { 819 fprintf(stderr, 820 "ATTENTION: option value of option %s ignored.\n", 821 cache->info[opt].name); 822 } 823 } else if (!parseValue(&cache->values[opt], cache->info[opt].type, value)) 824 XML_WARNING("illegal option value: %s.", value); 825 } 826} 827 828#if WITH_XMLCONFIG 829 830/** \brief Elements in configuration files. */ 831enum OptConfElem { 832 OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_ENGINE, OC_OPTION, OC_COUNT 833}; 834static const char *OptConfElems[] = { 835 [OC_APPLICATION] = "application", 836 [OC_DEVICE] = "device", 837 [OC_DRICONF] = "driconf", 838 [OC_ENGINE] = "engine", 839 [OC_OPTION] = "option", 840}; 841 842static int compare(const void *a, const void *b) { 843 return strcmp(*(char *const*)a, *(char *const*)b); 844} 845/** \brief Binary search in a string array. */ 846static uint32_t 847bsearchStr(const char *name, const char *elems[], uint32_t count) 848{ 849 const char **found; 850 found = bsearch(&name, elems, count, sizeof(char *), compare); 851 if (found) 852 return found - elems; 853 else 854 return count; 855} 856 857/** \brief Handler for start element events. */ 858static void 859optConfStartElem(void *userData, const char *name, 860 const char **attr) 861{ 862 struct OptConfData *data = (struct OptConfData *)userData; 863 enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT); 864 switch (elem) { 865 case OC_DRICONF: 866 if (data->inDriConf) 867 XML_WARNING1("nested <driconf> elements."); 868 if (attr[0]) 869 XML_WARNING1("attributes specified on <driconf> element."); 870 data->inDriConf++; 871 break; 872 case OC_DEVICE: 873 if (!data->inDriConf) 874 XML_WARNING1("<device> should be inside <driconf>."); 875 if (data->inDevice) 876 XML_WARNING1("nested <device> elements."); 877 data->inDevice++; 878 if (!data->ignoringDevice && !data->ignoringApp) 879 parseDeviceAttr(data, attr); 880 break; 881 case OC_APPLICATION: 882 if (!data->inDevice) 883 XML_WARNING1("<application> should be inside <device>."); 884 if (data->inApp) 885 XML_WARNING1("nested <application> or <engine> elements."); 886 data->inApp++; 887 if (!data->ignoringDevice && !data->ignoringApp) 888 parseAppAttr(data, attr); 889 break; 890 case OC_ENGINE: 891 if (!data->inDevice) 892 XML_WARNING1("<engine> should be inside <device>."); 893 if (data->inApp) 894 XML_WARNING1("nested <application> or <engine> elements."); 895 data->inApp++; 896 if (!data->ignoringDevice && !data->ignoringApp) 897 parseEngineAttr(data, attr); 898 break; 899 case OC_OPTION: 900 if (!data->inApp) 901 XML_WARNING1("<option> should be inside <application>."); 902 if (data->inOption) 903 XML_WARNING1("nested <option> elements."); 904 data->inOption++; 905 if (!data->ignoringDevice && !data->ignoringApp) 906 parseOptConfAttr(data, attr); 907 break; 908 default: 909 XML_WARNING("unknown element: %s.", name); 910 } 911} 912 913/** \brief Handler for end element events. */ 914static void 915optConfEndElem(void *userData, const char *name) 916{ 917 struct OptConfData *data = (struct OptConfData *)userData; 918 enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT); 919 switch (elem) { 920 case OC_DRICONF: 921 data->inDriConf--; 922 break; 923 case OC_DEVICE: 924 if (data->inDevice-- == data->ignoringDevice) 925 data->ignoringDevice = 0; 926 break; 927 case OC_APPLICATION: 928 case OC_ENGINE: 929 if (data->inApp-- == data->ignoringApp) 930 data->ignoringApp = 0; 931 break; 932 case OC_OPTION: 933 data->inOption--; 934 break; 935 default: 936 /* unknown element, warning was produced on start tag */; 937 } 938} 939 940static void 941_parseOneConfigFile(XML_Parser p) 942{ 943#define BUF_SIZE 0x1000 944 struct OptConfData *data = (struct OptConfData *)XML_GetUserData(p); 945 int status; 946 int fd; 947 948 if ((fd = open(data->name, O_RDONLY)) == -1) { 949 __driUtilMessage("Can't open configuration file %s: %s.", 950 data->name, strerror(errno)); 951 return; 952 } 953 954 while (1) { 955 int bytesRead; 956 void *buffer = XML_GetBuffer(p, BUF_SIZE); 957 if (!buffer) { 958 __driUtilMessage("Can't allocate parser buffer."); 959 break; 960 } 961 bytesRead = read(fd, buffer, BUF_SIZE); 962 if (bytesRead == -1) { 963 __driUtilMessage("Error reading from configuration file %s: %s.", 964 data->name, strerror(errno)); 965 break; 966 } 967 status = XML_ParseBuffer(p, bytesRead, bytesRead == 0); 968 if (!status) { 969 XML_ERROR("%s.", XML_ErrorString(XML_GetErrorCode(p))); 970 break; 971 } 972 if (bytesRead == 0) 973 break; 974 } 975 976 close(fd); 977#undef BUF_SIZE 978} 979 980/** \brief Parse the named configuration file */ 981static void 982parseOneConfigFile(struct OptConfData *data, const char *filename) 983{ 984 XML_Parser p; 985 986 p = XML_ParserCreate(NULL); /* use encoding specified by file */ 987 XML_SetElementHandler(p, optConfStartElem, optConfEndElem); 988 XML_SetUserData(p, data); 989 data->parser = p; 990 data->name = filename; 991 data->ignoringDevice = 0; 992 data->ignoringApp = 0; 993 data->inDriConf = 0; 994 data->inDevice = 0; 995 data->inApp = 0; 996 data->inOption = 0; 997 998 _parseOneConfigFile(p); 999 XML_ParserFree(p); 1000} 1001 1002static int 1003scandir_filter(const struct dirent *ent) 1004{ 1005#ifndef DT_REG /* systems without d_type in dirent results */ 1006 struct stat st; 1007 1008 if ((lstat(ent->d_name, &st) != 0) || 1009 (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))) 1010 return 0; 1011#else 1012 /* Allow through unknown file types for filesystems that don't support d_type 1013 * The full filepath isn't available here to stat the file 1014 */ 1015 if (ent->d_type != DT_REG && ent->d_type != DT_LNK && ent->d_type != DT_UNKNOWN) 1016 return 0; 1017#endif 1018 1019 int len = strlen(ent->d_name); 1020 if (len <= 5 || strcmp(ent->d_name + len - 5, ".conf")) 1021 return 0; 1022 1023 return 1; 1024} 1025 1026/** \brief Parse configuration files in a directory */ 1027static void 1028parseConfigDir(struct OptConfData *data, const char *dirname) 1029{ 1030 int i, count; 1031 struct dirent **entries = NULL; 1032 1033 count = scandir(dirname, &entries, scandir_filter, alphasort); 1034 if (count < 0) 1035 return; 1036 1037 for (i = 0; i < count; i++) { 1038 char filename[PATH_MAX]; 1039#ifdef DT_REG 1040 unsigned char d_type = entries[i]->d_type; 1041#endif 1042 1043 snprintf(filename, PATH_MAX, "%s/%s", dirname, entries[i]->d_name); 1044 free(entries[i]); 1045 1046#ifdef DT_REG 1047 /* In the case of unknown d_type, ensure it is a regular file 1048 * This can be accomplished with stat on the full filepath 1049 */ 1050 if (d_type == DT_UNKNOWN) { 1051 struct stat st; 1052 if (stat(filename, &st) != 0 || 1053 !S_ISREG(st.st_mode)) { 1054 continue; 1055 } 1056 } 1057#endif 1058 1059 parseOneConfigFile(data, filename); 1060 } 1061 1062 free(entries); 1063} 1064#else 1065# include "driconf_static.h" 1066 1067static void 1068parseStaticOptions(struct OptConfData *data, const struct driconf_option *options, 1069 unsigned num_options) 1070{ 1071 if (data->ignoringDevice || data->ignoringApp) 1072 return; 1073 for (unsigned i = 0; i < num_options; i++) { 1074 const char *optattr[] = { 1075 "name", options[i].name, 1076 "value", options[i].value, 1077 NULL 1078 }; 1079 parseOptConfAttr(data, optattr); 1080 } 1081} 1082 1083static void 1084parseStaticConfig(struct OptConfData *data) 1085{ 1086 data->ignoringDevice = 0; 1087 data->ignoringApp = 0; 1088 data->inDriConf = 0; 1089 data->inDevice = 0; 1090 data->inApp = 0; 1091 data->inOption = 0; 1092 1093 for (unsigned i = 0; i < ARRAY_SIZE(driconf); i++) { 1094 const struct driconf_device *d = driconf[i]; 1095 const char *devattr[] = { 1096 "driver", d->driver, 1097 "device", d->device, 1098 NULL 1099 }; 1100 1101 data->ignoringDevice = 0; 1102 data->inDevice++; 1103 parseDeviceAttr(data, devattr); 1104 data->inDevice--; 1105 1106 data->inApp++; 1107 1108 for (unsigned j = 0; j < d->num_engines; j++) { 1109 const struct driconf_engine *e = &d->engines[j]; 1110 const char *engattr[] = { 1111 "engine_name_match", e->engine_name_match, 1112 "engine_versions", e->engine_versions, 1113 NULL 1114 }; 1115 1116 data->ignoringApp = 0; 1117 parseEngineAttr(data, engattr); 1118 parseStaticOptions(data, e->options, e->num_options); 1119 } 1120 1121 for (unsigned j = 0; j < d->num_applications; j++) { 1122 const struct driconf_application *a = &d->applications[j]; 1123 const char *appattr[] = { 1124 "name", a->name, 1125 "executable", a->executable, 1126 "executable_regexp", a->executable_regexp, 1127 "sha1", a->sha1, 1128 "application_name_match", a->application_name_match, 1129 "application_versions", a->application_versions, 1130 NULL 1131 }; 1132 1133 data->ignoringApp = 0; 1134 parseAppAttr(data, appattr); 1135 parseStaticOptions(data, a->options, a->num_options); 1136 } 1137 1138 data->inApp--; 1139 } 1140} 1141#endif /* WITH_XMLCONFIG */ 1142 1143/** \brief Initialize an option cache based on info */ 1144static void 1145initOptionCache(driOptionCache *cache, const driOptionCache *info) 1146{ 1147 unsigned i, size = 1 << info->tableSize; 1148 cache->info = info->info; 1149 cache->tableSize = info->tableSize; 1150 cache->values = malloc(((size_t)1 << info->tableSize) * sizeof(driOptionValue)); 1151 if (cache->values == NULL) { 1152 fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); 1153 abort(); 1154 } 1155 memcpy(cache->values, info->values, 1156 ((size_t)1 << info->tableSize) * sizeof(driOptionValue)); 1157 for (i = 0; i < size; ++i) { 1158 if (cache->info[i].type == DRI_STRING) 1159 XSTRDUP(cache->values[i]._string, info->values[i]._string); 1160 } 1161} 1162 1163#ifndef SYSCONFDIR 1164#define SYSCONFDIR "/etc" 1165#endif 1166 1167#ifndef DATADIR 1168#define DATADIR "/usr/share" 1169#endif 1170 1171static const char *datadir = DATADIR "/drirc.d"; 1172static const char *execname; 1173 1174void 1175driInjectDataDir(const char *dir) 1176{ 1177 datadir = dir; 1178} 1179 1180void 1181driInjectExecName(const char *exec) 1182{ 1183 execname = exec; 1184} 1185 1186void 1187driParseConfigFiles(driOptionCache *cache, const driOptionCache *info, 1188 int screenNum, const char *driverName, 1189 const char *kernelDriverName, 1190 const char *deviceName, 1191 const char *applicationName, uint32_t applicationVersion, 1192 const char *engineName, uint32_t engineVersion) 1193{ 1194 initOptionCache(cache, info); 1195 struct OptConfData userData = {0}; 1196 1197 userData.cache = cache; 1198 userData.screenNum = screenNum; 1199 userData.driverName = driverName; 1200 userData.kernelDriverName = kernelDriverName; 1201 userData.deviceName = deviceName; 1202 userData.applicationName = applicationName ? applicationName : ""; 1203 userData.applicationVersion = applicationVersion; 1204 userData.engineName = engineName ? engineName : ""; 1205 userData.engineVersion = engineVersion; 1206 userData.execName = execname ? execname : util_get_process_name(); 1207 1208#if WITH_XMLCONFIG 1209 char *home; 1210 1211 parseConfigDir(&userData, datadir); 1212 parseOneConfigFile(&userData, SYSCONFDIR "/drirc"); 1213 1214 if ((home = getenv("HOME"))) { 1215 char filename[PATH_MAX]; 1216 1217 snprintf(filename, PATH_MAX, "%s/.drirc", home); 1218 parseOneConfigFile(&userData, filename); 1219 } 1220#else 1221 parseStaticConfig(&userData); 1222#endif /* WITH_XMLCONFIG */ 1223} 1224 1225void 1226driDestroyOptionInfo(driOptionCache *info) 1227{ 1228 driDestroyOptionCache(info); 1229 if (info->info) { 1230 uint32_t i, size = 1 << info->tableSize; 1231 for (i = 0; i < size; ++i) { 1232 if (info->info[i].name) { 1233 free(info->info[i].name); 1234 } 1235 } 1236 free(info->info); 1237 } 1238} 1239 1240void 1241driDestroyOptionCache(driOptionCache *cache) 1242{ 1243 if (cache->info) { 1244 unsigned i, size = 1 << cache->tableSize; 1245 for (i = 0; i < size; ++i) { 1246 if (cache->info[i].type == DRI_STRING) 1247 free(cache->values[i]._string); 1248 } 1249 } 1250 free(cache->values); 1251} 1252 1253unsigned char 1254driCheckOption(const driOptionCache *cache, const char *name, 1255 driOptionType type) 1256{ 1257 uint32_t i = findOption(cache, name); 1258 return cache->info[i].name != NULL && cache->info[i].type == type; 1259} 1260 1261unsigned char 1262driQueryOptionb(const driOptionCache *cache, const char *name) 1263{ 1264 uint32_t i = findOption(cache, name); 1265 /* make sure the option is defined and has the correct type */ 1266 assert(cache->info[i].name != NULL); 1267 assert(cache->info[i].type == DRI_BOOL); 1268 return cache->values[i]._bool; 1269} 1270 1271int 1272driQueryOptioni(const driOptionCache *cache, const char *name) 1273{ 1274 uint32_t i = findOption(cache, name); 1275 /* make sure the option is defined and has the correct type */ 1276 assert(cache->info[i].name != NULL); 1277 assert(cache->info[i].type == DRI_INT || cache->info[i].type == DRI_ENUM); 1278 return cache->values[i]._int; 1279} 1280 1281float 1282driQueryOptionf(const driOptionCache *cache, const char *name) 1283{ 1284 uint32_t i = findOption(cache, name); 1285 /* make sure the option is defined and has the correct type */ 1286 assert(cache->info[i].name != NULL); 1287 assert(cache->info[i].type == DRI_FLOAT); 1288 return cache->values[i]._float; 1289} 1290 1291char * 1292driQueryOptionstr(const driOptionCache *cache, const char *name) 1293{ 1294 uint32_t i = findOption(cache, name); 1295 /* make sure the option is defined and has the correct type */ 1296 assert(cache->info[i].name != NULL); 1297 assert(cache->info[i].type == DRI_STRING); 1298 return cache->values[i]._string; 1299} 1300