1/*
2 * Copyright 1995-1998 by Metro Link, Inc.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Metro Link, Inc. not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  Metro Link, Inc. makes no
11 * representations about the suitability of this software for any purpose.
12 *  It is provided "as is" without express or implied warranty.
13 *
14 * METRO LINK, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL METRO LINK, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22/*
23 * Copyright (c) 1997-2002 by The XFree86 Project, Inc.
24 *
25 * Permission is hereby granted, free of charge, to any person obtaining a
26 * copy of this software and associated documentation files (the "Software"),
27 * to deal in the Software without restriction, including without limitation
28 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
29 * and/or sell copies of the Software, and to permit persons to whom the
30 * Software is furnished to do so, subject to the following conditions:
31 *
32 * The above copyright notice and this permission notice shall be included in
33 * all copies or substantial portions of the Software.
34 *
35 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
38 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
39 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
40 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
41 * OTHER DEALINGS IN THE SOFTWARE.
42 *
43 * Except as contained in this notice, the name of the copyright holder(s)
44 * and author(s) shall not be used in advertising or otherwise to promote
45 * the sale, use or other dealings in this Software without prior written
46 * authorization from the copyright holder(s) and author(s).
47 */
48
49#ifdef HAVE_XORG_CONFIG_H
50#include <xorg-config.h>
51#endif
52
53#include "dix.h"
54#include "os.h"
55#include "loaderProcs.h"
56#include "xf86Module.h"
57#include "loader.h"
58
59#include <sys/stat.h>
60#include <sys/types.h>
61#include <regex.h>
62#include <dirent.h>
63#include <limits.h>
64
65typedef struct _pattern {
66    const char *pattern;
67    regex_t rex;
68} PatternRec, *PatternPtr;
69
70/* Prototypes for static functions */
71static char *FindModule(const char *, const char *, PatternPtr);
72static Bool CheckVersion(const char *, XF86ModuleVersionInfo *,
73                         const XF86ModReqInfo *);
74static char *LoaderGetCanonicalName(const char *, PatternPtr);
75static void RemoveChild(ModuleDescPtr);
76
77const ModuleVersions LoaderVersionInfo = {
78    XORG_VERSION_CURRENT,
79    ABI_ANSIC_VERSION,
80    ABI_VIDEODRV_VERSION,
81    ABI_XINPUT_VERSION,
82    ABI_EXTENSION_VERSION,
83};
84
85static int ModuleDuplicated[] = { };
86
87static void
88FreeStringList(char **paths)
89{
90    char **p;
91
92    if (!paths)
93        return;
94
95    for (p = paths; *p; p++)
96        free(*p);
97
98    free(paths);
99}
100
101static char **defaultPathList = NULL;
102
103static Bool
104PathIsAbsolute(const char *path)
105{
106    return *path == '/';
107}
108
109/*
110 * Convert a comma-separated path into a NULL-terminated array of path
111 * elements, rejecting any that are not full absolute paths, and appending
112 * a '/' when it isn't already present.
113 */
114static char **
115InitPathList(const char *path)
116{
117    char *fullpath = NULL;
118    char *elem = NULL;
119    char **list = NULL, **save = NULL;
120    int len;
121    int addslash;
122    int n = 0;
123
124    fullpath = strdup(path);
125    if (!fullpath)
126        return NULL;
127    elem = strtok(fullpath, ",");
128    while (elem) {
129        if (PathIsAbsolute(elem)) {
130            len = strlen(elem);
131            addslash = (elem[len - 1] != '/');
132            if (addslash)
133                len++;
134            save = list;
135            list = reallocarray(list, n + 2, sizeof(char *));
136            if (!list) {
137                if (save) {
138                    save[n] = NULL;
139                    FreeStringList(save);
140                }
141                free(fullpath);
142                return NULL;
143            }
144            list[n] = malloc(len + 1);
145            if (!list[n]) {
146                FreeStringList(list);
147                free(fullpath);
148                return NULL;
149            }
150            strcpy(list[n], elem);
151            if (addslash) {
152                list[n][len - 1] = '/';
153                list[n][len] = '\0';
154            }
155            n++;
156        }
157        elem = strtok(NULL, ",");
158    }
159    if (list)
160        list[n] = NULL;
161    free(fullpath);
162    return list;
163}
164
165void
166LoaderSetPath(const char *path)
167{
168    if (!path)
169        return;
170
171    FreeStringList(defaultPathList);
172    defaultPathList = InitPathList(path);
173}
174
175/* Standard set of module subdirectories to search, in order of preference */
176static const char *stdSubdirs[] = {
177    "",
178    "input/",
179    "drivers/",
180    "extensions/",
181    NULL
182};
183
184/*
185 * Standard set of module name patterns to check, in order of preference
186 * These are regular expressions (suitable for use with POSIX regex(3)).
187 *
188 * This list assumes that you're an ELFish platform and therefore your
189 * shared libraries are named something.so.  If we're ever nuts enough
190 * to port this DDX to, say, Darwin, we'll need to fix this.
191 */
192static PatternRec stdPatterns[] = {
193#ifdef __CYGWIN__
194    {"^cyg(.*)\\.dll$",},
195    {"(.*)_drv\\.dll$",},
196    {"(.*)\\.dll$",},
197#else
198    {"^lib(.*)\\.so$",},
199    {"(.*)_drv\\.so$",},
200    {"(.*)\\.so$",},
201#endif
202    {NULL,}
203};
204
205static PatternPtr
206InitPatterns(const char **patternlist)
207{
208    char errmsg[80];
209    int i, e;
210    PatternPtr patterns = NULL;
211    PatternPtr p = NULL;
212    static int firstTime = 1;
213    const char **s;
214
215    if (firstTime) {
216        /* precompile stdPatterns */
217        firstTime = 0;
218        for (p = stdPatterns; p->pattern; p++)
219            if ((e = regcomp(&p->rex, p->pattern, REG_EXTENDED)) != 0) {
220                regerror(e, &p->rex, errmsg, sizeof(errmsg));
221                FatalError("InitPatterns: regcomp error for `%s': %s\n",
222                           p->pattern, errmsg);
223            }
224    }
225
226    if (patternlist) {
227        for (i = 0, s = patternlist; *s; i++, s++)
228            if (*s == DEFAULT_LIST)
229                i += ARRAY_SIZE(stdPatterns) - 1 - 1;
230        patterns = xallocarray(i + 1, sizeof(PatternRec));
231        if (!patterns) {
232            return NULL;
233        }
234        for (i = 0, s = patternlist; *s; i++, s++)
235            if (*s != DEFAULT_LIST) {
236                p = patterns + i;
237                p->pattern = *s;
238                if ((e = regcomp(&p->rex, p->pattern, REG_EXTENDED)) != 0) {
239                    regerror(e, &p->rex, errmsg, sizeof(errmsg));
240                    ErrorF("InitPatterns: regcomp error for `%s': %s\n",
241                           p->pattern, errmsg);
242                    i--;
243                }
244            }
245            else {
246                for (p = stdPatterns; p->pattern; p++, i++)
247                    patterns[i] = *p;
248                if (p != stdPatterns)
249                    i--;
250            }
251        patterns[i].pattern = NULL;
252    }
253    else
254        patterns = stdPatterns;
255    return patterns;
256}
257
258static void
259FreePatterns(PatternPtr patterns)
260{
261    if (patterns && patterns != stdPatterns)
262        free(patterns);
263}
264
265static char *
266FindModuleInSubdir(const char *dirpath, const char *module)
267{
268    struct dirent *direntry = NULL;
269    DIR *dir = NULL;
270    char *ret = NULL, tmpBuf[PATH_MAX];
271    struct stat stat_buf;
272
273    dir = opendir(dirpath);
274    if (!dir)
275        return NULL;
276
277    while ((direntry = readdir(dir))) {
278        if (direntry->d_name[0] == '.')
279            continue;
280        snprintf(tmpBuf, PATH_MAX, "%s%s/", dirpath, direntry->d_name);
281        /* the stat with the appended / fails for normal files,
282           and works for sub dirs fine, looks a bit strange in strace
283           but does seem to work */
284        if ((stat(tmpBuf, &stat_buf) == 0) && S_ISDIR(stat_buf.st_mode)) {
285            if ((ret = FindModuleInSubdir(tmpBuf, module)))
286                break;
287            continue;
288        }
289
290#ifdef __CYGWIN__
291        snprintf(tmpBuf, PATH_MAX, "cyg%s.dll", module);
292#else
293        snprintf(tmpBuf, PATH_MAX, "lib%s.so", module);
294#endif
295        if (strcmp(direntry->d_name, tmpBuf) == 0) {
296            if (asprintf(&ret, "%s%s", dirpath, tmpBuf) == -1)
297                ret = NULL;
298            break;
299        }
300
301#ifdef __CYGWIN__
302        snprintf(tmpBuf, PATH_MAX, "%s_drv.dll", module);
303#else
304        snprintf(tmpBuf, PATH_MAX, "%s_drv.so", module);
305#endif
306        if (strcmp(direntry->d_name, tmpBuf) == 0) {
307            if (asprintf(&ret, "%s%s", dirpath, tmpBuf) == -1)
308                ret = NULL;
309            break;
310        }
311
312#ifdef __CYGWIN__
313        snprintf(tmpBuf, PATH_MAX, "%s.dll", module);
314#else
315        snprintf(tmpBuf, PATH_MAX, "%s.so", module);
316#endif
317        if (strcmp(direntry->d_name, tmpBuf) == 0) {
318            if (asprintf(&ret, "%s%s", dirpath, tmpBuf) == -1)
319                ret = NULL;
320            break;
321        }
322    }
323
324    closedir(dir);
325    return ret;
326}
327
328static char *
329FindModule(const char *module, const char *dirname, PatternPtr patterns)
330{
331    char buf[PATH_MAX + 1];
332    char *name = NULL;
333    const char **s;
334
335    if (strlen(dirname) > PATH_MAX)
336        return NULL;
337
338    for (s = stdSubdirs; *s; s++) {
339        snprintf(buf, PATH_MAX, "%s%s", dirname, *s);
340        if ((name = FindModuleInSubdir(buf, module)))
341            break;
342    }
343
344    return name;
345}
346
347const char **
348LoaderListDir(const char *subdir, const char **patternlist)
349{
350    char buf[PATH_MAX + 1];
351    char **pathlist;
352    char **elem;
353    PatternPtr patterns = NULL;
354    PatternPtr p;
355    DIR *d;
356    struct dirent *dp;
357    regmatch_t match[2];
358    struct stat stat_buf;
359    int len, dirlen;
360    char *fp;
361    char **listing = NULL;
362    char **save;
363    char **ret = NULL;
364    int n = 0;
365
366    if (!(pathlist = defaultPathList))
367        return NULL;
368    if (!(patterns = InitPatterns(patternlist)))
369        goto bail;
370
371    for (elem = pathlist; *elem; elem++) {
372        dirlen = snprintf(buf, PATH_MAX, "%s/%s", *elem, subdir);
373        fp = buf + dirlen;
374        if (stat(buf, &stat_buf) == 0 && S_ISDIR(stat_buf.st_mode) &&
375            (d = opendir(buf))) {
376            if (buf[dirlen - 1] != '/') {
377                buf[dirlen++] = '/';
378                fp++;
379            }
380            while ((dp = readdir(d))) {
381                if (dirlen + strlen(dp->d_name) > PATH_MAX)
382                    continue;
383                strcpy(fp, dp->d_name);
384                if (!(stat(buf, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode)))
385                    continue;
386                for (p = patterns; p->pattern; p++) {
387                    if (regexec(&p->rex, dp->d_name, 2, match, 0) == 0 &&
388                        match[1].rm_so != -1) {
389                        len = match[1].rm_eo - match[1].rm_so;
390                        save = listing;
391                        listing = reallocarray(listing, n + 2, sizeof(char *));
392                        if (!listing) {
393                            if (save) {
394                                save[n] = NULL;
395                                FreeStringList(save);
396                            }
397                            closedir(d);
398                            goto bail;
399                        }
400                        listing[n] = malloc(len + 1);
401                        if (!listing[n]) {
402                            FreeStringList(listing);
403                            closedir(d);
404                            goto bail;
405                        }
406                        strncpy(listing[n], dp->d_name + match[1].rm_so, len);
407                        listing[n][len] = '\0';
408                        n++;
409                        break;
410                    }
411                }
412            }
413            closedir(d);
414        }
415    }
416    if (listing)
417        listing[n] = NULL;
418    ret = listing;
419
420 bail:
421    FreePatterns(patterns);
422    return (const char **) ret;
423}
424
425static Bool
426CheckVersion(const char *module, XF86ModuleVersionInfo * data,
427             const XF86ModReqInfo * req)
428{
429    int vercode[4];
430    long ver = data->xf86version;
431    MessageType errtype;
432
433    LogMessage(X_INFO, "Module %s: vendor=\"%s\"\n",
434               data->modname ? data->modname : "UNKNOWN!",
435               data->vendor ? data->vendor : "UNKNOWN!");
436
437    vercode[0] = ver / 10000000;
438    vercode[1] = (ver / 100000) % 100;
439    vercode[2] = (ver / 1000) % 100;
440    vercode[3] = ver % 1000;
441    LogWrite(1, "\tcompiled for %d.%d.%d", vercode[0], vercode[1], vercode[2]);
442    if (vercode[3] != 0)
443        LogWrite(1, ".%d", vercode[3]);
444    LogWrite(1, ", module version = %d.%d.%d\n", data->majorversion,
445             data->minorversion, data->patchlevel);
446
447    if (data->moduleclass)
448        LogWrite(2, "\tModule class: %s\n", data->moduleclass);
449
450    ver = -1;
451    if (data->abiclass) {
452        int abimaj, abimin;
453        int vermaj, vermin;
454
455        if (!strcmp(data->abiclass, ABI_CLASS_ANSIC))
456            ver = LoaderVersionInfo.ansicVersion;
457        else if (!strcmp(data->abiclass, ABI_CLASS_VIDEODRV))
458            ver = LoaderVersionInfo.videodrvVersion;
459        else if (!strcmp(data->abiclass, ABI_CLASS_XINPUT))
460            ver = LoaderVersionInfo.xinputVersion;
461        else if (!strcmp(data->abiclass, ABI_CLASS_EXTENSION))
462            ver = LoaderVersionInfo.extensionVersion;
463
464        abimaj = GET_ABI_MAJOR(data->abiversion);
465        abimin = GET_ABI_MINOR(data->abiversion);
466        LogWrite(2, "\tABI class: %s, version %d.%d\n",
467                 data->abiclass, abimaj, abimin);
468        if (ver != -1) {
469            vermaj = GET_ABI_MAJOR(ver);
470            vermin = GET_ABI_MINOR(ver);
471            if (abimaj != vermaj) {
472                if (LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL)
473                    errtype = X_WARNING;
474                else
475                    errtype = X_ERROR;
476                LogMessageVerb(errtype, 0, "%s: module ABI major version (%d) "
477                               "doesn't match the server's version (%d)\n",
478                               module, abimaj, vermaj);
479                if (!(LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL))
480                    return FALSE;
481            }
482            else if (abimin > vermin) {
483                if (LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL)
484                    errtype = X_WARNING;
485                else
486                    errtype = X_ERROR;
487                LogMessageVerb(errtype, 0, "%s: module ABI minor version (%d) "
488                               "is newer than the server's version (%d)\n",
489                               module, abimin, vermin);
490                if (!(LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL))
491                    return FALSE;
492            }
493        }
494    }
495
496    /* Check against requirements that the caller has specified */
497    if (req) {
498        if (data->majorversion != req->majorversion) {
499            LogMessageVerb(X_WARNING, 2, "%s: module major version (%d) "
500                           "doesn't match required major version (%d)\n",
501                           module, data->majorversion, req->majorversion);
502            return FALSE;
503        }
504        else if (data->minorversion < req->minorversion) {
505            LogMessageVerb(X_WARNING, 2, "%s: module minor version (%d) is "
506                          "less than the required minor version (%d)\n",
507                          module, data->minorversion, req->minorversion);
508            return FALSE;
509        }
510        else if (data->minorversion == req->minorversion &&
511                 data->patchlevel < req->patchlevel) {
512            LogMessageVerb(X_WARNING, 2, "%s: module patch level (%d) "
513                           "is less than the required patch level "
514                           "(%d)\n", module, data->patchlevel, req->patchlevel);
515            return FALSE;
516        }
517        if (req->moduleclass) {
518            if (!data->moduleclass ||
519                strcmp(req->moduleclass, data->moduleclass)) {
520                LogMessageVerb(X_WARNING, 2, "%s: Module class (%s) doesn't "
521                               "match the required class (%s)\n", module,
522                               data->moduleclass ? data->moduleclass : "<NONE>",
523                               req->moduleclass);
524                return FALSE;
525            }
526        }
527        else if (req->abiclass != ABI_CLASS_NONE) {
528            if (!data->abiclass || strcmp(req->abiclass, data->abiclass)) {
529                LogMessageVerb(X_WARNING, 2, "%s: ABI class (%s) doesn't match"
530                               " the required ABI class (%s)\n", module,
531                               data->abiclass ? data->abiclass : "<NONE>",
532                               req->abiclass);
533                return FALSE;
534            }
535        }
536        if (req->abiclass != ABI_CLASS_NONE) {
537            int reqmaj, reqmin, maj, min;
538
539            reqmaj = GET_ABI_MAJOR(req->abiversion);
540            reqmin = GET_ABI_MINOR(req->abiversion);
541            maj = GET_ABI_MAJOR(data->abiversion);
542            min = GET_ABI_MINOR(data->abiversion);
543            if (maj != reqmaj) {
544                LogMessageVerb(X_WARNING, 2, "%s: ABI major version (%d) "
545                               "doesn't match the required ABI major version "
546                               "(%d)\n", module, maj, reqmaj);
547                return FALSE;
548            }
549            /* XXX Maybe this should be the other way around? */
550            if (min > reqmin) {
551                LogMessageVerb(X_WARNING, 2, "%s: module ABI minor version "
552                               "(%d) is newer than that available (%d)\n",
553                               module, min, reqmin);
554                return FALSE;
555            }
556        }
557    }
558    return TRUE;
559}
560
561static ModuleDescPtr
562AddSibling(ModuleDescPtr head, ModuleDescPtr new)
563{
564    new->sib = head;
565    return new;
566}
567
568void *
569LoadSubModule(void *_parent, const char *module,
570              const char **subdirlist, const char **patternlist,
571              void *options, const XF86ModReqInfo * modreq,
572              int *errmaj, int *errmin)
573{
574    ModuleDescPtr submod;
575    ModuleDescPtr parent = (ModuleDescPtr) _parent;
576
577    LogMessageVerb(X_INFO, 3, "Loading sub module \"%s\"\n", module);
578
579    if (PathIsAbsolute(module)) {
580        LogMessage(X_ERROR, "LoadSubModule: "
581                   "Absolute module path not permitted: \"%s\"\n", module);
582        if (errmaj)
583            *errmaj = LDR_BADUSAGE;
584        if (errmin)
585            *errmin = 0;
586        return NULL;
587    }
588
589    submod = LoadModule(module, options, modreq, errmaj);
590    if (submod && submod != (ModuleDescPtr) 1) {
591        parent->child = AddSibling(parent->child, submod);
592        submod->parent = parent;
593    }
594    return submod;
595}
596
597ModuleDescPtr
598DuplicateModule(ModuleDescPtr mod, ModuleDescPtr parent)
599{
600    ModuleDescPtr ret;
601
602    if (!mod)
603        return NULL;
604
605    ret = calloc(1, sizeof(ModuleDesc));
606    if (ret == NULL)
607        return NULL;
608
609    ret->handle = mod->handle;
610
611    ret->SetupProc = mod->SetupProc;
612    ret->TearDownProc = mod->TearDownProc;
613    ret->TearDownData = ModuleDuplicated;
614    ret->child = DuplicateModule(mod->child, ret);
615    ret->sib = DuplicateModule(mod->sib, parent);
616    ret->parent = parent;
617    ret->VersionInfo = mod->VersionInfo;
618
619    return ret;
620}
621
622static const char *compiled_in_modules[] = {
623    "ddc",
624    "fb",
625    "i2c",
626    "ramdac",
627    "dbe",
628    "record",
629    "extmod",
630    "dri",
631    "dri2",
632#ifdef DRI3
633    "dri3",
634#endif
635#ifdef PRESENT
636    "present",
637#endif
638    NULL
639};
640
641/*
642 * LoadModule: load a module
643 *
644 * module       The module name.  Normally this is not a filename but the
645 *              module's "canonical name.  A full pathname is, however,
646 *              also accepted.
647 * options      A NULL terminated list of Options that are passed to the
648 *              module's SetupProc function.
649 * modreq       An optional XF86ModReqInfo* containing
650 *              version/ABI/vendor-ABI requirements to check for when
651 *              loading the module.  The following fields of the
652 *              XF86ModReqInfo struct are checked:
653 *                majorversion - must match the module's majorversion exactly
654 *                minorversion - the module's minorversion must be >= this
655 *                patchlevel   - the module's minorversion.patchlevel must be
656 *                               >= this.  Patchlevel is ignored when
657 *                               minorversion is not set.
658 *                abiclass     - (string) must match the module's abiclass
659 *                abiversion   - must be consistent with the module's
660 *                               abiversion (major equal, minor no older)
661 *                moduleclass  - string must match the module's moduleclass
662 *                               string
663 *              "don't care" values are ~0 for numbers, and NULL for strings
664 * errmaj       Major error return.
665 *
666 */
667ModuleDescPtr
668LoadModule(const char *module, void *options, const XF86ModReqInfo *modreq,
669           int *errmaj)
670{
671    XF86ModuleData *initdata = NULL;
672    char **pathlist = NULL;
673    char *found = NULL;
674    char *name = NULL;
675    char **path_elem = NULL;
676    char *p = NULL;
677    ModuleDescPtr ret = NULL;
678    PatternPtr patterns = NULL;
679    int noncanonical = 0;
680    char *m = NULL;
681    const char **cim;
682
683    LogMessageVerb(X_INFO, 3, "LoadModule: \"%s\"", module);
684
685    patterns = InitPatterns(NULL);
686    name = LoaderGetCanonicalName(module, patterns);
687    noncanonical = (name && strcmp(module, name) != 0);
688    if (noncanonical) {
689        LogWrite(3, " (%s)\n", name);
690        LogMessageVerb(X_WARNING, 1,
691                       "LoadModule: given non-canonical module name \"%s\"\n",
692                       module);
693        m = name;
694    }
695    else {
696        LogWrite(3, "\n");
697        m = (char *) module;
698    }
699
700    /* Backward compatibility, vbe and int10 are merged into int10 now */
701    if (!strcmp(m, "vbe"))
702        m = name = strdup("int10");
703
704    for (cim = compiled_in_modules; *cim; cim++)
705        if (!strcmp(m, *cim)) {
706            LogMessageVerb(X_INFO, 3, "Module \"%s\" already built-in\n", m);
707            ret = (ModuleDescPtr) 1;
708            goto LoadModule_exit;
709        }
710
711    if (!name) {
712        if (errmaj)
713            *errmaj = LDR_BADUSAGE;
714        goto LoadModule_fail;
715    }
716    ret = calloc(1, sizeof(ModuleDesc));
717    if (!ret) {
718        if (errmaj)
719            *errmaj = LDR_NOMEM;
720        goto LoadModule_fail;
721    }
722
723    pathlist = defaultPathList;
724    if (!pathlist) {
725        /* This could be a malloc failure too */
726        if (errmaj)
727            *errmaj = LDR_BADUSAGE;
728        goto LoadModule_fail;
729    }
730
731    /*
732     * if the module name is not a full pathname, we need to
733     * check the elements in the path
734     */
735    if (PathIsAbsolute(module))
736        found = xstrdup(module);
737    path_elem = pathlist;
738    while (!found && *path_elem != NULL) {
739        found = FindModule(m, *path_elem, patterns);
740        path_elem++;
741        /*
742         * When the module name isn't the canonical name, search for the
743         * former if no match was found for the latter.
744         */
745        if (!*path_elem && m == name) {
746            path_elem = pathlist;
747            m = (char *) module;
748        }
749    }
750
751    /*
752     * did we find the module?
753     */
754    if (!found) {
755        LogMessage(X_WARNING, "Warning, couldn't open module %s\n", module);
756        if (errmaj)
757            *errmaj = LDR_NOENT;
758        goto LoadModule_fail;
759    }
760    ret->handle = LoaderOpen(found, errmaj);
761    if (ret->handle == NULL)
762        goto LoadModule_fail;
763
764    /* drop any explicit suffix from the module name */
765    p = strchr(name, '.');
766    if (p)
767        *p = '\0';
768
769    /*
770     * now check if the special data object <modulename>ModuleData is
771     * present.
772     */
773    if (asprintf(&p, "%sModuleData", name) == -1) {
774        p = NULL;
775        if (errmaj)
776            *errmaj = LDR_NOMEM;
777        goto LoadModule_fail;
778    }
779    initdata = LoaderSymbolFromModule(ret, p);
780    if (initdata) {
781        ModuleSetupProc setup;
782        ModuleTearDownProc teardown;
783        XF86ModuleVersionInfo *vers;
784
785        vers = initdata->vers;
786        setup = initdata->setup;
787        teardown = initdata->teardown;
788
789        if (vers) {
790            if (!CheckVersion(module, vers, modreq)) {
791                if (errmaj)
792                    *errmaj = LDR_MISMATCH;
793                goto LoadModule_fail;
794            }
795        }
796        else {
797            LogMessage(X_ERROR, "LoadModule: Module %s does not supply"
798                       " version information\n", module);
799            if (errmaj)
800                *errmaj = LDR_INVALID;
801            goto LoadModule_fail;
802        }
803        if (setup)
804            ret->SetupProc = setup;
805        if (teardown)
806            ret->TearDownProc = teardown;
807        ret->VersionInfo = vers;
808    }
809    else {
810        /* no initdata, fail the load */
811        LogMessage(X_ERROR, "LoadModule: Module %s does not have a %s "
812                   "data object.\n", module, p);
813        if (errmaj)
814            *errmaj = LDR_INVALID;
815        goto LoadModule_fail;
816    }
817    if (ret->SetupProc) {
818        ret->TearDownData = ret->SetupProc(ret, options, errmaj, NULL);
819        if (!ret->TearDownData) {
820            goto LoadModule_fail;
821        }
822    }
823    else if (options) {
824        LogMessage(X_WARNING, "Module Options present, but no SetupProc "
825                   "available for %s\n", module);
826    }
827    goto LoadModule_exit;
828
829 LoadModule_fail:
830    UnloadModule(ret);
831    ret = NULL;
832
833 LoadModule_exit:
834    FreePatterns(patterns);
835    free(found);
836    free(name);
837    free(p);
838
839    return ret;
840}
841
842void
843UnloadModule(void *_mod)
844{
845    ModuleDescPtr mod = _mod;
846
847    if (mod == (ModuleDescPtr) 1)
848        return;
849
850    if (mod == NULL)
851        return;
852
853    if (mod->VersionInfo) {
854        const char *name = mod->VersionInfo->modname;
855
856        if (mod->parent)
857            LogMessageVerbSigSafe(X_INFO, 3, "UnloadSubModule: \"%s\"\n", name);
858        else
859            LogMessageVerbSigSafe(X_INFO, 3, "UnloadModule: \"%s\"\n", name);
860
861        if (mod->TearDownData != ModuleDuplicated) {
862            if ((mod->TearDownProc) && (mod->TearDownData))
863                mod->TearDownProc(mod->TearDownData);
864            LoaderUnload(name, mod->handle);
865        }
866    }
867
868    if (mod->child)
869        UnloadModule(mod->child);
870    if (mod->sib)
871        UnloadModule(mod->sib);
872    free(mod);
873}
874
875void
876UnloadSubModule(void *_mod)
877{
878    ModuleDescPtr mod = (ModuleDescPtr) _mod;
879
880    /* Some drivers are calling us on built-in submodules, ignore them */
881    if (mod == (ModuleDescPtr) 1)
882        return;
883    RemoveChild(mod);
884    UnloadModule(mod);
885}
886
887static void
888RemoveChild(ModuleDescPtr child)
889{
890    ModuleDescPtr mdp;
891    ModuleDescPtr prevsib;
892    ModuleDescPtr parent;
893
894    if (!child->parent)
895        return;
896
897    parent = child->parent;
898    if (parent->child == child) {
899        parent->child = child->sib;
900        return;
901    }
902
903    prevsib = parent->child;
904    mdp = prevsib->sib;
905    while (mdp && mdp != child) {
906        prevsib = mdp;
907        mdp = mdp->sib;
908    }
909    if (mdp == child)
910        prevsib->sib = child->sib;
911    child->sib = NULL;
912    return;
913}
914
915void
916LoaderErrorMsg(const char *name, const char *modname, int errmaj, int errmin)
917{
918    const char *msg;
919    MessageType type = X_ERROR;
920
921    switch (errmaj) {
922    case LDR_NOERROR:
923        msg = "no error";
924        break;
925    case LDR_NOMEM:
926        msg = "out of memory";
927        break;
928    case LDR_NOENT:
929        msg = "module does not exist";
930        break;
931    case LDR_NOLOAD:
932        msg = "loader failed";
933        break;
934    case LDR_ONCEONLY:
935        msg = "already loaded";
936        type = X_INFO;
937        break;
938    case LDR_MISMATCH:
939        msg = "module requirement mismatch";
940        break;
941    case LDR_BADUSAGE:
942        msg = "invalid argument(s) to LoadModule()";
943        break;
944    case LDR_INVALID:
945        msg = "invalid module";
946        break;
947    case LDR_BADOS:
948        msg = "module doesn't support this OS";
949        break;
950    case LDR_MODSPECIFIC:
951        msg = "module-specific error";
952        break;
953    default:
954        msg = "unknown error";
955    }
956    if (name)
957        LogMessage(type, "%s: Failed to load module \"%s\" (%s, %d)\n",
958                   name, modname, msg, errmin);
959    else
960        LogMessage(type, "Failed to load module \"%s\" (%s, %d)\n",
961                   modname, msg, errmin);
962}
963
964/* Given a module path or file name, return the module's canonical name */
965static char *
966LoaderGetCanonicalName(const char *modname, PatternPtr patterns)
967{
968    char *str;
969    const char *s;
970    int len;
971    PatternPtr p;
972    regmatch_t match[2];
973
974    /* Strip off any leading path */
975    s = strrchr(modname, '/');
976    if (s == NULL)
977        s = modname;
978    else
979        s++;
980
981    /* Find the first regex that is matched */
982    for (p = patterns; p->pattern; p++)
983        if (regexec(&p->rex, s, 2, match, 0) == 0 && match[1].rm_so != -1) {
984            len = match[1].rm_eo - match[1].rm_so;
985            str = malloc(len + 1);
986            if (!str)
987                return NULL;
988            strncpy(str, s + match[1].rm_so, len);
989            str[len] = '\0';
990            return str;
991        }
992
993    /* If there is no match, return the whole name minus the leading path */
994    return strdup(s);
995}
996
997/*
998 * Return the module version information.
999 */
1000unsigned long
1001LoaderGetModuleVersion(ModuleDescPtr mod)
1002{
1003    if (!mod || mod == (ModuleDescPtr) 1 || !mod->VersionInfo)
1004        return 0;
1005
1006    return MODULE_VERSION_NUMERIC(mod->VersionInfo->majorversion,
1007                                  mod->VersionInfo->minorversion,
1008                                  mod->VersionInfo->patchlevel);
1009}
1010