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