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