1313 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1313 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
| /* $XFree86: xc/programs/Xserver/hw/xfree86/loader/loadmod.c,v 1.73 2003/11/03 05:11:51 tsi Exp $ */
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  * Copyright 1995-1998 by Metro Link, Inc.
 | |
|  *
 | |
|  * Permission to use, copy, modify, distribute, and sell this software and its
 | |
|  * documentation for any purpose is hereby granted without fee, provided that
 | |
|  * the above copyright notice appear in all copies and that both that
 | |
|  * copyright notice and this permission notice appear in supporting
 | |
|  * documentation, and that the name of Metro Link, Inc. not be used in
 | |
|  * advertising or publicity pertaining to distribution of the software without
 | |
|  * specific, written prior permission.  Metro Link, Inc. makes no
 | |
|  * representations about the suitability of this software for any purpose.
 | |
|  *  It is provided "as is" without express or implied warranty.
 | |
|  *
 | |
|  * METRO LINK, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 | |
|  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 | |
|  * EVENT SHALL METRO LINK, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 | |
|  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 | |
|  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 | |
|  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 | |
|  * PERFORMANCE OF THIS SOFTWARE.
 | |
|  */
 | |
| /*
 | |
|  * Copyright (c) 1997-2002 by The XFree86 Project, Inc.
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a
 | |
|  * copy of this software and associated documentation files (the "Software"),
 | |
|  * to deal in the Software without restriction, including without limitation
 | |
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | |
|  * and/or sell copies of the Software, and to permit persons to whom the
 | |
|  * Software is furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | |
|  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 | |
|  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 | |
|  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 | |
|  * OTHER DEALINGS IN THE SOFTWARE.
 | |
|  *
 | |
|  * Except as contained in this notice, the name of the copyright holder(s)
 | |
|  * and author(s) shall not be used in advertising or otherwise to promote
 | |
|  * the sale, use or other dealings in this Software without prior written
 | |
|  * authorization from the copyright holder(s) and author(s).
 | |
|  */
 | |
| 
 | |
| #include "os.h"
 | |
| /* For stat() and related stuff */
 | |
| #define NO_OSLIB_PROTOTYPES
 | |
| #include "xf86_OSlib.h"
 | |
| #define LOADERDECLARATIONS
 | |
| #include "loaderProcs.h"
 | |
| #include "misc.h"
 | |
| #include "xf86.h"
 | |
| #include "xf86Priv.h"
 | |
| #ifdef XINPUT
 | |
| #include "xf86Xinput.h"
 | |
| #endif
 | |
| #include "loader.h"
 | |
| #include "xf86Optrec.h"
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <regex.h>
 | |
| #include <dirent.h>
 | |
| #include <limits.h>
 | |
| 
 | |
| extern int check_unresolved_sema;
 | |
| 
 | |
| typedef struct _pattern {
 | |
|     const char *pattern;
 | |
|     regex_t rex;
 | |
| } PatternRec, *PatternPtr;
 | |
| 
 | |
| /* Prototypes for static functions */
 | |
| static char *FindModule(const char *, const char *, const char **,
 | |
| 			PatternPtr);
 | |
| static Bool CheckVersion(const char *, XF86ModuleVersionInfo *,
 | |
| 			 const XF86ModReqInfo *);
 | |
| static void UnloadModuleOrDriver(ModuleDescPtr mod);
 | |
| static char *LoaderGetCanonicalName(const char *, PatternPtr);
 | |
| static void RemoveChild(ModuleDescPtr);
 | |
| 
 | |
| ModuleVersions LoaderVersionInfo = {
 | |
|     XORG_VERSION_CURRENT,
 | |
|     ABI_ANSIC_VERSION,
 | |
|     ABI_VIDEODRV_VERSION,
 | |
|     ABI_XINPUT_VERSION,
 | |
|     ABI_EXTENSION_VERSION,
 | |
|     ABI_FONT_VERSION
 | |
| };
 | |
| 
 | |
| #if 0
 | |
| void
 | |
| LoaderFixups(void)
 | |
