--- 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?
--- 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 }
--- 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,
--- 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 <stdlib.h>
#include <string.h>
#include <lua5.1/lua.h>
@@ -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 }
--- 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 <stdlib.h>
+#include <unistd.h>
#include <dlfcn.h>
#include <string.h>
#include <assert.h>
@@ -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);
}
--- 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 "<name>" will be searched for at "<path>/mod_<name>.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