loadmod.c revision 35c4bbdf
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 "os.h"
54/* For stat() and related stuff */
55#define NO_OSLIB_PROTOTYPES
56#include "xf86_OSlib.h"
57#define LOADERDECLARATIONS
58#include "loaderProcs.h"
59#include "misc.h"
60#include "xf86.h"
61#include "xf86Priv.h"
62#include "xf86Xinput.h"
63#include "loader.h"
64#include "xf86Optrec.h"
65
66#include <sys/types.h>
67#include <regex.h>
68#include <dirent.h>
69#include <limits.h>
70
71typedef struct _pattern {
72    const char *pattern;
73    regex_t rex;
74} PatternRec, *PatternPtr;
75
76/* Prototypes for static functions */
77static char *FindModule(const char *, const char *, const char **, PatternPtr);
78static Bool CheckVersion(const char *, XF86ModuleVersionInfo *,
79                         const XF86ModReqInfo *);
80static char *LoaderGetCanonicalName(const char *, PatternPtr);
81static void RemoveChild(ModuleDescPtr);
82
83const ModuleVersions LoaderVersionInfo = {
84    XORG_VERSION_CURRENT,
85    ABI_ANSIC_VERSION,
86    ABI_VIDEODRV_VERSION,
87    ABI_XINPUT_VERSION,
88    ABI_EXTENSION_VERSION,
89    ABI_FONT_VERSION
90};
91
92static int ModuleDuplicated[] = { };
93
94static void
95FreeStringList(char **paths)
96{
97    char **p;
98
99    if (!paths)
100        return;
101
102    for (p = paths; *p; p++)
103        free(*p);
104
105    free(paths);
106}
107
108static char **defaultPathList = NULL;
109
110static Bool
111PathIsAbsolute(const char *path)
112{
113    return *path == '/';
114}
115
116/*
117 * Convert a comma-separated path into a NULL-terminated array of path
118 * elements, rejecting any that are not full absolute paths, and appending
119 * a '/' when it isn't already present.
120 */
121static char **
122InitPathList(const char *path)
123{
124    char *fullpath = NULL;
125    char *elem = NULL;
126    char **list = NULL, **save = NULL;
127    int len;
128    int addslash;
129    int n = 0;
130
131    if (!path)
132        return defaultPathList;
133
134    fullpath = strdup(path);
135    if (!fullpath)
136        return NULL;
137    elem = strtok(fullpath, ",");
138    while (elem) {
139        if (PathIsAbsolute(elem)) {
140            len = strlen(elem);
141            addslash = (elem[len - 1] != '/');
142            if (addslash)
143                len++;
144            save = list;
145            list = reallocarray(list, n + 2, sizeof(char *));
146            if (!list) {
147                if (save) {
148                    save[n] = NULL;
149                    FreeStringList(save);
150                }
151                free(fullpath);
152                return NULL;
153            }
154            list[n] = malloc(len + 1);
155            if (!list[n]) {
156                FreeStringList(list);
157                free(fullpath);
158                return NULL;
159            }
160            strcpy(list[n], elem);
161            if (addslash) {
162                list[n][len - 1] = '/';
163                list[n][len] = '\0';
164            }
165            n++;
166        }
167        elem = strtok(NULL, ",");
168    }
169    if (list)
170        list[n] = NULL;
171    free(fullpath);
172    return list;
173}
174
175static void
176FreePathList(char **pathlist)
177{
178    if (pathlist && pathlist != defaultPathList)
179        FreeStringList(pathlist);
180}
181
182void
183LoaderSetPath(const char *path)
184{
185    if (!path)
186        return;
187
188    defaultPathList = InitPathList(path);
189}
190
191/* Standard set of module subdirectories to search, in order of preference */
192static const char *stdSubdirs[] = {
193    "",
194    "input/",
195    "drivers/",
196    "extensions/",
197    "internal/",
198    NULL
199};
200
201/*
202 * Standard set of module name patterns to check, in order of preference
203 * These are regular expressions (suitable for use with POSIX regex(3)).
204 *
205 * This list assumes that you're an ELFish platform and therefore your
206 * shared libraries are named something.so.  If we're ever nuts enough
207 * to port this DDX to, say, Darwin, we'll need to fix this.
208 */
209static PatternRec stdPatterns[] = {
210#ifdef __CYGWIN__
211    {"^cyg(.*)\\.dll$",},
212    {"(.*)_drv\\.dll$",},
213    {"(.*)\\.dll$",},
214#else
215    {"^lib(.*)\\.so$",},
216    {"(.*)_drv\\.so$",},
217    {"(.*)\\.so$",},
218#endif
219    {NULL,}
220};
221
222static PatternPtr
223InitPatterns(const char **patternlist)
224{
225    char errmsg[80];
226    int i, e;
227    PatternPtr patterns = NULL;
228    PatternPtr p = NULL;
229    static int firstTime = 1;
230    const char **s;
231
232    if (firstTime) {
233        /* precompile stdPatterns */
234        firstTime = 0;
235        for (p = stdPatterns; p->pattern; p++)
236            if ((e = regcomp(&p->rex, p->pattern, REG_EXTENDED)) != 0) {
237                regerror(e, &p->rex, errmsg, sizeof(errmsg));
238                FatalError("InitPatterns: regcomp error for `%s': %s\n",
239                           p->pattern, errmsg);
240            }
241    }
242
243    if (patternlist) {
244        for (i = 0, s = patternlist; *s; i++, s++)
245            if (*s == DEFAULT_LIST)
246                i += sizeof(stdPatterns) / sizeof(stdPatterns[0]) - 1 - 1;
247        patterns = xallocarray(i + 1, sizeof(PatternRec));
248        if (!patterns) {
249            return NULL;
250        }
251        for (i = 0, s = patternlist; *s; i++, s++)
252            if (*s != DEFAULT_LIST) {
253                p = patterns + i;
254                p->pattern = *s;
255                if ((e = regcomp(&p->rex, p->pattern, REG_EXTENDED)) != 0) {
256                    regerror(e, &p->rex, errmsg, sizeof(errmsg));
257                    ErrorF("InitPatterns: regcomp error for `%s': %s\n",
258                           p->pattern, errmsg);
259                    i--;
260                }
261            }
262            else {
263                for (p = stdPatterns; p->pattern; p++, i++)
264                    patterns[i] = *p;
265                if (p != stdPatterns)
266                    i--;
267            }
268        patterns[i].pattern = NULL;
269    }
270    else
271        patterns = stdPatterns;
272    return patterns;
273}
274
275static void
276FreePatterns(PatternPtr patterns)
277{
278    if (patterns && patterns != stdPatterns)
279        free(patterns);
280}
281
282static const char **
283InitSubdirs(const char **subdirlist)
284{
285    int i;
286    const char **tmp_subdirlist = NULL;
287    char **subdirs = NULL;
288    const char **s, **stmp = NULL;
289    const char *osname;
290    const char *slash;
291    int oslen = 0, len;
292    Bool indefault;
293
294    if (subdirlist == NULL) {
295        subdirlist = tmp_subdirlist = malloc(2 * sizeof(char *));
296        if (subdirlist == NULL)
297            return NULL;
298        subdirlist[0] = DEFAULT_LIST;
299        subdirlist[1] = NULL;
300    }
301
302    LoaderGetOS(&osname, NULL, NULL, NULL);
303    oslen = strlen(osname);
304
305    {
306        /* Count number of entries and check for invalid paths */
307        for (i = 0, s = subdirlist; *s; i++, s++) {
308            if (*s == DEFAULT_LIST) {
309                i += sizeof(stdSubdirs) / sizeof(stdSubdirs[0]) - 1 - 1;
310            }
311            else {
312                /*
313                 * Path validity check.  Don't allow absolute paths, or
314                 * paths containing "..".  To catch absolute paths on
315                 * platforms that use driver letters, don't allow the ':'
316                 * character to appear at all.
317                 */
318                if (**s == '/' || **s == '\\' || strchr(*s, ':') ||
319                    strstr(*s, "..")) {
320                    xf86Msg(X_ERROR, "InitSubdirs: Bad subdir: \"%s\"\n", *s);
321                    free(tmp_subdirlist);
322                    return NULL;
323                }
324            }
325        }
326        subdirs = xallocarray(i * 2 + 1, sizeof(char *));
327        if (!subdirs) {
328            free(tmp_subdirlist);
329            return NULL;
330        }
331        i = 0;
332        s = subdirlist;
333        indefault = FALSE;
334        while (*s) {
335            if (*s == DEFAULT_LIST) {
336                /* Divert to the default list */
337                indefault = TRUE;
338                stmp = ++s;
339                s = stdSubdirs;
340            }
341            len = strlen(*s);
342            if (**s && (*s)[len - 1] != '/') {
343                slash = "/";
344                len++;
345            }
346            else
347                slash = "";
348            len += oslen + 2;
349            if (!(subdirs[i] = malloc(len))) {
350                while (--i >= 0)
351                    free(subdirs[i]);
352                free(subdirs);
353                free(tmp_subdirlist);
354                return NULL;
355            }
356            /* tack on the OS name */
357            sprintf(subdirs[i], "%s%s%s/", *s, slash, osname);
358            i++;
359            /* path as given */
360            subdirs[i] = strdup(*s);
361            i++;
362            s++;
363            if (indefault && !s) {
364                /* revert back to the main list */
365                indefault = FALSE;
366                s = stmp;
367            }
368        }
369        subdirs[i] = NULL;
370    }
371    free(tmp_subdirlist);
372    return (const char **) subdirs;
373}
374
375static void
376FreeSubdirs(const char **subdirs)
377{
378    const char **s;
379
380    if (subdirs) {
381        for (s = subdirs; *s; s++)
382            free((char *) *s);
383        free(subdirs);
384    }
385}
386
387static char *
388FindModuleInSubdir(const char *dirpath, const char *module)
389{
390    struct dirent *direntry = NULL;
391    DIR *dir = NULL;
392    char *ret = NULL, tmpBuf[PATH_MAX];
393    struct stat stat_buf;
394
395    dir = opendir(dirpath);
396    if (!dir)
397        return NULL;
398
399    while ((direntry = readdir(dir))) {
400        if (direntry->d_name[0] == '.')
401            continue;
402        snprintf(tmpBuf, PATH_MAX, "%s%s/", dirpath, direntry->d_name);
403        /* the stat with the appended / fails for normal files,
404           and works for sub dirs fine, looks a bit strange in strace
405           but does seem to work */
406        if ((stat(tmpBuf, &stat_buf) == 0) && S_ISDIR(stat_buf.st_mode)) {
407            if ((ret = FindModuleInSubdir(tmpBuf, module)))
408                break;
409            continue;
410        }
411
412#ifdef __CYGWIN__
413        snprintf(tmpBuf, PATH_MAX, "cyg%s.dll", module);
414#else
415        snprintf(tmpBuf, PATH_MAX, "lib%s.so", module);
416#endif
417        if (strcmp(direntry->d_name, tmpBuf) == 0) {
418            if (asprintf(&ret, "%s%s", dirpath, tmpBuf) == -1)
419                ret = NULL;
420            break;
421        }
422
423#ifdef __CYGWIN__
424        snprintf(tmpBuf, PATH_MAX, "%s_drv.dll", module);
425#else
426        snprintf(tmpBuf, PATH_MAX, "%s_drv.so", module);
427#endif
428        if (strcmp(direntry->d_name, tmpBuf) == 0) {
429            if (asprintf(&ret, "%s%s", dirpath, tmpBuf) == -1)
430                ret = NULL;
431            break;
432        }
433
434#ifdef __CYGWIN__
435        snprintf(tmpBuf, PATH_MAX, "%s.dll", module);
436#else
437        snprintf(tmpBuf, PATH_MAX, "%s.so", module);
438#endif
439        if (strcmp(direntry->d_name, tmpBuf) == 0) {
440            if (asprintf(&ret, "%s%s", dirpath, tmpBuf) == -1)
441                ret = NULL;
442            break;
443        }
444    }
445
446    closedir(dir);
447    return ret;
448}
449
450static char *
451FindModule(const char *module, const char *dirname, const char **subdirlist,
452           PatternPtr patterns)
453{
454    char buf[PATH_MAX + 1];
455    char *name = NULL;
456    const char **subdirs = NULL;
457    const char **s;
458
459    if (strlen(dirname) > PATH_MAX)
460        return NULL;
461
462    subdirs = InitSubdirs(subdirlist);
463    if (!subdirs)
464        return NULL;
465
466    for (s = subdirs; *s; s++) {
467        if ((strlen(dirname) + strlen(*s)) > PATH_MAX)
468            continue;
469        strcpy(buf, dirname);
470        strcat(buf, *s);
471        if ((name = FindModuleInSubdir(buf, module)))
472            break;
473    }
474
475    FreeSubdirs(subdirs);
476
477    return name;
478}
479
480const char **
481LoaderListDirs(const char **subdirlist, const char **patternlist)
482{
483    char buf[PATH_MAX + 1];
484    char **pathlist;
485    char **elem;
486    const char **subdirs;
487    const char **s;
488    PatternPtr patterns = NULL;
489    PatternPtr p;
490    DIR *d;
491    struct dirent *dp;
492    regmatch_t match[2];
493    struct stat stat_buf;
494    int len, dirlen;
495    char *fp;
496    char **listing = NULL;
497    char **save;
498    char **ret = NULL;
499    int n = 0;
500
501    if (!(pathlist = InitPathList(NULL)))
502        return NULL;
503    if (!(subdirs = InitSubdirs(subdirlist)))
504        goto bail;
505    if (!(patterns = InitPatterns(patternlist)))
506        goto bail;
507
508    for (elem = pathlist; *elem; elem++) {
509        for (s = subdirs; *s; s++) {
510            if ((dirlen = strlen(*elem) + strlen(*s)) > PATH_MAX)
511                continue;
512            strcpy(buf, *elem);
513            strcat(buf, *s);
514            fp = buf + dirlen;
515            if (stat(buf, &stat_buf) == 0 && S_ISDIR(stat_buf.st_mode) &&
516                (d = opendir(buf))) {
517                if (buf[dirlen - 1] != '/') {
518                    buf[dirlen++] = '/';
519                    fp++;
520                }
521                while ((dp = readdir(d))) {
522                    if (dirlen + strlen(dp->d_name) > PATH_MAX)
523                        continue;
524                    strcpy(fp, dp->d_name);
525                    if (!(stat(buf, &stat_buf) == 0 &&
526                          S_ISREG(stat_buf.st_mode)))
527                        continue;
528                    for (p = patterns; p->pattern; p++) {
529                        if (regexec(&p->rex, dp->d_name, 2, match, 0) == 0 &&
530                            match[1].rm_so != -1) {
531                            len = match[1].rm_eo - match[1].rm_so;
532                            save = listing;
533                            listing = reallocarray(listing, n + 2,
534                                                   sizeof(char *));
535                            if (!listing) {
536                                if (save) {
537                                    save[n] = NULL;
538                                    FreeStringList(save);
539                                }
540                                closedir(d);
541                                goto bail;
542                            }
543                            listing[n] = malloc(len + 1);
544                            if (!listing[n]) {
545                                FreeStringList(listing);
546                                closedir(d);
547                                goto bail;
548                            }
549                            strncpy(listing[n], dp->d_name + match[1].rm_so,
550                                    len);
551                            listing[n][len] = '\0';
552                            n++;
553                            break;
554                        }
555                    }
556                }
557                closedir(d);
558            }
559        }
560    }
561    if (listing)
562        listing[n] = NULL;
563    ret = listing;
564
565 bail:
566    FreePatterns(patterns);
567    FreeSubdirs(subdirs);
568    FreePathList(pathlist);
569    return (const char **) ret;
570}
571
572void
573LoaderFreeDirList(char **list)
574{
575    FreeStringList(list);
576}
577
578static Bool
579CheckVersion(const char *module, XF86ModuleVersionInfo * data,
580             const XF86ModReqInfo * req)
581{
582    int vercode[4];
583    long ver = data->xf86version;
584    MessageType errtype;
585
586    xf86Msg(X_INFO, "Module %s: vendor=\"%s\"\n",
587            data->modname ? data->modname : "UNKNOWN!",
588            data->vendor ? data->vendor : "UNKNOWN!");
589
590    vercode[0] = ver / 10000000;
591    vercode[1] = (ver / 100000) % 100;
592    vercode[2] = (ver / 1000) % 100;
593    vercode[3] = ver % 1000;
594    xf86ErrorF("\tcompiled for %d.%d.%d", vercode[0], vercode[1], vercode[2]);
595    if (vercode[3] != 0)
596        xf86ErrorF(".%d", vercode[3]);
597    xf86ErrorF(", module version = %d.%d.%d\n", data->majorversion,
598               data->minorversion, data->patchlevel);
599
600    if (data->moduleclass)
601        xf86ErrorFVerb(2, "\tModule class: %s\n", data->moduleclass);
602
603    ver = -1;
604    if (data->abiclass) {
605        int abimaj, abimin;
606        int vermaj, vermin;
607
608        if (!strcmp(data->abiclass, ABI_CLASS_ANSIC))
609            ver = LoaderVersionInfo.ansicVersion;
610        else if (!strcmp(data->abiclass, ABI_CLASS_VIDEODRV))
611            ver = LoaderVersionInfo.videodrvVersion;
612        else if (!strcmp(data->abiclass, ABI_CLASS_XINPUT))
613            ver = LoaderVersionInfo.xinputVersion;
614        else if (!strcmp(data->abiclass, ABI_CLASS_EXTENSION))
615            ver = LoaderVersionInfo.extensionVersion;
616        else if (!strcmp(data->abiclass, ABI_CLASS_FONT))
617            ver = LoaderVersionInfo.fontVersion;
618
619        abimaj = GET_ABI_MAJOR(data->abiversion);
620        abimin = GET_ABI_MINOR(data->abiversion);
621        xf86ErrorFVerb(2, "\tABI class: %s, version %d.%d\n",
622                       data->abiclass, abimaj, abimin);
623        if (ver != -1) {
624            vermaj = GET_ABI_MAJOR(ver);
625            vermin = GET_ABI_MINOR(ver);
626            if (abimaj != vermaj) {
627                if (LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL)
628                    errtype = X_WARNING;
629                else
630                    errtype = X_ERROR;
631                xf86MsgVerb(errtype, 0,
632                            "module ABI major version (%d) doesn't"
633                            " match the server's version (%d)\n",
634                            abimaj, vermaj);
635                if (!(LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL))
636                    return FALSE;
637            }
638            else if (abimin > vermin) {
639                if (LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL)
640                    errtype = X_WARNING;
641                else
642                    errtype = X_ERROR;
643                xf86MsgVerb(errtype, 0,
644                            "module ABI minor version (%d) is "
645                            "newer than the server's version "
646                            "(%d)\n", abimin, vermin);
647                if (!(LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL))
648                    return FALSE;
649            }
650        }
651    }
652
653    /* Check against requirements that the caller has specified */
654    if (req) {
655        if (req->majorversion != MAJOR_UNSPEC) {
656            if (data->majorversion != req->majorversion) {
657                xf86MsgVerb(X_WARNING, 2, "module major version (%d) "
658                            "doesn't match required major version (%d)\n",
659                            data->majorversion, req->majorversion);
660                return FALSE;
661            }
662            else if (req->minorversion != MINOR_UNSPEC) {
663                if (data->minorversion < req->minorversion) {
664                    xf86MsgVerb(X_WARNING, 2, "module minor version (%d) "
665                                "is less than the required minor version (%d)\n",
666                                data->minorversion, req->minorversion);
667                    return FALSE;
668                }
669                else if (data->minorversion == req->minorversion &&
670                         req->patchlevel != PATCH_UNSPEC) {
671                    if (data->patchlevel < req->patchlevel) {
672                        xf86MsgVerb(X_WARNING, 2, "module patch level (%d) "
673                                    "is less than the required patch level (%d)\n",
674                                    data->patchlevel, req->patchlevel);
675                        return FALSE;
676                    }
677                }
678            }
679        }
680        if (req->moduleclass) {
681            if (!data->moduleclass ||
682                strcmp(req->moduleclass, data->moduleclass)) {
683                xf86MsgVerb(X_WARNING, 2, "Module class (%s) doesn't match "
684                            "the required class (%s)\n",
685                            data->moduleclass ? data->moduleclass : "<NONE>",
686                            req->moduleclass);
687                return FALSE;
688            }
689        }
690        else if (req->abiclass != ABI_CLASS_NONE) {
691            if (!data->abiclass || strcmp(req->abiclass, data->abiclass)) {
692                xf86MsgVerb(X_WARNING, 2, "ABI class (%s) doesn't match the "
693                            "required ABI class (%s)\n",
694                            data->abiclass ? data->abiclass : "<NONE>",
695                            req->abiclass);
696                return FALSE;
697            }
698        }
699        if ((req->abiclass != ABI_CLASS_NONE) &&
700            req->abiversion != ABI_VERS_UNSPEC) {
701            int reqmaj, reqmin, maj, min;
702
703            reqmaj = GET_ABI_MAJOR(req->abiversion);
704            reqmin = GET_ABI_MINOR(req->abiversion);
705            maj = GET_ABI_MAJOR(data->abiversion);
706            min = GET_ABI_MINOR(data->abiversion);
707            if (maj != reqmaj) {
708                xf86MsgVerb(X_WARNING, 2, "ABI major version (%d) doesn't "
709                            "match the required ABI major version (%d)\n",
710                            maj, reqmaj);
711                return FALSE;
712            }
713            /* XXX Maybe this should be the other way around? */
714            if (min > reqmin) {
715                xf86MsgVerb(X_WARNING, 2, "module ABI minor version (%d) "
716                            "is newer than that available (%d)\n", min, reqmin);
717                return FALSE;
718            }
719        }
720    }
721    return TRUE;
722}
723
724static ModuleDescPtr
725AddSibling(ModuleDescPtr head, ModuleDescPtr new)
726{
727    new->sib = head;
728    return new;
729}
730
731void *
732LoadSubModule(void *_parent, const char *module,
733              const char **subdirlist, const char **patternlist,
734              void *options, const XF86ModReqInfo * modreq,
735              int *errmaj, int *errmin)
736{
737    ModuleDescPtr submod;
738    ModuleDescPtr parent = (ModuleDescPtr) _parent;
739
740    xf86MsgVerb(X_INFO, 3, "Loading sub module \"%s\"\n", module);
741
742    if (PathIsAbsolute(module)) {
743        xf86Msg(X_ERROR,
744                "LoadSubModule: Absolute module path not permitted: \"%s\"\n",
745                module);
746        if (errmaj)
747            *errmaj = LDR_BADUSAGE;
748        if (errmin)
749            *errmin = 0;
750        return NULL;
751    }
752
753    submod = LoadModule(module, NULL, subdirlist, patternlist, options,
754                        modreq, errmaj, errmin);
755    if (submod && submod != (ModuleDescPtr) 1) {
756        parent->child = AddSibling(parent->child, submod);
757        submod->parent = parent;
758    }
759    return submod;
760}
761
762static ModuleDescPtr
763NewModuleDesc(const char *name)
764{
765    ModuleDescPtr mdp = calloc(1, sizeof(ModuleDesc));
766
767    if (mdp)
768        mdp->name = xstrdup(name);
769
770    return mdp;
771}
772
773ModuleDescPtr
774DuplicateModule(ModuleDescPtr mod, ModuleDescPtr parent)
775{
776    ModuleDescPtr ret;
777
778    if (!mod)
779        return NULL;
780
781    ret = NewModuleDesc(mod->name);
782    if (ret == NULL)
783        return NULL;
784
785    ret->handle = mod->handle;
786
787    ret->SetupProc = mod->SetupProc;
788    ret->TearDownProc = mod->TearDownProc;
789    ret->TearDownData = ModuleDuplicated;
790    ret->child = DuplicateModule(mod->child, ret);
791    ret->sib = DuplicateModule(mod->sib, parent);
792    ret->parent = parent;
793    ret->VersionInfo = mod->VersionInfo;
794    ret->path = strdup(mod->path);
795
796    return ret;
797}
798
799static const char *compiled_in_modules[] = {
800    "ddc",
801    "i2c",
802    "ramdac",
803    "dbe",
804    "record",
805    "extmod",
806    "dri",
807    "dri2",
808#if DRI3
809    "dri3",
810#endif
811#if PRESENT
812    "present",
813#endif
814    NULL
815};
816
817/*
818 * LoadModule: load a module
819 *
820 * module       The module name.  Normally this is not a filename but the
821 *              module's "canonical name.  A full pathname is, however,
822 *              also accepted.
823 * path         A comma separated list of module directories.
824 * subdirlist   A NULL terminated list of subdirectories to search.  When
825 *              NULL, the default "stdSubdirs" list is used.  The default
826 *              list is also substituted for entries with value DEFAULT_LIST.
827 * patternlist  A NULL terminated list of regular expressions used to find
828 *              module filenames.  Each regex should contain exactly one
829 *              subexpression that corresponds to the canonical module name.
830 *              When NULL, the default "stdPatterns" list is used.  The
831 *              default list is also substituted for entries with value
832 *              DEFAULT_LIST.
833 * options      A NULL terminated list of Options that are passed to the
834 *              module's SetupProc function.
835 * modreq       An optional XF86ModReqInfo* containing
836 *              version/ABI/vendor-ABI requirements to check for when
837 *              loading the module.  The following fields of the
838 *              XF86ModReqInfo struct are checked:
839 *                majorversion - must match the module's majorversion exactly
840 *                minorversion - the module's minorversion must be >= this
841 *                patchlevel   - the module's minorversion.patchlevel must be
842 *                               >= this.  Patchlevel is ignored when
843 *                               minorversion is not set.
844 *                abiclass     - (string) must match the module's abiclass
845 *                abiversion   - must be consistent with the module's
846 *                               abiversion (major equal, minor no older)
847 *                moduleclass  - string must match the module's moduleclass
848 *                               string
849 *              "don't care" values are ~0 for numbers, and NULL for strings
850 * errmaj       Major error return.
851 * errmin       Minor error return.
852 *
853 */
854ModuleDescPtr
855LoadModule(const char *module, const char *path, const char **subdirlist,
856           const char **patternlist, void *options,
857           const XF86ModReqInfo * modreq, int *errmaj, int *errmin)
858{
859    XF86ModuleData *initdata = NULL;
860    char **pathlist = NULL;
861    char *found = NULL;
862    char *name = NULL;
863    char **path_elem = NULL;
864    char *p = NULL;
865    ModuleDescPtr ret = NULL;
866    PatternPtr patterns = NULL;
867    int noncanonical = 0;
868    char *m = NULL;
869    const char **cim;
870
871    xf86MsgVerb(X_INFO, 3, "LoadModule: \"%s\"", module);
872
873    patterns = InitPatterns(patternlist);
874    name = LoaderGetCanonicalName(module, patterns);
875    noncanonical = (name && strcmp(module, name) != 0);
876    if (noncanonical) {
877        xf86ErrorFVerb(3, " (%s)\n", name);
878        xf86MsgVerb(X_WARNING, 1,
879                    "LoadModule: given non-canonical module name \"%s\"\n",
880                    module);
881        m = name;
882    }
883    else {
884        xf86ErrorFVerb(3, "\n");
885        m = (char *) module;
886    }
887
888    for (cim = compiled_in_modules; *cim; cim++)
889        if (!strcmp(m, *cim)) {
890            xf86MsgVerb(X_INFO, 3, "Module \"%s\" already built-in\n", m);
891            ret = (ModuleDescPtr) 1;
892            goto LoadModule_exit;
893        }
894
895    if (!name) {
896        if (errmaj)
897            *errmaj = LDR_BADUSAGE;
898        if (errmin)
899            *errmin = 0;
900        goto LoadModule_fail;
901    }
902    ret = NewModuleDesc(name);
903    if (!ret) {
904        if (errmaj)
905            *errmaj = LDR_NOMEM;
906        if (errmin)
907            *errmin = 0;
908        goto LoadModule_fail;
909    }
910
911    pathlist = InitPathList(path);
912    if (!pathlist) {
913        /* This could be a malloc failure too */
914        if (errmaj)
915            *errmaj = LDR_BADUSAGE;
916        if (errmin)
917            *errmin = 1;
918        goto LoadModule_fail;
919    }
920
921    /*
922     * if the module name is not a full pathname, we need to
923     * check the elements in the path
924     */
925    if (PathIsAbsolute(module))
926        found = xstrdup(module);
927    path_elem = pathlist;
928    while (!found && *path_elem != NULL) {
929        found = FindModule(m, *path_elem, subdirlist, patterns);
930        path_elem++;
931        /*
932         * When the module name isn't the canonical name, search for the
933         * former if no match was found for the latter.
934         */
935        if (!*path_elem && m == name) {
936            path_elem = pathlist;
937            m = (char *) module;
938        }
939    }
940
941    /*
942     * did we find the module?
943     */
944    if (!found) {
945        xf86Msg(X_WARNING, "Warning, couldn't open module %s\n", module);
946        if (errmaj)
947            *errmaj = LDR_NOENT;
948        if (errmin)
949            *errmin = 0;
950        goto LoadModule_fail;
951    }
952    ret->handle = LoaderOpen(found, errmaj, errmin);
953    if (ret->handle == NULL)
954        goto LoadModule_fail;
955    ret->path = strdup(found);
956
957    /* drop any explicit suffix from the module name */
958    p = strchr(name, '.');
959    if (p)
960        *p = '\0';
961
962    /*
963     * now check if the special data object <modulename>ModuleData is
964     * present.
965     */
966    if (asprintf(&p, "%sModuleData", name) == -1) {
967        p = NULL;
968        if (errmaj)
969            *errmaj = LDR_NOMEM;
970        if (errmin)
971            *errmin = 0;
972        goto LoadModule_fail;
973    }
974    initdata = LoaderSymbolFromModule(ret->handle, p);
975    if (initdata) {
976        ModuleSetupProc setup;
977        ModuleTearDownProc teardown;
978        XF86ModuleVersionInfo *vers;
979
980        vers = initdata->vers;
981        setup = initdata->setup;
982        teardown = initdata->teardown;
983
984        if (vers) {
985            if (!CheckVersion(module, vers, modreq)) {
986                if (errmaj)
987                    *errmaj = LDR_MISMATCH;
988                if (errmin)
989                    *errmin = 0;
990                goto LoadModule_fail;
991            }
992        }
993        else {
994            xf86Msg(X_ERROR,
995                    "LoadModule: Module %s does not supply"
996                    " version information\n", module);
997            if (errmaj)
998                *errmaj = LDR_INVALID;
999            if (errmin)
1000                *errmin = 0;
1001            goto LoadModule_fail;
1002        }
1003        if (setup)
1004            ret->SetupProc = setup;
1005        if (teardown)
1006            ret->TearDownProc = teardown;
1007        ret->VersionInfo = vers;
1008    }
1009    else {
1010        /* no initdata, fail the load */
1011        xf86Msg(X_ERROR, "LoadModule: Module %s does not have a %s "
1012                "data object.\n", module, p);
1013        if (errmaj)
1014            *errmaj = LDR_INVALID;
1015        if (errmin)
1016            *errmin = 0;
1017        goto LoadModule_fail;
1018    }
1019    if (ret->SetupProc) {
1020        ret->TearDownData = ret->SetupProc(ret, options, errmaj, errmin);
1021        if (!ret->TearDownData) {
1022            goto LoadModule_fail;
1023        }
1024    }
1025    else if (options) {
1026        xf86Msg(X_WARNING, "Module Options present, but no SetupProc "
1027                "available for %s\n", module);
1028    }
1029    goto LoadModule_exit;
1030
1031 LoadModule_fail:
1032    UnloadModule(ret);
1033    ret = NULL;
1034
1035 LoadModule_exit:
1036    FreePathList(pathlist);
1037    FreePatterns(patterns);
1038    free(found);
1039    free(name);
1040    free(p);
1041
1042    return ret;
1043}
1044
1045void
1046UnloadModule(void *_mod)
1047{
1048    ModuleDescPtr mod = _mod;
1049
1050    if (mod == (ModuleDescPtr) 1)
1051        return;
1052
1053    if (mod == NULL || mod->name == NULL)
1054        return;
1055
1056    if (mod->parent)
1057        LogMessageVerbSigSafe(X_INFO, 3, "UnloadSubModule: \"%s\"\n",
1058                              mod->name);
1059    else
1060        LogMessageVerbSigSafe(X_INFO, 3, "UnloadModule: \"%s\"\n", mod->name);
1061
1062    if (mod->TearDownData != ModuleDuplicated) {
1063        if ((mod->TearDownProc) && (mod->TearDownData))
1064            mod->TearDownProc(mod->TearDownData);
1065        LoaderUnload(mod->name, mod->handle);
1066    }
1067
1068    if (mod->child)
1069        UnloadModule(mod->child);
1070    if (mod->sib)
1071        UnloadModule(mod->sib);
1072    free(mod->path);
1073    free(mod->name);
1074    free(mod);
1075}
1076
1077void
1078UnloadSubModule(void *_mod)
1079{
1080    ModuleDescPtr mod = (ModuleDescPtr) _mod;
1081
1082    /* Some drivers are calling us on built-in submodules, ignore them */
1083    if (mod == (ModuleDescPtr) 1)
1084        return;
1085    RemoveChild(mod);
1086    UnloadModule(mod);
1087}
1088
1089static void
1090RemoveChild(ModuleDescPtr child)
1091{
1092    ModuleDescPtr mdp;
1093    ModuleDescPtr prevsib;
1094    ModuleDescPtr parent;
1095
1096    if (!child->parent)
1097        return;
1098
1099    parent = child->parent;
1100    if (parent->child == child) {
1101        parent->child = child->sib;
1102        return;
1103    }
1104
1105    prevsib = parent->child;
1106    mdp = prevsib->sib;
1107    while (mdp && mdp != child) {
1108        prevsib = mdp;
1109        mdp = mdp->sib;
1110    }
1111    if (mdp == child)
1112        prevsib->sib = child->sib;
1113    child->sib = NULL;
1114    return;
1115}
1116
1117void
1118LoaderErrorMsg(const char *name, const char *modname, int errmaj, int errmin)
1119{
1120    const char *msg;
1121    MessageType type = X_ERROR;
1122
1123    switch (errmaj) {
1124    case LDR_NOERROR:
1125        msg = "no error";
1126        break;
1127    case LDR_NOMEM:
1128        msg = "out of memory";
1129        break;
1130    case LDR_NOENT:
1131        msg = "module does not exist";
1132        break;
1133    case LDR_NOSUBENT:
1134        msg = "a required submodule could not be loaded";
1135        break;
1136    case LDR_NOSPACE:
1137        msg = "too many modules";
1138        break;
1139    case LDR_NOMODOPEN:
1140        msg = "open failed";
1141        break;
1142    case LDR_UNKTYPE:
1143        msg = "unknown module type";
1144        break;
1145    case LDR_NOLOAD:
1146        msg = "loader failed";
1147        break;
1148    case LDR_ONCEONLY:
1149        msg = "already loaded";
1150        type = X_INFO;
1151        break;
1152    case LDR_NOPORTOPEN:
1153        msg = "port open failed";
1154        break;
1155    case LDR_NOHARDWARE:
1156        msg = "no hardware found";
1157        break;
1158    case LDR_MISMATCH:
1159        msg = "module requirement mismatch";
1160        break;
1161    case LDR_BADUSAGE:
1162        msg = "invalid argument(s) to LoadModule()";
1163        break;
1164    case LDR_INVALID:
1165        msg = "invalid module";
1166        break;
1167    case LDR_BADOS:
1168        msg = "module doesn't support this OS";
1169        break;
1170    case LDR_MODSPECIFIC:
1171        msg = "module-specific error";
1172        break;
1173    default:
1174        msg = "unknown error";
1175    }
1176    if (name)
1177        xf86Msg(type, "%s: Failed to load module \"%s\" (%s, %d)\n",
1178                name, modname, msg, errmin);
1179    else
1180        xf86Msg(type, "Failed to load module \"%s\" (%s, %d)\n",
1181                modname, msg, errmin);
1182}
1183
1184/* Given a module path or file name, return the module's canonical name */
1185static char *
1186LoaderGetCanonicalName(const char *modname, PatternPtr patterns)
1187{
1188    char *str;
1189    const char *s;
1190    int len;
1191    PatternPtr p;
1192    regmatch_t match[2];
1193
1194    /* Strip off any leading path */
1195    s = strrchr(modname, '/');
1196    if (s == NULL)
1197        s = modname;
1198    else
1199        s++;
1200
1201    /* Find the first regex that is matched */
1202    for (p = patterns; p->pattern; p++)
1203        if (regexec(&p->rex, s, 2, match, 0) == 0 && match[1].rm_so != -1) {
1204            len = match[1].rm_eo - match[1].rm_so;
1205            str = malloc(len + 1);
1206            if (!str)
1207                return NULL;
1208            strncpy(str, s + match[1].rm_so, len);
1209            str[len] = '\0';
1210            return str;
1211        }
1212
1213    /* If there is no match, return the whole name minus the leading path */
1214    return strdup(s);
1215}
1216
1217/*
1218 * Return the module version information.
1219 */
1220unsigned long
1221LoaderGetModuleVersion(ModuleDescPtr mod)
1222{
1223    if (!mod || mod == (ModuleDescPtr) 1 || !mod->VersionInfo)
1224        return 0;
1225
1226    return MODULE_VERSION_NUMERIC(mod->VersionInfo->majorversion,
1227                                  mod->VersionInfo->minorversion,
1228                                  mod->VersionInfo->patchlevel);
1229}
1230