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 == 0) 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 = 6; 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 *application_name_match = NULL; 685 const char *application_versions = NULL; 686 driOptionInfo version_range = { 687 .type = DRI_INT, 688 }; 689 690 for (i = 0; attr[i]; i += 2) { 691 if (!strcmp(attr[i], "name")) /* not needed here */; 692 else if (!strcmp(attr[i], "executable")) exec = attr[i+1]; 693 else if (!strcmp(attr[i], "sha1")) sha1 = attr[i+1]; 694 else if (!strcmp(attr[i], "application_name_match")) 695 application_name_match = attr[i+1]; 696 else if (!strcmp(attr[i], "application_versions")) 697 application_versions = attr[i+1]; 698 else XML_WARNING("unknown application attribute: %s.", attr[i]); 699 } 700 if (exec && strcmp(exec, data->execName)) { 701 data->ignoringApp = data->inApp; 702 } else if (sha1) { 703 /* SHA1_DIGEST_STRING_LENGTH includes terminating null byte */ 704 if (strlen(sha1) != (SHA1_DIGEST_STRING_LENGTH - 1)) { 705 XML_WARNING("Incorrect sha1 application attribute"); 706 data->ignoringApp = data->inApp; 707 } else { 708 size_t len; 709 char* content; 710 char path[PATH_MAX]; 711 if (util_get_process_exec_path(path, ARRAY_SIZE(path)) > 0 && 712 (content = os_read_file(path, &len))) { 713 uint8_t sha1x[SHA1_DIGEST_LENGTH]; 714 char sha1s[SHA1_DIGEST_STRING_LENGTH]; 715 _mesa_sha1_compute(content, len, sha1x); 716 _mesa_sha1_format((char*) sha1s, sha1x); 717 free(content); 718 719 if (strcmp(sha1, sha1s)) { 720 data->ignoringApp = data->inApp; 721 } 722 } else { 723 data->ignoringApp = data->inApp; 724 } 725 } 726 } else if (application_name_match) { 727 regex_t re; 728 729 if (regcomp(&re, application_name_match, REG_EXTENDED|REG_NOSUB) == 0) { 730 if (regexec(&re, data->applicationName, 0, NULL, 0) == REG_NOMATCH) 731 data->ignoringApp = data->inApp; 732 regfree(&re); 733 } else 734 XML_WARNING("Invalid application_name_match=\"%s\".", application_name_match); 735 } 736 if (application_versions) { 737 driOptionValue v = { ._int = data->applicationVersion }; 738 if (parseRange(&version_range, application_versions)) { 739 if (!checkValue(&v, &version_range)) 740 data->ignoringApp = data->inApp; 741 } else { 742 XML_WARNING("Failed to parse application_versions range=\"%s\".", 743 application_versions); 744 } 745 } 746} 747 748/** \brief Parse attributes of an application element. */ 749static void 750parseEngineAttr(struct OptConfData *data, const char **attr) 751{ 752 uint32_t i; 753 const char *engine_name_match = NULL, *engine_versions = NULL; 754 driOptionInfo version_range = { 755 .type = DRI_INT, 756 }; 757 for (i = 0; attr[i]; i += 2) { 758 if (!strcmp(attr[i], "name")) /* not needed here */; 759 else if (!strcmp(attr[i], "engine_name_match")) engine_name_match = attr[i+1]; 760 else if (!strcmp(attr[i], "engine_versions")) engine_versions = attr[i+1]; 761 else XML_WARNING("unknown application attribute: %s.", attr[i]); 762 } 763 if (engine_name_match) { 764 regex_t re; 765 766 if (regcomp(&re, engine_name_match, REG_EXTENDED|REG_NOSUB) == 0) { 767 if (regexec(&re, data->engineName, 0, NULL, 0) == REG_NOMATCH) 768 data->ignoringApp = data->inApp; 769 regfree(&re); 770 } else 771 XML_WARNING("Invalid engine_name_match=\"%s\".", engine_name_match); 772 } 773 if (engine_versions) { 774 driOptionValue v = { ._int = data->engineVersion }; 775 if (parseRange(&version_range, engine_versions)) { 776 if (!checkValue(&v, &version_range)) 777 data->ignoringApp = data->inApp; 778 } else { 779 XML_WARNING("Failed to parse engine_versions range=\"%s\".", 780 engine_versions); 781 } 782 } 783} 784 785/** \brief Parse attributes of an option element. */ 786static void 787parseOptConfAttr(struct OptConfData *data, const char **attr) 788{ 789 uint32_t i; 790 const char *name = NULL, *value = NULL; 791 for (i = 0; attr[i]; i += 2) { 792 if (!strcmp(attr[i], "name")) name = attr[i+1]; 793 else if (!strcmp(attr[i], "value")) value = attr[i+1]; 794 else XML_WARNING("unknown option attribute: %s.", attr[i]); 795 } 796 if (!name) XML_WARNING1("name attribute missing in option."); 797 if (!value) XML_WARNING1("value attribute missing in option."); 798 if (name && value) { 799 driOptionCache *cache = data->cache; 800 uint32_t opt = findOption(cache, name); 801 if (cache->info[opt].name == NULL) 802 /* don't use XML_WARNING, drirc defines options for all drivers, 803 * but not all drivers support them */ 804 return; 805 else if (getenv(cache->info[opt].name)) { 806 /* don't use XML_WARNING, we want the user to see this! */ 807 if (be_verbose()) { 808 fprintf(stderr, 809 "ATTENTION: option value of option %s ignored.\n", 810 cache->info[opt].name); 811 } 812 } else if (!parseValue(&cache->values[opt], cache->info[opt].type, value)) 813 XML_WARNING("illegal option value: %s.", value); 814 } 815} 816 817#if WITH_XMLCONFIG 818 819/** \brief Elements in configuration files. */ 820enum OptConfElem { 821 OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_ENGINE, OC_OPTION, OC_COUNT 822}; 823static const char *OptConfElems[] = { 824 [OC_APPLICATION] = "application", 825 [OC_DEVICE] = "device", 826 [OC_DRICONF] = "driconf", 827 [OC_ENGINE] = "engine", 828 [OC_OPTION] = "option", 829}; 830 831static int compare(const void *a, const void *b) { 832 return strcmp(*(char *const*)a, *(char *const*)b); 833} 834/** \brief Binary search in a string array. */ 835static uint32_t 836bsearchStr(const char *name, const char *elems[], uint32_t count) 837{ 838 const char **found; 839 found = bsearch(&name, elems, count, sizeof(char *), compare); 840 if (found) 841 return found - elems; 842 else 843 return count; 844} 845 846/** \brief Handler for start element events. */ 847static void 848optConfStartElem(void *userData, const char *name, 849 const char **attr) 850{ 851 struct OptConfData *data = (struct OptConfData *)userData; 852 enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT); 853 switch (elem) { 854 case OC_DRICONF: 855 if (data->inDriConf) 856 XML_WARNING1("nested <driconf> elements."); 857 if (attr[0]) 858 XML_WARNING1("attributes specified on <driconf> element."); 859 data->inDriConf++; 860 break; 861 case OC_DEVICE: 862 if (!data->inDriConf) 863 XML_WARNING1("<device> should be inside <driconf>."); 864 if (data->inDevice) 865 XML_WARNING1("nested <device> elements."); 866 data->inDevice++; 867 if (!data->ignoringDevice && !data->ignoringApp) 868 parseDeviceAttr(data, attr); 869 break; 870 case OC_APPLICATION: 871 if (!data->inDevice) 872 XML_WARNING1("<application> should be inside <device>."); 873 if (data->inApp) 874 XML_WARNING1("nested <application> or <engine> elements."); 875 data->inApp++; 876 if (!data->ignoringDevice && !data->ignoringApp) 877 parseAppAttr(data, attr); 878 break; 879 case OC_ENGINE: 880 if (!data->inDevice) 881 XML_WARNING1("<engine> should be inside <device>."); 882 if (data->inApp) 883 XML_WARNING1("nested <application> or <engine> elements."); 884 data->inApp++; 885 if (!data->ignoringDevice && !data->ignoringApp) 886 parseEngineAttr(data, attr); 887 break; 888 case OC_OPTION: 889 if (!data->inApp) 890 XML_WARNING1("<option> should be inside <application>."); 891 if (data->inOption) 892 XML_WARNING1("nested <option> elements."); 893 data->inOption++; 894 if (!data->ignoringDevice && !data->ignoringApp) 895 parseOptConfAttr(data, attr); 896 break; 897 default: 898 XML_WARNING("unknown element: %s.", name); 899 } 900} 901 902/** \brief Handler for end element events. */ 903static void 904optConfEndElem(void *userData, const char *name) 905{ 906 struct OptConfData *data = (struct OptConfData *)userData; 907 enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT); 908 switch (elem) { 909 case OC_DRICONF: 910 data->inDriConf--; 911 break; 912 case OC_DEVICE: 913 if (data->inDevice-- == data->ignoringDevice) 914 data->ignoringDevice = 0; 915 break; 916 case OC_APPLICATION: 917 case OC_ENGINE: 918 if (data->inApp-- == data->ignoringApp) 919 data->ignoringApp = 0; 920 break; 921 case OC_OPTION: 922 data->inOption--; 923 break; 924 default: 925 /* unknown element, warning was produced on start tag */; 926 } 927} 928 929static void 930_parseOneConfigFile(XML_Parser p) 931{ 932#define BUF_SIZE 0x1000 933 struct OptConfData *data = (struct OptConfData *)XML_GetUserData(p); 934 int status; 935 int fd; 936 937 if ((fd = open(data->name, O_RDONLY)) == -1) { 938 __driUtilMessage("Can't open configuration file %s: %s.", 939 data->name, strerror(errno)); 940 return; 941 } 942 943 while (1) { 944 int bytesRead; 945 void *buffer = XML_GetBuffer(p, BUF_SIZE); 946 if (!buffer) { 947 __driUtilMessage("Can't allocate parser buffer."); 948 break; 949 } 950 bytesRead = read(fd, buffer, BUF_SIZE); 951 if (bytesRead == -1) { 952 __driUtilMessage("Error reading from configuration file %s: %s.", 953 data->name, strerror(errno)); 954 break; 955 } 956 status = XML_ParseBuffer(p, bytesRead, bytesRead == 0); 957 if (!status) { 958 XML_ERROR("%s.", XML_ErrorString(XML_GetErrorCode(p))); 959 break; 960 } 961 if (bytesRead == 0) 962 break; 963 } 964 965 close(fd); 966#undef BUF_SIZE 967} 968 969/** \brief Parse the named configuration file */ 970static void 971parseOneConfigFile(struct OptConfData *data, const char *filename) 972{ 973 XML_Parser p; 974 975 p = XML_ParserCreate(NULL); /* use encoding specified by file */ 976 XML_SetElementHandler(p, optConfStartElem, optConfEndElem); 977 XML_SetUserData(p, data); 978 data->parser = p; 979 data->name = filename; 980 data->ignoringDevice = 0; 981 data->ignoringApp = 0; 982 data->inDriConf = 0; 983 data->inDevice = 0; 984 data->inApp = 0; 985 data->inOption = 0; 986 987 _parseOneConfigFile(p); 988 XML_ParserFree(p); 989} 990 991static int 992scandir_filter(const struct dirent *ent) 993{ 994#ifndef DT_REG /* systems without d_type in dirent results */ 995 struct stat st; 996 997 if ((lstat(ent->d_name, &st) != 0) || 998 (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))) 999 return 0; 1000#else 1001 if (ent->d_type != DT_REG && ent->d_type != DT_LNK) 1002 return 0; 1003#endif 1004 1005 int len = strlen(ent->d_name); 1006 if (len <= 5 || strcmp(ent->d_name + len - 5, ".conf")) 1007 return 0; 1008 1009 return 1; 1010} 1011 1012/** \brief Parse configuration files in a directory */ 1013static void 1014parseConfigDir(struct OptConfData *data, const char *dirname) 1015{ 1016 int i, count; 1017 struct dirent **entries = NULL; 1018 1019 count = scandir(dirname, &entries, scandir_filter, alphasort); 1020 if (count < 0) 1021 return; 1022 1023 for (i = 0; i < count; i++) { 1024 char filename[PATH_MAX]; 1025 1026 snprintf(filename, PATH_MAX, "%s/%s", dirname, entries[i]->d_name); 1027 free(entries[i]); 1028 1029 parseOneConfigFile(data, filename); 1030 } 1031 1032 free(entries); 1033} 1034#else 1035# include "driconf_static.h" 1036 1037static void 1038parseStaticOptions(struct OptConfData *data, const struct driconf_option *options, 1039 unsigned num_options) 1040{ 1041 if (data->ignoringDevice || data->ignoringApp) 1042 return; 1043 for (unsigned i = 0; i < num_options; i++) { 1044 const char *optattr[] = { 1045 "name", options[i].name, 1046 "value", options[i].value, 1047 NULL 1048 }; 1049 parseOptConfAttr(data, optattr); 1050 } 1051} 1052 1053static void 1054parseStaticConfig(struct OptConfData *data) 1055{ 1056 data->ignoringDevice = 0; 1057 data->ignoringApp = 0; 1058 data->inDriConf = 0; 1059 data->inDevice = 0; 1060 data->inApp = 0; 1061 data->inOption = 0; 1062 1063 for (unsigned i = 0; i < ARRAY_SIZE(driconf); i++) { 1064 const struct driconf_device *d = driconf[i]; 1065 const char *devattr[] = { 1066 "driver", d->driver, 1067 "device", d->device, 1068 NULL 1069 }; 1070 1071 data->ignoringDevice = 0; 1072 data->inDevice++; 1073 parseDeviceAttr(data, devattr); 1074 data->inDevice--; 1075 1076 data->inApp++; 1077 1078 for (unsigned j = 0; j < d->num_engines; j++) { 1079 const struct driconf_engine *e = &d->engines[j]; 1080 const char *engattr[] = { 1081 "engine_name_match", e->engine_name_match, 1082 "engine_versions", e->engine_versions, 1083 NULL 1084 }; 1085 1086 data->ignoringApp = 0; 1087 parseEngineAttr(data, engattr); 1088 parseStaticOptions(data, e->options, e->num_options); 1089 } 1090 1091 for (unsigned j = 0; j < d->num_applications; j++) { 1092 const struct driconf_application *a = &d->applications[j]; 1093 const char *appattr[] = { 1094 "name", a->name, 1095 "executable", a->executable, 1096 "executable_regexp", a->executable_regexp, 1097 "sha1", a->sha1, 1098 "application_name_match", a->application_name_match, 1099 "application_versions", a->application_versions, 1100 NULL 1101 }; 1102 1103 data->ignoringApp = 0; 1104 parseAppAttr(data, appattr); 1105 parseStaticOptions(data, a->options, a->num_options); 1106 } 1107 1108 data->inApp--; 1109 } 1110} 1111#endif /* WITH_XMLCONFIG */ 1112 1113/** \brief Initialize an option cache based on info */ 1114static void 1115initOptionCache(driOptionCache *cache, const driOptionCache *info) 1116{ 1117 unsigned i, size = 1 << info->tableSize; 1118 cache->info = info->info; 1119 cache->tableSize = info->tableSize; 1120 cache->values = malloc(((size_t)1 << info->tableSize) * sizeof(driOptionValue)); 1121 if (cache->values == NULL) { 1122 fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); 1123 abort(); 1124 } 1125 memcpy(cache->values, info->values, 1126 ((size_t)1 << info->tableSize) * sizeof(driOptionValue)); 1127 for (i = 0; i < size; ++i) { 1128 if (cache->info[i].type == DRI_STRING) 1129 XSTRDUP(cache->values[i]._string, info->values[i]._string); 1130 } 1131} 1132 1133#ifndef SYSCONFDIR 1134#define SYSCONFDIR "/etc" 1135#endif 1136 1137#ifndef DATADIR 1138#define DATADIR "/usr/share" 1139#endif 1140 1141static const char *datadir = DATADIR "/drirc.d"; 1142static const char *execname; 1143 1144void 1145driInjectDataDir(const char *dir) 1146{ 1147 datadir = dir; 1148} 1149 1150void 1151driInjectExecName(const char *exec) 1152{ 1153 execname = exec; 1154} 1155 1156void 1157driParseConfigFiles(driOptionCache *cache, const driOptionCache *info, 1158 int screenNum, const char *driverName, 1159 const char *kernelDriverName, 1160 const char *deviceName, 1161 const char *applicationName, uint32_t applicationVersion, 1162 const char *engineName, uint32_t engineVersion) 1163{ 1164 initOptionCache(cache, info); 1165 struct OptConfData userData; 1166 1167 userData.cache = cache; 1168 userData.screenNum = screenNum; 1169 userData.driverName = driverName; 1170 userData.kernelDriverName = kernelDriverName; 1171 userData.deviceName = deviceName; 1172 userData.applicationName = applicationName ? applicationName : ""; 1173 userData.applicationVersion = applicationVersion; 1174 userData.engineName = engineName ? engineName : ""; 1175 userData.engineVersion = engineVersion; 1176 userData.execName = execname ? execname : util_get_process_name(); 1177 1178#if WITH_XMLCONFIG 1179 char *home; 1180 1181 parseConfigDir(&userData, datadir); 1182 parseOneConfigFile(&userData, SYSCONFDIR "/drirc"); 1183 1184 if ((home = getenv("HOME"))) { 1185 char filename[PATH_MAX]; 1186 1187 snprintf(filename, PATH_MAX, "%s/.drirc", home); 1188 parseOneConfigFile(&userData, filename); 1189 } 1190#else 1191 parseStaticConfig(&userData); 1192#endif /* WITH_XMLCONFIG */ 1193} 1194 1195void 1196driDestroyOptionInfo(driOptionCache *info) 1197{ 1198 driDestroyOptionCache(info); 1199 if (info->info) { 1200 uint32_t i, size = 1 << info->tableSize; 1201 for (i = 0; i < size; ++i) { 1202 if (info->info[i].name) { 1203 free(info->info[i].name); 1204 } 1205 } 1206 free(info->info); 1207 } 1208} 1209 1210void 1211driDestroyOptionCache(driOptionCache *cache) 1212{ 1213 if (cache->info) { 1214 unsigned i, size = 1 << cache->tableSize; 1215 for (i = 0; i < size; ++i) { 1216 if (cache->info[i].type == DRI_STRING) 1217 free(cache->values[i]._string); 1218 } 1219 } 1220 free(cache->values); 1221} 1222 1223unsigned char 1224driCheckOption(const driOptionCache *cache, const char *name, 1225 driOptionType type) 1226{ 1227 uint32_t i = findOption(cache, name); 1228 return cache->info[i].name != NULL && cache->info[i].type == type; 1229} 1230 1231unsigned char 1232driQueryOptionb(const driOptionCache *cache, const char *name) 1233{ 1234 uint32_t i = findOption(cache, name); 1235 /* make sure the option is defined and has the correct type */ 1236 assert(cache->info[i].name != NULL); 1237 assert(cache->info[i].type == DRI_BOOL); 1238 return cache->values[i]._bool; 1239} 1240 1241int 1242driQueryOptioni(const driOptionCache *cache, const char *name) 1243{ 1244 uint32_t i = findOption(cache, name); 1245 /* make sure the option is defined and has the correct type */ 1246 assert(cache->info[i].name != NULL); 1247 assert(cache->info[i].type == DRI_INT || cache->info[i].type == DRI_ENUM); 1248 return cache->values[i]._int; 1249} 1250 1251float 1252driQueryOptionf(const driOptionCache *cache, const char *name) 1253{ 1254 uint32_t i = findOption(cache, name); 1255 /* make sure the option is defined and has the correct type */ 1256 assert(cache->info[i].name != NULL); 1257 assert(cache->info[i].type == DRI_FLOAT); 1258 return cache->values[i]._float; 1259} 1260 1261char * 1262driQueryOptionstr(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_STRING); 1268 return cache->values[i]._string; 1269} 1270