diff options
Diffstat (limited to 'xorg-server/hw/xfree86/loader/loadmod.c')
-rw-r--r-- | xorg-server/hw/xfree86/loader/loadmod.c | 1295 |
1 files changed, 1295 insertions, 0 deletions
diff --git a/xorg-server/hw/xfree86/loader/loadmod.c b/xorg-server/hw/xfree86/loader/loadmod.c new file mode 100644 index 000000000..7f3bbe49c --- /dev/null +++ b/xorg-server/hw/xfree86/loader/loadmod.c @@ -0,0 +1,1295 @@ +/* + * 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). + */ + +#ifdef HAVE_XORG_CONFIG_H +#include <xorg-config.h> +#endif + +#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> + +#define TestFree(a) if (a) { xfree (a); a = NULL; } + +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); +static ModuleDescPtr doLoadModule(const char *, const char *, const char **, + const char **, pointer, + const XF86ModReqInfo *, int *, int *, + int flags); + +const ModuleVersions LoaderVersionInfo = { + XORG_VERSION_CURRENT, + ABI_ANSIC_VERSION, + ABI_VIDEODRV_VERSION, + ABI_XINPUT_VERSION, + ABI_EXTENSION_VERSION, + ABI_FONT_VERSION +}; + +static void +FreeStringList(char **paths) +{ + char **p; + + if (!paths) + return; + + for (p = paths; *p; p++) + xfree(*p); + + xfree(paths); +} + +static char **defaultPathList = NULL; + +static Bool +PathIsAbsolute(const char *path) +{ + return (*path == '/'); +} + +/* + * 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) { + if (PathIsAbsolute(elem)) + { + 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; + xfree(fullpath); + 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[] = { + "", + "fonts/", + "input/", + "drivers/", + "multimedia/", + "extensions/", + "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)). + * + * This list assumes that you're an ELFish platform and therefore your + * shared libraries are named something.so. If we're ever nuts enough + * to port this DDX to, say, Darwin, we'll need to fix this. + */ +static PatternRec stdPatterns[] = { + {"^lib(.*)\\.so$",}, + {"(.*)_drv\\.so$",}, + {"(.*)\\.so$",}, + {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 * +FindModuleInSubdir(const char *dirpath, const char *module) +{ + struct dirent *direntry = NULL; + DIR *dir = NULL; + char *ret = NULL, tmpBuf[PATH_MAX]; + struct stat stat_buf; + + dir = opendir(dirpath); + if (!dir) + return NULL; + + while ((direntry = readdir(dir))) { + if (direntry->d_name[0] == '.') + continue; + if ((stat(direntry->d_name, &stat_buf) == 0) && S_ISDIR(stat_buf.st_mode)) { + snprintf(tmpBuf, PATH_MAX, "%s/%s", dirpath, direntry->d_name); + if ((ret = FindModuleInSubdir(tmpBuf, module))) + break; + continue; + } + + snprintf(tmpBuf, PATH_MAX, "lib%s.so", module); + if (strcmp(direntry->d_name, tmpBuf) == 0) { + ret = malloc(strlen(tmpBuf) + strlen(dirpath) + 2); + sprintf(ret, "%s/%s", dirpath, tmpBuf); + break; + } + + snprintf(tmpBuf, PATH_MAX, "%s_drv.so", module); + if (strcmp(direntry->d_name, tmpBuf) == 0) { + ret = malloc(strlen(tmpBuf) + strlen(dirpath) + 2); + sprintf(ret, "%s/%s", dirpath, tmpBuf); + break; + } + + snprintf(tmpBuf, PATH_MAX, "%s.so", module); + if (strcmp(direntry->d_name, tmpBuf) == 0) { + ret = malloc(strlen(tmpBuf) + strlen(dirpath) + 2); + sprintf(ret, "%s/%s", dirpath, tmpBuf); + break; + } + } + + closedir(dir); + return ret; +} + +static char * +FindModule(const char *module, const char *dirname, const char **subdirlist, + PatternPtr patterns) +{ + char buf[PATH_MAX + 1]; + char *dirpath = NULL; + char *name = NULL; + int dirlen; + const char **subdirs = NULL; + const char **s; + + dirpath = (char *)dirname; + if (strlen(dirpath) > PATH_MAX) + return NULL; + + subdirs = InitSubdirs(subdirlist); + if (!subdirs) + return NULL; + + for (s = subdirs; *s; s++) { + if ((dirlen = strlen(dirpath) + strlen(*s)) > PATH_MAX) + continue; + strcpy(buf, dirpath); + strcat(buf, *s); + if ((name = FindModuleInSubdir(buf, module))) + break; + } + + FreeSubdirs(subdirs); + if (dirpath != dirname) + xfree(dirpath); + + return name; +} + +_X_EXPORT 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; +} + +_X_EXPORT 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!"); + + /* Check for the different scheme used in XFree86 4.0.x releases: + * ((((((((major << 7) | minor) << 7) | subminor) << 5) | beta) << 5) | alpha) + * Since it wasn't used in 4.1.0 or later, limit to versions in the 4.0.x + * range, which limits the overlap with the new version scheme to conflicts + * with 6.71.8.764 through 6.72.39.934. + */ + if ((ver > (4 << 24)) && (ver < ( (4 << 24) + (1 << 17)))) { + /* 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->abiclass)) { + 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 newer than that available (%d)\n", min, reqmin); + return FALSE; + } + } + } + return TRUE; +} + +static ModuleDescPtr +AddSibling(ModuleDescPtr head, ModuleDescPtr new) +{ + new->sib = head; + return (new); +} + +_X_EXPORT 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); + + if (PathIsAbsolute(module)) { + xf86Msg(X_ERROR, + "LoadSubModule: Absolute module path not permitted: \"%s\"\n", + module); + if (errmaj) + *errmaj = LDR_BADUSAGE; + if (errmin) + *errmin = 0; + return NULL; + } + + submod = doLoadModule(module, NULL, subdirlist, patternlist, options, + modreq, errmaj, errmin, LD_FLAG_GLOBAL); + if (submod && submod != (ModuleDescPtr) 1) { + parent->child = AddSibling(parent->child, submod); + submod->parent = parent; + } + return submod; +} + +static ModuleDescPtr +NewModuleDesc(const char *name) +{ + ModuleDescPtr mdp = xalloc(sizeof(ModuleDesc)); + + if (mdp) { + mdp->child = NULL; + mdp->sib = NULL; + mdp->parent = NULL; + mdp->name = xstrdup(name); + mdp->handle = -1; + mdp->SetupProc = NULL; + mdp->TearDownProc = NULL; + mdp->TearDownData = NULL; + } + + return (mdp); +} + +_X_EXPORT 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->handle = mod->handle; + ret->SetupProc = mod->SetupProc; + ret->TearDownProc = mod->TearDownProc; + ret->TearDownData = NULL; + ret->child = DuplicateModule(mod->child, ret); + ret->sib = DuplicateModule(mod->sib, parent); + ret->parent = parent; + ret->VersionInfo = mod->VersionInfo; + + return ret; +} + +static const char *compiled_in_modules[] = { + "ddc", + "i2c", + "ramdac", + NULL +}; + +static ModuleDescPtr +doLoadModule(const char *module, const char *path, const char **subdirlist, + const char **patternlist, pointer options, + const XF86ModReqInfo * modreq, + int *errmaj, int *errmin, int flags) +{ + 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; + const char **cim; + + xf86MsgVerb(X_INFO, 3, "LoadModule: \"%s\"\n", module); + + for (cim = compiled_in_modules; *cim; cim++) + if (!strcmp (module, *cim)) + { + xf86MsgVerb(X_INFO, 3, "Module \"%s\" already built-in\n", module); + return (ModuleDescPtr) 1; + } + + 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 + */ + if (PathIsAbsolute(module)) + found = xstrdup(module); + 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, flags); + if (ret->handle < 0) + goto LoadModule_fail; + + /* drop any explicit suffix from the module name */ + p = strchr(name, '.'); + if (p) + *p = '\0'; + + /* + * 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->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; +} + +/* + * 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) +{ + return doLoadModule(module, path, subdirlist, patternlist, options, + modreq, errmaj, errmin, LD_FLAG_GLOBAL); +} + +void +UnloadModule(ModuleDescPtr mod) +{ + UnloadModuleOrDriver(mod); +} + +static void +UnloadModuleOrDriver(ModuleDescPtr mod) +{ + if (mod == (ModuleDescPtr) 1) + return; + + 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); + xfree(mod); +#ifdef __alpha__ + istream_mem_barrier(); +#endif +} + +_X_EXPORT 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); + xfree(mod); +} + +static void +FreeModuleDesc(ModuleDescPtr head) +{ + ModuleDescPtr sibs, prev; + + if (head == (ModuleDescPtr) 1) + return; + if (head->child) + FreeModuleDesc(head->child); + sibs = head; + while (sibs) { + prev = sibs; + sibs = sibs->sib; + TestFree(prev->name); + xfree(prev); + } +} + +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; +} + +_X_EXPORT void +LoaderErrorMsg(const char *name, const char *modname, int errmaj, int errmin) +{ + const char *msg; + MessageType type = X_ERROR; + + 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 = "already loaded"; + type = X_INFO; + 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 = "unknown error"; + } + if (name) + xf86Msg(type, "%s: Failed to load module \"%s\" (%s, %d)\n", + name, modname, msg, errmin); + else + xf86Msg(type, "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 == (ModuleDescPtr) 1 || !mod->VersionInfo) + return 0; + + return MODULE_VERSION_NUMERIC(mod->VersionInfo->majorversion, + mod->VersionInfo->minorversion, + mod->VersionInfo->patchlevel); +} |