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