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