2002-10-29 03:42:40 +00:00
|
|
|
/* Module manager */
|
2002-07-09 12:24:59 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
** Copyright 2001, Thomas Kurschel. All rights reserved.
|
|
|
|
|
** Distributed under the terms of the NewOS License.
|
|
|
|
|
*/
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
|
2002-09-23 02:41:52 +00:00
|
|
|
#include <kmodule.h>
|
2002-07-09 12:24:59 +00:00
|
|
|
#include <lock.h>
|
|
|
|
|
#include <Errors.h>
|
|
|
|
|
#include <khash.h>
|
2002-10-29 23:07:06 +00:00
|
|
|
#include <malloc.h>
|
2002-07-09 12:24:59 +00:00
|
|
|
#include <elf.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/stat.h>
|
2002-11-29 08:33:57 +00:00
|
|
|
#include <errno.h>
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
#define MODULE_HASH_SIZE 16
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
static bool modules_disable_user_addons = false;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
#define TRACE_MODULE 0
|
|
|
|
|
#if TRACE_MODULE
|
|
|
|
|
# define TRACE(x) dprintf x
|
|
|
|
|
#else
|
|
|
|
|
# define TRACE(x) ;
|
|
|
|
|
#endif
|
|
|
|
|
#define FATAL(x) dprintf x
|
2002-07-09 12:24:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
2002-11-29 08:33:57 +00:00
|
|
|
MODULE_QUERIED = 0,
|
|
|
|
|
MODULE_LOADED,
|
|
|
|
|
MODULE_INIT,
|
|
|
|
|
MODULE_READY,
|
|
|
|
|
MODULE_UNINIT,
|
|
|
|
|
MODULE_ERROR
|
2002-07-09 12:24:59 +00:00
|
|
|
} module_state;
|
|
|
|
|
|
|
|
|
|
/* This represents the actual loaded module. The module is loaded and
|
2002-11-29 08:33:57 +00:00
|
|
|
* may have more than one exported image, i.e. the module foo may actually have
|
|
|
|
|
* module_info structures for foo and bar.
|
|
|
|
|
* To allow for this each module_info structure within the module loaded is
|
|
|
|
|
* represented by a loaded_module_info structure.
|
2002-07-09 12:24:59 +00:00
|
|
|
*/
|
|
|
|
|
typedef struct loaded_module {
|
2002-10-29 03:42:40 +00:00
|
|
|
struct loaded_module *next;
|
|
|
|
|
module_info **info; /* the module_info we use */
|
|
|
|
|
char *path; /* the full path for the module */
|
2002-11-29 08:33:57 +00:00
|
|
|
image_id image;
|
|
|
|
|
int32 ref_count; /* how many ref's to this file */
|
2002-07-09 12:24:59 +00:00
|
|
|
} loaded_module;
|
|
|
|
|
|
|
|
|
|
/* This is used to keep a list of module and the file it's found
|
|
|
|
|
* in. It's used when we do searches to record that a module_info for
|
|
|
|
|
* a particular module is found in a particular file which covers us for
|
|
|
|
|
* the case where we have a single file exporting a number of modules.
|
|
|
|
|
*/
|
|
|
|
|
typedef struct module {
|
2002-10-29 03:42:40 +00:00
|
|
|
struct module *next;
|
2002-11-29 08:33:57 +00:00
|
|
|
struct loaded_module *loaded_module;
|
2002-10-29 03:42:40 +00:00
|
|
|
char *name;
|
|
|
|
|
char *file;
|
2002-11-29 08:33:57 +00:00
|
|
|
int32 ref_count;
|
|
|
|
|
module_info *info; /* will only be valid if ref_cnt > 0 */
|
2002-10-29 03:42:40 +00:00
|
|
|
int offset; /* this is the offset in the headers */
|
2002-11-29 08:33:57 +00:00
|
|
|
module_state state; /* state of module */
|
2002-10-29 03:42:40 +00:00
|
|
|
bool keep_loaded;
|
2002-07-09 12:24:59 +00:00
|
|
|
} module;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define INC_MOD_REF_COUNT(x) \
|
2002-11-29 08:33:57 +00:00
|
|
|
x->ref_count++; \
|
|
|
|
|
x->loaded_module->ref_count++;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
|
|
|
|
#define DEC_MOD_REF_COUNT(x) \
|
2002-11-29 08:33:57 +00:00
|
|
|
x->ref_count--; \
|
|
|
|
|
x->loaded_module->ref_count--;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct module_iterator {
|
2002-10-29 03:42:40 +00:00
|
|
|
char *prefix;
|
|
|
|
|
int base_path_id;
|
|
|
|
|
struct module_dir_iterator *base_dir;
|
|
|
|
|
struct module_dir_iterator *cur_dir;
|
|
|
|
|
int err;
|
|
|
|
|
int module_pos; /* This is used to keep track of which module_info
|
|
|
|
|
* within a module we're addressing. */
|
2002-11-29 08:33:57 +00:00
|
|
|
module_info **current_header;
|
|
|
|
|
char *current_path;
|
2002-07-09 12:24:59 +00:00
|
|
|
} module_iterator;
|
|
|
|
|
|
|
|
|
|
typedef struct module_dir_iterator {
|
2002-10-29 03:42:40 +00:00
|
|
|
struct module_dir_iterator *parent_dir;
|
|
|
|
|
struct module_dir_iterator *sub_dir;
|
|
|
|
|
char *name;
|
|
|
|
|
int file;
|
|
|
|
|
int hdr_prefix;
|
2002-07-09 12:24:59 +00:00
|
|
|
} module_dir_iterator;
|
|
|
|
|
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/* locking scheme: there is a global lock only; having several locks
|
2002-07-09 12:24:59 +00:00
|
|
|
* 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
|
|
|
|
|
*/
|
2002-11-29 08:33:57 +00:00
|
|
|
static recursive_lock gModulesLock;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
|
|
|
|
/* These are the standard paths that we look on for mdoules to load.
|
|
|
|
|
* By default we only look on these plus the prefix, though we do search
|
|
|
|
|
* below the prefix.
|
|
|
|
|
* i.e. using media as the prefix will match
|
|
|
|
|
* /boot/user-addons/media
|
|
|
|
|
* /boot/addons/media
|
|
|
|
|
* /boot/addons/media/encoders
|
|
|
|
|
* but will NOT match
|
|
|
|
|
* /boot/addons/kernel/media
|
|
|
|
|
*/
|
2002-11-29 08:33:57 +00:00
|
|
|
const char *const gModulePaths[] = {
|
2002-07-09 12:24:59 +00:00
|
|
|
"/boot/user-addons",
|
|
|
|
|
"/boot/addons"
|
|
|
|
|
};
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
#define NUM_MODULE_PATHS (sizeof(gModulePaths) / sizeof(gModulePaths[0]))
|
|
|
|
|
|
|
|
|
|
/* we store the loaded modules by directory path, and all known modules by module name
|
|
|
|
|
* in a hash table for quick access
|
|
|
|
|
*/
|
|
|
|
|
hash_table *gLoadedModulesHash;
|
|
|
|
|
hash_table *gModulesHash;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 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 loaded module using its path */
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
static uint32
|
|
|
|
|
loaded_module_hash(void *_module, const void *_key, uint32 range)
|
|
|
|
|
{
|
|
|
|
|
loaded_module *loadedModule = (loaded_module *)_module;
|
|
|
|
|
const char *path = (const char *)_key;
|
|
|
|
|
|
|
|
|
|
if (loadedModule != NULL)
|
|
|
|
|
return hash_hash_string(loadedModule->path) % range;
|
|
|
|
|
|
|
|
|
|
if (path != NULL)
|
|
|
|
|
return hash_hash_string(path) % range;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** compares a loaded module to a path */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
loaded_module_compare(void *_module, const void *_key)
|
|
|
|
|
{
|
|
|
|
|
loaded_module *loadedModule = (loaded_module *)_module;
|
|
|
|
|
const char *path = (const char *)_key;
|
|
|
|
|
if (path == NULL)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return strcmp(loadedModule->path, path);
|
|
|
|
|
}
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** Try to load the module file we've found into memory.
|
|
|
|
|
* This may fail if all the symbols can't be resolved.
|
|
|
|
|
* Returns 0 on success, -1 on failure.
|
2002-07-09 12:24:59 +00:00
|
|
|
*
|
2002-11-29 08:33:57 +00:00
|
|
|
* NB hdrs can be passed as a NULL if the modules ** header
|
|
|
|
|
* pointer isn't required.
|
2002-07-09 12:24:59 +00:00
|
|
|
*
|
2002-11-29 08:33:57 +00:00
|
|
|
* Returns
|
|
|
|
|
* NULL on failure
|
|
|
|
|
* pointer to modules symbol on success
|
2002-07-09 12:24:59 +00:00
|
|
|
*/
|
2002-08-09 17:03:03 +00:00
|
|
|
|
|
|
|
|
static module_info **
|
2002-11-29 08:33:57 +00:00
|
|
|
load_module_file(const char *path)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
loaded_module *loadedModule;
|
|
|
|
|
|
|
|
|
|
image_id image = elf_load_kspace(path, "");
|
|
|
|
|
if (image < 0) {
|
|
|
|
|
dprintf("load_module_file failed: %s\n", strerror(image));
|
2002-07-09 12:24:59 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
loadedModule = (loaded_module *)malloc(sizeof(loaded_module));
|
|
|
|
|
if (!loadedModule) {
|
|
|
|
|
elf_unload_kspace(path);
|
2002-07-09 12:24:59 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
loadedModule->info = (module_info **)elf_lookup_symbol(image, "modules");
|
|
|
|
|
if (!loadedModule->info) {
|
|
|
|
|
FATAL(("load_module_file: Failed to load %s due to lack of 'modules' symbol\n", path));
|
|
|
|
|
elf_unload_kspace(path);
|
|
|
|
|
free(loadedModule);
|
2002-07-09 12:24:59 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
loadedModule->path = strdup(path);
|
|
|
|
|
if (!loadedModule->path) {
|
|
|
|
|
elf_unload_kspace(path);
|
|
|
|
|
free(loadedModule);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
loadedModule->image = image;
|
|
|
|
|
loadedModule->ref_count = 0;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
recursive_lock_lock(&gModulesLock);
|
|
|
|
|
hash_insert(gLoadedModulesHash, loadedModule);
|
|
|
|
|
recursive_lock_unlock(&gModulesLock);
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
return loadedModule->info;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
unload_module_file(const char *path)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
loaded_module *loadedModule;
|
2002-07-19 01:32:42 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("unload_mdoule_file: %s\n", path));
|
|
|
|
|
|
|
|
|
|
loadedModule = (loaded_module *)hash_lookup(gLoadedModulesHash, path);
|
|
|
|
|
if (loadedModule == NULL)
|
2002-07-09 12:24:59 +00:00
|
|
|
return;
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
if (loadedModule->ref_count != 0) {
|
|
|
|
|
FATAL(("Can't unload %s due to ref_cnt = %ld\n", loadedModule->path, loadedModule->ref_count));
|
2002-07-09 12:24:59 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
recursive_lock_lock(&gModulesLock);
|
|
|
|
|
hash_remove(gLoadedModulesHash, loadedModule);
|
|
|
|
|
recursive_lock_unlock(&gModulesLock);
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
elf_unload_kspace(loadedModule->path);
|
|
|
|
|
free(loadedModule->path);
|
|
|
|
|
free(loadedModule);
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** Extract the information from the module_info structure pointed at
|
|
|
|
|
* by "info" and create the entries required for access to it's details.
|
2002-07-09 12:24:59 +00:00
|
|
|
*/
|
2002-08-09 17:03:03 +00:00
|
|
|
|
|
|
|
|
static int
|
2002-11-29 08:33:57 +00:00
|
|
|
create_module(module_info *info, const char *file, int offset, module **_module)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
module *module;
|
2002-07-19 01:32:42 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
if (!info->name)
|
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
|
|
module = (struct module *)hash_lookup(gModulesHash, info->name);
|
|
|
|
|
if (module) {
|
|
|
|
|
FATAL(("Duplicate module name (%s) detected... ignoring new one\n", info->name));
|
|
|
|
|
return B_FILE_EXISTS;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-07-19 01:32:42 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
if ((module = (struct module *)malloc(sizeof(module))) == NULL)
|
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
|
|
|
|
|
TRACE(("create_module(%s, %s)\n", info->name, file));
|
|
|
|
|
|
|
|
|
|
module->loaded_module = NULL;
|
|
|
|
|
module->name = strdup(info->name);
|
|
|
|
|
if (module->name == NULL) {
|
|
|
|
|
free(module);
|
|
|
|
|
return B_NO_MEMORY;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
module->file = strdup(file);
|
|
|
|
|
if (module->file == NULL) {
|
|
|
|
|
free(module->name);
|
|
|
|
|
free(module);
|
|
|
|
|
return B_NO_MEMORY;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
module->state = MODULE_QUERIED;
|
|
|
|
|
module->offset = offset;
|
|
|
|
|
// record where the module_info can be found in the module_info array
|
|
|
|
|
module->ref_count = 0;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
if (info->flags & B_KEEP_LOADED) {
|
|
|
|
|
TRACE(("module %s wants to be kept loaded\n", module->name));
|
|
|
|
|
module->keep_loaded = true;
|
|
|
|
|
}
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
recursive_lock_lock(&gModulesLock);
|
|
|
|
|
hash_insert(gModulesHash, module);
|
|
|
|
|
recursive_lock_unlock(&gModulesLock);
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
if (_module)
|
|
|
|
|
*_module = module;
|
|
|
|
|
|
|
|
|
|
return B_OK;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** 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)
|
2002-07-09 12:24:59 +00:00
|
|
|
*/
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
static status_t
|
|
|
|
|
check_module_file(const char *path, const char *searchedName)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
module_info **header = NULL, **info;
|
|
|
|
|
int index = 0, match = B_ENTRY_NOT_FOUND;
|
|
|
|
|
|
|
|
|
|
ASSERT(hash_lookup(gLoadedModulesHash, path) == NULL);
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
if ((header = load_module_file(path)) == NULL)
|
2002-07-09 12:24:59 +00:00
|
|
|
return -1;
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
for (info = header; *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;
|
2002-07-17 12:51:48 +00:00
|
|
|
}
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-07-17 16:08:53 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
// 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\n", path));
|
|
|
|
|
unload_module_file(path);
|
2002-07-19 01:32:42 +00:00
|
|
|
}
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-07-17 16:08:53 +00:00
|
|
|
return match;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** 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.
|
2002-07-09 12:24:59 +00:00
|
|
|
*/
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
static status_t
|
|
|
|
|
recurse_directory(const char *path, const char *searchedName)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
status_t status;
|
|
|
|
|
DIR *dir = opendir(path);
|
|
|
|
|
if (dir == NULL);
|
|
|
|
|
return errno;
|
2002-07-10 21:48:27 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
errno = 0;
|
2002-07-10 21:48:27 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
// loop until we have a match or we run out of entries
|
|
|
|
|
while (true) {
|
|
|
|
|
struct dirent *dirent;
|
2002-10-17 03:09:25 +00:00
|
|
|
struct stat st;
|
2002-11-29 08:33:57 +00:00
|
|
|
char *newPath;
|
|
|
|
|
size_t size = 0;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("scanning %s\n", path));
|
2002-07-10 21:48:27 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
size = strlen(path) + strlen(dirent->d_name) + 2;
|
|
|
|
|
newPath = (char *)malloc(size);
|
|
|
|
|
if (newPath == NULL) {
|
|
|
|
|
status = B_NO_MEMORY;
|
|
|
|
|
goto exit;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
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;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
// If we couldn't stat the current file, we will just ignore it;
|
|
|
|
|
// it's a problem of the file system, not ours.
|
|
|
|
|
continue;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
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(gLoadedModulesHash, newPath) != NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
status = check_module_file(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);
|
|
|
|
|
}
|
2002-07-10 21:48:27 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
exit:
|
|
|
|
|
closedir(dir);
|
|
|
|
|
return status;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** This is only called if we fail to find a module already in our cache...
|
|
|
|
|
* saves us some extra checking here :)
|
2002-07-09 12:24:59 +00:00
|
|
|
*/
|
2002-08-09 17:03:03 +00:00
|
|
|
|
|
|
|
|
static module *
|
|
|
|
|
search_module(const char *name)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
|
|
|
|
int i, res = 0;
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("search_module(%s)\n", name));
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
for (i = 0; i < (int)NUM_MODULE_PATHS; ++i) {
|
|
|
|
|
if ((res = recurse_directory(gModulePaths[i], name)) == 1)
|
2002-07-09 12:24:59 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
if (res != 1)
|
2002-07-09 12:24:59 +00:00
|
|
|
return NULL;
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
return (module *)hash_lookup(gModulesHash, name);
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** Initializes a loaded module depending on its state */
|
|
|
|
|
|
|
|
|
|
static inline status_t
|
2002-08-09 17:03:03 +00:00
|
|
|
init_module(module *module)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
switch (module->state) {
|
|
|
|
|
case MODULE_QUERIED:
|
|
|
|
|
case MODULE_LOADED:
|
|
|
|
|
{
|
|
|
|
|
status_t status;
|
|
|
|
|
module->state = MODULE_INIT;
|
|
|
|
|
|
|
|
|
|
TRACE(("initing module %s... \n", module->name));
|
|
|
|
|
status = module->info->std_ops(B_MODULE_INIT);
|
|
|
|
|
TRACE(("...done (%s)\n", strerror(status)));
|
|
|
|
|
|
|
|
|
|
if (!status)
|
|
|
|
|
module->state = MODULE_READY;
|
2002-07-09 12:24:59 +00:00
|
|
|
else
|
2002-11-29 08:33:57 +00:00
|
|
|
module->state = MODULE_LOADED;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case MODULE_READY:
|
|
|
|
|
return B_NO_ERROR;
|
|
|
|
|
|
|
|
|
|
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;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
|
|
|
|
default:
|
2002-11-29 08:33:57 +00:00
|
|
|
return B_ERROR;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
// never trespasses here
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** Uninitializes a module depeding on its state */
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
static inline int
|
|
|
|
|
uninit_module(module *module)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
switch (module->state) {
|
|
|
|
|
case MODULE_QUERIED:
|
|
|
|
|
case MODULE_LOADED:
|
2002-07-09 12:24:59 +00:00
|
|
|
return B_NO_ERROR;
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
case MODULE_INIT:
|
|
|
|
|
panic("Trying to unload module %s which is initializing\n", module->name);
|
2002-07-11 22:21:56 +00:00
|
|
|
return B_ERROR;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
case MODULE_UNINIT:
|
|
|
|
|
panic("Trying to unload module %s which is un-initializing\n", module->name);
|
2002-07-11 22:21:56 +00:00
|
|
|
return B_ERROR;
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
case MODULE_READY:
|
|
|
|
|
{
|
|
|
|
|
status_t status;
|
|
|
|
|
|
|
|
|
|
module->state = MODULE_UNINIT;
|
|
|
|
|
|
|
|
|
|
TRACE(("uniniting 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;
|
|
|
|
|
return 0;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
FATAL(("Error unloading module %s (%s)\n", module->name, strerror(status)));
|
|
|
|
|
|
|
|
|
|
module->state = MODULE_ERROR;
|
2002-07-09 12:24:59 +00:00
|
|
|
module->keep_loaded = true;
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2002-07-09 12:24:59 +00:00
|
|
|
default:
|
2002-07-11 22:21:56 +00:00
|
|
|
return B_ERROR;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
// never trespasses here
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
static status_t
|
|
|
|
|
process_module_info(module_iterator *iterator, char *buffer, size_t *_bufferSize)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
module *module = NULL;
|
|
|
|
|
module_info **info = iterator->current_header;
|
|
|
|
|
status_t status = B_NO_ERROR;
|
|
|
|
|
|
|
|
|
|
if (!info || !(*info)) {
|
|
|
|
|
status = B_BAD_VALUE;
|
2002-07-09 12:24:59 +00:00
|
|
|
} else {
|
2002-11-29 08:33:57 +00:00
|
|
|
status = create_module(*info, iterator->current_path, iterator->module_pos++, &module);
|
|
|
|
|
if (status == B_OK) {
|
|
|
|
|
strlcpy(buffer, module->name, *_bufferSize);
|
|
|
|
|
*_bufferSize = strlen(module->name);
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
// If we have a valid "info" pointer, traverse to the next, or mark the end
|
|
|
|
|
if (info && *(++info) != NULL)
|
|
|
|
|
iterator->current_header++;
|
2002-07-09 12:24:59 +00:00
|
|
|
else
|
2002-11-29 08:33:57 +00:00
|
|
|
iterator->current_header = NULL;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
return status;
|
|
|
|
|
}
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
|
module_create_dir_iterator(module_iterator *iter, int file, const char *name)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
|
|
|
|
module_dir_iterator *dir;
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-07-09 12:24:59 +00:00
|
|
|
/* if we're creating a dir_iterator, there is no way that the
|
2002-11-29 08:33:57 +00:00
|
|
|
* current_header value can be valid, so make sure and reset it
|
2002-07-09 12:24:59 +00:00
|
|
|
* here.
|
|
|
|
|
*/
|
2002-11-29 08:33:57 +00:00
|
|
|
iter->current_header = NULL;
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-10-29 23:07:06 +00:00
|
|
|
dir = (struct module_dir_iterator *)malloc(sizeof(*dir));
|
2002-07-09 12:24:59 +00:00
|
|
|
if (dir == NULL )
|
2002-07-11 22:21:56 +00:00
|
|
|
return ENOMEM;
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-10-29 23:07:06 +00:00
|
|
|
dir->name = strdup(name);
|
2002-08-09 17:03:03 +00:00
|
|
|
if (dir->name == NULL) {
|
2002-10-29 23:07:06 +00:00
|
|
|
free(dir);
|
2002-07-11 22:21:56 +00:00
|
|
|
return ENOMEM;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dir->file = file;
|
|
|
|
|
dir->sub_dir = NULL;
|
|
|
|
|
dir->parent_dir = iter->cur_dir;
|
2002-08-09 17:03:03 +00:00
|
|
|
|
|
|
|
|
if (iter->cur_dir)
|
2002-07-09 12:24:59 +00:00
|
|
|
iter->cur_dir->sub_dir = dir;
|
|
|
|
|
else
|
|
|
|
|
iter->base_dir = dir;
|
|
|
|
|
|
|
|
|
|
iter->cur_dir = dir;
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("created dir iterator for %s\n", name));
|
2002-07-09 12:24:59 +00:00
|
|
|
return B_NO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
|
module_enter_dir(module_iterator *iter, const char *path)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-07-14 05:15:34 +00:00
|
|
|
int dir;
|
2002-07-09 12:24:59 +00:00
|
|
|
int res;
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
// ToDo: use opendir() instead
|
2002-07-14 05:15:34 +00:00
|
|
|
dir = sys_open_dir(path);
|
2002-08-09 17:03:03 +00:00
|
|
|
if (dir < 0) {
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("couldn't open directory %s (%s)\n", path, strerror(dir)));
|
2002-07-14 05:15:34 +00:00
|
|
|
|
2002-07-09 12:24:59 +00:00
|
|
|
// there are so many errors for "not found" that we don't bother
|
|
|
|
|
// and always assume that the directory suddenly disappeared
|
|
|
|
|
return B_NO_ERROR;
|
|
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
|
2002-07-14 05:15:34 +00:00
|
|
|
res = module_create_dir_iterator(iter, dir, path);
|
2002-07-09 12:24:59 +00:00
|
|
|
if (res != B_NO_ERROR) {
|
2002-07-14 05:15:34 +00:00
|
|
|
sys_close(dir);
|
2002-07-09 12:24:59 +00:00
|
|
|
return ENOMEM;
|
|
|
|
|
}
|
2002-07-14 05:15:34 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("entered directory %s\n", path));
|
2002-07-09 12:24:59 +00:00
|
|
|
return B_NO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
static inline void
|
2002-11-29 08:33:57 +00:00
|
|
|
destroy_dir_iterator(module_iterator *iter)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
module_dir_iterator *dir = iter->cur_dir;
|
|
|
|
|
|
|
|
|
|
TRACE(("destroying directory iterator for sub-dir %s\n", dir->name));
|
|
|
|
|
|
|
|
|
|
if (dir->parent_dir)
|
2002-07-09 12:24:59 +00:00
|
|
|
dir->parent_dir->sub_dir = NULL;
|
2002-11-29 08:33:57 +00:00
|
|
|
|
2002-07-09 12:24:59 +00:00
|
|
|
iter->cur_dir = dir->parent_dir;
|
|
|
|
|
|
2002-10-29 23:07:06 +00:00
|
|
|
free(dir->name);
|
|
|
|
|
free(dir);
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
static inline void
|
2002-11-29 08:33:57 +00:00
|
|
|
module_leave_dir(module_iterator *iter)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
|
|
|
|
module_dir_iterator *parent_dir;
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("leaving directory %s\n", iter->cur_dir->name));
|
|
|
|
|
|
2002-07-09 12:24:59 +00:00
|
|
|
parent_dir = iter->cur_dir->parent_dir;
|
2002-11-29 08:33:57 +00:00
|
|
|
iter->current_header = NULL;
|
|
|
|
|
sys_close(iter->cur_dir->file);
|
|
|
|
|
destroy_dir_iterator(iter);
|
|
|
|
|
|
2002-07-09 12:24:59 +00:00
|
|
|
iter->cur_dir = parent_dir;
|
|
|
|
|
}
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
compose_path(char *path, module_iterator *iter, const char *name, bool full_path)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
|
|
|
|
module_dir_iterator *dir;
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
if (full_path) {
|
|
|
|
|
strlcpy(path, iter->base_dir->name, SYS_MAX_PATH_LEN);
|
|
|
|
|
strlcat(path, "/", SYS_MAX_PATH_LEN);
|
2002-07-09 12:24:59 +00:00
|
|
|
} else {
|
2002-11-29 08:33:57 +00:00
|
|
|
strlcpy(path, iter->prefix, SYS_MAX_PATH_LEN);
|
|
|
|
|
if (*iter->prefix)
|
|
|
|
|
strlcat(path, "/", SYS_MAX_PATH_LEN);
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
for (dir = iter->base_dir->sub_dir; dir; dir = dir->sub_dir) {
|
|
|
|
|
strlcat(path, dir->name, SYS_MAX_PATH_LEN);
|
|
|
|
|
strlcat(path, "/", SYS_MAX_PATH_LEN);
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
strlcat(path, name, SYS_MAX_PATH_LEN);
|
|
|
|
|
|
|
|
|
|
TRACE(("name: %s, %s -> %s\n", name, full_path ? "full path" : "relative path", path));
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** Logic as follows...
|
|
|
|
|
* If we have a headers pointer,
|
|
|
|
|
* - check if the next structure is NULL, if not process that module_info structure
|
|
|
|
|
* - if it's null, close the file, NULL the headers pointer and fall through
|
2002-07-09 12:24:59 +00:00
|
|
|
*
|
2002-11-29 08:33:57 +00:00
|
|
|
* This function tries to find the next module filename and then set the headers
|
|
|
|
|
* pointer in the cur_dir structure.
|
2002-08-09 17:03:03 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
|
module_traverse_dir(module_iterator *iter)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-10-17 03:09:25 +00:00
|
|
|
struct stat st;
|
2002-08-09 17:03:03 +00:00
|
|
|
char buffer[SYS_MAX_NAME_LEN + sizeof(struct dirent)];
|
|
|
|
|
struct dirent *dirent = (struct dirent *)buffer;
|
2002-07-09 12:24:59 +00:00
|
|
|
char path[SYS_MAX_PATH_LEN];
|
2002-10-17 03:09:25 +00:00
|
|
|
int res;
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/* If (*iter->current_header) != NULL we have another module within
|
2002-07-09 12:24:59 +00:00
|
|
|
* the existing file to return, so just return.
|
|
|
|
|
* Otherwise, actually find the next file to read.
|
|
|
|
|
*/
|
2002-11-29 08:33:57 +00:00
|
|
|
if (iter->current_header) {
|
|
|
|
|
if (*iter->current_header != NULL)
|
2002-10-17 03:09:25 +00:00
|
|
|
return B_OK;
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
unload_module_file(iter->current_path);
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("scanning %s\n", iter->cur_dir->name));
|
2002-08-09 17:03:03 +00:00
|
|
|
if ((res = sys_read_dir(iter->cur_dir->file, dirent, sizeof(buffer), 1)) <= 0) {
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("got error: %s\n", strerror(res)));
|
2002-07-09 12:24:59 +00:00
|
|
|
module_leave_dir(iter);
|
|
|
|
|
return B_NO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("got %s\n", dirent->d_name));
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-10-17 03:09:25 +00:00
|
|
|
if (strcmp(dirent->d_name, ".") == 0
|
|
|
|
|
|| strcmp(dirent->d_name, "..") == 0 )
|
2002-07-09 12:24:59 +00:00
|
|
|
return B_NO_ERROR;
|
2002-08-09 17:03:03 +00:00
|
|
|
|
|
|
|
|
compose_path(path, iter, dirent->d_name, true);
|
2002-07-09 12:24:59 +00:00
|
|
|
|
|
|
|
|
/* As we're doing a new file, reset the pointers that might get
|
|
|
|
|
* screwed up...
|
|
|
|
|
*/
|
2002-11-29 08:33:57 +00:00
|
|
|
iter->current_header = NULL;
|
2002-07-09 12:24:59 +00:00
|
|
|
iter->module_pos = 0;
|
2002-10-17 03:09:25 +00:00
|
|
|
|
|
|
|
|
if ((res = stat(path, &st)) != B_NO_ERROR)
|
2002-07-09 12:24:59 +00:00
|
|
|
return res;
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-10-17 03:09:25 +00:00
|
|
|
if (S_ISREG(st.st_mode)) {
|
2002-07-09 12:24:59 +00:00
|
|
|
module_info **hdrs = NULL;
|
|
|
|
|
if ((hdrs = load_module_file(path)) != NULL) {
|
2002-11-29 08:33:57 +00:00
|
|
|
iter->current_header = hdrs;
|
|
|
|
|
iter->current_path = strdup(path);
|
2002-07-09 12:24:59 +00:00
|
|
|
return B_NO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
return EINVAL; /* not sure what we should return here */
|
|
|
|
|
}
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-10-17 03:09:25 +00:00
|
|
|
if (S_ISDIR(st.st_mode))
|
2002-07-09 12:24:59 +00:00
|
|
|
return module_enter_dir(iter, path);
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("entry %s not a file nor a directory - ignored\n", dirent->d_name));
|
2002-07-09 12:24:59 +00:00
|
|
|
return B_NO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** Basically try each of the directories we have listed as module paths,
|
|
|
|
|
* trying each with the prefix we've been allocated.
|
2002-07-09 12:24:59 +00:00
|
|
|
*/
|
2002-08-09 17:03:03 +00:00
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
|
module_enter_base_path(module_iterator *iter)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
|
|
|
|
char path[SYS_MAX_PATH_LEN];
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-07-09 12:24:59 +00:00
|
|
|
++iter->base_path_id;
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
if (iter->base_path_id >= NUM_MODULE_PATHS) {
|
|
|
|
|
TRACE(("no locations left\n"));
|
|
|
|
|
return B_ENTRY_NOT_FOUND;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("trying base path (%s)\n", gModulePaths[iter->base_path_id]));
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-07-09 12:24:59 +00:00
|
|
|
if (iter->base_path_id == 0 && modules_disable_user_addons) {
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("ignoring user add-ons (they are disabled)\n"));
|
2002-07-09 12:24:59 +00:00
|
|
|
return B_NO_ERROR;
|
|
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
strcpy(path, gModulePaths[iter->base_path_id]);
|
2002-07-09 12:24:59 +00:00
|
|
|
if (*iter->prefix) {
|
|
|
|
|
strcat(path, "/");
|
|
|
|
|
strlcat(path, iter->prefix, sizeof(path));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return module_enter_dir(iter, path);
|
|
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
// #pragma mark -
|
|
|
|
|
// Exported Kernel API (private part)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Setup the module structures and data for use - must be called
|
|
|
|
|
* before any other module call.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
status_t
|
|
|
|
|
module_init(kernel_args *ka, module_info **sys_module_headers)
|
|
|
|
|
{
|
|
|
|
|
recursive_lock_create(&gModulesLock);
|
|
|
|
|
|
|
|
|
|
gModulesHash = hash_init(MODULE_HASH_SIZE, 0, module_compare, module_hash);
|
|
|
|
|
if (gModulesHash == NULL)
|
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
|
|
|
|
|
gLoadedModulesHash = hash_init(MODULE_HASH_SIZE, 0, loaded_module_compare, loaded_module_hash);
|
|
|
|
|
if (gLoadedModulesHash == NULL)
|
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
if (sys_module_headers) {
|
|
|
|
|
if (register_module_image("", "(built-in)", 0, sys_module_headers) == NULL)
|
|
|
|
|
return ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
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().
|
2002-07-09 12:24:59 +00:00
|
|
|
*/
|
2002-08-09 17:03:03 +00:00
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
open_module_list(const char *prefix)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
module_iterator *iterator;
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("open_module_list(prefix = %s)\n", prefix));
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
iterator = (module_iterator *)malloc(sizeof( module_iterator));
|
|
|
|
|
if (!iterator)
|
2002-07-09 12:24:59 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
// ToDo: possibly, the prefix don't have to be copied, just referenced
|
|
|
|
|
iterator->prefix = strdup(prefix);
|
|
|
|
|
if (iterator->prefix == NULL) {
|
|
|
|
|
free(iterator);
|
2002-07-09 12:24:59 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
iterator->base_path_id = -1;
|
|
|
|
|
iterator->base_dir = iterator->cur_dir = NULL;
|
|
|
|
|
iterator->err = B_NO_ERROR;
|
|
|
|
|
iterator->module_pos = 0;
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
return (void *)iterator;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** Frees the cookie allocated by open_module_list()
|
2002-07-09 12:24:59 +00:00
|
|
|
*/
|
2002-08-09 17:03:03 +00:00
|
|
|
|
2002-09-23 02:41:52 +00:00
|
|
|
status_t
|
2002-11-29 08:33:57 +00:00
|
|
|
close_module_list(void *cookie)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
module_iterator *iterator = (module_iterator *)cookie;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("close_module_list()\n"));
|
|
|
|
|
|
|
|
|
|
if (iterator == NULL)
|
|
|
|
|
return B_BAD_VALUE;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
while (iterator->cur_dir)
|
|
|
|
|
module_leave_dir(iterator);
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
free(iterator->prefix);
|
|
|
|
|
free(iterator);
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
return 0;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2002-09-23 02:41:52 +00:00
|
|
|
status_t
|
2002-11-29 08:33:57 +00:00
|
|
|
read_next_module_name(void *cookie, char *buffer, size_t *_bufferSize)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
module_iterator *iterator = (module_iterator *)cookie;
|
|
|
|
|
status_t status;
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
if (iterator == NULL || buffer == NULL || _bufferSize == NULL)
|
|
|
|
|
return B_BAD_VALUE;
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("read_next_module_name: looking for next module\n"));
|
|
|
|
|
|
|
|
|
|
status = iterator->err;
|
|
|
|
|
recursive_lock_lock(&gModulesLock);
|
|
|
|
|
|
|
|
|
|
while (status == B_OK) {
|
|
|
|
|
TRACE(("searching for module\n"));
|
|
|
|
|
|
|
|
|
|
if (iterator->cur_dir != NULL) {
|
|
|
|
|
if ((status = module_traverse_dir(iterator)) == B_NO_ERROR) {
|
|
|
|
|
// By this point we should have a valid pointer to a
|
|
|
|
|
// module_info structure in iterator->current_header
|
|
|
|
|
status = process_module_info(iterator, buffer, _bufferSize);
|
|
|
|
|
if (status == B_OK)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
status = module_enter_base_path(iterator);
|
|
|
|
|
}
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
iterator->err = status;
|
|
|
|
|
recursive_lock_unlock(&gModulesLock);
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("finished with status %s\n", strerror(status)));
|
|
|
|
|
return status;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/** 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
|
|
|
|
|
* loaded_module pointer, which would be hard to test for)
|
2002-07-09 12:24:59 +00:00
|
|
|
*/
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
status_t
|
|
|
|
|
get_next_loaded_module_name(uint32 *cookie, char *buffer, size_t *_bufferSize)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
hash_iterator *iterator = (hash_iterator *)*cookie;
|
|
|
|
|
loaded_module *loadedModule;
|
|
|
|
|
status_t status;
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
TRACE(("get_next_loaded_module_name()\n"));
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
if (cookie == NULL || buffer == NULL || _bufferSize == NULL)
|
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
|
|
if (iterator == NULL) {
|
|
|
|
|
iterator = hash_open(gLoadedModulesHash, NULL);
|
|
|
|
|
if (iterator == NULL)
|
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
|
|
|
|
|
*(hash_iterator **)cookie = iterator;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-11-17 01:38:35 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
recursive_lock_lock(&gModulesLock);
|
|
|
|
|
|
|
|
|
|
loadedModule = hash_next(gLoadedModulesHash, iterator);
|
|
|
|
|
if (loadedModule != NULL) {
|
|
|
|
|
strlcpy(buffer, loadedModule->path, *_bufferSize);
|
|
|
|
|
*_bufferSize = strlen(loadedModule->path);
|
|
|
|
|
status = B_OK;
|
|
|
|
|
} else {
|
|
|
|
|
hash_close(gLoadedModulesHash, iterator, true);
|
|
|
|
|
status = B_ENTRY_NOT_FOUND;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
recursive_lock_unlock(&gModulesLock);
|
|
|
|
|
|
|
|
|
|
return status;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-09-23 02:41:52 +00:00
|
|
|
status_t
|
2002-11-29 08:33:57 +00:00
|
|
|
get_module(const char *path, module_info **_info)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
loaded_module *loadedModule;
|
|
|
|
|
module *module;
|
|
|
|
|
status_t status;
|
|
|
|
|
|
|
|
|
|
TRACE(("get_module(%s)\n", path));
|
|
|
|
|
if (path == NULL)
|
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
|
|
recursive_lock_lock(&gModulesLock);
|
|
|
|
|
|
|
|
|
|
module = (struct module *)hash_lookup(gModulesHash, 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;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
/* We now need to find the loaded_module for the module. This should
|
2002-07-09 12:24:59 +00:00
|
|
|
* be in memory if we have just run search_modules, but may not be
|
|
|
|
|
* if we are used cached information.
|
2002-11-29 08:33:57 +00:00
|
|
|
* We can't use the module->loaded_module pointer, because it is not
|
|
|
|
|
* reliable at this point (it won't be set to NULL when the loaded_module
|
|
|
|
|
* is unloaded).
|
2002-07-09 12:24:59 +00:00
|
|
|
*/
|
2002-11-29 08:33:57 +00:00
|
|
|
loadedModule = (loaded_module *)hash_lookup(gLoadedModulesHash, module->file);
|
|
|
|
|
if (loadedModule == NULL) {
|
|
|
|
|
if (load_module_file(module->file) == NULL)
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
loadedModule = (loaded_module *)hash_lookup(gLoadedModulesHash, module->file);
|
|
|
|
|
if (loadedModule == NULL)
|
|
|
|
|
goto err;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
// (re)set in-memory data for the loaded module
|
|
|
|
|
module->info = loadedModule->info[module->offset];
|
|
|
|
|
module->loaded_module = loadedModule;
|
|
|
|
|
INC_MOD_REF_COUNT(module);
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
// The state will be adjusted by the call to init_module
|
|
|
|
|
// if we have just loaded the file
|
|
|
|
|
if (module->ref_count == 1)
|
|
|
|
|
status = init_module(module);
|
|
|
|
|
else
|
|
|
|
|
status = B_OK;
|
|
|
|
|
|
|
|
|
|
recursive_lock_unlock(&gModulesLock);
|
2002-07-09 12:24:59 +00:00
|
|
|
|
2002-11-29 08:33:57 +00:00
|
|
|
if (status == B_OK)
|
|
|
|
|
*_info = module->info;
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
recursive_lock_unlock(&gModulesLock);
|
|
|
|
|
return B_ENTRY_NOT_FOUND;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
2002-09-23 02:41:52 +00:00
|
|
|
|
|
|
|
|
status_t
|
|
|
|
|
put_module(const char *path)
|
2002-07-09 12:24:59 +00:00
|
|
|
{
|
2002-11-29 08:33:57 +00:00
|
|
|
module *module;
|
|
|
|
|
|
|
|
|
|
TRACE(("put_module(path = %s)\n", path));
|
|
|
|
|
|
|
|
|
|
recursive_lock_lock(&gModulesLock);
|
|
|
|
|
|
|
|
|
|
module = (struct module *)hash_lookup(gModulesHash, path);
|
|
|
|
|
if (module == NULL) {
|
|
|
|
|
FATAL(("module: We don't seem to have a reference to module %s\n", path));
|
|
|
|
|
recursive_lock_unlock(&gModulesLock);
|
|
|
|
|
return B_BAD_VALUE;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
DEC_MOD_REF_COUNT(module);
|
|
|
|
|
|
|
|
|
|
// If there are nomore references to this module - must we keep it loaded?
|
|
|
|
|
if (module->ref_count == 0 && !module->keep_loaded) {
|
|
|
|
|
// destruct the module, and check if we can also unload the
|
|
|
|
|
// loaded module it refers to
|
|
|
|
|
uninit_module(module);
|
|
|
|
|
if (module->loaded_module->ref_count == 0)
|
|
|
|
|
unload_module_file(module->file);
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|
2002-11-29 08:33:57 +00:00
|
|
|
|
|
|
|
|
recursive_lock_unlock(&gModulesLock);
|
|
|
|
|
return B_OK;
|
2002-07-09 12:24:59 +00:00
|
|
|
}
|