/* $XFree86: xc/programs/Xserver/hw/xfree86/loader/loadmod.c,v 1.68 2002/07/30 18:36:18 dawes 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. */ /* This file is best viewed with tab stops set to 4 spaces */ #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 #include #include #include 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 = { XF86_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; char **subdirs = NULL; const char **s, **stmp = NULL; const char *osname; const char *slash; int oslen = 0, len; Bool indefault; if (subdirlist == NULL) { 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); return NULL; } } } subdirs = xalloc((i * 2 + 1) * sizeof(char *)); if (!subdirs) 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))) 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; } 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; int errtype = 0; 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 : "", 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 : "", 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 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); }