/* * Copyright 2002-2005, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Copyright 2001, Thomas Kurschel. All rights reserved. * Distributed under the terms of the NewOS License. */ /* The Module manager * Manages kernel add-ons and their exported modules. */ #include #include #include #include #include #include #include #include #include #include //#define TRACE_MODULE #ifdef TRACE_MODULE # define TRACE(x) dprintf x #else # define TRACE(x) ; #endif #define FATAL(x) dprintf x #define MODULE_HASH_SIZE 16 #define SUPPORT_BOOTFS /** The modules referenced by this structure are built-in * modules that can't be loaded from disk. */ extern module_info gDeviceManagerModule; extern module_info gDeviceRootModule; extern module_info gDeviceForDriversModule; extern module_info gFrameBufferConsoleModule; // file systems extern module_info gRootFileSystem; extern module_info gDeviceFileSystem; extern module_info gBootFileSystem; extern module_info gPipeFileSystem; static module_info *sBuiltInModules[] = { &gDeviceManagerModule, &gDeviceRootModule, &gDeviceForDriversModule, &gFrameBufferConsoleModule, &gRootFileSystem, &gDeviceFileSystem, &gBootFileSystem, &gPipeFileSystem, NULL }; typedef enum { MODULE_QUERIED = 0, MODULE_LOADED, MODULE_INIT, MODULE_READY, MODULE_UNINIT, MODULE_ERROR } module_state; /* Each loaded module image (which can export several modules) is put * in a hash (gModuleImagesHash) to be easily found when you search * for a specific file name. * ToDo: Could use only the inode number for hashing. Would probably be * a little bit slower, but would lower the memory foot print quite a lot. */ typedef struct module_image { struct module_image *next; module_info **info; /* the module_info we use */ module_dependency *dependencies; char *path; /* the full path for the module */ image_id image; int32 ref_count; /* how many ref's to this file */ bool keep_loaded; } module_image; /* Each known module will have this structure which is put in the * gModulesHash, and looked up by name. */ typedef struct module { struct module *next; module_image *module_image; char *name; char *file; int32 ref_count; module_info *info; /* will only be valid if ref_count > 0 */ int32 offset; /* this is the offset in the headers */ module_state state; /* state of module */ uint32 flags; } module; #define B_BUILT_IN_MODULE 2 typedef struct module_path { const char *name; uint32 base_length; } module_path; typedef struct module_iterator { module_path *stack; int32 stack_size; int32 stack_current; char *prefix; size_t prefix_length; DIR *current_dir; status_t status; int32 module_offset; /* This is used to keep track of which module_info * within a module we're addressing. */ module_image *module_image; module_info **current_header; const char *current_path; int32 path_base_length; const char *current_module_path; bool builtin_modules; } module_iterator; static bool sDisableUserAddOns = false; /* locking scheme: there is a global lock only; having several locks * makes trouble if dependent modules get loaded concurrently -> * they have to wait for each other, i.e. we need one lock per module; * also we must detect circular references during init and not dead-lock */ static recursive_lock sModulesLock; /* These are the standard base paths where we start to look for modules * to load. Order is important, the last entry here will be searched * first. * ToDo: the first entry is only there for bootfs compatibility * ToDo: these should probably be retrieved by using find_directory(). */ static const char * const sModulePaths[] = { #ifdef SUPPORT_BOOTFS "/boot/addons/kernel", #endif "/boot/beos/system/add-ons/kernel", "/boot/home/config/add-ons/kernel", }; #define NUM_MODULE_PATHS (sizeof(sModulePaths) / sizeof(sModulePaths[0])) #define FIRST_USER_MODULE_PATH (NUM_MODULE_PATHS - 1) /* first user path */ /* we store the loaded modules by directory path, and all known modules by module name * in a hash table for quick access */ static hash_table *sModuleImagesHash; static hash_table *sModulesHash; extern dev_t gBootDevice; /** calculates hash for a module using its name */ static uint32 module_hash(void *_module, const void *_key, uint32 range) { module *module = (struct module *)_module; const char *name = (const char *)_key; if (module != NULL) return hash_hash_string(module->name) % range; if (name != NULL) return hash_hash_string(name) % range; return 0; } /** compares a module to a given name */ static int module_compare(void *_module, const void *_key) { module *module = (struct module *)_module; const char *name = (const char *)_key; if (name == NULL) return -1; return strcmp(module->name, name); } /** calculates the hash of a module image using its path */ static uint32 module_image_hash(void *_module, const void *_key, uint32 range) { module_image *image = (module_image *)_module; const char *path = (const char *)_key; if (image != NULL) return hash_hash_string(image->path) % range; if (path != NULL) return hash_hash_string(path) % range; return 0; } /** compares a module image to a path */ static int module_image_compare(void *_module, const void *_key) { module_image *image = (module_image *)_module; const char *path = (const char *)_key; if (path == NULL) return -1; return strcmp(image->path, path); } static inline void inc_module_ref_count(struct module *module) { module->ref_count++; } static inline void dec_module_ref_count(struct module *module) { module->ref_count--; } /** Try to load the module image at the specified location. * If it could be loaded, it returns B_OK, and stores a pointer * to the module_image object in "_moduleImage". */ static status_t load_module_image(const char *path, module_image **_moduleImage) { module_image *moduleImage; status_t status; image_id image; TRACE(("load_module_image(path = \"%s\", _image = %p)\n", path, _moduleImage)); ASSERT(_moduleImage != NULL); image = load_kernel_add_on(path); if (image < 0) { dprintf("load_module_image failed: %s\n", strerror(image)); return image; } moduleImage = (module_image *)malloc(sizeof(module_image)); if (!moduleImage) { status = B_NO_MEMORY; goto err; } if (get_image_symbol(image, "modules", B_SYMBOL_TYPE_DATA, (void **)&moduleImage->info) != B_OK) { TRACE(("load_module_image: Failed to load \"%s\" due to lack of 'modules' symbol\n", path)); status = B_BAD_TYPE; goto err1; } moduleImage->dependencies = NULL; get_image_symbol(image, "module_dependencies", B_SYMBOL_TYPE_DATA, (void **)&moduleImage->dependencies); // this is allowed to be NULL moduleImage->path = strdup(path); if (!moduleImage->path) { status = B_NO_MEMORY; goto err1; } moduleImage->image = image; moduleImage->ref_count = 0; moduleImage->keep_loaded = false; recursive_lock_lock(&sModulesLock); hash_insert(sModuleImagesHash, moduleImage); recursive_lock_unlock(&sModulesLock); *_moduleImage = moduleImage; return B_OK; err1: free(moduleImage); err: unload_kernel_add_on(image); return status; } static status_t unload_module_image(module_image *moduleImage, const char *path) { TRACE(("unload_module_image(image = %p, path = %s)\n", moduleImage, path)); recursive_lock_lock(&sModulesLock); if (moduleImage == NULL) { // if no image was specified, lookup it up in the hash table moduleImage = (module_image *)hash_lookup(sModuleImagesHash, path); if (moduleImage == NULL) { recursive_lock_unlock(&sModulesLock); return B_ENTRY_NOT_FOUND; } } if (moduleImage->ref_count != 0) { FATAL(("Can't unload %s due to ref_cnt = %ld\n", moduleImage->path, moduleImage->ref_count)); return B_ERROR; } hash_remove(sModuleImagesHash, moduleImage); recursive_lock_unlock(&sModulesLock); unload_kernel_add_on(moduleImage->image); free(moduleImage->path); free(moduleImage); return B_OK; } static void put_module_image(module_image *image) { int32 refCount = atomic_add(&image->ref_count, -1); ASSERT(refCount > 0); // Don't unload anything when there is no boot device yet // (because chances are that we will never be able to access it again) if (refCount == 1 && !image->keep_loaded && gBootDevice > 0) unload_module_image(image, NULL); } static status_t get_module_image(const char *path, module_image **_image) { struct module_image *image; TRACE(("get_module_image(path = \"%s\", _image = %p)\n", path, _image)); image = (module_image *)hash_lookup(sModuleImagesHash, path); if (image == NULL) { status_t status = load_module_image(path, &image); if (status < B_OK) return status; } atomic_add(&image->ref_count, 1); *_image = image; return B_OK; } /** Extract the information from the module_info structure pointed at * by "info" and create the entries required for access to it's details. */ static status_t create_module(module_info *info, const char *file, int offset, module **_module) { module *module; TRACE(("create_module(info = %p, file = \"%s\", offset = %d, _module = %p)\n", info, file, offset, _module)); if (!info->name) return B_BAD_VALUE; module = (struct module *)hash_lookup(sModulesHash, info->name); if (module) { FATAL(("Duplicate module name (%s) detected... ignoring new one\n", info->name)); return B_FILE_EXISTS; } if ((module = (struct module *)malloc(sizeof(struct module))) == NULL) return B_NO_MEMORY; TRACE(("create_module: name = \"%s\", file = \"%s\"\n", info->name, file)); module->module_image = NULL; module->name = strdup(info->name); if (module->name == NULL) { free(module); return B_NO_MEMORY; } module->file = strdup(file); if (module->file == NULL) { free(module->name); free(module); return B_NO_MEMORY; } module->state = MODULE_QUERIED; module->info = info; module->offset = offset; // record where the module_info can be found in the module_info array module->ref_count = 0; module->flags = info->flags; recursive_lock_lock(&sModulesLock); hash_insert(sModulesHash, module); recursive_lock_unlock(&sModulesLock); if (_module) *_module = module; return B_OK; } /** Loads the file at "path" and scans all modules contained therein. * Returns B_OK if "searchedName" could be found under those modules, * B_ENTRY_NOT_FOUND if not. * Must only be called for files that haven't been scanned yet. * "searchedName" is allowed to be NULL (if all modules should be scanned) */ static status_t check_module_image(const char *path, const char *searchedName) { module_image *image; module_info **info; int index = 0, match = B_ENTRY_NOT_FOUND; TRACE(("check_module_image(path = \"%s\", searchedName = \"%s\")\n", path, searchedName)); ASSERT(hash_lookup(sModuleImagesHash, path) == NULL); if (load_module_image(path, &image) < B_OK) return B_ENTRY_NOT_FOUND; for (info = image->info; *info; info++) { // try to create a module for every module_info, check if the // name matches if it was a new entry if (create_module(*info, path, index++, NULL) == B_OK) { if (searchedName && !strcmp((*info)->name, searchedName)) match = B_OK; } } // The module we looked for couldn't be found, so we can unload the // loaded module at this point if (match != B_OK) { TRACE(("check_module_file: unloading module file \"%s\" (not used yet)\n", path)); unload_module_image(image, path); } return match; } #ifdef SUPPORT_BOOTFS /** Recursively scans through the provided path for the specified module * named "searchedName". * If "searchedName" is NULL, all modules will be scanned. * Returns B_OK if the module could be found, B_ENTRY_NOT_FOUND if not, * or some other error occured during scanning. */ static status_t recurse_directory(const char *path, const char *searchedName) { status_t status; DIR *dir = opendir(path); if (dir == NULL) return errno; errno = 0; // loop until we have a match or we run out of entries while (true) { struct dirent *dirent; struct stat st; char *newPath; size_t size = 0; TRACE(("scanning %s\n", path)); dirent = readdir(dir); if (dirent == NULL) { // we tell the upper layer we couldn't find anything in here status = errno == 0 ? B_ENTRY_NOT_FOUND : errno; goto exit; } if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, "..") || !strcmp(dirent->d_name, "boot")) continue; size = strlen(path) + strlen(dirent->d_name) + 2; newPath = (char *)malloc(size); if (newPath == NULL) { status = B_NO_MEMORY; goto exit; } strlcpy(newPath, path, size); strlcat(newPath, "/", size); // two slashes wouldn't hurt strlcat(newPath, dirent->d_name, size); if (stat(newPath, &st) != 0) { free(newPath); errno = 0; // If we couldn't stat the current file, we will just ignore it; // it's a problem of the file system, not ours. continue; } if (S_ISREG(st.st_mode)) { // if it's a file, check if we already have it in the hash table, // because then we know it doesn't contain the module we are // searching for (we are here because it couldn't be found in // the first place) if (hash_lookup(sModuleImagesHash, newPath) != NULL) continue; status = check_module_image(newPath, searchedName); } else if (S_ISDIR(st.st_mode)) status = recurse_directory(newPath, searchedName); else status = B_ERROR; if (status == B_OK) goto exit; free(newPath); } exit: closedir(dir); return status; } #endif /** This is only called if we fail to find a module already in our cache... * saves us some extra checking here :) */ static module * search_module(const char *name) { status_t status = B_ENTRY_NOT_FOUND; uint32 i; TRACE(("search_module(%s)\n", name)); for (i = 0; i < NUM_MODULE_PATHS; i++) { char path[B_FILE_NAME_LENGTH]; if (sDisableUserAddOns && i >= FIRST_USER_MODULE_PATH) return NULL; // let's the VFS find that module for us status = vfs_get_module_path(sModulePaths[i], name, path, sizeof(path)); if (status == B_OK) { status = check_module_image(path, name); if (status == B_OK) break; } #ifdef SUPPORT_BOOTFS // BeOS uses the module name to locate the module on disk. We now have the // above vfs_get_module_path() call to achieve this. // "bootfs" has a very low maximum path length, which makes it unable to // contain the standard module directories). if ((status = recurse_directory(sModulePaths[i], name)) == B_OK) break; #endif } if (status != B_OK) return NULL; return (module *)hash_lookup(sModulesHash, name); } static status_t put_dependent_modules(struct module *module) { module_dependency *dependencies; int32 i = 0; if (module->module_image == NULL || (dependencies = module->module_image->dependencies) == NULL) return B_OK; for (; dependencies[i].name != NULL; i++) { status_t status = put_module(dependencies[i].name); if (status < B_OK) return status; } return B_OK; } static status_t get_dependent_modules(struct module *module) { module_dependency *dependencies; int32 i = 0; // built-in modules don't have a module_image structure if (module->module_image == NULL || (dependencies = module->module_image->dependencies) == NULL) return B_OK; TRACE(("resolving module dependencies...\n")); for (; dependencies[i].name != NULL; i++) { status_t status = get_module(dependencies[i].name, dependencies[i].info); if (status < B_OK) { TRACE(("loading dependent module \"%s\" failed!\n", dependencies[i].name)); return status; } } return B_OK; } /** Initializes a loaded module depending on its state */ static inline status_t init_module(module *module) { switch (module->state) { case MODULE_QUERIED: case MODULE_LOADED: { status_t status; module->state = MODULE_INIT; // resolve dependencies status = get_dependent_modules(module); if (status < B_OK) { module->state = MODULE_LOADED; return status; } // init module TRACE(("initializing module %s (at %p)... \n", module->name, module->info->std_ops)); status = module->info->std_ops(B_MODULE_INIT); TRACE(("...done (%s)\n", strerror(status))); if (status >= B_OK) module->state = MODULE_READY; else { put_dependent_modules(module); module->state = MODULE_LOADED; } return status; } case MODULE_READY: return B_OK; case MODULE_INIT: FATAL(("circular reference to %s\n", module->name)); return B_ERROR; case MODULE_UNINIT: FATAL(("tried to load module %s which is currently unloading\n", module->name)); return B_ERROR; case MODULE_ERROR: FATAL(("cannot load module %s because its earlier unloading failed\n", module->name)); return B_ERROR; default: return B_ERROR; } // never trespasses here } /** Uninitializes a module depeding on its state */ static inline int uninit_module(module *module) { switch (module->state) { case MODULE_QUERIED: case MODULE_LOADED: return B_NO_ERROR; case MODULE_INIT: panic("Trying to unload module %s which is initializing\n", module->name); return B_ERROR; case MODULE_UNINIT: panic("Trying to unload module %s which is un-initializing\n", module->name); return B_ERROR; case MODULE_READY: { status_t status; module->state = MODULE_UNINIT; TRACE(("uninitializing module %s...\n", module->name)); status = module->info->std_ops(B_MODULE_UNINIT); TRACE(("...done (%s)\n", strerror(status))); if (status == B_NO_ERROR) { module->state = MODULE_LOADED; put_dependent_modules(module); return 0; } FATAL(("Error unloading module %s (%s)\n", module->name, strerror(status))); module->state = MODULE_ERROR; module->flags |= B_KEEP_LOADED; return status; } default: return B_ERROR; } // never trespasses here } static const char * iterator_pop_path_from_stack(module_iterator *iterator, uint32 *_baseLength) { if (iterator->stack_current <= 0) return NULL; if (_baseLength) *_baseLength = iterator->stack[iterator->stack_current - 1].base_length; return iterator->stack[--iterator->stack_current].name; } static status_t iterator_push_path_on_stack(module_iterator *iterator, const char *path, uint32 baseLength) { if (iterator->stack_current + 1 > iterator->stack_size) { // allocate new space on the stack module_path *stack = (module_path *)realloc(iterator->stack, (iterator->stack_size + 8) * sizeof(module_path)); if (stack == NULL) return B_NO_MEMORY; iterator->stack = stack; iterator->stack_size += 8; } iterator->stack[iterator->stack_current].name = path; iterator->stack[iterator->stack_current++].base_length = baseLength; return B_OK; } static status_t iterator_get_next_module(module_iterator *iterator, char *buffer, size_t *_bufferSize) { status_t status; TRACE(("iterator_get_next_module() -- start\n")); if (iterator->builtin_modules) { int32 i; for (i = iterator->module_offset; sBuiltInModules[i] != NULL; i++) { // the module name must fit the prefix if (strncmp(sBuiltInModules[i]->name, iterator->prefix, iterator->prefix_length)) continue; *_bufferSize = strlcpy(buffer, sBuiltInModules[i]->name, *_bufferSize); iterator->module_offset = i + 1; return B_OK; } iterator->builtin_modules = false; } nextPath: if (iterator->current_dir == NULL) { // get next directory path from the stack const char *path = iterator_pop_path_from_stack(iterator, &iterator->path_base_length); if (path == NULL) { // we are finished, there are no more entries on the stack return B_ENTRY_NOT_FOUND; } free((void *)iterator->current_path); iterator->current_path = path; iterator->current_dir = opendir(path); TRACE(("open directory at %s -> %p\n", path, iterator->current_dir)); if (iterator->current_dir == NULL) { // we don't throw an error here, but silently go to // the next directory on the stack goto nextPath; } } nextModuleImage: if (iterator->current_header == NULL) { // get next entry from the current directory char path[B_PATH_NAME_LENGTH]; struct dirent *dirent; struct stat st; int32 passedOffset, commonLength; errno = 0; if ((dirent = readdir(iterator->current_dir)) == NULL) { closedir(iterator->current_dir); iterator->current_dir = NULL; if (errno < B_OK) return errno; goto nextPath; } // check if the prefix matches passedOffset = strlen(iterator->current_path) + 1; commonLength = iterator->path_base_length + iterator->prefix_length - passedOffset; if (commonLength > 0) { // the prefix still reaches into the new path part int32 length = strlen(dirent->d_name); if (commonLength > length) commonLength = length; if (strncmp(dirent->d_name, iterator->prefix + passedOffset - iterator->path_base_length, commonLength)) goto nextModuleImage; } // we're not interested in traversing these again if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, "..")) goto nextModuleImage; // build absolute path to current file strlcpy(path, iterator->current_path, sizeof(path)); strlcat(path, "/", sizeof(path)); strlcat(path, dirent->d_name, sizeof(path)); // find out if it's a directory or a file if (stat(path, &st) < 0) return errno; iterator->current_module_path = strdup(path); if (iterator->current_module_path == NULL) return B_NO_MEMORY; if (S_ISDIR(st.st_mode)) { status = iterator_push_path_on_stack(iterator, iterator->current_module_path, iterator->path_base_length); if (status < B_OK) return status; iterator->current_module_path = NULL; goto nextModuleImage; } if (!S_ISREG(st.st_mode)) return B_BAD_TYPE; TRACE(("open module at %s\n", path)); status = get_module_image(path, &iterator->module_image); if (status < B_OK) { free((void *)iterator->current_module_path); iterator->current_module_path = NULL; goto nextModuleImage; } iterator->current_header = iterator->module_image->info; iterator->module_offset = 0; } // search the current module image until we've got a match while (*iterator->current_header != NULL) { module_info *info = *iterator->current_header; // ToDo: we might want to create a module here and cache it in the hash table iterator->current_header++; iterator->module_offset++; if (strncmp(info->name, iterator->prefix, iterator->prefix_length)) continue; *_bufferSize = strlcpy(buffer, info->name, *_bufferSize); return B_OK; } // leave this module and get the next one iterator->current_header = NULL; free((void *)iterator->current_module_path); iterator->current_module_path = NULL; put_module_image(iterator->module_image); iterator->module_image = NULL; goto nextModuleImage; } static void register_builtin_modules(struct module_info **info) { for (; *info; info++) { (*info)->flags |= B_BUILT_IN_MODULE; // this is an internal flag, it doesn't have to be set by modules itself if (create_module(*info, "", -1, NULL) != B_OK) dprintf("creation of built-in module \"%s\" failed!\n", (*info)->name); } } static status_t register_preloaded_module_image(struct preloaded_image *image) { module_image *moduleImage; struct module_info **info; status_t status; int32 index = 0; if (image->id < 0) return B_BAD_VALUE; moduleImage = (module_image *)malloc(sizeof(module_image)); if (moduleImage == NULL) return B_NO_MEMORY; if (get_image_symbol(image->id, "modules", B_SYMBOL_TYPE_DATA, (void **)&moduleImage->info) != B_OK) { status = B_BAD_TYPE; goto error; } moduleImage->dependencies = NULL; get_image_symbol(image->id, "module_dependencies", B_SYMBOL_TYPE_DATA, (void **)&moduleImage->dependencies); // this is allowed to be NULL // Try to recreate the full module path, so that we don't try to load the // image again when asked for a module it does not export (would only be // problematic if it had got replaced and the new file actually exports // that module). Also helpful for recurse_directory(). { // ToDo: this is kind of a hack to have the full path in the hash // (it always assumes the preloaded add-ons to be in the system directory) char path[B_FILE_NAME_LENGTH]; const char *name, *suffix; if (moduleImage->info[0] && (suffix = strstr(name = moduleImage->info[0]->name, image->name)) != NULL) { // even if strlcpy() is used here, it's by no means safe against buffer overflows size_t length = strlcpy(path, "/boot/beos/system/add-ons/kernel/", sizeof(path)); strlcpy(path + length, name, strlen(image->name) + 1 + (suffix - name)); moduleImage->path = strdup(path); } else moduleImage->path = strdup(image->name); } if (moduleImage->path == NULL) { status = B_NO_MEMORY; goto error; } moduleImage->image = image->id; moduleImage->ref_count = 0; moduleImage->keep_loaded = false; hash_insert(sModuleImagesHash, moduleImage); for (info = moduleImage->info; *info; info++) { create_module(*info, moduleImage->path, index++, NULL); } return B_OK; error: free(moduleImage); // we don't need this image anymore unload_kernel_add_on(image->id); return status; } static int dump_modules(int argc, char **argv) { hash_iterator iterator; struct module_image *image; struct module *module; hash_rewind(sModulesHash, &iterator); dprintf("-- known modules:\n"); while ((module = (struct module *)hash_next(sModulesHash, &iterator)) != NULL) { dprintf("%p: \"%s\", \"%s\" (%ld), refcount = %ld, state = %d, mimage = %p\n", module, module->name, module->file, module->offset, module->ref_count, module->state, module->module_image); } hash_rewind(sModuleImagesHash, &iterator); dprintf("\n-- loaded module images:\n"); while ((image = (struct module_image *)hash_next(sModuleImagesHash, &iterator)) != NULL) { dprintf("%p: \"%s\" (image_id = %ld), info = %p, refcount = %ld, %s\n", image, image->path, image->image, image->info, image->ref_count, image->keep_loaded ? "keep loaded" : "can be unloaded"); } return 0; } // #pragma mark - // Exported Kernel API (private part) /** Unloads a module in case it's not in use. This is the counterpart * to load_module(). */ status_t unload_module(const char *path) { struct module_image *moduleImage; recursive_lock_lock(&sModulesLock); moduleImage = (module_image *)hash_lookup(sModuleImagesHash, path); recursive_lock_unlock(&sModulesLock); if (moduleImage == NULL) return B_ENTRY_NOT_FOUND; put_module_image(moduleImage); return B_OK; } /** Unlike get_module(), this function lets you specify the add-on to * be loaded by path. * However, you must not use the exported modules without having called * get_module() on them. When you're done with the NULL terminated * \a modules array, you have to call unload_module(), no matter if * you're actually using any of the modules or not - of course, the * add-on won't be unloaded until the last put_module(). */ status_t load_module(const char *path, module_info ***_modules) { module_image *moduleImage; status_t status = get_module_image(path, &moduleImage); if (status != B_OK) return status; *_modules = moduleImage->info; return B_OK; } /** Setup the module structures and data for use - must be called * before any other module call. */ status_t module_init(kernel_args *args) { struct preloaded_image *image; if (recursive_lock_init(&sModulesLock, "modules rlock") < B_OK) return B_ERROR; sModulesHash = hash_init(MODULE_HASH_SIZE, 0, module_compare, module_hash); if (sModulesHash == NULL) return B_NO_MEMORY; sModuleImagesHash = hash_init(MODULE_HASH_SIZE, 0, module_image_compare, module_image_hash); if (sModuleImagesHash == NULL) return B_NO_MEMORY; // register built-in modules register_builtin_modules(sBuiltInModules); // register preloaded images for (image = args->preloaded_images; image != NULL; image = image->next) { register_preloaded_module_image(image); } // ToDo: set sDisableUserAddOns from kernel_args! add_debugger_command("modules", &dump_modules, "list all known & loaded modules"); return B_OK; } // #pragma mark - // Exported Kernel API (public part) /** This returns a pointer to a structure that can be used to * iterate through a list of all modules available under * a given prefix. * All paths will be searched and the returned list will * contain all modules available under the prefix. * The structure is then used by read_next_module_name(), and * must be freed by calling close_module_list(). * * During early boot, when there is no boot device accessible, * it will only find built-in modules, not those that have * been preloaded - use get_next_loaded_module_name() instead * if you need to have a list of loaded modules. * ToDo: this could be changed */ void * open_module_list(const char *prefix) { module_iterator *iterator; uint32 i; TRACE(("open_module_list(prefix = %s)\n", prefix)); iterator = (module_iterator *)malloc(sizeof(module_iterator)); if (!iterator) return NULL; memset(iterator, 0, sizeof(module_iterator)); // ToDo: possibly, the prefix don't have to be copied, just referenced iterator->prefix = strdup(prefix ? prefix : ""); if (iterator->prefix == NULL) { free(iterator); return NULL; } iterator->prefix_length = strlen(prefix); // first, we'll traverse over the built-in modules iterator->builtin_modules = true; // put all search paths on the stack for (i = 0; i < NUM_MODULE_PATHS; i++) { const char *path; if (sDisableUserAddOns && i >= FIRST_USER_MODULE_PATH) break; path = strdup(sModulePaths[i]); if (path == NULL) { // ToDo: should we abort the whole operation here? // if we do, don't forget to empty the stack continue; } iterator_push_path_on_stack(iterator, path, strlen(path) + 1); } return (void *)iterator; } /** Frees the cookie allocated by open_module_list() */ status_t close_module_list(void *cookie) { module_iterator *iterator = (module_iterator *)cookie; const char *path; TRACE(("close_module_list()\n")); if (iterator == NULL) return B_BAD_VALUE; // free stack while ((path = iterator_pop_path_from_stack(iterator, NULL)) != NULL) free((void *)path); // close what have been left open if (iterator->module_image != NULL) put_module_image(iterator->module_image); if (iterator->current_dir != NULL) closedir(iterator->current_dir); free(iterator->stack); free((void *)iterator->current_path); free((void *)iterator->current_module_path); free(iterator->prefix); free(iterator); return 0; } /** Return the next module name from the available list, using * a structure previously created by a call to open_module_list. * Returns B_OK as long as it found another module, B_ENTRY_NOT_FOUND * when done. */ status_t read_next_module_name(void *cookie, char *buffer, size_t *_bufferSize) { module_iterator *iterator = (module_iterator *)cookie; status_t status; TRACE(("read_next_module_name: looking for next module\n")); if (iterator == NULL || buffer == NULL || _bufferSize == NULL) return B_BAD_VALUE; if (iterator->status < B_OK) return iterator->status; status = iterator->status; recursive_lock_lock(&sModulesLock); status = iterator_get_next_module(iterator, buffer, _bufferSize); iterator->status = status; recursive_lock_unlock(&sModulesLock); TRACE(("read_next_module_name: finished with status %s\n", strerror(status))); return status; } /** Iterates through all loaded modules, and stores its path in "buffer". * ToDo: check if the function in BeOS really does that (could also mean: * iterate through all modules that are currently loaded; have a valid * module_image pointer, which would be hard to test for) */ status_t get_next_loaded_module_name(uint32 *_cookie, char *buffer, size_t *_bufferSize) { hash_iterator *iterator = (hash_iterator *)*_cookie; struct module *module; status_t status; TRACE(("get_next_loaded_module_name()\n")); if (_cookie == NULL || buffer == NULL || _bufferSize == NULL) return B_BAD_VALUE; if (iterator == NULL) { iterator = hash_open(sModulesHash, NULL); if (iterator == NULL) return B_NO_MEMORY; *(hash_iterator **)_cookie = iterator; } recursive_lock_lock(&sModulesLock); module = hash_next(sModulesHash, iterator); if (module != NULL) { *_bufferSize = strlcpy(buffer, module->name, *_bufferSize); status = B_OK; } else { hash_close(sModulesHash, iterator, true); status = B_ENTRY_NOT_FOUND; } recursive_lock_unlock(&sModulesLock); return status; } status_t get_module(const char *path, module_info **_info) { module_image *moduleImage; module *module; status_t status; TRACE(("get_module(%s)\n", path)); if (path == NULL) return B_BAD_VALUE; recursive_lock_lock(&sModulesLock); module = (struct module *)hash_lookup(sModulesHash, path); // if we don't have it cached yet, search for it if (module == NULL) { module = search_module(path); if (module == NULL) { FATAL(("module: Search for %s failed.\n", path)); goto err; } } if ((module->flags & B_BUILT_IN_MODULE) == 0) { /* We now need to find the module_image for the module. This should * be in memory if we have just run search_module(), but may not be * if we are using cached information. * We can't use the module->module_image pointer, because it is not * reliable at this point (it won't be set to NULL when the module_image * is unloaded). */ if (get_module_image(module->file, &moduleImage) < B_OK) goto err; // (re)set in-memory data for the loaded module module->info = moduleImage->info[module->offset]; module->module_image = moduleImage; // the module image must not be unloaded anymore if (module->flags & B_KEEP_LOADED) module->module_image->keep_loaded = true; } // The state will be adjusted by the call to init_module // if we have just loaded the file if (module->ref_count == 0) status = init_module(module); else status = B_OK; if (status == B_OK) { inc_module_ref_count(module); *_info = module->info; } recursive_lock_unlock(&sModulesLock); return status; err: recursive_lock_unlock(&sModulesLock); return B_ENTRY_NOT_FOUND; } status_t put_module(const char *path) { module *module; TRACE(("put_module(path = %s)\n", path)); recursive_lock_lock(&sModulesLock); module = (struct module *)hash_lookup(sModulesHash, path); if (module == NULL) { FATAL(("module: We don't seem to have a reference to module %s\n", path)); recursive_lock_unlock(&sModulesLock); return B_BAD_VALUE; } dec_module_ref_count(module); // ToDo: not sure if this should ever be called for keep_loaded modules... if (module->ref_count == 0) uninit_module(module); if ((module->flags & B_BUILT_IN_MODULE) == 0) put_module_image(module->module_image); recursive_lock_unlock(&sModulesLock); return B_OK; }