# HG changeset patch # User Tero Marttila # Date 1238599847 -10800 # Node ID 50ff7ac8a72555f58d41606ffd52ac5f6d825d15 # Parent 5c1eeb45c7f2aea806b92aecada7885d4b25069c implement modules_path + module_load with NULL path diff -r 5c1eeb45c7f2 -r 50ff7ac8a725 TODO --- a/TODO Wed Apr 01 17:29:29 2009 +0300 +++ b/TODO Wed Apr 01 18:30:47 2009 +0300 @@ -11,10 +11,9 @@ * reconnect, maybe cycling servers? config: - * A more advanced structured value parser that can then handle all the various configuration values sanely + * An even more advanced structured value parser that can then handle config values for more things like the command line --options modules: - * Automatic module-name -> path mapping, and autoloading of modules somehow * proper unload support, there needs to be some kind of completion notification thing that can then destroy the module once it's completely unloaded - maybe some kind of module-resource-reference-counting thing, which will eventually be needed anyways? diff -r 5c1eeb45c7f2 -r 50ff7ac8a725 src/error.c --- a/src/error.c Wed Apr 01 17:29:29 2009 +0300 +++ b/src/error.c Wed Apr 01 18:30:47 2009 +0300 @@ -13,6 +13,7 @@ { ERR_CALLOC, "calloc", ERR_EXTRA_NONE }, { ERR_STRDUP, "strdup", ERR_EXTRA_NONE }, { ERR_SIGACTION, "sigaction", ERR_EXTRA_ERRNO }, + { ERR_ACCESS_READ, "access(R_OK)", ERR_EXTRA_ERRNO }, { ERR_GETADDRINFO, "getaddrinfo", ERR_EXTRA_GAI }, { ERR_GETADDRINFO_EMPTY, "getaddrinfo: no results", ERR_EXTRA_NONE }, { ERR_EVENT_NEW, "event_new", ERR_EXTRA_NONE }, @@ -70,8 +71,11 @@ { _ERR_INVALID, NULL, 0 } }, _module_error_desc[] = { + { ERR_MODULE_NAME, "invalid module name", ERR_EXTRA_NONE }, + { ERR_MODULE_DUP, "module already loaded", ERR_EXTRA_NONE }, + { ERR_MODULE_PATH, "invalid module path", ERR_EXTRA_STR }, { ERR_MODULE_OPEN, "module dlopen() failed", ERR_EXTRA_STR }, - { ERR_MODULE_NAME, "invalid module name", ERR_EXTRA_NONE }, + { ERR_MODULE_SYM, "module dlsym() failed", ERR_EXTRA_STR }, { ERR_MODULE_INIT_FUNC, "invalid module init func", ERR_EXTRA_STR }, { ERR_MODULE_CONF, "module_conf", ERR_EXTRA_STR }, { _ERR_INVALID, NULL, 0 } diff -r 5c1eeb45c7f2 -r 50ff7ac8a725 src/error.h --- a/src/error.h Wed Apr 01 17:29:29 2009 +0300 +++ b/src/error.h Wed Apr 01 18:30:47 2009 +0300 @@ -45,6 +45,7 @@ ERR_CALLOC, ERR_STRDUP, ERR_SIGACTION, + ERR_ACCESS_READ, /** DNS resolver */ _ERR_RESOLVER = 0x000200, diff -r 5c1eeb45c7f2 -r 50ff7ac8a725 src/lua_objs.c --- a/src/lua_objs.c Wed Apr 01 17:29:29 2009 +0300 +++ b/src/lua_objs.c Wed Apr 01 18:30:47 2009 +0300 @@ -1,6 +1,7 @@ #include "lua_objs.h" #include "log.h" +#include #include #include @@ -473,8 +474,43 @@ */ struct lua_modules { struct modules *modules; + + // strdup'd path for module_path + // XXX: remove when gc'd + char *path; }; +static int lua_modules_path (lua_State *L) +{ + struct lua_modules *lua_modules = lua_obj_get_obj(L, __func__, "spbot.modules"); + char *path = NULL; + const char *old_path; + + if (!lua_isnoneornil(L, 2)) { + // the new path + if ((path = strdup(luaL_checkstring(L, 2))) == NULL) + return luaL_error(L, "strdup"); + } + + // set or get + old_path = modules_path(lua_modules->modules, path); + + // return the old path + if (old_path) + lua_pushstring(L, old_path); + else + lua_pushnil(L); + + if (path) { + // replace the old path + free(lua_modules->path); + lua_modules->path = path; + } + + // ok + return 1; +} + static int lua_modules_load (lua_State *L) { struct lua_modules *lua_modules = lua_obj_get_obj(L, __func__, "spbot.modules"); @@ -484,7 +520,7 @@ // the module name/path info.name = luaL_checkstring(L, 2); - info.path = luaL_checkstring(L, 3); + info.path = lua_isnoneornil(L, 3) ? NULL : luaL_checkstring(L, 3); // load if (module_load(lua_modules->modules, &module, &info, &err)) @@ -511,6 +547,7 @@ } static const struct luaL_Reg lua_modules_methods[] = { + { "path", &lua_modules_path }, { "load", &lua_modules_load }, { "module", &lua_modules_module }, { NULL, NULL } diff -r 5c1eeb45c7f2 -r 50ff7ac8a725 src/module.c --- a/src/module.c Wed Apr 01 17:29:29 2009 +0300 +++ b/src/module.c Wed Apr 01 18:30:47 2009 +0300 @@ -2,6 +2,7 @@ #include "log.h" #include +#include #include #include #include @@ -26,6 +27,17 @@ return SUCCESS; } +const char* modules_path (struct modules *modules, const char *path) +{ + const char *old_path = modules->path; + + if (path) + // replace + modules->path = path; + + return old_path; +} + const char* module_name (struct module *module) { return module->info.name; @@ -53,27 +65,105 @@ return SUCCESS; } -err_t module_load (struct modules *modules, struct module **module_ptr, const struct module_info *info, struct error_info *err) +/** + * XXX: ugly function to format to a dynamically allocated string + */ +char *strfmt (const char *fmt, ...) +{ + va_list vargs; + size_t len; + char *buf; + + // figure out the length of the resulting string + va_start(vargs, fmt); + + len = vsnprintf(NULL, 0, fmt, vargs) + 1; + + va_end(vargs); + + // malloc + if ((buf = malloc(len)) == NULL) + return NULL; + + // format + va_start(vargs, fmt); + + vsnprintf(buf, len, fmt, vargs); + + va_end(vargs); + + // ok + return buf; +} + +/** + * Looks up a module's path by name, returning a dynamically allocated string with the path on success + */ +static char* module_find (struct modules *modules, struct module_info *info, struct error_info *err) +{ + char *path = NULL; + + // build the path... + if ((path = strfmt("%s/mod_%s.so", modules->path, info->name)) == NULL) + JUMP_SET_ERROR(err, ERR_STRDUP); + + // exists and readable? + if (access(path, R_OK)) + // XXX: this doesn't contain the path... + JUMP_SET_ERROR_STR(err, ERR_MODULE_PATH, "module not found in search path"); + + // ok + return path; + +error: + // release the dynamically allocated path + free(path); + + return NULL; +} + +err_t module_load (struct modules *modules, struct module **module_ptr, const struct module_info *_info, struct error_info *err) { struct module *module; // validate the module name - if (strlen(info->name) > MODULE_NAME_MAX) + if (strlen(_info->name) > MODULE_NAME_MAX) return SET_ERROR(err, ERR_MODULE_NAME); + // already open with the same name? + if (module_get(modules, _info->name)) + return SET_ERROR(err, ERR_MODULE_DUP); + // alloc if ((module = calloc(1, sizeof(*module))) == NULL) return SET_ERROR(err, ERR_CALLOC); // store - module->info = *info; + module->info = *_info; module->modules = modules; + + // add to modules list + TAILQ_INSERT_TAIL(&modules->list, module, modules_list); + + // lookup path? + if (!module->info.path) { + // have a search path? + if (!modules->path) + JUMP_SET_ERROR_STR(err, ERR_MODULE_PATH, "no module search path defined"); + + // try and resolve the path automatically + if ((module->path_buf = module_find(modules, &module->info, err)) == NULL) + goto error; + + // use that path + module->info.path = module->path_buf; + } // clear dlerrors (void) dlerror(); // load it - if ((module->handle = dlopen(info->path, RTLD_NOW)) == NULL) + if ((module->handle = dlopen(module->info.path, RTLD_NOW)) == NULL) JUMP_SET_ERROR_STR(err, ERR_MODULE_OPEN, dlerror()); // load the funcs symbol @@ -84,9 +174,6 @@ if ((module->desc->init(modules->nexus, &module->ctx, err))) goto error; - // add to modules list - TAILQ_INSERT_TAIL(&modules->list, module, modules_list); - // ok if (module_ptr) *module_ptr = module; @@ -94,8 +181,8 @@ return SUCCESS; error: - // XXX: cleanup - free(module); + // cleanup + module_destroy(module); return ERROR_CODE(err); } @@ -172,9 +259,12 @@ TAILQ_REMOVE(&module->modules->list, module, modules_list); // unload the dl handle - if (dlclose(module->handle)) + if (module->handle && dlclose(module->handle)) log_error("dlclose(%s): %s", module->info.name, dlerror()); + // release the path buf, if any + free(module->path_buf); + // free the module info free(module); } diff -r 5c1eeb45c7f2 -r 50ff7ac8a725 src/module.h --- a/src/module.h Wed Apr 01 17:29:29 2009 +0300 +++ b/src/module.h Wed Apr 01 18:30:47 2009 +0300 @@ -69,6 +69,9 @@ /** The identifying info for the module */ struct module_info info; + /** Possible dynamically allocated path */ + char *path_buf; + /** The dlopen handle */ void *handle; @@ -97,6 +100,9 @@ /** The nexus in use */ struct nexus *nexus; + /** Module search path */ + const char *path; + /** List of loaded modules */ TAILQ_HEAD(module_ctx_modules, module) list; }; @@ -107,8 +113,10 @@ enum module_error_code { _ERR_MODULE_BEGIN = _ERR_MODULE, + ERR_MODULE_NAME, ///< invalid module_info.name + ERR_MODULE_DUP, ///< module already opened + ERR_MODULE_PATH, ///< resolving the path failed ERR_MODULE_OPEN, ///< dlopen() failed - ERR_MODULE_NAME, ///< invalid module_info.name ERR_MODULE_SYM, ///< invalid symbol ERR_MODULE_INIT_FUNC, ///< invalid module_init_func_t ERR_MODULE_CONF, ///< value error in configuration data @@ -136,12 +144,27 @@ err_t modules_create (struct modules **modules_ptr, struct nexus *nexus); /** + * Set a search path for finding modules by name. The given string won't be copied. + * + * A module called "" will be searched for at "/mod_.so" + * + * If path is NULL, this doesn't change anything. This returns the old path, which may be NULL. + * + * @param modules the modules state + * @param path the new search path, or NULL to just get the old one + * @return the old search path + */ +const char* modules_path (struct modules *modules, const char *path); + +/** * Return a module's name */ const char* module_name (struct module *module); /** - * Load a new module + * Load a new module, as named by info. + * + * If info->path is not given, the module will be searched for using the path set by modules_path(). * * @param modules the module-loading context * @param module_ptr return the new module via this, if not NULL