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