terom@54: #include "module.h" terom@55: #include "log.h" terom@54: terom@54: #include terom@108: #include terom@54: #include terom@55: #include terom@55: #include terom@54: terom@54: err_t modules_create (struct modules **modules_ptr, struct nexus *nexus) terom@54: { terom@55: struct modules *modules; terom@54: terom@54: // alloc terom@54: if ((modules = calloc(1, sizeof(*modules))) == NULL) terom@54: return ERR_CALLOC; terom@54: terom@54: // init terom@55: TAILQ_INIT(&modules->list); terom@54: terom@54: // store terom@54: modules->nexus = nexus; terom@55: terom@55: // ok terom@55: *modules_ptr = modules; terom@55: terom@55: return SUCCESS; terom@54: } terom@54: terom@108: const char* modules_path (struct modules *modules, const char *path) terom@108: { terom@108: const char *old_path = modules->path; terom@108: terom@108: if (path) terom@108: // replace terom@108: modules->path = path; terom@108: terom@108: return old_path; terom@108: } terom@108: terom@110: /** terom@110: * module_get without the refcount stuff terom@110: */ terom@134: static struct module* modules_lookup (struct modules *modules, const char *name) terom@110: { terom@110: struct module *module = NULL; terom@110: terom@110: // look for it... terom@110: TAILQ_FOREACH(module, &modules->list, modules_list) { terom@110: if (strcasecmp(module->info.name, name) == 0) { terom@110: // found it terom@110: return module; terom@110: } terom@110: } terom@110: terom@110: // no such module terom@110: return NULL; terom@110: } terom@110: terom@134: struct module* modules_get (struct modules *modules, const char *name) terom@110: { terom@110: struct module *module; terom@110: terom@110: // get it terom@134: if (!(module = modules_lookup(modules, name))) terom@110: return NULL; terom@110: terom@110: // up the refcount terom@110: module->refcount++; terom@110: terom@110: // ok terom@110: return module; terom@110: } terom@110: terom@110: err_t modules_unload (struct modules *modules) terom@110: { terom@110: struct module *module; terom@110: err_t err; terom@110: terom@110: // unload each module in turn terom@110: while ((module = TAILQ_FIRST(&modules->list))) { terom@110: if ((err = module_unload(module))) terom@110: log_warn("module_unload(%s) failed: %s", module->info.name, error_name(err)); terom@110: } terom@110: terom@110: // ok terom@110: return SUCCESS; terom@110: } terom@110: terom@110: void modules_destroy (struct modules *modules) terom@110: { terom@110: struct module *module; terom@110: terom@110: // destroy each module terom@110: while ((module = TAILQ_FIRST(&modules->list))) terom@110: module_destroy(module); terom@110: terom@110: // ourselves terom@110: free(modules); terom@110: } terom@110: terom@110: terom@103: const char* module_name (struct module *module) terom@103: { terom@138: return module ? module->info.name : NULL; terom@103: } terom@103: terom@55: /** terom@57: * Load the symbol named "_". terom@55: */ terom@55: static err_t module_symbol (struct module *module, void **sym, const char *suffix) terom@55: { terom@55: char sym_name[MODULE_SYMBOL_MAX]; terom@55: terom@55: // validate the length of the suffix terom@57: assert(strlen(module->info.name) <= MODULE_NAME_MAX); terom@55: assert(strlen(suffix) <= MODULE_SUFFIX_MAX); terom@55: terom@55: // format terom@55: sprintf(sym_name, "%s_%s", module->info.name, suffix); terom@55: terom@55: // load terom@57: if ((*sym = dlsym(module->handle, sym_name)) == NULL) terom@57: return ERR_MODULE_SYM; terom@55: terom@55: // ok terom@55: return SUCCESS; terom@55: } terom@55: terom@108: /** terom@108: * XXX: ugly function to format to a dynamically allocated string terom@108: */ terom@108: char *strfmt (const char *fmt, ...) terom@108: { terom@108: va_list vargs; terom@108: size_t len; terom@108: char *buf; terom@108: terom@108: // figure out the length of the resulting string terom@108: va_start(vargs, fmt); terom@108: terom@108: len = vsnprintf(NULL, 0, fmt, vargs) + 1; terom@108: terom@108: va_end(vargs); terom@108: terom@108: // malloc terom@108: if ((buf = malloc(len)) == NULL) terom@108: return NULL; terom@108: terom@108: // format terom@108: va_start(vargs, fmt); terom@108: terom@108: vsnprintf(buf, len, fmt, vargs); terom@108: terom@108: va_end(vargs); terom@108: terom@108: // ok terom@108: return buf; terom@108: } terom@108: terom@108: /** terom@108: * Looks up a module's path by name, returning a dynamically allocated string with the path on success terom@108: */ terom@108: static char* module_find (struct modules *modules, struct module_info *info, struct error_info *err) terom@108: { terom@108: char *path = NULL; terom@108: terom@108: // build the path... terom@108: if ((path = strfmt("%s/mod_%s.so", modules->path, info->name)) == NULL) terom@108: JUMP_SET_ERROR(err, ERR_STRDUP); terom@108: terom@108: // exists and readable? terom@108: if (access(path, R_OK)) terom@108: // XXX: this doesn't contain the path... terom@108: JUMP_SET_ERROR_STR(err, ERR_MODULE_PATH, "module not found in search path"); terom@108: terom@108: // ok terom@108: return path; terom@108: terom@108: error: terom@108: // release the dynamically allocated path terom@108: free(path); terom@108: terom@108: return NULL; terom@108: } terom@108: terom@108: err_t module_load (struct modules *modules, struct module **module_ptr, const struct module_info *_info, struct error_info *err) terom@54: { terom@54: struct module *module; terom@54: terom@55: // validate the module name terom@108: if (strlen(_info->name) > MODULE_NAME_MAX) terom@55: return SET_ERROR(err, ERR_MODULE_NAME); terom@55: terom@108: // already open with the same name? terom@134: if (modules_lookup(modules, _info->name)) terom@108: return SET_ERROR(err, ERR_MODULE_DUP); terom@108: terom@55: // alloc terom@55: if ((module = calloc(1, sizeof(*module))) == NULL) terom@55: return SET_ERROR(err, ERR_CALLOC); terom@55: terom@110: // initialize terom@108: module->info = *_info; terom@70: module->modules = modules; terom@110: module->refcount = 1; terom@108: terom@108: // add to modules list terom@108: TAILQ_INSERT_TAIL(&modules->list, module, modules_list); terom@108: terom@108: // lookup path? terom@108: if (!module->info.path) { terom@108: // have a search path? terom@108: if (!modules->path) terom@108: JUMP_SET_ERROR_STR(err, ERR_MODULE_PATH, "no module search path defined"); terom@108: terom@108: // try and resolve the path automatically terom@108: if ((module->path_buf = module_find(modules, &module->info, err)) == NULL) terom@108: goto error; terom@108: terom@108: // use that path terom@108: module->info.path = module->path_buf; terom@108: } terom@54: terom@55: // clear dlerrors terom@55: (void) dlerror(); terom@55: terom@55: // load it terom@108: if ((module->handle = dlopen(module->info.path, RTLD_NOW)) == NULL) terom@57: JUMP_SET_ERROR_STR(err, ERR_MODULE_OPEN, dlerror()); terom@55: terom@57: // load the funcs symbol terom@100: if ((ERROR_CODE(err) = module_symbol(module, (void **) &module->desc, "module"))) terom@57: JUMP_SET_ERROR_STR(err, ERROR_CODE(err), dlerror()); terom@55: terom@57: // call the init func terom@100: if ((module->desc->init(modules->nexus, &module->ctx, err))) terom@55: goto error; terom@55: terom@55: // ok terom@111: if (module_ptr) { terom@111: module->refcount++; terom@65: *module_ptr = module; terom@111: } terom@55: terom@55: return SUCCESS; terom@55: terom@55: error: terom@108: // cleanup terom@110: module->refcount = 0; terom@108: module_destroy(module); terom@55: terom@55: return ERROR_CODE(err); terom@54: } terom@55: terom@110: void module_put (struct module *module) terom@66: { terom@110: assert(module->refcount > 0); terom@110: terom@110: // decrement, just return if still alive terom@110: if (--module->refcount > 0) terom@110: return; terom@110: terom@110: // refcount zero, destroy terom@110: module_destroy(module); terom@66: } terom@66: terom@100: err_t module_conf_raw (struct module *module, const char *name, char *value, struct error_info *err) terom@55: { terom@110: // sanity-check terom@100: if (!module->desc->config_options) terom@100: RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "module does not have any config options"); terom@100: terom@100: // wrong state terom@100: if (module->unloading) terom@100: RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "module is being unloaded"); terom@100: terom@100: // parse/apply using the module's stuff terom@100: return config_apply_raw(module->desc->config_options, module->modules->nexus, module->ctx, name, value, err); terom@100: } terom@100: terom@100: const struct config_option* module_conf_lookup (struct module *module, const char *name, struct error_info *err) terom@100: { terom@110: // sanity-check terom@100: if (!module->desc->config_options) terom@100: JUMP_SET_ERROR_STR(err, ERR_MODULE_CONF, "module does not have any config options"); terom@100: terom@100: // direct lookup terom@100: return config_lookup(module->desc->config_options, name, err); terom@100: terom@100: error: terom@100: return NULL; terom@100: } terom@100: terom@100: err_t module_conf (struct module *module, const struct config_option *opt, const struct config_value *value, struct error_info *err) terom@100: { terom@100: // wrong state terom@100: if (module->unloading) terom@100: RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "module is being unloaded"); terom@100: terom@100: // apply with the module's ctx terom@100: return config_apply_opt(opt, module->ctx, value, err); terom@55: } terom@70: terom@70: err_t module_unload (struct module *module) terom@70: { terom@70: err_t err; terom@110: terom@111: // wrong state terom@111: if (module->unloading) terom@111: return ERR_MODULE_STATE; terom@100: terom@110: // remove from modules list terom@110: TAILQ_REMOVE(&module->modules->list, module, modules_list); terom@110: terom@110: // update status terom@110: module->unloading = true; terom@110: terom@119: if (module->desc->unload) { terom@119: // invoke the unload func, passing it our reference terom@119: // note that the module might not exist anymore after calling this terom@119: if ((err = module->desc->unload(module->ctx, module))) { terom@119: // mark it as "unloaded" terom@119: module_unloaded(module); terom@119: terom@119: return err; terom@119: } terom@119: terom@119: } else { terom@119: // no unload procedure, just destroy it as soon as needed terom@111: module_unloaded(module); terom@111: terom@111: } terom@70: // ok terom@70: return SUCCESS; terom@70: } terom@70: terom@110: /** terom@110: * A wrapper for module_destroy suitable for use as a libevent callback terom@110: */ terom@110: static void _module_destroy (int fd, short what, void *arg) terom@110: { terom@110: struct module *module = arg; terom@110: terom@110: (void) fd; terom@110: (void) what; terom@110: terom@110: // execute terom@110: module_destroy(module); terom@110: } terom@110: terom@110: void module_unloaded (struct module *module) terom@110: { terom@110: struct timeval tv = { 0, 0 }; terom@110: assert(module->refcount > 0); terom@110: terom@110: // decrement, just return if still alive terom@110: if (--module->refcount > 0) terom@110: return; terom@110: terom@110: // schedule a deferred module_destroy terom@110: if (event_base_once(module->modules->nexus->ev_base, -1, EV_TIMEOUT, &_module_destroy, module, &tv)) terom@110: // XXX: how to reach? terom@110: log_fatal("event_base_once failed, unable to schedule deferred module_destroy"); terom@110: } terom@110: terom@70: void module_destroy (struct module *module) terom@70: { terom@110: // XXX: warn about destroy with significant refcount... terom@110: if (module->refcount) terom@110: log_warn("destroying module %s with refcount>0: %zu", module_name(module), module->refcount); terom@110: else terom@110: log_debug("destroying module %s", module_name(module)); terom@110: terom@110: // still need to remove from modules_list? terom@110: if (!module->unloading) terom@110: TAILQ_REMOVE(&module->modules->list, module, modules_list); terom@110: terom@110: // run the destroy hook terom@119: if (module->desc && module->desc->destroy && module->ctx) terom@110: module->desc->destroy(module->ctx); terom@70: terom@70: // unload the dl handle terom@108: if (module->handle && dlclose(module->handle)) terom@70: log_error("dlclose(%s): %s", module->info.name, dlerror()); terom@70: terom@108: // release the path buf, if any terom@108: free(module->path_buf); terom@108: terom@70: // free the module info terom@70: free(module); terom@70: } terom@70: terom@70: