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