/* Userland modules emulation support */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ASSERT(condition) if (!(condition)) { debugger("Assertion failed!"); } typedef enum { MODULE_LOADED = 0, MODULE_INITING, MODULE_READY, MODULE_UNINITING, MODULE_ERROR } module_state; typedef struct module { struct module * next; uint32 id; char * name; module_info * info; struct module_addon * addon; // the module addon this module live in // if NULL, builtin module addon int32 ref_count; // reference count of get_module() made on this module bool keep_loaded; module_state state; } module; typedef struct module_addon { struct module_addon * next; int32 ref_count; // reference count of get_module() made using this addon bool keep_loaded; char * path; image_id addon_image; // if -1, not loaded in memory currently module_info ** infos; // valid only when addon_image != -1 } module_addon; typedef struct module_list_cookie { char * prefix; char * search_paths; char * search_path; char * next_path_token; BList * dir_stack; module_addon * ma; // current module addon looked up module_info ** mi; // current module addon module info } module_list_cookie; #define LOCK_MODULES acquire_sem(g_modules_lock) #define UNLOCK_MODULES release_sem(g_modules_lock) // local prototypes // ------------------ static module * search_module(const char * name); static status_t init_module(module * m); static status_t uninit_module(module * m); static module * find_loaded_module_by_name(const char * name); static module * find_loaded_module_by_id(uint32 id); static module_addon * load_module_addon(const char * path); static status_t unload_module_addon(module_addon * ma); // globals // ------------------ static sem_id g_modules_lock = -1; // One lock for rule them all, etc... static module * g_modules = NULL; static module_addon * g_module_addons = NULL; static int32 g_next_module_id = 1; // Public routines // --------------- extern "C" { _EXPORT status_t get_module(const char * name, module_info ** mi) { status_t status; module * m; // printf("get_module(%s)\n", name); m = find_loaded_module_by_name(name); if (!m) m = search_module(name); if (!m) return B_NAME_NOT_FOUND; *mi = m->info; status = B_OK; if (m->addon) // built-in modules don't comes from addon... atomic_add(&m->addon->ref_count, 1); if (atomic_add(&m->ref_count, 1) == 0) { // first time we reference this module, so let's init it: status = init_module(m); if (status != B_OK) { printf("Failed to init module %s: %s.\n", m->name, strerror(status)); unload_module_addon(m->addon); // unload the module addon... }; }; return status; } _EXPORT status_t put_module(const char * name) { module * m; // printf("put_module(%s)\n", name); m = find_loaded_module_by_name(name); if (!m) // Hum??? Sorry, this module name was never get_module()'d return B_NAME_NOT_FOUND; if (atomic_add(&m->ref_count, -1) <= 1) // this module is no more used... uninit_module(m); if (!m->addon) // built-in modules are module addon less... return B_OK; if (atomic_add(&m->addon->ref_count, -1) > 1) // Still other module(s) using this module addon return B_OK; // okay, this module addon is no more used // let's free up some memory return unload_module_addon(m->addon); } _EXPORT status_t get_next_loaded_module_name(uint32 *cookie, char *buf, size_t *bufsize) { module * m; status_t status; if (buf == NULL && bufsize == NULL) return B_BAD_VALUE; LOCK_MODULES; if (*cookie == 0) // first call expected value m = g_modules; else { // find last loaded module returned, and seek to next one m = (module *) find_loaded_module_by_id((int) *cookie); if (m) m = m->next; }; // find next loaded module while (m) { if (m->ref_count) break; m = m->next; }; status = B_OK; if (m) { ASSERT(m->info); if (buf != NULL) strncpy(buf, m->info->name, *bufsize); else *bufsize = strlen(m->info->name + 1); *cookie = m->id; } else status = B_BAD_INDEX; UNLOCK_MODULES; return status; } _EXPORT void * open_module_list(const char *prefix) { module_list_cookie * mlc; char * addon_path; if (prefix == NULL) return NULL; mlc = (module_list_cookie *) malloc(sizeof(*mlc)); mlc->prefix = strdup(prefix); addon_path = getenv("ADDON_PATH"); mlc->search_paths = (addon_path ? strdup(addon_path) : NULL); mlc->search_path = strtok_r(mlc->search_paths, ":", &mlc->next_path_token); mlc->dir_stack = new BList(); mlc->ma = NULL; mlc->mi = NULL; return mlc; } _EXPORT status_t read_next_module_name(void *cookie, char *buf, size_t *bufsize) { module_list_cookie * mlc = (module_list_cookie *) cookie; if (!bufsize) return B_BAD_VALUE; if (!mlc) return B_BAD_VALUE; /* Okay, take some time to understand how this function works! Basicly, we iterate thru: - each searchable add-ons path root - each (sub-)directory under the current add-ons path root - each module add-on file in the current (sub-)directory - each module name published by current module add-on As the iteration involve sub-directory walks, we use recursive calls. Sorry if this code sounds too complex... */ if (mlc->ma && mlc->mi) { // we have a module addon still loaded from a last call // so keep looking at his exported module names list while (*mlc->mi) { module_info * mi = *mlc->mi; mlc->mi++; if(strstr(mi->name, mlc->prefix)) { // We find a matching module name. At least. Yeah!!! if (buf) strncpy(buf, mi->name, *bufsize); *bufsize = strlen(mi->name); return B_OK; }; }; // We've iterate all module names of this module addon. Find another one... atomic_add(&mlc->ma->ref_count, -1); unload_module_addon(mlc->ma); mlc->ma = NULL; mlc->mi = NULL; }; // Iterate all searchable add-ons paths while (mlc->search_path) { BDirectory * dir; BEntry entry; BPath path; status_t status; // Get current directory dir = (BDirectory *) mlc->dir_stack->LastItem(); if (!dir) { // find add-ons root directory in this search path if (strncmp(mlc->search_path, "%A/", 3) == 0) { // resolve "%A/..." path app_info ai; be_app->GetAppInfo(&ai); entry.SetTo(&ai.ref); entry.GetPath(&path); path.GetParent(&path); path.Append(mlc->search_path + 3); } else { path.SetTo(mlc->search_path); }; // We look *only* under prefix-matching sub-path path.Append(mlc->prefix); // printf("Looking module(s) in %s/%s...\n", mlc->search_path, mlc->prefix); dir = new BDirectory(path.Path()); if (dir) mlc->dir_stack->AddItem(dir); }; // Iterate current directory content if (dir) { while (dir->GetNextEntry(&entry) == B_OK) { entry.GetPath(&path); // printf(" %s ?\n", path.Path()); if (entry.IsDirectory()) { BDirectory * subdir; // push this directory on dir_stack subdir = new BDirectory(path.Path()); if (!subdir) continue; mlc->dir_stack->AddItem(subdir); // recursivly search this sub-directory return read_next_module_name(cookie, buf, bufsize); }; if (entry.IsFile() || entry.IsSymLink()) { mlc->ma = load_module_addon(path.Path()); if (!mlc->ma) // Oh-oh, not a loadable module addon!? // WTF it's doing there?!? continue; atomic_add(&mlc->ma->ref_count, 1); // call ourself to enter the module names list iteration at // function begining code... mlc->mi = mlc->ma->infos; return read_next_module_name(cookie, buf, bufsize); }; }; // We walk thru all this directory content, go back to parent status = mlc->dir_stack->RemoveItem(dir); delete dir; }; if (!mlc->dir_stack->IsEmpty()) continue; // We walk thru all this search path content, next now mlc->search_path = strtok_r(NULL, ":", &mlc->next_path_token); }; // Module(s) list search done, ending... return B_ERROR; } _EXPORT status_t close_module_list(void *cookie) { module_list_cookie * mlc = (module_list_cookie *) cookie; BDirectory * dir; ASSERT(mlc); ASSERT(mlc->prefix); if (mlc->ma) { atomic_add(&mlc->ma->ref_count, -1); unload_module_addon(mlc->ma); }; while((dir = (BDirectory *) mlc->dir_stack->FirstItem())) { mlc->dir_stack->RemoveItem(dir); delete dir; }; delete mlc->dir_stack; free(mlc->search_paths); free(mlc->prefix); free(mlc); return B_ERROR; } // #pragma mark - // Some KernelExport.h support from userland _EXPORT void dprintf(const char *fmt, ...) { va_list args; va_start(args, fmt); vprintf(fmt, args); va_end(args); } _EXPORT void kprintf(const char *fmt, ...) { va_list args; va_start(args, fmt); vprintf(fmt, args); va_end(args); } _EXPORT status_t load_driver_symbols(const char *driver_name) { // Userland debugger will extract symbols itself... return B_OK; } _EXPORT thread_id spawn_kernel_thread(thread_entry func, const char *name, long priority, void *arg) { return spawn_thread(func, name, priority, arg); } _EXPORT int send_signal_etc(pid_t thid, uint sig, uint32 flags) { return send_signal(thid, sig); } } // extern "C" // #pragma mark - // Private routines static module_addon * load_module_addon(const char * path) { module_addon * ma; image_id addon_id; module_info ** mi; status_t status; ASSERT(path); addon_id = load_add_on(path); if (addon_id < 0) { printf("Failed to load %s addon: %s.\n", path, strerror(addon_id)); return NULL; }; // printf("Addon %s loaded.\n", path); ma = NULL; status = get_image_symbol(addon_id, "modules", B_SYMBOL_TYPE_DATA, (void **) &mi); if (status != B_OK) { // No "modules" symbol found in this addon printf("Symbol \"modules\" not found in %s addon: not a module addon!\n", path); goto error; }; ma = (module_addon *) malloc(sizeof(*ma)); if (!ma) // Gasp: not enough memory! goto error; LOCK_MODULES; ma->ref_count = 0; ma->keep_loaded = false; ma->path = strdup(path); ma->addon_image = addon_id; ma->infos = mi; while(*mi) { module * m; m = (module *) malloc(sizeof(*m)); if (!m) // Gasp, again: not enough memory! goto error; m->ref_count = 0; m->id = atomic_add(&g_next_module_id, 1); m->info = (*mi); m->name = strdup(m->info->name); m->addon = ma; m->keep_loaded = (m->info->flags & B_KEEP_LOADED) ? true : false; m->state = MODULE_LOADED; m->next = g_modules; g_modules = m; mi++; }; // add this module addon to the list ma->next = g_module_addons; g_module_addons = ma; UNLOCK_MODULES; return ma; error: printf("Error while load_module_addon(%s)\n", path); if (ma) { // remove any appended modules by this module addon until we got error... module * prev; module * m; prev = NULL; m = g_modules; while (m) { if (m->addon == ma) { module * tmp = m; m = tmp->next; if (prev) prev->next = tmp->next; else g_modules = tmp->next; if (tmp->name) free(tmp->name); free(tmp); continue; }; prev = m; m = m->next; }; UNLOCK_MODULES; if (ma->path) free(ma->path); free(ma); }; unload_add_on(addon_id); // printf("Addon %s unloaded.\n", path); return NULL; } static status_t unload_module_addon(module_addon * ma) { module * m; module * prev; status_t status; if (!ma) // built-in modules are addon-less, so nothing to do... return B_OK; if (ma->keep_loaded) { printf("B_KEEP_LOADED flag set for %s module addon. Will be *never* unloaded!\n", ma->path); return B_OK; }; if (ma->ref_count) // still someone needing this module addon, it seems? return B_OK; if (ma->addon_image < 0) // built-in addon, it seems... return B_OK; status = unload_add_on(ma->addon_image); if (status != B_OK) { printf("Failed to unload %s addon: %s.\n", ma->path, strerror(status)); return status; }; // printf("Addon %s unloaded.\n", ma->path); LOCK_MODULES; // remove the modules coming from this module addon from g_modules list prev = NULL; m = g_modules; while (m) { if (m->addon == ma) { module * tmp = m; m = tmp->next; if (prev) prev->next = tmp->next; else g_modules = tmp->next; if (tmp->name) free(tmp->name); free(tmp); continue; }; prev = m; m = m->next; }; // remove the module addon from g_module_addons list: if (g_module_addons == ma) g_module_addons = ma->next; else { module_addon * tmp; tmp = g_module_addons; while (tmp && tmp->next != ma) tmp = tmp->next; ASSERT(tmp); tmp->next = ma->next; }; if (ma->path) free(ma->path); free(ma); UNLOCK_MODULES; return B_OK; } static module * search_module(const char * name) { BPath path; BPath addons_path; BEntry entry; module * found_module; char * search_paths; char * search_path; char * next_path_token; // printf("search_module(%s):\n", name); search_paths = getenv("ADDON_PATH"); if (!search_paths) // Nowhere to search addons!!! return NULL; search_paths = strdup(search_paths); search_path = strtok_r(search_paths, ":", &next_path_token); found_module = NULL; while (search_path && found_module == NULL) { if (strncmp(search_path, "%A/", 3) == 0) { // compute "%A/..." path app_info ai; be_app->GetAppInfo(&ai); entry.SetTo(&ai.ref); entry.GetPath(&addons_path); addons_path.GetParent(&addons_path); addons_path.Append(search_path + 3); } else { addons_path.SetTo(search_path); }; // printf("Looking into %s\n", search_path); path.SetTo(addons_path.Path()); path.Append(name); while(path != addons_path) { // printf(" %s ?\n", path.Path()); entry.SetTo(path.Path()); if (entry.IsFile() || entry.IsSymLink()) { module_addon * ma; // try to load the module addon ma = load_module_addon(path.Path()); if (ma) { found_module = find_loaded_module_by_name(name); if (found_module) break; unload_module_addon(ma); }; // if (ma) }; // if (entry.IsFile() || entry.IsSymLink()) // okay, remove the current path leaf and try again... path.GetParent(&path); }; search_path = strtok_r(NULL, ":", &next_path_token); }; free(search_paths); /* if (found_module) printf(" Found it in %s addon module!\n", found_module->addon ? found_module->addon->path : "BUILTIN"); */ return found_module; } static status_t init_module(module * m) { status_t status; ASSERT(m); switch (m->state) { case MODULE_LOADED: m->state = MODULE_INITING; ASSERT(m->info); // printf("Initing module %s... ", m->name); status = m->info->std_ops(B_MODULE_INIT); // printf("done (%s).\n", strerror(status)); m->state = (status == B_OK) ? MODULE_READY : MODULE_LOADED; if (m->state == MODULE_READY && m->keep_loaded && m->addon) { // one module (at least) was inited and request to never being // unload from memory, so keep the corresponding addon loaded // printf("module %s set B_KEEP_LOADED flag:\nmodule addon %s will never be unloaded!\n", // m->name, m->addon->path); m->addon->keep_loaded = true; }; break; case MODULE_READY: status = B_OK; break; case MODULE_INITING: // circular reference!!! case MODULE_UNINITING: // initing a module currently unloading... case MODULE_ERROR: // module failed to unload previously... default: // Unknown module state!!! status = B_ERROR; break; }; return status; } static status_t uninit_module(module * m) { status_t status; ASSERT(m); switch (m->state) { case MODULE_READY: m->state = MODULE_UNINITING; ASSERT(m->info); // printf("Uniniting module %s... ", m->name); status = m->info->std_ops(B_MODULE_UNINIT); // printf("done (%s).\n", strerror(status)); m->state = (status == B_OK) ? MODULE_LOADED : MODULE_ERROR; break; case MODULE_LOADED: // No need to uninit it, all is fine so. status = B_OK; break; case MODULE_INITING: // uniniting while initializing case MODULE_UNINITING: // uniniting already pending case MODULE_ERROR: // module failed previously... default: // Unknown module state!!! status = B_ERROR; break; }; return status; } static module * find_loaded_module_by_name(const char * name) { module * m; LOCK_MODULES; m = g_modules; while (m) { if (strcmp(name, m->name) == 0) break; m = m->next; }; UNLOCK_MODULES; return m; } static module * find_loaded_module_by_id(uint32 id) { module * m; LOCK_MODULES; m = g_modules; while (m) { if (m->id == id) break; m = m->next; }; UNLOCK_MODULES; return m; } #if 0 // #pragma mark - #define NET_CORE_MODULE_NAME "network/core/v1" #define NET_ETHERNET_MODULE_NAME "network/interfaces/ethernet" #define NET_IPV4_MODULE_NAME "network/protocols/ipv4/v1" #define MODULE_LIST_PREFIX "network" int main(int argc, char **argv) { module_info * core; module_info * ethernet; module_info * ipv4; char module_name[256]; uint32 cookie; size_t sz; void * ml_cookie; new BApplication("application/x-vnd-OBOS-net_server"); printf("open_module_list(%s):\n", MODULE_LIST_PREFIX); ml_cookie = open_module_list(MODULE_LIST_PREFIX); sz = sizeof(module_name); while(read_next_module_name(ml_cookie, module_name, &sz) == B_OK) { if (strlen(module_name)) printf(" %s\n", module_name); sz = sizeof(module_name); }; close_module_list(ml_cookie); printf("close_module_list()\n"); // return 0; core = NULL; get_module(NET_CORE_MODULE_NAME, (module_info **) &core); ethernet = NULL; get_module(NET_ETHERNET_MODULE_NAME, (module_info **) ðernet); ipv4 = NULL; get_module(NET_IPV4_MODULE_NAME, (module_info **) &ipv4); printf("get_next_loaded_module_name() test:\n"); cookie = 0; sz = sizeof(module_name); while (get_next_loaded_module_name(&cookie, module_name, &sz) == B_OK) printf("%ld: %s\n", cookie, module_name); if (ipv4) put_module(NET_IPV4_MODULE_NAME); if (ethernet) put_module(NET_ETHERNET_MODULE_NAME); if (core) put_module(NET_CORE_MODULE_NAME); printf("get_next_loaded_module_name() test:\n"); cookie = 0; sz = sizeof(module_name); while (get_next_loaded_module_name(&cookie, module_name, &sz) == B_OK) printf("%ld: %s\n", cookie, module_name); delete be_app; return 0; } #endif