Mercurial > irccd
changeset 964:0dd6afe7386d
irccd: implement plugin loaders
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 29 Jan 2021 13:50:44 +0100 |
parents | 371e1cc2c697 |
children | a518664b20a0 |
files | irccd/main.c lib/irccd/config.h.in lib/irccd/dl-plugin.c lib/irccd/dl-plugin.h lib/irccd/irccd.c lib/irccd/irccd.h lib/irccd/js-plugin.c lib/irccd/js-plugin.h lib/irccd/jsapi-plugin.c lib/irccd/limits.h lib/irccd/plugin.c lib/irccd/plugin.h |
diffstat | 12 files changed, 303 insertions(+), 113 deletions(-) [+] |
line wrap: on
line diff
--- a/irccd/main.c Thu Jan 28 14:20:58 2021 +0100 +++ b/irccd/main.c Fri Jan 29 13:50:44 2021 +0100 @@ -19,6 +19,7 @@ #include <stdio.h> #include <err.h> +#include <irccd/dl-plugin.h> #include <irccd/irccd.h> #include <irccd/log.h> #include <irccd/server.h> @@ -47,4 +48,14 @@ if (argc > 0) return run(argc, argv); + + irc_bot_init(); + + /* TODO: temp. */ + irc_log_set_verbose(true); + + irc_bot_plugin_loader_add(irc_dl_plugin_loader_new()); + irc_bot_plugin_loader_add(irc_js_plugin_loader_new()); + irc_bot_plugin_find("foo"); + }
--- a/lib/irccd/config.h.in Thu Jan 28 14:20:58 2021 +0100 +++ b/lib/irccd/config.h.in Fri Jan 29 13:50:44 2021 +0100 @@ -24,6 +24,8 @@ #define IRCCD_VERSION_PATCH @IRCCD_VERSION_PATCH@ #define IRCCD_VERSION "@IRCCD_VERSION_MAJOR@.@IRCCD_VERSION_MINOR@.@IRCCD_VERSION_PATCH@" +#define IRCCD_LIBDIR "@CMAKE_INSTALL_FULL_LIBDIR@" + #cmakedefine IRCCD_WITH_JS #cmakedefine IRCCD_WITH_SSL
--- a/lib/irccd/dl-plugin.c Thu Jan 28 14:20:58 2021 +0100 +++ b/lib/irccd/dl-plugin.c Fri Jan 29 13:50:44 2021 +0100 @@ -16,13 +16,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <sys/stat.h> #include <assert.h> #include <ctype.h> #include <dlfcn.h> #include <errno.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> +#include "config.h" #include "dl-plugin.h" #include "log.h" #include "plugin.h" @@ -38,6 +41,7 @@ } while (0) struct self { + struct irc_plugin plugin; char prefix[32]; void *handle; }; @@ -164,57 +168,98 @@ if (self->handle) dlclose(self->handle); + free(self); memset(self, 0, sizeof (*self)); } -static bool -init(struct self *self, const char *path) +static struct self * +init(const char *path) { - memset(self, 0, sizeof (*self)); + struct self self; + struct stat st; - if (!(self->handle = dlopen(path, RTLD_NOW))) { - irc_log_warn("plugin: %s: %s", strerror(errno)); - return false; + /* + * It's not possible to get the exact error code when loading a plugin + * using dlopen, since we're trying a lot of files that potentially not + * exist we check presence before even though there's a possible + * condition but at least we can print an error message if there are + * other errors than missing file. + */ + if (stat(path, &st) < 0 && errno == ENOENT) + return NULL; + + if (!(self.handle = dlopen(path, RTLD_NOW))) { + irc_log_warn("plugin: %s: %s", path, dlerror()); + return NULL; } /* Compute prefix name */ - strlcpy(self->prefix, irc_util_basename(path), sizeof (self->prefix)); + strlcpy(self.prefix, irc_util_basename(path), sizeof (self.prefix)); /* Remove plugin extension. */ - self->prefix[strcspn(self->prefix, ".")] = '\0'; + self.prefix[strcspn(self.prefix, ".")] = '\0'; /* Remove every invalid identifiers. */ - for (char *p = self->prefix; *p; ++p) + for (char *p = self.prefix; *p; ++p) if (!isalnum(*p)) *p = '_'; - return true; + return irc_util_memdup(&self, sizeof (self)); } -bool -irc_dl_plugin_open(struct irc_plugin *plg, const char *path) +static struct irc_plugin * +wrap_open(struct irc_plugin_loader *ldr, const char *path) { - struct self self; + (void)ldr; + + return irc_dl_plugin_open(path); +} - if (!init(&self, path)) +struct irc_plugin * +irc_dl_plugin_open(const char *path) +{ + struct self *self; + + if (!(self = init(path))) return false; /* Data and all callbacks. */ - plg->data = irc_util_memdup(&self, sizeof (self)); - plg->set_template = set_template; - plg->get_template = get_template; - plg->get_templates = get_templates; - plg->set_path = set_path; - plg->get_path = get_path; - plg->get_paths = get_paths; - plg->set_option = set_option; - plg->get_option = get_option; - plg->get_options = get_options; - plg->load = load; - plg->reload = reload; - plg->unload = unload; - plg->handle = handle; - plg->finish = finish; + self->plugin.data = self; + self->plugin.set_template = set_template; + self->plugin.get_template = get_template; + self->plugin.get_templates = get_templates; + self->plugin.set_path = set_path; + self->plugin.get_path = get_path; + self->plugin.get_paths = get_paths; + self->plugin.set_option = set_option; + self->plugin.get_option = get_option; + self->plugin.get_options = get_options; + self->plugin.load = load; + self->plugin.reload = reload; + self->plugin.unload = unload; + self->plugin.handle = handle; + self->plugin.finish = finish; + + return &self->plugin; +} - return true; +struct irc_plugin_loader * +irc_dl_plugin_loader_new(void) +{ + struct irc_plugin_loader *ldr; + + ldr = irc_util_calloc(1, sizeof (*ldr)); + ldr->open = wrap_open; + +#if defined(_WIN32) + strlcpy(ldr->extensions, "dll", sizeof (ldr->extensions)); +#elif defined(__APPLE__) + strlcpy(ldr->extensions, "so:dylib", sizeof (ldr->extensions)); +#else + strlcpy(ldr->extensions, "so", sizeof (ldr->extensions)); +#endif + + strlcpy(ldr->paths, IRCCD_LIBDIR "/irccd", sizeof (ldr->paths)); + + return ldr; }
--- a/lib/irccd/dl-plugin.h Thu Jan 28 14:20:58 2021 +0100 +++ b/lib/irccd/dl-plugin.h Fri Jan 29 13:50:44 2021 +0100 @@ -19,9 +19,8 @@ #ifndef IRCCD_DL_PLUGIN_H #define IRCCD_DL_PLUGIN_H -#include <stdbool.h> - struct irc_plugin; +struct irc_plugin_loader; #if defined(_WIN32) # define IRC_DL_EXPORT __declspec(dllexport) @@ -29,13 +28,10 @@ # define IRC_DL_EXPORT #endif -#if defined(__APPLE__) -# define IRC_DL_EXT ".dylib" -#else -# define IRC_DL_EXT ".so" -#endif +struct irc_plugin * +irc_dl_plugin_open(const char *); -bool -irc_dl_plugin_open(struct irc_plugin *, const char *); +struct irc_plugin_loader * +irc_dl_plugin_loader_new(void); #endif /* !IRCCD_DL_PLUGIN_H */
--- a/lib/irccd/irccd.c Thu Jan 28 14:20:58 2021 +0100 +++ b/lib/irccd/irccd.c Fri Jan 29 13:50:44 2021 +0100 @@ -385,8 +385,56 @@ irc_plugin_load(p); } +static struct irc_plugin * +find_plugin(struct irc_plugin_loader *ldr, const char *base, const char *name) +{ + char path[PATH_MAX], buf[IRC_EXTENSIONS_LEN], *t, *ext; + struct irc_plugin *p; + + strlcpy(buf, ldr->extensions, sizeof (buf)); + + for (t = buf; (ext = strtok_r(t, ":", &t)); ) { + snprintf(path, sizeof (path), "%s/%s.%s", base, name, ext); + irc_log_info("irccd: trying %s", path); + + if ((p = irc_plugin_loader_open(ldr, path))) + return p; + } + + return NULL; +} + +void +irc_bot_plugin_find(const char *name) +{ + char buf[IRC_PATHS_LEN], *t, *token; + struct irc_plugin *p = NULL; + struct irc_plugin_loader *ldr; + + irc_log_info("irccd: trying to find plugin %s", name); + + SLIST_FOREACH(ldr, &irc.plugin_loaders, link) { + /* Copy the paths to tokenize it. */ + strlcpy(buf, ldr->paths, sizeof (buf)); + + /* + * For every directory (separated by colon) call find_plugin + * which will append the extension and try to open it. + */ + for (t = buf; (token = strtok_r(t, ":", &t)); ) { + if ((p = find_plugin(ldr, token, name))) + break; + } + } + + if (p) + irc_bot_plugin_add(p); + else + irc_log_warn("irccd: could not find plugin %s", name); +} + struct irc_plugin * -irc_bot_plugin_find(const char *name) +irc_bot_plugin_get(const char *name) { struct irc_plugin *p; @@ -402,7 +450,7 @@ { struct irc_plugin *p; - if (!(p = irc_bot_plugin_find(name))) + if (!(p = irc_bot_plugin_get(name))) return; irc_plugin_unload(p); @@ -412,6 +460,14 @@ } void +irc_bot_plugin_loader_add(struct irc_plugin_loader *ldr) +{ + assert(ldr); + + SLIST_INSERT_HEAD(&irc.plugin_loaders, ldr, link); +} + +void irc_bot_rule_insert(struct irc_rule *rule, size_t index) { assert(rule);
--- a/lib/irccd/irccd.h Thu Jan 28 14:20:58 2021 +0100 +++ b/lib/irccd/irccd.h Fri Jan 29 13:50:44 2021 +0100 @@ -19,8 +19,6 @@ #ifndef IRCCD_H #define IRCCD_H -#include <sys/queue.h> - #include "peer.h" #include "plugin.h" #include "rule.h" @@ -30,6 +28,7 @@ struct irc_server_list servers; struct irc_peer_list peers; struct irc_plugin_list plugins; + struct irc_plugin_loader_list plugin_loaders; struct irc_rule_list rules; } irc; @@ -51,13 +50,19 @@ void irc_bot_plugin_add(struct irc_plugin *); +void +irc_bot_plugin_find(const char *); + struct irc_plugin * -irc_bot_plugin_find(const char *); +irc_bot_plugin_get(const char *); void irc_bot_plugin_remove(const char *); void +irc_bot_plugin_loader_add(struct irc_plugin_loader *); + +void irc_bot_rule_insert(struct irc_rule *, size_t); void
--- a/lib/irccd/js-plugin.c Thu Jan 28 14:20:58 2021 +0100 +++ b/lib/irccd/js-plugin.c Fri Jan 29 13:50:44 2021 +0100 @@ -386,7 +386,9 @@ return ret; err: - close(fd); + if (fd != -1) + close(fd); + free(ret); return false; @@ -416,38 +418,39 @@ free(ptr); } -static bool -init(struct irc_plugin *plg, const char *script) +static struct irc_js_plugin_data * +init(const char *path, const char *script) { - struct irc_js_plugin_data js = {0}; + struct irc_js_plugin_data *js; + + js = irc_util_calloc(1, sizeof (*js)); + js->ctx = duk_create_heap(wrap_malloc, wrap_realloc, wrap_free, NULL, NULL); - /* Load all modules. */ - js.ctx = duk_create_heap(wrap_malloc, wrap_realloc, wrap_free, NULL, NULL); - irc_jsapi_load(js.ctx); - irc_jsapi_chrono_load(js.ctx); - irc_jsapi_directory_load(js.ctx); - irc_jsapi_file_load(js.ctx); - irc_jsapi_logger_load(js.ctx); - irc_jsapi_plugin_load(js.ctx, plg); - irc_jsapi_server_load(js.ctx); - irc_jsapi_system_load(js.ctx); - irc_jsapi_timer_load(js.ctx); - irc_jsapi_unicode_load(js.ctx); - irc_jsapi_util_load(js.ctx); + irc_jsapi_load(js->ctx); + irc_jsapi_chrono_load(js->ctx); + irc_jsapi_directory_load(js->ctx); + irc_jsapi_file_load(js->ctx); + irc_jsapi_logger_load(js->ctx); + irc_jsapi_plugin_load(js->ctx, &js->plugin); + irc_jsapi_server_load(js->ctx); + irc_jsapi_system_load(js->ctx); + irc_jsapi_timer_load(js->ctx); + irc_jsapi_unicode_load(js->ctx); + irc_jsapi_util_load(js->ctx); - if (duk_peval_string(js.ctx, script) != 0) { - irc_log_warn("plugin %s: %s", plg->name, duk_to_string(js.ctx, -1)); - duk_destroy_heap(js.ctx); - return false; + if (duk_peval_string(js->ctx, script) != 0) { + irc_log_warn("plugin: %s: %s", path, duk_to_string(js->ctx, -1)); + duk_destroy_heap(js->ctx); + free(js); + return NULL; } - plg->license = js.license = metadata(js.ctx, "license"); - plg->version = js.version = metadata(js.ctx, "version"); - plg->author = js.author = metadata(js.ctx, "author"); - plg->description = js.description = metadata(js.ctx, "summary"); - plg->data = irc_util_memdup(&js, sizeof (js)); + js->plugin.license = js->license = metadata(js->ctx, "license"); + js->plugin.version = js->version = metadata(js->ctx, "version"); + js->plugin.author = js->author = metadata(js->ctx, "author"); + js->plugin.description = js->description = metadata(js->ctx, "summary"); - return true; + return js; } static void @@ -476,12 +479,23 @@ if (self->ctx) duk_destroy_heap(self->ctx); + freelist(self->options); + freelist(self->templates); + freelist(self->paths); + free(self->license); free(self->version); free(self->author); free(self->description); + free(self); +} - memset(self, 0, sizeof (*self)); +static struct irc_plugin * +wrap_open(struct irc_plugin_loader *ldr, const char *path) +{ + (void)ldr; + + return irc_js_plugin_open(path); } struct irc_plugin * @@ -490,36 +504,56 @@ assert(path); char *script = NULL; - struct irc_plugin *plg = irc_util_calloc(1, sizeof (*plg)); + struct irc_js_plugin_data *self; + /* + * Duktape can't open script from file path so we need to read the + * whole script at once. + */ if (!(script = eat(path))) { - irc_log_warn("plugin: %s", strerror(errno)); - return NULL; - } + if (errno != ENOENT) + irc_log_warn("irccd: %s: %s", path, strerror(errno)); - if (!(init(plg, script))) { - free(script); - free(plg); return NULL; } - plg->set_template = set_template; - plg->get_template = get_template; - plg->get_templates = get_templates; - plg->set_path = set_path; - plg->get_path = get_path; - plg->get_paths = get_paths; - plg->set_option = set_option; - plg->get_option = get_option; - plg->get_options = get_options; - plg->load = load; - plg->reload = reload; - plg->unload = unload; - plg->handle = handle; - plg->finish = finish; + /* Init already log errors. */ + if (!(self = init(path, script))) { + free(script); + return NULL; + } + + self->plugin.data = self; + self->plugin.set_template = set_template; + self->plugin.get_template = get_template; + self->plugin.get_templates = get_templates; + self->plugin.set_path = set_path; + self->plugin.get_path = get_path; + self->plugin.get_paths = get_paths; + self->plugin.set_option = set_option; + self->plugin.get_option = get_option; + self->plugin.get_options = get_options; + self->plugin.load = load; + self->plugin.reload = reload; + self->plugin.unload = unload; + self->plugin.handle = handle; + self->plugin.finish = finish; /* No longer needed. */ free(script); - return plg; + return &self->plugin; } + +struct irc_plugin_loader * +irc_js_plugin_loader_new(void) +{ + struct irc_plugin_loader *ldr; + + ldr = irc_util_calloc(1, sizeof (*ldr)); + ldr->open = wrap_open; + strlcpy(ldr->extensions, "js", sizeof (ldr->extensions)); + strlcpy(ldr->paths, IRCCD_LIBDIR "/irccd", sizeof (ldr->paths)); + + return ldr; +}
--- a/lib/irccd/js-plugin.h Thu Jan 28 14:20:58 2021 +0100 +++ b/lib/irccd/js-plugin.h Fri Jan 29 13:50:44 2021 +0100 @@ -19,13 +19,12 @@ #ifndef IRCCD_JS_PLUGIN_H #define IRCCD_JS_PLUGIN_H -#include <stdbool.h> - #include <duktape.h> -struct irc_plugin; +#include "plugin.h" struct irc_js_plugin_data { + struct irc_plugin plugin; duk_context *ctx; char **options; char **templates; @@ -39,4 +38,7 @@ struct irc_plugin * irc_js_plugin_open(const char *); +struct irc_plugin_loader * +irc_js_plugin_loader_new(void); + #endif /* !IRCCD_JS_PLUGIN_H */
--- a/lib/irccd/jsapi-plugin.c Thu Jan 28 14:20:58 2021 +0100 +++ b/lib/irccd/jsapi-plugin.c Fri Jan 29 13:50:44 2021 +0100 @@ -134,7 +134,7 @@ find(duk_context *ctx) { const char *name = duk_require_string(ctx, 0); - struct irc_plugin *plg = irc_bot_plugin_find(name); + struct irc_plugin *plg = irc_bot_plugin_get(name); if (!plg) (void)duk_error(ctx, DUK_ERR_REFERENCE_ERROR, "plugin %s not found", name);
--- a/lib/irccd/limits.h Thu Jan 28 14:20:58 2021 +0100 +++ b/lib/irccd/limits.h Fri Jan 29 13:50:44 2021 +0100 @@ -19,27 +19,31 @@ #ifndef IRCCD_LIMITS_H #define IRCCD_LIMITS_H +#include <limits.h> + /* Server limits. */ -#define IRC_NICKNAME_LEN 32 /* Nickname. */ -#define IRC_USERNAME_LEN 32 /* User name. */ -#define IRC_REALNAME_LEN 64 /* Real name. */ -#define IRC_CHANNEL_LEN 64 /* Channel name. */ -#define IRC_PASSWORD_LEN 64 /* Password length. */ -#define IRC_CTCPVERSION_LEN 64 /* Custom CTCP version answer. */ -#define IRC_USERMODES_LEN 8 /* Number of modes (e.g. ohv). */ -#define IRC_CHANTYPES_LEN 8 -#define IRC_CMDCHAR_LEN 4 /* Prefix for plugin commands (e.g. !). */ -#define IRC_MESSAGE_LEN 512 /* Official length per message. */ -#define IRC_ARGS_MAX 32 /* Own supported number of arguments per message. */ +#define IRC_NICKNAME_LEN 32 /* Nickname. */ +#define IRC_USERNAME_LEN 32 /* User name. */ +#define IRC_REALNAME_LEN 64 /* Real name. */ +#define IRC_CHANNEL_LEN 64 /* Channel name. */ +#define IRC_PASSWORD_LEN 64 /* Password length. */ +#define IRC_CTCPVERSION_LEN 64 /* Custom CTCP version answer. */ +#define IRC_USERMODES_LEN 8 /* Number of modes (e.g. ohv). */ +#define IRC_CHANTYPES_LEN 8 /* Channel types. */ +#define IRC_CMDCHAR_LEN 4 /* Prefix for plugin commands (e.g. !). */ +#define IRC_MESSAGE_LEN 512 /* Official length per message. */ +#define IRC_ARGS_MAX 32 /* Own supported number of arguments per message. */ /* Network limits. */ -#define IRC_HOST_LEN 64 /* Hostname length.. */ -#define IRC_BUF_LEN 128000 /* Network buffer input/output. */ +#define IRC_HOST_LEN 64 /* Hostname length.. */ +#define IRC_BUF_LEN 128000 /* Network buffer input/output. */ /* Generic limits. */ -#define IRC_ID_LEN 16 /* Plugin/server identifiers. */ +#define IRC_ID_LEN 16 /* Plugin/server identifiers. */ +#define IRC_PATHS_LEN (PATH_MAX * 8) /* Colon separated list of paths. */ +#define IRC_EXTENSIONS_LEN 32 /* Colon separated list of extensions for plugins. */ /* Rule limits. */ -#define IRC_RULE_LEN 1024 /* Space-separated list of values. */ +#define IRC_RULE_LEN 1024 /* Space-separated list of values. */ #endif /* !IRCCD_LIMITS_H */
--- a/lib/irccd/plugin.c Thu Jan 28 14:20:58 2021 +0100 +++ b/lib/irccd/plugin.c Fri Jan 29 13:50:44 2021 +0100 @@ -170,3 +170,21 @@ free(plg); } + +struct irc_plugin * +irc_plugin_loader_open(struct irc_plugin_loader *ldr, const char *path) +{ + assert(ldr); + assert(path); + + return ldr->open(ldr, path); +} + +void +irc_plugin_loader_finish(struct irc_plugin_loader *ldr) +{ + assert(ldr); + + if (ldr->finish) + ldr->finish(ldr); +}
--- a/lib/irccd/plugin.h Thu Jan 28 14:20:58 2021 +0100 +++ b/lib/irccd/plugin.h Fri Jan 29 13:50:44 2021 +0100 @@ -58,6 +58,17 @@ LIST_HEAD(irc_plugin_list, irc_plugin); +struct irc_plugin_loader { + char paths[IRC_PATHS_LEN]; + char extensions[IRC_EXTENSIONS_LEN]; + struct irc_plugin *(*open)(struct irc_plugin_loader *, const char *); + void (*finish)(struct irc_plugin_loader *); + void *data; + SLIST_ENTRY(irc_plugin_loader) link; +}; + +SLIST_HEAD(irc_plugin_loader_list, irc_plugin_loader); + void irc_plugin_set_template(struct irc_plugin *, const char *, const char *); @@ -100,4 +111,10 @@ void irc_plugin_finish(struct irc_plugin *); +struct irc_plugin * +irc_plugin_loader_open(struct irc_plugin_loader *, const char *); + +void +irc_plugin_loader_finish(struct irc_plugin_loader *); + #endif /* !IRCCD_PLUGIN_H */