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
63 /* For systems like Hurd */
64 #ifndef PATH_MAX
65 #define PATH_MAX 4096
66 #endif
67
68 static bool
be_verbose(void)69 be_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. */
87 static int
strToI(const char * string,const char ** tail,int base)88 strToI(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. */
149 static float
strToF(const char * string,const char ** tail)150 strToF(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. */
213 static unsigned char
parseValue(driOptionValue * v,driOptionType type,const char * string)214 parseValue(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 */
258 static uint32_t
findOption(const driOptionCache * cache,const char * name)259 findOption(const driOptionCache *cache, const char *name)
260 {
261 uint32_t len = strlen(name);
262 uint32_t size = 1 << cache->tableSize, mask = size - 1;
263 uint32_t hash = 0;
264 uint32_t i, shift;
265
266 /* compute a hash from the variable length name */
267 for (i = 0, shift = 0; i < len; ++i, shift = (shift+8) & 31)
268 hash += (uint32_t)name[i] << shift;
269 hash *= hash;
270 hash = (hash >> (16-cache->tableSize/2)) & mask;
271
272 /* this is just the starting point of the linear search for the option */
273 for (i = 0; i < size; ++i, hash = (hash+1) & mask) {
274 /* if we hit an empty entry then the option is not defined (yet) */
275 if (cache->info[hash].name == NULL)
276 break;
277 else if (!strcmp(name, cache->info[hash].name))
278 break;
279 }
280 /* this assertion fails if the hash table is full */
281 assert (i < size);
282
283 return hash;
284 }
285
286 /** \brief Like strdup with error checking. */
287 #define XSTRDUP(dest,source) do { \
288 if (!(dest = strdup(source))) { \
289 fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); \
290 abort(); \
291 } \
292 } while (0)
293
294 /** \brief Check if a value is in info->range. */
295 UNUSED static bool
checkValue(const driOptionValue * v,const driOptionInfo * info)296 checkValue(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
315 void
driParseOptionInfo(driOptionCache * info,const driOptionDescription * configOptions,unsigned numOptions)316 driParseOptionInfo(driOptionCache *info,
317 const driOptionDescription *configOptions,
318 unsigned numOptions)
319 {
320 /* Make the hash table big enough to fit more than the maximum number of
321 * config options we've ever seen in a driver.
322 */
323 info->tableSize = 7;
324 info->info = calloc((size_t)1 << info->tableSize, sizeof(driOptionInfo));
325 info->values = calloc((size_t)1 << info->tableSize, sizeof(driOptionValue));
326 if (info->info == NULL || info->values == NULL) {
327 fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
328 abort();
329 }
330
331 UNUSED bool in_section = false;
332 for (int o = 0; o < numOptions; o++) {
333 const driOptionDescription *opt = &configOptions[o];
334
335 if (opt->info.type == DRI_SECTION) {
336 in_section = true;
337 continue;
338 }
339
340 /* for driconf xml generation, options must always be preceded by a
341 * DRI_CONF_SECTION
342 */
343 assert(in_section);
344
345 const char *name = opt->info.name;
346 int i = findOption(info, name);
347 driOptionInfo *optinfo = &info->info[i];
348 driOptionValue *optval = &info->values[i];
349
350 assert(!optinfo->name); /* No duplicate options in your list. */
351
352 optinfo->type = opt->info.type;
353 optinfo->range = opt->info.range;
354 XSTRDUP(optinfo->name, name);
355
356 switch (opt->info.type) {
357 case DRI_BOOL:
358 optval->_bool = opt->value._bool;
359 break;
360
361 case DRI_INT:
362 case DRI_ENUM:
363 optval->_int = opt->value._int;
364 break;
365
366 case DRI_FLOAT:
367 optval->_float = opt->value._float;
368 break;
369
370 case DRI_STRING:
371 XSTRDUP(optval->_string, opt->value._string);
372 break;
373
374 case DRI_SECTION:
375 unreachable("handled above");
376 }
377
378 /* Built-in default values should always be valid. */
379 assert(checkValue(optval, optinfo));
380
381 char *envVal = getenv(name);
382 if (envVal != NULL) {
383 driOptionValue v;
384
385 /* make sure the value is initialized to something sensible */
386 v._string = NULL;
387
388 if (parseValue(&v, opt->info.type, envVal) &&
389 checkValue(&v, optinfo)) {
390 /* don't use XML_WARNING, we want the user to see this! */
391 if (be_verbose()) {
392 fprintf(stderr,
393 "ATTENTION: default value of option %s overridden by environment.\n",
394 name);
395 }
396 *optval = v;
397 } else {
398 fprintf(stderr, "illegal environment value for %s: \"%s\". Ignoring.\n",
399 name, envVal);
400 }
401 }
402 }
403 }
404
405 char *
driGetOptionsXml(const driOptionDescription * configOptions,unsigned numOptions)406 driGetOptionsXml(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 */
541 static void
__driUtilMessage(const char * f,...)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. */
589 struct 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. */
612 static unsigned char
parseRange(driOptionInfo * info,const char * string)613 parseRange(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. */
648 static void
parseDeviceAttr(struct OptConfData * data,const char ** attr)649 parseDeviceAttr(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. */
678 static void
parseAppAttr(struct OptConfData * data,const char ** attr)679 parseAppAttr(struct OptConfData *data, const char **attr)
680 {
681 uint32_t i;
682 const char *exec = NULL;
683 const char *sha1 = NULL;
684 const char *exec_regexp = NULL;
685 const char *application_name_match = NULL;
686 const char *application_versions = NULL;
687 driOptionInfo version_range = {
688 .type = DRI_INT,
689 };
690
691 for (i = 0; attr[i]; i += 2) {
692 if (!strcmp(attr[i], "name")) /* not needed here */;
693 else if (!strcmp(attr[i], "executable")) exec = attr[i+1];
694 else if (!strcmp(attr[i], "executable_regexp")) exec_regexp = attr[i+1];
695 else if (!strcmp(attr[i], "sha1")) sha1 = attr[i+1];
696 else if (!strcmp(attr[i], "application_name_match"))
697 application_name_match = attr[i+1];
698 else if (!strcmp(attr[i], "application_versions"))
699 application_versions = attr[i+1];
700 else XML_WARNING("unknown application attribute: %s.", attr[i]);
701 }
702 if (exec && strcmp(exec, data->execName)) {
703 data->ignoringApp = data->inApp;
704 } else if (exec_regexp) {
705 regex_t re;
706
707 if (regcomp(&re, exec_regexp, REG_EXTENDED|REG_NOSUB) == 0) {
708 if (regexec(&re, data->execName, 0, NULL, 0) == REG_NOMATCH)
709 data->ignoringApp = data->inApp;
710 regfree(&re);
711 } else
712 XML_WARNING("Invalid executable_regexp=\"%s\".", exec_regexp);
713 } else if (sha1) {
714 /* SHA1_DIGEST_STRING_LENGTH includes terminating null byte */
715 if (strlen(sha1) != (SHA1_DIGEST_STRING_LENGTH - 1)) {
716 XML_WARNING("Incorrect sha1 application attribute");
717 data->ignoringApp = data->inApp;
718 } else {
719 size_t len;
720 char* content;
721 char path[PATH_MAX];
722 if (util_get_process_exec_path(path, ARRAY_SIZE(path)) > 0 &&
723 (content = os_read_file(path, &len))) {
724 uint8_t sha1x[SHA1_DIGEST_LENGTH];
725 char sha1s[SHA1_DIGEST_STRING_LENGTH];
726 _mesa_sha1_compute(content, len, sha1x);
727 _mesa_sha1_format((char*) sha1s, sha1x);
728 free(content);
729
730 if (strcmp(sha1, sha1s)) {
731 data->ignoringApp = data->inApp;
732 }
733 } else {
734 data->ignoringApp = data->inApp;
735 }
736 }
737 } else if (application_name_match) {
738 regex_t re;
739
740 if (regcomp(&re, application_name_match, REG_EXTENDED|REG_NOSUB) == 0) {
741 if (regexec(&re, data->applicationName, 0, NULL, 0) == REG_NOMATCH)
742 data->ignoringApp = data->inApp;
743 regfree(&re);
744 } else
745 XML_WARNING("Invalid application_name_match=\"%s\".", application_name_match);
746 }
747 if (application_versions) {
748 driOptionValue v = { ._int = data->applicationVersion };
749 if (parseRange(&version_range, application_versions)) {
750 if (!checkValue(&v, &version_range))
751 data->ignoringApp = data->inApp;
752 } else {
753 XML_WARNING("Failed to parse application_versions range=\"%s\".",
754 application_versions);
755 }
756 }
757 }
758
759 /** \brief Parse attributes of an application element. */
760 static void
parseEngineAttr(struct OptConfData * data,const char ** attr)761 parseEngineAttr(struct OptConfData *data, const char **attr)
762 {
763 uint32_t i;
764 const char *engine_name_match = NULL, *engine_versions = NULL;
765 driOptionInfo version_range = {
766 .type = DRI_INT,
767 };
768 for (i = 0; attr[i]; i += 2) {
769 if (!strcmp(attr[i], "name")) /* not needed here */;
770 else if (!strcmp(attr[i], "engine_name_match")) engine_name_match = attr[i+1];
771 else if (!strcmp(attr[i], "engine_versions")) engine_versions = attr[i+1];
772 else XML_WARNING("unknown application attribute: %s.", attr[i]);
773 }
774 if (engine_name_match) {
775 regex_t re;
776
777 if (regcomp(&re, engine_name_match, REG_EXTENDED|REG_NOSUB) == 0) {
778 if (regexec(&re, data->engineName, 0, NULL, 0) == REG_NOMATCH)
779 data->ignoringApp = data->inApp;
780 regfree(&re);
781 } else
782 XML_WARNING("Invalid engine_name_match=\"%s\".", engine_name_match);
783 }
784 if (engine_versions) {
785 driOptionValue v = { ._int = data->engineVersion };
786 if (parseRange(&version_range, engine_versions)) {
787 if (!checkValue(&v, &version_range))
788 data->ignoringApp = data->inApp;
789 } else {
790 XML_WARNING("Failed to parse engine_versions range=\"%s\".",
791 engine_versions);
792 }
793 }
794 }
795
796 /** \brief Parse attributes of an option element. */
797 static void
parseOptConfAttr(struct OptConfData * data,const char ** attr)798 parseOptConfAttr(struct OptConfData *data, const char **attr)
799 {
800 uint32_t i;
801 const char *name = NULL, *value = NULL;
802 for (i = 0; attr[i]; i += 2) {
803 if (!strcmp(attr[i], "name")) name = attr[i+1];
804 else if (!strcmp(attr[i], "value")) value = attr[i+1];
805 else XML_WARNING("unknown option attribute: %s.", attr[i]);
806 }
807 if (!name) XML_WARNING1("name attribute missing in option.");
808 if (!value) XML_WARNING1("value attribute missing in option.");
809 if (name && value) {
810 driOptionCache *cache = data->cache;
811 uint32_t opt = findOption(cache, name);
812 if (cache->info[opt].name == NULL)
813 /* don't use XML_WARNING, drirc defines options for all drivers,
814 * but not all drivers support them */
815 return;
816 else if (getenv(cache->info[opt].name)) {
817 /* don't use XML_WARNING, we want the user to see this! */
818 if (be_verbose()) {
819 fprintf(stderr,
820 "ATTENTION: option value of option %s ignored.\n",
821 cache->info[opt].name);
822 }
823 } else if (!parseValue(&cache->values[opt], cache->info[opt].type, value))
824 XML_WARNING("illegal option value: %s.", value);
825 }
826 }
827
828 #if WITH_XMLCONFIG
829
830 /** \brief Elements in configuration files. */
831 enum OptConfElem {
832 OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_ENGINE, OC_OPTION, OC_COUNT
833 };
834 static const char *OptConfElems[] = {
835 [OC_APPLICATION] = "application",
836 [OC_DEVICE] = "device",
837 [OC_DRICONF] = "driconf",
838 [OC_ENGINE] = "engine",
839 [OC_OPTION] = "option",
840 };
841
compare(const void * a,const void * b)842 static int compare(const void *a, const void *b) {
843 return strcmp(*(char *const*)a, *(char *const*)b);
844 }
845 /** \brief Binary search in a string array. */
846 static uint32_t
bsearchStr(const char * name,const char * elems[],uint32_t count)847 bsearchStr(const char *name, const char *elems[], uint32_t count)
848 {
849 const char **found;
850 found = bsearch(&name, elems, count, sizeof(char *), compare);
851 if (found)
852 return found - elems;
853 else
854 return count;
855 }
856
857 /** \brief Handler for start element events. */
858 static void
optConfStartElem(void * userData,const char * name,const char ** attr)859 optConfStartElem(void *userData, const char *name,
860 const char **attr)
861 {
862 struct OptConfData *data = (struct OptConfData *)userData;
863 enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT);
864 switch (elem) {
865 case OC_DRICONF:
866 if (data->inDriConf)
867 XML_WARNING1("nested <driconf> elements.");
868 if (attr[0])
869 XML_WARNING1("attributes specified on <driconf> element.");
870 data->inDriConf++;
871 break;
872 case OC_DEVICE:
873 if (!data->inDriConf)
874 XML_WARNING1("<device> should be inside <driconf>.");
875 if (data->inDevice)
876 XML_WARNING1("nested <device> elements.");
877 data->inDevice++;
878 if (!data->ignoringDevice && !data->ignoringApp)
879 parseDeviceAttr(data, attr);
880 break;
881 case OC_APPLICATION:
882 if (!data->inDevice)
883 XML_WARNING1("<application> should be inside <device>.");
884 if (data->inApp)
885 XML_WARNING1("nested <application> or <engine> elements.");
886 data->inApp++;
887 if (!data->ignoringDevice && !data->ignoringApp)
888 parseAppAttr(data, attr);
889 break;
890 case OC_ENGINE:
891 if (!data->inDevice)
892 XML_WARNING1("<engine> should be inside <device>.");
893 if (data->inApp)
894 XML_WARNING1("nested <application> or <engine> elements.");
895 data->inApp++;
896 if (!data->ignoringDevice && !data->ignoringApp)
897 parseEngineAttr(data, attr);
898 break;
899 case OC_OPTION:
900 if (!data->inApp)
901 XML_WARNING1("<option> should be inside <application>.");
902 if (data->inOption)
903 XML_WARNING1("nested <option> elements.");
904 data->inOption++;
905 if (!data->ignoringDevice && !data->ignoringApp)
906 parseOptConfAttr(data, attr);
907 break;
908 default:
909 XML_WARNING("unknown element: %s.", name);
910 }
911 }
912
913 /** \brief Handler for end element events. */
914 static void
optConfEndElem(void * userData,const char * name)915 optConfEndElem(void *userData, const char *name)
916 {
917 struct OptConfData *data = (struct OptConfData *)userData;
918 enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT);
919 switch (elem) {
920 case OC_DRICONF:
921 data->inDriConf--;
922 break;
923 case OC_DEVICE:
924 if (data->inDevice-- == data->ignoringDevice)
925 data->ignoringDevice = 0;
926 break;
927 case OC_APPLICATION:
928 case OC_ENGINE:
929 if (data->inApp-- == data->ignoringApp)
930 data->ignoringApp = 0;
931 break;
932 case OC_OPTION:
933 data->inOption--;
934 break;
935 default:
936 /* unknown element, warning was produced on start tag */;
937 }
938 }
939
940 static void
_parseOneConfigFile(XML_Parser p)941 _parseOneConfigFile(XML_Parser p)
942 {
943 #define BUF_SIZE 0x1000
944 struct OptConfData *data = (struct OptConfData *)XML_GetUserData(p);
945 int status;
946 int fd;
947
948 if ((fd = open(data->name, O_RDONLY)) == -1) {
949 __driUtilMessage("Can't open configuration file %s: %s.",
950 data->name, strerror(errno));
951 return;
952 }
953
954 while (1) {
955 int bytesRead;
956 void *buffer = XML_GetBuffer(p, BUF_SIZE);
957 if (!buffer) {
958 __driUtilMessage("Can't allocate parser buffer.");
959 break;
960 }
961 bytesRead = read(fd, buffer, BUF_SIZE);
962 if (bytesRead == -1) {
963 __driUtilMessage("Error reading from configuration file %s: %s.",
964 data->name, strerror(errno));
965 break;
966 }
967 status = XML_ParseBuffer(p, bytesRead, bytesRead == 0);
968 if (!status) {
969 XML_ERROR("%s.", XML_ErrorString(XML_GetErrorCode(p)));
970 break;
971 }
972 if (bytesRead == 0)
973 break;
974 }
975
976 close(fd);
977 #undef BUF_SIZE
978 }
979
980 /** \brief Parse the named configuration file */
981 static void
parseOneConfigFile(struct OptConfData * data,const char * filename)982 parseOneConfigFile(struct OptConfData *data, const char *filename)
983 {
984 XML_Parser p;
985
986 p = XML_ParserCreate(NULL); /* use encoding specified by file */
987 XML_SetElementHandler(p, optConfStartElem, optConfEndElem);
988 XML_SetUserData(p, data);
989 data->parser = p;
990 data->name = filename;
991 data->ignoringDevice = 0;
992 data->ignoringApp = 0;
993 data->inDriConf = 0;
994 data->inDevice = 0;
995 data->inApp = 0;
996 data->inOption = 0;
997
998 _parseOneConfigFile(p);
999 XML_ParserFree(p);
1000 }
1001
1002 static int
scandir_filter(const struct dirent * ent)1003 scandir_filter(const struct dirent *ent)
1004 {
1005 #ifndef DT_REG /* systems without d_type in dirent results */
1006 struct stat st;
1007
1008 if ((lstat(ent->d_name, &st) != 0) ||
1009 (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)))
1010 return 0;
1011 #else
1012 /* Allow through unknown file types for filesystems that don't support d_type
1013 * The full filepath isn't available here to stat the file
1014 */
1015 if (ent->d_type != DT_REG && ent->d_type != DT_LNK && ent->d_type != DT_UNKNOWN)
1016 return 0;
1017 #endif
1018
1019 int len = strlen(ent->d_name);
1020 if (len <= 5 || strcmp(ent->d_name + len - 5, ".conf"))
1021 return 0;
1022
1023 return 1;
1024 }
1025
1026 /** \brief Parse configuration files in a directory */
1027 static void
parseConfigDir(struct OptConfData * data,const char * dirname)1028 parseConfigDir(struct OptConfData *data, const char *dirname)
1029 {
1030 int i, count;
1031 struct dirent **entries = NULL;
1032
1033 count = scandir(dirname, &entries, scandir_filter, alphasort);
1034 if (count < 0)
1035 return;
1036
1037 for (i = 0; i < count; i++) {
1038 char filename[PATH_MAX];
1039 #ifdef DT_REG
1040 unsigned char d_type = entries[i]->d_type;
1041 #endif
1042
1043 snprintf(filename, PATH_MAX, "%s/%s", dirname, entries[i]->d_name);
1044 free(entries[i]);
1045
1046 #ifdef DT_REG
1047 /* In the case of unknown d_type, ensure it is a regular file
1048 * This can be accomplished with stat on the full filepath
1049 */
1050 if (d_type == DT_UNKNOWN) {
1051 struct stat st;
1052 if (stat(filename, &st) != 0 ||
1053 !S_ISREG(st.st_mode)) {
1054 continue;
1055 }
1056 }
1057 #endif
1058
1059 parseOneConfigFile(data, filename);
1060 }
1061
1062 free(entries);
1063 }
1064 #else
1065 # include "driconf_static.h"
1066
1067 static void
parseStaticOptions(struct OptConfData * data,const struct driconf_option * options,unsigned num_options)1068 parseStaticOptions(struct OptConfData *data, const struct driconf_option *options,
1069 unsigned num_options)
1070 {
1071 if (data->ignoringDevice || data->ignoringApp)
1072 return;
1073 for (unsigned i = 0; i < num_options; i++) {
1074 const char *optattr[] = {
1075 "name", options[i].name,
1076 "value", options[i].value,
1077 NULL
1078 };
1079 parseOptConfAttr(data, optattr);
1080 }
1081 }
1082
1083 static void
parseStaticConfig(struct OptConfData * data)1084 parseStaticConfig(struct OptConfData *data)
1085 {
1086 data->ignoringDevice = 0;
1087 data->ignoringApp = 0;
1088 data->inDriConf = 0;
1089 data->inDevice = 0;
1090 data->inApp = 0;
1091 data->inOption = 0;
1092
1093 for (unsigned i = 0; i < ARRAY_SIZE(driconf); i++) {
1094 const struct driconf_device *d = driconf[i];
1095 const char *devattr[] = {
1096 "driver", d->driver,
1097 "device", d->device,
1098 NULL
1099 };
1100
1101 data->ignoringDevice = 0;
1102 data->inDevice++;
1103 parseDeviceAttr(data, devattr);
1104 data->inDevice--;
1105
1106 data->inApp++;
1107
1108 for (unsigned j = 0; j < d->num_engines; j++) {
1109 const struct driconf_engine *e = &d->engines[j];
1110 const char *engattr[] = {
1111 "engine_name_match", e->engine_name_match,
1112 "engine_versions", e->engine_versions,
1113 NULL
1114 };
1115
1116 data->ignoringApp = 0;
1117 parseEngineAttr(data, engattr);
1118 parseStaticOptions(data, e->options, e->num_options);
1119 }
1120
1121 for (unsigned j = 0; j < d->num_applications; j++) {
1122 const struct driconf_application *a = &d->applications[j];
1123 const char *appattr[] = {
1124 "name", a->name,
1125 "executable", a->executable,
1126 "executable_regexp", a->executable_regexp,
1127 "sha1", a->sha1,
1128 "application_name_match", a->application_name_match,
1129 "application_versions", a->application_versions,
1130 NULL
1131 };
1132
1133 data->ignoringApp = 0;
1134 parseAppAttr(data, appattr);
1135 parseStaticOptions(data, a->options, a->num_options);
1136 }
1137
1138 data->inApp--;
1139 }
1140 }
1141 #endif /* WITH_XMLCONFIG */
1142
1143 /** \brief Initialize an option cache based on info */
1144 static void
initOptionCache(driOptionCache * cache,const driOptionCache * info)1145 initOptionCache(driOptionCache *cache, const driOptionCache *info)
1146 {
1147 unsigned i, size = 1 << info->tableSize;
1148 cache->info = info->info;
1149 cache->tableSize = info->tableSize;
1150 cache->values = malloc(((size_t)1 << info->tableSize) * sizeof(driOptionValue));
1151 if (cache->values == NULL) {
1152 fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
1153 abort();
1154 }
1155 memcpy(cache->values, info->values,
1156 ((size_t)1 << info->tableSize) * sizeof(driOptionValue));
1157 for (i = 0; i < size; ++i) {
1158 if (cache->info[i].type == DRI_STRING)
1159 XSTRDUP(cache->values[i]._string, info->values[i]._string);
1160 }
1161 }
1162
1163 #ifndef SYSCONFDIR
1164 #define SYSCONFDIR "/etc"
1165 #endif
1166
1167 #ifndef DATADIR
1168 #define DATADIR "/usr/share"
1169 #endif
1170
1171 static const char *datadir = DATADIR "/drirc.d";
1172 static const char *execname;
1173
1174 void
driInjectDataDir(const char * dir)1175 driInjectDataDir(const char *dir)
1176 {
1177 datadir = dir;
1178 }
1179
1180 void
driInjectExecName(const char * exec)1181 driInjectExecName(const char *exec)
1182 {
1183 execname = exec;
1184 }
1185
1186 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)1187 driParseConfigFiles(driOptionCache *cache, const driOptionCache *info,
1188 int screenNum, const char *driverName,
1189 const char *kernelDriverName,
1190 const char *deviceName,
1191 const char *applicationName, uint32_t applicationVersion,
1192 const char *engineName, uint32_t engineVersion)
1193 {
1194 initOptionCache(cache, info);
1195 struct OptConfData userData = {0};
1196
1197 userData.cache = cache;
1198 userData.screenNum = screenNum;
1199 userData.driverName = driverName;
1200 userData.kernelDriverName = kernelDriverName;
1201 userData.deviceName = deviceName;
1202 userData.applicationName = applicationName ? applicationName : "";
1203 userData.applicationVersion = applicationVersion;
1204 userData.engineName = engineName ? engineName : "";
1205 userData.engineVersion = engineVersion;
1206 userData.execName = execname ? execname : util_get_process_name();
1207
1208 #if WITH_XMLCONFIG
1209 char *home;
1210
1211 parseConfigDir(&userData, datadir);
1212 parseOneConfigFile(&userData, SYSCONFDIR "/drirc");
1213
1214 if ((home = getenv("HOME"))) {
1215 char filename[PATH_MAX];
1216
1217 snprintf(filename, PATH_MAX, "%s/.drirc", home);
1218 parseOneConfigFile(&userData, filename);
1219 }
1220 #else
1221 parseStaticConfig(&userData);
1222 #endif /* WITH_XMLCONFIG */
1223 }
1224
1225 void
driDestroyOptionInfo(driOptionCache * info)1226 driDestroyOptionInfo(driOptionCache *info)
1227 {
1228 driDestroyOptionCache(info);
1229 if (info->info) {
1230 uint32_t i, size = 1 << info->tableSize;
1231 for (i = 0; i < size; ++i) {
1232 if (info->info[i].name) {
1233 free(info->info[i].name);
1234 }
1235 }
1236 free(info->info);
1237 }
1238 }
1239
1240 void
driDestroyOptionCache(driOptionCache * cache)1241 driDestroyOptionCache(driOptionCache *cache)
1242 {
1243 if (cache->info) {
1244 unsigned i, size = 1 << cache->tableSize;
1245 for (i = 0; i < size; ++i) {
1246 if (cache->info[i].type == DRI_STRING)
1247 free(cache->values[i]._string);
1248 }
1249 }
1250 free(cache->values);
1251 }
1252
1253 unsigned char
driCheckOption(const driOptionCache * cache,const char * name,driOptionType type)1254 driCheckOption(const driOptionCache *cache, const char *name,
1255 driOptionType type)
1256 {
1257 uint32_t i = findOption(cache, name);
1258 return cache->info[i].name != NULL && cache->info[i].type == type;
1259 }
1260
1261 unsigned char
driQueryOptionb(const driOptionCache * cache,const char * name)1262 driQueryOptionb(const driOptionCache *cache, const char *name)
1263 {
1264 uint32_t i = findOption(cache, name);
1265 /* make sure the option is defined and has the correct type */
1266 assert(cache->info[i].name != NULL);
1267 assert(cache->info[i].type == DRI_BOOL);
1268 return cache->values[i]._bool;
1269 }
1270
1271 int
driQueryOptioni(const driOptionCache * cache,const char * name)1272 driQueryOptioni(const driOptionCache *cache, const char *name)
1273 {
1274 uint32_t i = findOption(cache, name);
1275 /* make sure the option is defined and has the correct type */
1276 assert(cache->info[i].name != NULL);
1277 assert(cache->info[i].type == DRI_INT || cache->info[i].type == DRI_ENUM);
1278 return cache->values[i]._int;
1279 }
1280
1281 float
driQueryOptionf(const driOptionCache * cache,const char * name)1282 driQueryOptionf(const driOptionCache *cache, const char *name)
1283 {
1284 uint32_t i = findOption(cache, name);
1285 /* make sure the option is defined and has the correct type */
1286 assert(cache->info[i].name != NULL);
1287 assert(cache->info[i].type == DRI_FLOAT);
1288 return cache->values[i]._float;
1289 }
1290
1291 char *
driQueryOptionstr(const driOptionCache * cache,const char * name)1292 driQueryOptionstr(const driOptionCache *cache, const char *name)
1293 {
1294 uint32_t i = findOption(cache, name);
1295 /* make sure the option is defined and has the correct type */
1296 assert(cache->info[i].name != NULL);
1297 assert(cache->info[i].type == DRI_STRING);
1298 return cache->values[i]._string;
1299 }
1300