• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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