| {
 | |
|     /* Need to call LRS here because the frame buffers get loaded last,
 | |
|      * and the drivers depend on them. */
 | |
| 
 | |
|     LoaderResolveSymbols();
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static void
 | |
| FreeStringList(char **paths)
 | |
| {
 | |
|     char **p;
 | |
| 
 | |
|     if (!paths)
 | |
| 	return;
 | |
| 
 | |
|     for (p = paths; *p; p++)
 | |
| 	xfree(*p);
 | |
| 
 | |
|     xfree(paths);
 | |
| }
 | |
| 
 | |
| static char **defaultPathList = NULL;
 | |
| 
 | |
| /*
 | |
|  * Convert a comma-separated path into a NULL-terminated array of path
 | |
|  * elements, rejecting any that are not full absolute paths, and appending
 | |
|  * a '/' when it isn't already present.
 | |
|  */
 | |
| static char **
 | |
| InitPathList(const char *path)
 | |
| {
 | |
|     char *fullpath = NULL;
 | |
|     char *elem = NULL;
 | |
|     char **list = NULL, **save = NULL;
 | |
|     int len;
 | |
|     int addslash;
 | |
|     int n = 0;
 | |
| 
 | |
|     if (!path)
 | |
| 	return defaultPathList;
 | |
| 
 | |
|     fullpath = xstrdup(path);
 | |
|     if (!fullpath)
 | |
| 	return NULL;
 | |
|     elem = strtok(fullpath, ",");
 | |
|     while (elem) {
 | |
| 	/* Only allow fully specified paths */
 | |
| #ifndef __UNIXOS2__
 | |
| 	if (*elem == '/')
 | |
| #else
 | |
| 	if (*elem == '/' || (strlen(elem) > 2 && isalpha(elem[0]) &&
 | |
| 			     elem[1] == ':' && elem[2] == '/'))
 | |
| #endif
 | |
| 	{
 | |
| 	    len = strlen(elem);
 | |
| 	    addslash = (elem[len - 1] != '/');
 | |
| 	    if (addslash)
 | |
| 		len++;
 | |
| 	    save = list;
 | |
| 	    list = xrealloc(list, (n + 2) * sizeof(char *));
 | |
| 	    if (!list) {
 | |
| 		if (save) {
 | |
| 		    save[n] = NULL;
 | |
| 		    FreeStringList(save);
 | |
| 		}
 | |
| 		xfree(fullpath);
 | |
| 		return NULL;
 | |
| 	    }
 | |
| 	    list[n] = xalloc(len + 1);
 | |
| 	    if (!list[n]) {
 | |
| 		FreeStringList(list);
 | |
| 		xfree(fullpath);
 | |
| 		return NULL;
 | |
| 	    }
 | |
| 	    strcpy(list[n], elem);
 | |
| 	    if (addslash) {
 | |
| 		list[n][len - 1] = '/';
 | |
| 		list[n][len] = '\0';
 | |
| 	    }
 | |
| 	    n++;
 | |
| 	}
 | |
| 	elem = strtok(NULL, ",");
 | |
|     }
 | |
|     if (list)
 | |
| 	list[n] = NULL;
 | |
|     return list;
 | |
| }
 | |
| 
 | |
| static void
 | |
| FreePathList(char **pathlist)
 | |
| {
 | |
|     if (pathlist && pathlist != defaultPathList)
 | |
| 	FreeStringList(pathlist);
 | |
| }
 | |
| 
 | |
| void
 | |
| LoaderSetPath(const char *path)
 | |
| {
 | |
|     if (!path)
 | |
| 	return;
 | |
| 
 | |
|     defaultPathList = InitPathList(path);
 | |
| }
 | |
| 
 | |
| /* Standard set of module subdirectories to search, in order of preference */
 | |
| static const char *stdSubdirs[] = {
 | |
|     "drivers/",
 | |
|     "input/",
 | |
|     "multimedia/",
 | |
|     "extensions/",
 | |
|     "fonts/",
 | |
|     "internal/",
 | |
|     "",
 | |
|     NULL
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Standard set of module name patterns to check, in order of preference
 | |
|  * These are regular expressions (suitable for use with POSIX regex(3)).
 | |
|  */
 | |
| static PatternRec stdPatterns[] = {
 | |
|     {"^lib(.*)\\.so$",},
 | |
|     {"^lib(.*)\\.a$",},
 | |
|     {"(.*)_drv\\.so$",},
 | |
|     {"(.*)_drv\\.o$",},
 | |
|     {"(.*)\\.so$",},
 | |
|     {"(.*)\\.a$",},
 | |
|     {"(.*)\\.o$",},
 | |
|     {NULL,}
 | |
| };
 | |
| 
 | |
| static PatternPtr
 | |
| InitPatterns(const char **patternlist)
 | |
| {
 | |
|     char errmsg[80];
 | |
|     int i, e;
 | |
|     PatternPtr patterns = NULL;
 | |
|     PatternPtr p = NULL;
 | |
|     static int firstTime = 1;
 | |
|     const char **s;
 | |
| 
 | |
|     if (firstTime) {
 | |
| 	/* precompile stdPatterns */
 | |
| 	firstTime = 0;
 | |
| 	for (p = stdPatterns; p->pattern; p++)
 | |
| 	    if ((e = regcomp(&p->rex, p->pattern, REG_EXTENDED)) != 0) {
 | |
| 		regerror(e, &p->rex, errmsg, sizeof(errmsg));
 | |
| 		FatalError("InitPatterns: regcomp error for `%s': %s\n",
 | |
| 			   p->pattern, errmsg);
 | |
| 	    }
 | |
|     }
 | |
| 
 | |
|     if (patternlist) {
 | |
| 	for (i = 0, s = patternlist; *s; i++, s++)
 | |
| 	    if (*s == DEFAULT_LIST)
 | |
| 		i += sizeof(stdPatterns) / sizeof(stdPatterns[0]) - 1 - 1;
 | |
| 	patterns = xalloc((i + 1) * sizeof(PatternRec));
 | |
| 	if (!patterns) {
 | |
| 	    return NULL;
 | |
| 	}
 | |
| 	for (i = 0, s = patternlist; *s; i++, s++)
 | |
| 	    if (*s != DEFAULT_LIST) {
 | |
| 		p = patterns + i;
 | |
| 		p->pattern = *s;
 | |
| 		if ((e = regcomp(&p->rex, p->pattern, REG_EXTENDED)) != 0) {
 | |
| 		    regerror(e, &p->rex, errmsg, sizeof(errmsg));
 | |
| 		    ErrorF("InitPatterns: regcomp error for `%s': %s\n",
 | |
| 			   p->pattern, errmsg);
 | |
| 		    i--;
 | |
| 		}
 | |
| 	    } else {
 | |
| 		for (p = stdPatterns; p->pattern; p++, i++)
 | |
| 		    patterns[i] = *p;
 | |
| 		if (p != stdPatterns)
 | |
| 		    i--;
 | |
| 	    }
 | |
| 	patterns[i].pattern = NULL;
 | |
|     } else
 | |
| 	patterns = stdPatterns;
 | |
|     return patterns;
 | |
| }
 | |
| 
 | |
| static void
 | |
| FreePatterns(PatternPtr patterns)
 | |
| {
 | |
|     if (patterns && patterns != stdPatterns)
 | |
| 	xfree(patterns);
 | |
| }
 | |
| 
 | |
| static const char **
 | |
| InitSubdirs(const char **subdirlist)
 | |
| {
 | |
|     int i;
 | |
|     const char **tmp_subdirlist = NULL;
 | |
|     char **subdirs = NULL;
 | |
|     const char **s, **stmp = NULL;
 | |
|     const char *osname;
 | |
|     const char *slash;
 | |
|     int oslen = 0, len;
 | |
|     Bool indefault;
 | |
| 
 | |
|     if (subdirlist == NULL) {
 | |
| 	subdirlist = tmp_subdirlist = xalloc(2 * sizeof(char *));
 | |
| 	if (subdirlist == NULL)
 | |
| 	    return NULL;
 | |
| 	subdirlist[0] = DEFAULT_LIST;
 | |
| 	subdirlist[1] = NULL;
 | |
|     }
 | |
| 
 | |
|     LoaderGetOS(&osname, NULL, NULL, NULL);
 | |
|     oslen = strlen(osname);
 | |
| 
 | |
|     {
 | |
| 	/* Count number of entries and check for invalid paths */
 | |
| 	for (i = 0, s = subdirlist; *s; i++, s++) {
 | |
| 	    if (*s == DEFAULT_LIST) {
 | |
| 		i += sizeof(stdSubdirs) / sizeof(stdSubdirs[0]) - 1 - 1;
 | |
| 	    } else {
 | |
| 		/*
 | |
| 		 * Path validity check.  Don't allow absolute paths, or
 | |
| 		 * paths containing "..".  To catch absolute paths on
 | |
| 		 * platforms that use driver letters, don't allow the ':'
 | |
| 		 * character to appear at all.
 | |
| 		 */
 | |
| 		if (**s == '/' || **s == '\\' || strchr(*s, ':') ||
 | |
| 		    strstr(*s, "..")) {
 | |
| 		    xf86Msg(X_ERROR, "InitSubdirs: Bad subdir: \"%s\"\n", *s);
 | |
| 		    if (tmp_subdirlist)
 | |
| 			xfree(tmp_subdirlist);
 | |
| 		    return NULL;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 	subdirs = xalloc((i * 2 + 1) * sizeof(char *));
 | |
| 	if (!subdirs) {
 | |
| 	    if (tmp_subdirlist)
 | |
| 		xfree(tmp_subdirlist);
 | |
| 	    return NULL;
 | |
| 	}
 | |
| 	i = 0;
 | |
| 	s = subdirlist;
 | |
| 	indefault = FALSE;
 | |
| 	while (*s) {
 | |
| 	    if (*s == DEFAULT_LIST) {
 | |
| 		/* Divert to the default list */
 | |
| 		indefault = TRUE;
 | |
| 		stmp = ++s;
 | |
| 		s = stdSubdirs;
 | |
| 	    }
 | |
| 	    len = strlen(*s);
 | |
| 	    if (**s && (*s)[len - 1] != '/') {
 | |
| 		slash = "/";
 | |
| 		len++;
 | |
| 	    } else
 | |
| 		slash = "";
 | |
| 	    len += oslen + 2;
 | |
| 	    if (!(subdirs[i] = xalloc(len))) {
 | |
| 		while (--i >= 0)
 | |
| 		    xfree(subdirs[i]);
 | |
| 		xfree(subdirs);
 | |
| 		if (tmp_subdirlist)
 | |
| 		    xfree(tmp_subdirlist);
 | |
| 		return NULL;
 | |
| 	    }
 | |
| 	    /* tack on the OS name */
 | |
| 	    sprintf(subdirs[i], "%s%s%s/", *s, slash, osname);
 | |
| 	    i++;
 | |
| 	    /* path as given */
 | |
| 	    subdirs[i] = xstrdup(*s);
 | |
| 	    i++;
 | |
| 	    s++;
 | |
| 	    if (indefault && !s) {
 | |
| 		/* revert back to the main list */
 | |
| 		indefault = FALSE;
 | |
| 		s = stmp;
 | |
| 	    }
 | |
| 	}
 | |
| 	subdirs[i] = NULL;
 | |
|     }
 | |
|     if (tmp_subdirlist)
 | |
| 	xfree(tmp_subdirlist);
 | |
|     return (const char **)subdirs;
 | |
| }
 | |
| 
 | |
| static void
 | |
| FreeSubdirs(const char **subdirs)
 | |
| {
 | |
|     const char **s;
 | |
| 
 | |
|     if (subdirs) {
 | |
| 	for (s = subdirs; *s; s++)
 | |
| 	    xfree(*s);
 | |
| 	xfree(subdirs);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static char *
 | |
| FindModule(const char *module, const char *dir, const char **subdirlist,
 | |
| 	   PatternPtr patterns)
 | |
| {
 | |
|     char buf[PATH_MAX + 1];
 | |
|     char *dirpath = NULL;
 | |
|     char *name = NULL;
 | |
|     struct stat stat_buf;
 | |
|     int len, dirlen;
 | |
|     char *fp;
 | |
|     DIR *d;
 | |
|     const char **subdirs = NULL;
 | |
|     PatternPtr p = NULL;
 | |
|     const char **s;
 | |
|     struct dirent *dp;
 | |
|     regmatch_t match[2];
 | |
| 
 | |
|     subdirs = InitSubdirs(subdirlist);
 | |
|     if (!subdirs)
 | |
| 	return NULL;
 | |
| 
 | |
| #ifndef __EMX__
 | |
|     dirpath = (char *)dir;
 | |
| #else
 | |
|     dirpath = xalloc(strlen(dir) + 10);
 | |
|     strcpy(dirpath, (char *)__XOS2RedirRoot(dir));
 | |
| #endif
 | |
|     if (strlen(dirpath) > PATH_MAX)
 | |
| 	return NULL;
 | |
|     /*xf86Msg(X_INFO,"OS2DIAG: FindModule: dirpath=%s\n",dirpath); */
 | |
| 
 | |
|     for (s = subdirs; *s; s++) {
 | |
| 	if ((dirlen = strlen(dirpath) + strlen(*s)) > PATH_MAX)
 | |
| 	    continue;
 | |
| 	strcpy(buf, dirpath);
 | |
| 	strcat(buf, *s);
 | |
| 	/*xf86Msg(X_INFO,"OS2DIAG: FindModule: buf=%s\n",buf); */
 | |
| 	fp = buf + dirlen;
 | |
| 	if (stat(buf, &stat_buf) == 0 && S_ISDIR(stat_buf.st_mode) &&
 | |
| 	    (d = opendir(buf))) {
 | |
| 	    if (buf[dirlen - 1] != '/') {
 | |
| 		buf[dirlen++] = '/';
 | |
| 		fp++;
 | |
| 	    }
 | |
| 	    while ((dp = readdir(d))) {
 | |
| 		if (dirlen + strlen(dp->d_name) + 1 > PATH_MAX)
 | |
| 		    continue;
 | |
| 		strcpy(fp, dp->d_name);
 | |
| 		if (!(stat(buf, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode)))
 | |
| 		    continue;
 | |
| 		for (p = patterns; p->pattern; p++) {
 | |
| 		    if (regexec(&p->rex, dp->d_name, 2, match, 0) == 0 &&
 | |
| 			match[1].rm_so != -1) {
 | |
| 			len = match[1].rm_eo - match[1].rm_so;
 | |
| 			if (len == strlen(module) &&
 | |
| 			    strncmp(module, dp->d_name + match[1].rm_so,
 | |
| 				    len) == 0) {
 | |
| 			    /*xf86Msg(X_INFO,"OS2DIAG: matching %s\n",buf); */
 | |
| 			    name = buf;
 | |
| 			    break;
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 		if (name)
 | |
| 		    break;
 | |
| 	    }
 | |
| 	    closedir(d);
 | |
| 	    if (name)
 | |
| 		break;
 | |
| 	}
 | |
|     }
 | |
|     FreeSubdirs(subdirs);
 | |
|     if (dirpath != dir)
 | |
| 	xfree(dirpath);
 | |
| 
 | |
|     if (name) {
 | |
| 	return xstrdup(name);
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| char **
 | |
| LoaderListDirs(const char **subdirlist, const char **patternlist)
 | |
| {
 | |
|     char buf[PATH_MAX + 1];
 | |
|     char **pathlist;
 | |
|     char **elem;
 | |
|     const char **subdirs;
 | |
|     const char **s;
 | |
|     PatternPtr patterns;
 | |
|     PatternPtr p;
 | |
|     DIR *d;
 | |
|     struct dirent *dp;
 | |
|     regmatch_t match[2];
 | |
|     struct stat stat_buf;
 | |
|     int len, dirlen;
 | |
|     char *fp;
 | |
|     char **listing = NULL;
 | |
|     char **save;
 | |
|     int n = 0;
 | |
| 
 | |
|     if (!(pathlist = InitPathList(NULL)))
 | |
| 	return NULL;
 | |
|     if (!(subdirs = InitSubdirs(subdirlist))) {
 | |
| 	FreePathList(pathlist);
 | |
| 	return NULL;
 | |
|     }
 | |
|     if (!(patterns = InitPatterns(patternlist))) {
 | |
| 	FreePathList(pathlist);
 | |
| 	FreeSubdirs(subdirs);
 | |
| 	return NULL;
 | |
|     }
 | |
| 
 | |
|     for (elem = pathlist; *elem; elem++) {
 | |
| 	for (s = subdirs; *s; s++) {
 | |
| 	    if ((dirlen = strlen(*elem) + strlen(*s)) > PATH_MAX)
 | |
| 		continue;
 | |
| 	    strcpy(buf, *elem);
 | |
| 	    strcat(buf, *s);
 | |
| 	    fp = buf + dirlen;
 | |
| 	    if (stat(buf, &stat_buf) == 0 && S_ISDIR(stat_buf.st_mode) &&
 | |
| 		(d = opendir(buf))) {
 | |
| 		if (buf[dirlen - 1] != '/') {
 | |
| 		    buf[dirlen++] = '/';
 | |
| 		    fp++;
 | |
| 		}
 | |
| 		while ((dp = readdir(d))) {
 | |
| 		    if (dirlen + strlen(dp->d_name) > PATH_MAX)
 | |
| 			continue;
 | |
| 		    strcpy(fp, dp->d_name);
 | |
| 		    if (!(stat(buf, &stat_buf) == 0 &&
 | |
| 			  S_ISREG(stat_buf.st_mode)))
 | |
| 			continue;
 | |
| 		    for (p = patterns; p->pattern; p++) {
 | |
| 			if (regexec(&p->rex, dp->d_name, 2, match, 0) == 0 &&
 | |
| 			    match[1].rm_so != -1) {
 | |
| 			    len = match[1].rm_eo - match[1].rm_so;
 | |
| 			    save = listing;
 | |
| 			    listing = xrealloc(listing,
 | |
| 					       (n + 2) * sizeof(char *));
 | |
| 			    if (!listing) {
 | |
| 				if (save) {
 | |
| 				    save[n] = NULL;
 | |
| 				    FreeStringList(save);
 | |
| 				}
 | |
| 				FreePathList(pathlist);
 | |
| 				FreeSubdirs(subdirs);
 | |
| 				FreePatterns(patterns);
 | |
| 				return NULL;
 | |
| 			    }
 | |
| 			    listing[n] = xalloc(len + 1);
 | |
| 			    if (!listing[n]) {
 | |
| 				FreeStringList(listing);
 | |
| 				FreePathList(pathlist);
 | |
| 				FreeSubdirs(subdirs);
 | |
| 				FreePatterns(patterns);
 | |
| 				return NULL;
 | |
| 			    }
 | |
| 			    strncpy(listing[n], dp->d_name + match[1].rm_so,
 | |
| 				    len);
 | |
| 			    listing[n][len] = '\0';
 | |
| 			    n++;
 | |
| 			    break;
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 		closedir(d);
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     if (listing)
 | |
| 	listing[n] = NULL;
 | |
|     return listing;
 | |
| }
 | |
| 
 | |
| void
 | |
| LoaderFreeDirList(char **list)
 | |
| {
 | |
|     FreeStringList(list);
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| CheckVersion(const char *module, XF86ModuleVersionInfo * data,
 | |
| 	     const XF86ModReqInfo * req)
 | |
| {
 | |
|     int vercode[4];
 | |
|     char verstr[4];
 | |
|     long ver = data->xf86version;
 | |
|     MessageType errtype;
 | |
| 
 | |
|     xf86Msg(X_INFO, "Module %s: vendor=\"%s\"\n",
 | |
| 	    data->modname ? data->modname : "UNKNOWN!",
 | |
| 	    data->vendor ? data->vendor : "UNKNOWN!");
 | |
| 
 | |
|     if (ver > (4 << 24)) {
 | |
| 	/* 4.0.x and earlier */
 | |
| 	verstr[1] = verstr[3] = 0;
 | |
| 	verstr[2] = (ver & 0x1f) ? (ver & 0x1f) + 'a' - 1 : 0;
 | |
| 	ver >>= 5;
 | |
| 	verstr[0] = (ver & 0x1f) ? (ver & 0x1f) + 'A' - 1 : 0;
 | |
| 	ver >>= 5;
 | |
| 	vercode[2] = ver & 0x7f;
 | |
| 	ver >>= 7;
 | |
| 	vercode[1] = ver & 0x7f;
 | |
| 	ver >>= 7;
 | |
| 	vercode[0] = ver;
 | |
| 	xf86ErrorF("\tcompiled for %d.%d", vercode[0], vercode[1]);
 | |
| 	if (vercode[2] != 0)
 | |
| 	    xf86ErrorF(".%d", vercode[2]);
 | |
| 	xf86ErrorF("%s%s, module version = %d.%d.%d\n", verstr, verstr + 2,
 | |
| 		   data->majorversion, data->minorversion, data->patchlevel);
 | |
|     } else {
 | |
| 	vercode[0] = ver / 10000000;
 | |
| 	vercode[1] = (ver / 100000) % 100;
 | |
| 	vercode[2] = (ver / 1000) % 100;
 | |
| 	vercode[3] = ver % 1000;
 | |
| 	xf86ErrorF("\tcompiled for %d.%d.%d", vercode[0], vercode[1],
 | |
| 		   vercode[2]);
 | |
| 	if (vercode[3] != 0)
 | |
| 	    xf86ErrorF(".%d", vercode[3]);
 | |
| 	xf86ErrorF(", module version = %d.%d.%d\n", data->majorversion,
 | |
| 		   data->minorversion, data->patchlevel);
 | |
|     }
 | |
| 
 | |
|     if (data->moduleclass)
 | |
| 	xf86ErrorFVerb(2, "\tModule class: %s\n", data->moduleclass);
 | |
| 
 | |
|     ver = -1;
 | |
|     if (data->abiclass) {
 | |
| 	int abimaj, abimin;
 | |
| 	int vermaj, vermin;
 | |
| 
 | |
| 	if (!strcmp(data->abiclass, ABI_CLASS_ANSIC))
 | |
| 	    ver = LoaderVersionInfo.ansicVersion;
 | |
| 	else if (!strcmp(data->abiclass, ABI_CLASS_VIDEODRV))
 | |
| 	    ver = LoaderVersionInfo.videodrvVersion;
 | |
| 	else if (!strcmp(data->abiclass, ABI_CLASS_XINPUT))
 | |
| 	    ver = LoaderVersionInfo.xinputVersion;
 | |
| 	else if (!strcmp(data->abiclass, ABI_CLASS_EXTENSION))
 | |
| 	    ver = LoaderVersionInfo.extensionVersion;
 | |
| 	else if (!strcmp(data->abiclass, ABI_CLASS_FONT))
 | |
| 	    ver = LoaderVersionInfo.fontVersion;
 | |
| 
 | |
| 	abimaj = GET_ABI_MAJOR(data->abiversion);
 | |
| 	abimin = GET_ABI_MINOR(data->abiversion);
 | |
| 	xf86ErrorFVerb(2, "\tABI class: %s, version %d.%d\n",
 | |
| 		       data->abiclass, abimaj, abimin);
 | |
| 	if (ver != -1) {
 | |
| 	    vermaj = GET_ABI_MAJOR(ver);
 | |
| 	    vermin = GET_ABI_MINOR(ver);
 | |
| 	    if (abimaj != vermaj) {
 | |
| 		if (LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL)
 | |
| 		    errtype = X_WARNING;
 | |
| 		else
 | |
| 		    errtype = X_ERROR;
 | |
| 		xf86MsgVerb(errtype, 0,
 | |
| 			    "module ABI major version (%d) doesn't"
 | |
| 			    " match the server's version (%d)\n",
 | |
| 			    abimaj, vermaj);
 | |
| 		if (!(LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL))
 | |
| 		    return FALSE;
 | |
| 	    } else if (abimin > vermin) {
 | |
| 		if (LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL)
 | |
| 		    errtype = X_WARNING;
 | |
| 		else
 | |
| 		    errtype = X_ERROR;
 | |
| 		xf86MsgVerb(errtype, 0,
 | |
| 			    "module ABI minor version (%d) is "
 | |
| 			    "newer than the server's version "
 | |
| 			    "(%d)\n", abimin, vermin);
 | |
| 		if (!(LoaderOptions & LDR_OPT_ABI_MISMATCH_NONFATAL))
 | |
| 		    return FALSE;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     /* Check against requirements that the caller has specified */
 | |
|     if (req) {
 | |
| 	if (req->majorversion != MAJOR_UNSPEC) {
 | |
| 	    if (data->majorversion != req->majorversion) {
 | |
| 		xf86MsgVerb(X_WARNING, 2, "module major version (%d) "
 | |
| 			    "doesn't match required major version (%d)\n",
 | |
| 			    data->majorversion, req->majorversion);
 | |
| 		return FALSE;
 | |
| 	    } else if (req->minorversion != MINOR_UNSPEC) {
 | |
| 		if (data->minorversion < req->minorversion) {
 | |
| 		    xf86MsgVerb(X_WARNING, 2, "module minor version (%d) "
 | |
| 				"is less than the required minor version (%d)\n",
 | |
| 				data->minorversion, req->minorversion);
 | |
| 		    return FALSE;
 | |
| 		} else if (data->minorversion == req->minorversion &&
 | |
| 			   req->patchlevel != PATCH_UNSPEC) {
 | |
| 		    if (data->patchlevel < req->patchlevel) {
 | |
| 			xf86MsgVerb(X_WARNING, 2, "module patch level (%d) "
 | |
| 				    "is less than the required patch level (%d)\n",
 | |
| 				    data->patchlevel, req->patchlevel);
 | |
| 			return FALSE;
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 	if (req->moduleclass) {
 | |
| 	    if (!data->moduleclass ||
 | |
| 		strcmp(req->moduleclass, data->moduleclass)) {
 | |
| 		xf86MsgVerb(X_WARNING, 2, "Module class (%s) doesn't match "
 | |
| 			    "the required class (%s)\n",
 | |
| 			    data->moduleclass ? data->moduleclass : "<NONE>",
 | |
| 			    req->moduleclass);
 | |
| 		return FALSE;
 | |
| 	    }
 | |
| 	} else if (req->abiclass != ABI_CLASS_NONE) {
 | |
| 	    if (!data->abiclass || strcmp(req->abiclass, data->moduleclass)) {
 | |
| 		xf86MsgVerb(X_WARNING, 2, "ABI class (%s) doesn't match the "
 | |
| 			    "required ABI class (%s)\n",
 | |
| 			    data->abiclass ? data->abiclass : "<NONE>",
 | |
| 			    req->abiclass);
 | |
| 		return FALSE;
 | |
| 	    }
 | |
| 	}
 | |
| 	if ((req->abiclass != ABI_CLASS_NONE) &&
 | |
| 	    req->abiversion != ABI_VERS_UNSPEC) {
 | |
| 	    int reqmaj, reqmin, maj, min;
 | |
| 
 | |
| 	    reqmaj = GET_ABI_MAJOR(req->abiversion);
 | |
| 	    reqmin = GET_ABI_MINOR(req->abiversion);
 | |
| 	    maj = GET_ABI_MAJOR(data->abiversion);
 | |
| 	    min = GET_ABI_MINOR(data->abiversion);
 | |
| 	    if (maj != reqmaj) {
 | |
| 		xf86MsgVerb(X_WARNING, 2, "ABI major version (%d) doesn't "
 | |
| 			    "match the required ABI major version (%d)\n",
 | |
| 			    maj, reqmaj);
 | |
| 		return FALSE;
 | |
| 	    }
 | |
| 	    /* XXX Maybe this should be the other way around? */
 | |
| 	    if (min > reqmin) {
 | |
| 		xf86MsgVerb(X_WARNING, 2, "module ABI minor version (%d) "
 | |
| 			    "is new than that available (%d)\n", min, reqmin);
 | |
| 		return FALSE;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| #ifdef NOTYET
 | |
|     if (data->checksum) {
 | |
| 	/* verify the checksum field */
 | |
| 	/* TO BE DONE */
 | |
|     } else {
 | |
| 	ErrorF("\t*** Checksum field is 0 - this module is untrusted!\n");
 | |
|     }
 | |
| #endif
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| ModuleDescPtr
 | |
| LoadSubModule(ModuleDescPtr parent, const char *module,
 | |
| 	      const char **subdirlist, const char **patternlist,
 | |
| 	      pointer options, const XF86ModReqInfo * modreq,
 | |
| 	      int *errmaj, int *errmin)
 | |
| {
 | |
|     ModuleDescPtr submod;
 | |
| 
 | |
|     xf86MsgVerb(X_INFO, 3, "Loading sub module \"%s\"\n", module);
 | |
| 
 | |
|     /* Absolute module paths are not allowed here */
 | |
| #ifndef __UNIXOS2__
 | |
|     if (module[0] == '/')
 | |
| #else
 | |
|     if (isalpha(module[0]) && module[1] == ':' && module[2] == '/')
 | |
| #endif
 | |
|     {
 | |
| 	xf86Msg(X_ERROR,
 | |
| 		"LoadSubModule: Absolute module path not permitted: \"%s\"\n",
 | |
| 		module);
 | |
| 	if (errmaj)
 | |
| 	    *errmaj = LDR_BADUSAGE;
 | |
| 	if (errmin)
 | |
| 	    *errmin = 0;
 | |
| 	return NULL;
 | |
|     }
 | |
| 
 | |
|     submod = LoadModule(module, NULL, subdirlist, patternlist, options,
 | |
| 			modreq, errmaj, errmin);
 | |
|     if (submod) {
 | |
| 	parent->child = AddSibling(parent->child, submod);
 | |
| 	submod->parent = parent;
 | |
|     }
 | |
|     return submod;
 | |
| }
 | |
| 
 | |
| ModuleDescPtr
 | |
| DuplicateModule(ModuleDescPtr mod, ModuleDescPtr parent)
 | |
| {
 | |
|     ModuleDescPtr ret;
 | |
| 
 | |
|     if (!mod)
 | |
| 	return NULL;
 | |
| 
 | |
|     ret = NewModuleDesc(mod->name);
 | |
|     if (ret == NULL)
 | |
| 	return NULL;
 | |
| 
 | |
|     if (LoaderHandleOpen(mod->handle) == -1)
 | |
| 	return NULL;
 | |
| 
 | |
|     ret->filename = xstrdup(mod->filename);
 | |
|     ret->identifier = mod->identifier;
 | |
|     ret->client_id = mod->client_id;
 | |
|     ret->in_use = mod->in_use;
 | |
|     ret->handle = mod->handle;
 | |
|     ret->SetupProc = mod->SetupProc;
 | |
|     ret->TearDownProc = mod->TearDownProc;
 | |
|     ret->TearDownData = NULL;
 | |
|     ret->path = mod->path;
 | |
|     ret->child = DuplicateModule(mod->child, ret);
 | |
|     ret->sib = DuplicateModule(mod->sib, parent);
 | |
|     ret->parent = parent;
 | |
|     ret->VersionInfo = mod->VersionInfo;
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * LoadModule: load a module
 | |
|  *
 | |
|  * module       The module name.  Normally this is not a filename but the
 | |
|  *              module's "canonical name.  A full pathname is, however,
 | |
|  *              also accepted.
 | |
|  * path         A comma separated list of module directories.
 | |
|  * subdirlist   A NULL terminated list of subdirectories to search.  When
 | |
|  *              NULL, the default "stdSubdirs" list is used.  The default
 | |
|  *              list is also substituted for entries with value DEFAULT_LIST.
 | |
|  * patternlist  A NULL terminated list of regular expressions used to find
 | |
|  *              module filenames.  Each regex should contain exactly one
 | |
|  *              subexpression that corresponds to the canonical module name.
 | |
|  *              When NULL, the default "stdPatterns" list is used.  The
 | |
|  *              default list is also substituted for entries with value
 | |
|  *              DEFAULT_LIST.
 | |
|  * options      A NULL terminated list of Options that are passed to the
 | |
|  *              module's SetupProc function.
 | |
|  * modreq       An optional XF86ModReqInfo* containing
 | |
|  *              version/ABI/vendor-ABI requirements to check for when
 | |
|  *              loading the module.  The following fields of the
 | |
|  *              XF86ModReqInfo struct are checked:
 | |
|  *                majorversion - must match the module's majorversion exactly
 | |
|  *                minorversion - the module's minorversion must be >= this
 | |
|  *                patchlevel   - the module's minorversion.patchlevel must be
 | |
|  *                               >= this.  Patchlevel is ignored when
 | |
|  *                               minorversion is not set.
 | |
|  *                abiclass     - (string) must match the module's abiclass
 | |
|  *                abiversion   - must be consistent with the module's
 | |
|  *                               abiversion (major equal, minor no older)
 | |
|  *                moduleclass  - string must match the module's moduleclass
 | |
|  *                               string
 | |
|  *              "don't care" values are ~0 for numbers, and NULL for strings
 | |
|  * errmaj       Major error return.
 | |
|  * errmin       Minor error return.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| ModuleDescPtr
 | |
| LoadModule(const char *module, const char *path, const char **subdirlist,
 | |
| 	   const char **patternlist, pointer options,
 | |
| 	   const XF86ModReqInfo * modreq, int *errmaj, int *errmin)
 | |
| {
 | |
|     XF86ModuleData *initdata = NULL;
 | |
|     char **pathlist = NULL;
 | |
|     char *found = NULL;
 | |
|     char *name = NULL;
 | |
|     char **path_elem = NULL;
 | |
|     char *p = NULL;
 | |
|     ModuleDescPtr ret = NULL;
 | |
|     int wasLoaded = 0;
 | |
|     PatternPtr patterns = NULL;
 | |
|     int noncanonical = 0;
 | |
|     char *m = NULL;
 | |
| 
 | |
|     /*xf86Msg(X_INFO,"OS2DIAG: LoadModule: %s\n",module); */
 | |
|     xf86MsgVerb(X_INFO, 3, "LoadModule: \"%s\"", module);
 | |
| 
 | |
|     patterns = InitPatterns(patternlist);
 | |
|     name = LoaderGetCanonicalName(module, patterns);
 | |
|     noncanonical = (name && strcmp(module, name) != 0);
 | |
|     if (noncanonical) {
 | |
| 	xf86ErrorFVerb(3, " (%s)\n", name);
 | |
| 	xf86MsgVerb(X_WARNING, 1,
 | |
| 		    "LoadModule: given non-canonical module name \"%s\"\n",
 | |
| 		    module);
 | |
| 	m = name;
 | |
|     } else {
 | |
| 	xf86ErrorFVerb(3, "\n");
 | |
| 	m = (char *)module;
 | |
|     }
 | |
|     if (!name) {
 | |
| 	if (errmaj)
 | |
| 	    *errmaj = LDR_BADUSAGE;
 | |
| 	if (errmin)
 | |
| 	    *errmin = 0;
 | |
| 	goto LoadModule_fail;
 | |
|     }
 | |
|     ret = NewModuleDesc(name);
 | |
|     if (!ret) {
 | |
| 	if (errmaj)
 | |
| 	    *errmaj = LDR_NOMEM;
 | |
| 	if (errmin)
 | |
| 	    *errmin = 0;
 | |
| 	goto LoadModule_fail;
 | |
|     }
 | |
| 
 | |
|     pathlist = InitPathList(path);
 | |
|     if (!pathlist) {
 | |
| 	/* This could be a malloc failure too */
 | |
| 	if (errmaj)
 | |
| 	    *errmaj = LDR_BADUSAGE;
 | |
| 	if (errmin)
 | |
| 	    *errmin = 1;
 | |
| 	goto LoadModule_fail;
 | |
|     }
 | |
| 
 | |
|     /* 
 | |
|      * if the module name is not a full pathname, we need to
 | |
|      * check the elements in the path
 | |
|      */
 | |
| #ifndef __UNIXOS2__
 | |
|     if (module[0] == '/')
 | |
| 	found = xstrdup(module);
 | |
| #else
 | |
|     /* accept a drive name here */
 | |
|     if (isalpha(module[0]) && module[1] == ':' && module[2] == '/')
 | |
| 	found = xstrdup(module);
 | |
| #endif
 | |
|     path_elem = pathlist;
 | |
|     while (!found && *path_elem != NULL) {
 | |
| 	found = FindModule(m, *path_elem, subdirlist, patterns);
 | |
| 	path_elem++;
 | |
| 	/*
 | |
| 	 * When the module name isn't the canonical name, search for the
 | |
| 	 * former if no match was found for the latter.
 | |
| 	 */
 | |
| 	if (!*path_elem && m == name) {
 | |
| 	    path_elem = pathlist;
 | |
| 	    m = (char *)module;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     /* 
 | |
|      * did we find the module?
 | |
|      */
 | |
|     if (!found) {
 | |
| 	xf86Msg(X_WARNING, "Warning, couldn't open module %s\n", module);
 | |
| 	if (errmaj)
 | |
| 	    *errmaj = LDR_NOENT;
 | |
| 	if (errmin)
 | |
| 	    *errmin = 0;
 | |
| 	goto LoadModule_fail;
 | |
|     }
 | |
|     ret->handle = LoaderOpen(found, name, 0, errmaj, errmin, &wasLoaded);
 | |
|     if (ret->handle < 0)
 | |
| 	goto LoadModule_fail;
 | |
| 
 | |
|     ret->filename = xstrdup(found);
 | |
| 
 | |
|     /*
 | |
|      * now check if the special data object <modulename>ModuleData is
 | |
|      * present.
 | |
|      */
 | |
|     p = xalloc(strlen(name) + strlen("ModuleData") + 1);
 | |
|     if (!p) {
 | |
| 	if (errmaj)
 | |
| 	    *errmaj = LDR_NOMEM;
 | |
| 	if (errmin)
 | |
| 	    *errmin = 0;
 | |
| 	goto LoadModule_fail;
 | |
|     }
 | |
|     strcpy(p, name);
 | |
|     strcat(p, "ModuleData");
 | |
|     initdata = LoaderSymbol(p);
 | |
|     if (initdata) {
 | |
| 	ModuleSetupProc setup;
 | |
| 	ModuleTearDownProc teardown;
 | |
| 	XF86ModuleVersionInfo *vers;
 | |
| 
 | |
| 	vers = initdata->vers;
 | |
| 	setup = initdata->setup;
 | |
| 	teardown = initdata->teardown;
 | |
| 
 | |
| 	if (!wasLoaded) {
 | |
| 	    if (vers) {
 | |
| 		if (!CheckVersion(module, vers, modreq)) {
 | |
| 		    if (errmaj)
 | |
| 			*errmaj = LDR_MISMATCH;
 | |
| 		    if (errmin)
 | |
| 			*errmin = 0;
 | |
| 		    goto LoadModule_fail;
 | |
| 		}
 | |
| 	    } else {
 | |
| 		xf86Msg(X_ERROR,
 | |
| 			"LoadModule: Module %s does not supply"
 | |
| 			" version information\n", module);
 | |
| 		if (errmaj)
 | |
| 		    *errmaj = LDR_INVALID;
 | |
| 		if (errmin)
 | |
| 		    *errmin = 0;
 | |
| 		goto LoadModule_fail;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (setup)
 | |
| 	    ret->SetupProc = setup;
 | |
| 	if (teardown)
 | |
| 	    ret->TearDownProc = teardown;
 | |
| 	ret->path = path;
 | |
| 	ret->VersionInfo = vers;
 | |
|     } else {
 | |
| 	/* No initdata is OK for external modules */
 | |
| 	if (options == EXTERN_MODULE)
 | |
| 	    goto LoadModule_exit;
 | |
| 
 | |
| 	/* no initdata, fail the load */
 | |
| 	xf86Msg(X_ERROR, "LoadModule: Module %s does not have a %s "
 | |
| 		"data object.\n", module, p);
 | |
| 	if (errmaj)
 | |
| 	    *errmaj = LDR_INVALID;
 | |
| 	if (errmin)
 | |
| 	    *errmin = 0;
 | |
| 	goto LoadModule_fail;
 | |
|     }
 | |
|     if (ret->SetupProc) {
 | |
| 	ret->TearDownData = ret->SetupProc(ret, options, errmaj, errmin);
 | |
| 	if (!ret->TearDownData) {
 | |
| 	    goto LoadModule_fail;
 | |
| 	}
 | |
|     } else if (options) {
 | |
| 	xf86Msg(X_WARNING, "Module Options present, but no SetupProc "
 | |
| 		"available for %s\n", module);
 | |
|     }
 | |
|     goto LoadModule_exit;
 | |
| 
 | |
|   LoadModule_fail:
 | |
|     UnloadModule(ret);
 | |
|     ret = NULL;
 | |
| 
 | |
|   LoadModule_exit:
 | |
|     FreePathList(pathlist);
 | |
|     FreePatterns(patterns);
 | |
|     TestFree(found);
 | |
|     TestFree(name);
 | |
|     TestFree(p);
 | |
| 
 | |
|     /*
 | |
|      * If you need to do something to keep the
 | |
|      * instruction cache in sync with the main
 | |
|      * memory before jumping to that code, you may
 | |
|      * do it here.
 | |
|      */
 | |
| #ifdef __alpha__
 | |
|     istream_mem_barrier();
 | |
| #endif
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| ModuleDescPtr
 | |
| LoadDriver(const char *module, const char *path, int handle, pointer options,
 | |
| 	   int *errmaj, int *errmin)
 | |
| {
 | |
|     return LoadModule(module, path, NULL, NULL, options, NULL, errmaj,
 | |
| 		      errmin);
 | |
| }
 | |
| 
 | |
| void
 | |
| UnloadModule(ModuleDescPtr mod)
 | |
| {
 | |
|     UnloadModuleOrDriver(mod);
 | |
| }
 | |
| 
 | |
| void
 | |
| UnloadDriver(ModuleDescPtr mod)
 | |
| {
 | |
|     UnloadModuleOrDriver(mod);
 | |
| }
 | |
| 
 | |
| static void
 | |
| UnloadModuleOrDriver(ModuleDescPtr mod)
 | |
| {
 | |
|     if (mod == NULL || mod->name == NULL)
 | |
| 	return;
 | |
| 
 | |
|     xf86MsgVerb(X_INFO, 3, "UnloadModule: \"%s\"\n", mod->name);
 | |
| 
 | |
|     if ((mod->TearDownProc) && (mod->TearDownData))
 | |
| 	mod->TearDownProc(mod->TearDownData);
 | |
|     LoaderUnload(mod->handle);
 | |
| 
 | |
|     if (mod->child)
 | |
| 	UnloadModuleOrDriver(mod->child);
 | |
|     if (mod->sib)
 | |
| 	UnloadModuleOrDriver(mod->sib);
 | |
|     TestFree(mod->name);
 | |
|     TestFree(mod->filename);
 | |
|     xfree(mod);
 | |
| #ifdef __alpha__
 | |
|     istream_mem_barrier();
 | |
| #endif
 | |
| }
 | |
| 
 | |
| void
 | |
| UnloadSubModule(ModuleDescPtr mod)
 | |
| {
 | |
|     if (mod == NULL || mod->name == NULL)
 | |
| 	return;
 | |
| 
 | |
|     xf86MsgVerb(X_INFO, 3, "UnloadSubModule: \"%s\"\n", mod->name);
 | |
| 
 | |
|     if ((mod->TearDownProc) && (mod->TearDownData))
 | |
| 	mod->TearDownProc(mod->TearDownData);
 | |
|     LoaderUnload(mod->handle);
 | |
| 
 | |
|     RemoveChild(mod);
 | |
| 
 | |
|     if (mod->child)
 | |
| 	UnloadModuleOrDriver(mod->child);
 | |
| 
 | |
|     TestFree(mod->name);
 | |
|     TestFree(mod->filename);
 | |
|     xfree(mod);
 | |
| }
 | |
| 
 | |
| void
 | |
| FreeModuleDesc(ModuleDescPtr head)
 | |
| {
 | |
|     ModuleDescPtr sibs, prev;
 | |
| 
 | |
|     /*
 | |
|      * only free it if it's not marked as in use. In use means that it may
 | |
|      * be unloaded someday, and UnloadModule or UnloadDriver will free it
 | |
|      */
 | |
|     if (head->in_use)
 | |
| 	return;
 | |
|     if (head->child)
 | |
| 	FreeModuleDesc(head->child);
 | |
|     sibs = head;
 | |
|     while (sibs) {
 | |
| 	prev = sibs;
 | |
| 	sibs = sibs->sib;
 | |
| 	TestFree(prev->name);
 | |
| 	xfree(prev);
 | |
|     }
 | |
| }
 | |
| 
 | |
| ModuleDescPtr
 | |
| NewModuleDesc(const char *name)
 | |
| {
 | |
|     ModuleDescPtr mdp = xalloc(sizeof(ModuleDesc));
 | |
| 
 | |
|     if (mdp) {
 | |
| 	mdp->child = NULL;
 | |
| 	mdp->sib = NULL;
 | |
| 	mdp->parent = NULL;
 | |
| 	mdp->demand_next = NULL;
 | |
| 	mdp->name = xstrdup(name);
 | |
| 	mdp->filename = NULL;
 | |
| 	mdp->identifier = NULL;
 | |
| 	mdp->client_id = 0;
 | |
| 	mdp->in_use = 0;
 | |
| 	mdp->handle = -1;
 | |
| 	mdp->SetupProc = NULL;
 | |
| 	mdp->TearDownProc = NULL;
 | |
| 	mdp->TearDownData = NULL;
 | |
|     }
 | |
| 
 | |
|     return (mdp);
 | |
| }
 | |
| 
 | |
| ModuleDescPtr
 | |
| AddSibling(ModuleDescPtr head, ModuleDescPtr new)
 | |
| {
 | |
|     new->sib = head;
 | |
|     return (new);
 | |
| 
 | |
| }
 | |
| 
 | |
| static void
 | |
| RemoveChild(ModuleDescPtr child)
 | |
| {
 | |
|     ModuleDescPtr mdp;
 | |
|     ModuleDescPtr prevsib;
 | |
|     ModuleDescPtr parent;
 | |
| 
 | |
|     if (!child->parent)
 | |
| 	return;
 | |
| 
 | |
|     parent = child->parent;
 | |
|     if (parent->child == child) {
 | |
| 	parent->child = child->sib;
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|     prevsib = parent->child;
 | |
|     mdp = prevsib->sib;
 | |
|     while (mdp && mdp != child) {
 | |
| 	prevsib = mdp;
 | |
| 	mdp = mdp->sib;
 | |
|     }
 | |
|     if (mdp == child)
 | |
| 	prevsib->sib = child->sib;
 | |
|     return;
 | |
| }
 | |
| 
 | |
| void
 | |
| LoaderErrorMsg(const char *name, const char *modname, int errmaj, int errmin)
 | |
| {
 | |
|     const char *msg;
 | |
| 
 | |
|     switch (errmaj) {
 | |
|     case LDR_NOERROR:
 | |
| 	msg = "no error";
 | |
| 	break;
 | |
|     case LDR_NOMEM:
 | |
| 	msg = "out of memory";
 | |
| 	break;
 | |
|     case LDR_NOENT:
 | |
| 	msg = "module does not exist";
 | |
| 	break;
 | |
|     case LDR_NOSUBENT:
 | |
| 	msg = "a required submodule could not be loaded";
 | |
| 	break;
 | |
|     case LDR_NOSPACE:
 | |
| 	msg = "too many modules";
 | |
| 	break;
 | |
|     case LDR_NOMODOPEN:
 | |
| 	msg = "open failed";
 | |
| 	break;
 | |
|     case LDR_UNKTYPE:
 | |
| 	msg = "unknown module type";
 | |
| 	break;
 | |
|     case LDR_NOLOAD:
 | |
| 	msg = "loader failed";
 | |
| 	break;
 | |
|     case LDR_ONCEONLY:
 | |
| 	msg = "once-only module";
 | |
| 	break;
 | |
|     case LDR_NOPORTOPEN:
 | |
| 	msg = "port open failed";
 | |
| 	break;
 | |
|     case LDR_NOHARDWARE:
 | |
| 	msg = "no hardware found";
 | |
| 	break;
 | |
|     case LDR_MISMATCH:
 | |
| 	msg = "module requirement mismatch";
 | |
| 	break;
 | |
|     case LDR_BADUSAGE:
 | |
| 	msg = "invalid argument(s) to LoadModule()";
 | |
| 	break;
 | |
|     case LDR_INVALID:
 | |
| 	msg = "invalid module";
 | |
| 	break;
 | |
|     case LDR_BADOS:
 | |
| 	msg = "module doesn't support this OS";
 | |
| 	break;
 | |
|     case LDR_MODSPECIFIC:
 | |
| 	msg = "module-specific error";
 | |
| 	break;
 | |
|     default:
 | |
| 	msg = "uknown error";
 | |
|     }
 | |
|     if (name)
 | |
| 	xf86Msg(X_ERROR, "%s: Failed to load module \"%s\" (%s, %d)\n",
 | |
| 		name, modname, msg, errmin);
 | |
|     else
 | |
| 	xf86Msg(X_ERROR, "Failed to load module \"%s\" (%s, %d)\n",
 | |
| 		modname, msg, errmin);
 | |
| }
 | |
| 
 | |
| /* Given a module path or file name, return the module's canonical name */
 | |
| static char *
 | |
| LoaderGetCanonicalName(const char *modname, PatternPtr patterns)
 | |
| {
 | |
|     char *str;
 | |
|     const char *s;
 | |
|     int len;
 | |
|     PatternPtr p;
 | |
|     regmatch_t match[2];
 | |
| 
 | |
|     /* Strip off any leading path */
 | |
|     s = strrchr(modname, '/');
 | |
|     if (s == NULL)
 | |
| 	s = modname;
 | |
|     else
 | |
| 	s++;
 | |
| 
 | |
|     /* Find the first regex that is matched */
 | |
|     for (p = patterns; p->pattern; p++)
 | |
| 	if (regexec(&p->rex, s, 2, match, 0) == 0 && match[1].rm_so != -1) {
 | |
| 	    len = match[1].rm_eo - match[1].rm_so;
 | |
| 	    str = xalloc(len + 1);
 | |
| 	    if (!str)
 | |
| 		return NULL;
 | |
| 	    strncpy(str, s + match[1].rm_so, len);
 | |
| 	    str[len] = '\0';
 | |
| 	    return str;
 | |
| 	}
 | |
| 
 | |
|     /* If there is no match, return the whole name minus the leading path */
 | |
|     return xstrdup(s);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Return the module version information.
 | |
|  */
 | |
| unsigned long
 | |
| LoaderGetModuleVersion(ModuleDescPtr mod)
 | |
| {
 | |
|     if (!mod || !mod->VersionInfo)
 | |
| 	return 0;
 | |
| 
 | |
|     return MODULE_VERSION_NUMERIC(mod->VersionInfo->majorversion,
 | |
| 				  mod->VersionInfo->minorversion,
 | |
| 				  mod->VersionInfo->patchlevel);
 | |
| }
 |