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