/* * 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 #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" #include "xf86Xinput.h" #include "loader.h" #include "xf86Optrec.h" #include #include #include #include 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 *); const ModuleVersions LoaderVersionInfo = { XORG_VERSION_CURRENT, ABI_ANSIC_VERSION, ABI_VIDEODRV_VERSION, ABI_XINPUT_VERSION, ABI_EXTENSION_VERSION, ABI_FONT_VERSION }; static int ModuleDuplicated[] = {}; static void FreeStringList(char **paths) { char **p; if (!paths) return; for (p = paths; *p; p++) free(*p); free(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 = strdup(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 = realloc(list, (n + 2) * sizeof(char *)); if (!list) { if (save) { save[n] = NULL; FreeStringList(save); } free(fullpath); return NULL; } list[n] = malloc(len + 1); if (!list[n]) { FreeStringList(list); free(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; free(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[] = { "", "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 = malloc((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) free(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 = malloc(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); free(tmp_subdirlist); return NULL; } } } subdirs = malloc((i * 2 + 1) * sizeof(char *)); if (!subdirs) { free(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] = malloc(len))) { while (--i >= 0) free(subdirs[i]); free(subdirs); free(tmp_subdirlist); return NULL; } /* tack on the OS name */ sprintf(subdirs[i], "%s%s%s/", *s, slash, osname); i++; /* path as given */ subdirs[i] = strdup(*s); i++; s++; if (indefault && !s) { /* revert back to the main list */ indefault = FALSE; s = stmp; } } subdirs[i] = NULL; } free(tmp_subdirlist); return (const char **)subdirs; } static void FreeSubdirs(const char **subdirs) { const char **s; if (subdirs) { for (s = subdirs; *s; s++) free((char *)*s); free(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; snprintf(tmpBuf, PATH_MAX, "%s%s/", dirpath, direntry->d_name); /* the stat with the appended / fails for normal files, and works for sub dirs fine, looks a bit strange in strace but does seem to work */ if ((stat(tmpBuf, &stat_buf) == 0) && S_ISDIR(stat_buf.st_mode)) { if ((ret = FindModuleInSubdir(tmpBuf, module))) break; continue; } snprintf(tmpBuf, PATH_MAX, "lib%s.so", module); if (strcmp(direntry->d_name, tmpBuf) == 0) { if (asprintf(&ret, "%s%s", dirpath, tmpBuf) == -1) ret = NULL; break; } snprintf(tmpBuf, PATH_MAX, "%s_drv.so", module); if (strcmp(direntry->d_name, tmpBuf) == 0) { if (asprintf(&ret, "%s%s", dirpath, tmpBuf) == -1) ret = NULL; break; } snprintf(tmpBuf, PATH_MAX, "%s.so", module); if (strcmp(direntry->d_name, tmpBuf) == 0) { if (asprintf(&ret, "%s%s", dirpath, tmpBuf) == -1) ret = NULL; 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 *name = NULL; const char **subdirs = NULL; const char **s; if (strlen(dirname) > PATH_MAX) return NULL; subdirs = InitSubdirs(subdirlist); if (!subdirs) return NULL; for (s = subdirs; *s; s++) { if ((strlen(dirname) + strlen(*s)) > PATH_MAX) continue; strcpy(buf, dirname); strcat(buf, *s); if ((name = FindModuleInSubdir(buf, module))) break; } FreeSubdirs(subdirs); return name; } 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; char **ret = NULL; int n = 0; if (!(pathlist = InitPathList(NULL))) return NULL; if (!(subdirs = InitSubdirs(subdirlist))) goto bail; if (!(patterns = InitPatterns(patternlist))) goto bail; 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 = realloc(listing, (n + 2) * sizeof(char *)); if (!listing) { if (save) { save[n] = NULL; FreeStringList(save); } closedir(d); goto bail; } listing[n] = malloc(len + 1); if (!listing[n]) { FreeStringList(listing); closedir(d); goto bail; } strncpy(listing[n], dp->d_name + match[1].rm_so, len); listing[n][len] = '\0'; n++; break; } } } closedir(d); } } } if (listing) listing[n] = NULL; ret = listing; bail: FreePatterns(patterns); FreeSubdirs(subdirs); FreePathList(pathlist); return ret; } 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 : "", 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 : "", 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; } pointer LoadSubModule(pointer _parent, const char *module, const char **subdirlist, const char **patternlist, pointer options, const XF86ModReqInfo * modreq, int *errmaj, int *errmin) { ModuleDescPtr submod; ModuleDescPtr parent = (ModuleDescPtr)_parent; 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); if (submod && submod != (ModuleDescPtr) 1) { parent->child = AddSibling(parent->child, submod); submod->parent = parent; } return submod; } static ModuleDescPtr NewModuleDesc(const char *name) { ModuleDescPtr mdp = calloc(1, sizeof(ModuleDesc)); if (mdp) mdp->name = xstrdup(name); return mdp; } ModuleDescPtr DuplicateModule(ModuleDescPtr mod, ModuleDescPtr parent) { ModuleDescPtr ret; if (!mod) return NULL; ret = NewModuleDesc(mod->name); if (ret == NULL) return NULL; ret->handle = mod->handle; ret->SetupProc = mod->SetupProc; ret->TearDownProc = mod->TearDownProc; ret->TearDownData = ModuleDuplicated; ret->child = DuplicateModule(mod->child, ret); ret->sib = DuplicateModule(mod->sib, parent); ret->parent = parent; ret->VersionInfo = mod->VersionInfo; ret->path = strdup(mod->path); 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) { XF86ModuleData *initdata = NULL; char **pathlist = NULL; char *found = NULL; char *name = NULL; char **path_elem = NULL; char *p = NULL; ModuleDescPtr ret = NULL; PatternPtr patterns = NULL; int noncanonical = 0; char *m = NULL; const char **cim; 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; } for (cim = compiled_in_modules; *cim; cim++) if (!strcmp (m, *cim)) { xf86MsgVerb(X_INFO, 3, "Module \"%s\" already built-in\n", m); ret = (ModuleDescPtr) 1; goto LoadModule_exit; } 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, errmaj, errmin); if (ret->handle == NULL) goto LoadModule_fail; ret->path = strdup(found); /* drop any explicit suffix from the module name */ p = strchr(name, '.'); if (p) *p = '\0'; /* * now check if the special data object ModuleData is * present. */ if (asprintf(&p, "%sModuleData", name) == -1) { p = NULL; if (errmaj) *errmaj = LDR_NOMEM; if (errmin) *errmin = 0; goto LoadModule_fail; } initdata = LoaderSymbol(p); if (initdata) { ModuleSetupProc setup; ModuleTearDownProc teardown; XF86ModuleVersionInfo *vers; vers = initdata->vers; setup = initdata->setup; teardown = initdata->teardown; 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); free(found); free(name); free(p); 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); } void UnloadModule(pointer mod) { UnloadModuleOrDriver((ModuleDescPtr)mod); } static void UnloadModuleOrDriver(ModuleDescPtr mod) { if (mod == (ModuleDescPtr) 1) return; if (mod == NULL || mod->name == NULL) return; if (mod->parent) xf86MsgVerb(X_INFO, 3, "UnloadSubModule: \"%s\"\n", mod->name); else xf86MsgVerb(X_INFO, 3, "UnloadModule: \"%s\"\n", mod->name); if (mod->TearDownData != ModuleDuplicated) { if ((mod->TearDownProc) && (mod->TearDownData)) mod->TearDownProc(mod->TearDownData); LoaderUnload(mod->name, mod->handle); } if (mod->child) UnloadModuleOrDriver(mod->child); if (mod->sib) UnloadModuleOrDriver(mod->sib); free(mod->path); free(mod->name); free(mod); } void UnloadSubModule(pointer _mod) { ModuleDescPtr mod = (ModuleDescPtr)_mod; /* Some drivers are calling us on built-in submodules, ignore them */ if (mod == (ModuleDescPtr)1) return; RemoveChild(mod); UnloadModuleOrDriver(mod); } 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; child->sib = NULL; return; } 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 = malloc(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 strdup(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); }