From 1d05f429dc0caaa998de0925c07c933a8ed29e70 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sun, 30 Aug 2020 07:39:27 +0100 Subject: [PATCH] lws_plugins --- CMakeLists-implied-options.txt | 5 ++ CMakeLists.txt | 3 +- READMEs/README.lws_plugins.md | 105 +++++++++++++++++++++++++++++ cmake/lws_config.h.in | 1 + lib/misc/dir.c | 13 ++-- lib/plat/unix/CMakeLists.txt | 2 +- lib/plat/unix/unix-plugins.c | 3 - lib/plat/windows/windows-plugins.c | 21 ++++-- 8 files changed, 135 insertions(+), 18 deletions(-) create mode 100644 READMEs/README.lws_plugins.md diff --git a/CMakeLists-implied-options.txt b/CMakeLists-implied-options.txt index 5f5455ec1..5ba848b56 100644 --- a/CMakeLists-implied-options.txt +++ b/CMakeLists-implied-options.txt @@ -390,3 +390,8 @@ endif() if (NOT LWS_WITH_SSL) set(LWS_WITHOUT_BUILTIN_SHA1 OFF) endif() + +if (LWS_WITH_PLUGINS OR (LWS_WITH_EVLIB_PLUGINS AND LWS_WITH_EVENT_LIBS)) + set(LWS_WITH_PLUGINS_API 1) +endif() + diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a3f45157..801d2d927 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,7 +92,7 @@ option(LWS_WITH_LWSWS "Libwebsockets Webserver" OFF) option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out/err) APIs" OFF) option(LWS_IPV6 "Compile with support for ipv6" OFF) option(LWS_UNIX_SOCK "Compile with support for UNIX domain socket if OS supports it" ON) -option(LWS_WITH_PLUGINS "Support plugins for protocols and extensions" OFF) +option(LWS_WITH_PLUGINS "Support plugins for protocols and extensions (implies LWS_WITH_PLUGINS_API)" OFF) option(LWS_WITH_HTTP_PROXY "Support for active HTTP proxying" OFF) option(LWS_WITH_ZIP_FOPS "Support serving pre-zipped files" OFF) option(LWS_WITH_SOCKS5 "Allow use of SOCKS5 proxy on client connections" OFF) @@ -250,6 +250,7 @@ option(LWS_WITH_FSMOUNT "Overlayfs and fallback mounting apis" OFF) option(LWS_WITH_FANALYZER "Enable gcc -fanalyzer if compiler supports" OFF) option(LWS_HTTP_HEADERS_ALL "Override header reduction optimization and include all like older lws versions" OFF) option(LWS_WITH_SUL_DEBUGGING "Enable zombie lws_sul checking on object deletion" OFF) +option(LWS_WITH_PLUGINS_API "Build generic lws_plugins apis (see LWS_WITH_PLUGINS to also build protocol plugins)" OFF) # # to use miniz, enable both LWS_WITH_ZLIB and LWS_WITH_MINIZ diff --git a/READMEs/README.lws_plugins.md b/READMEs/README.lws_plugins.md new file mode 100644 index 000000000..5a4b7d9e8 --- /dev/null +++ b/READMEs/README.lws_plugins.md @@ -0,0 +1,105 @@ +# lws_plugins + +Lws now offers apis to manage your own user plugins with `LWS_WITH_PLUGINS_API`. +Lws uses these apis internally for protocol plugins and event loop plugins +if they're selected for build. But they are also exported for user code to +use them how you like. + +## Creating your plugin export + +### Specifying your plugin export type + +Lws plugins have a single exported struct with a specified header and a user +defined remainder. The public `lws_plugin_header_t` describes the common +plugin export header, it's defined via libwebsockets.h as + +``` +typedef struct lws_plugin_header { + const char *name; + const char *_class; + + unsigned int api_magic; + /* set to LWS_PLUGIN_API_MAGIC at plugin build time */ + + /* plugin-class specific superclass data follows */ +} lws_plugin_header_t; +``` + +The exported symbol name itself must match the plugin filename, for +example if the symbol name is `my_plugin`, then the filename of the +plugin might be `libmyapp-my_plugin.so` or similar... the matching +part is after the first `-` or `_`, up to the first `.`. The exact +details differ by platform but these rules cover the supported +platforms. If lws has the filename of the plugin, it can then +deduce the symbol export it should look for in the plugin. + +`name` is a freeform human-readable description for the plugin. + +`_class` is shared by your plugins and used to select them from other kinds +of plugin that may be in the same dir. So choose a unique name like +`"myapp xxx plugin"` or whatever shared by all plugins of that class. + +`api_magic` is set to `LWS_PLUGIN_API_MAGIC` to detect if the plugin is +incompatible with the lws plugin apis version. + +So for example your plugin type wrapping the header might look like + +``` +typedef struct myapp_plugin { + lws_plugin_header_t hdr; /* must be first */ + + /* optional extra data like function pointers from your plugin */ + mytype_t mymember; + /* ... */ +} myapp_plugin_t; +``` + +Typically, you will put function pointers to whatever capability your plugin +class offers as the additional members. + +## Building your own plugins + +Plugins are built standalone, cmake is recommended but you can do what you want. + +The only requirement is the single visible export of the plugin name, eg + +``` +const myapp_plugin_t my_plugin = { + .hdr = { + "my_plugin", + "myapp xxx plugin", + LWS_PLUGIN_API_MAGIC + }, + + .mymember = my_plugin_init, + /*...*/ +}; +``` + +## Bringing in plugins at runtime + +Lws provides an api to import plugins into the process space and another to +remove and destroy plugins. + +You can take two approaches depending on what you're doing, either bring in and +later destroy a whole class of plugins at once, and walk them via a linked-list, +or bring in and later destroy a single specific plugin from the class by filtering +on its specific export name. + +See `include/libwebsockets/lws-protocols-plugins.h` for documentation. + +``` +LWS_VISIBLE LWS_EXTERN int +lws_plugins_init(struct lws_plugin **pplugin, const char * const *d, + const char *_class, const char *filter, + each_plugin_cb_t each, void *each_user); + +LWS_VISIBLE LWS_EXTERN int +lws_plugins_destroy(struct lws_plugin **pplugin, each_plugin_cb_t each, + void *each_user); +``` + +`struct lws_plugin` is a public struct that contains the linked-list of loaded +plugins and a pointer to its exported header object, so you can walk this +after loading. + diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index ea03ac64a..394651acb 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -193,4 +193,5 @@ #cmakedefine LWS_WITH_EVENT_LIBS #cmakedefine LWS_WITH_EVLIB_PLUGINS #cmakedefine LWS_WITH_LIBUV_INTERNAL +#cmakedefine LWS_WITH_PLUGINS_API diff --git a/lib/misc/dir.c b/lib/misc/dir.c index 57dd0ff6c..1ebe96d48 100644 --- a/lib/misc/dir.c +++ b/lib/misc/dir.c @@ -304,15 +304,14 @@ lws_dir_rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) #endif -#if defined(LWS_WITH_PLUGINS) || \ - (defined(LWS_WITH_EVLIB_PLUGINS) && defined(LWS_WITH_EVENT_LIBS)) +#if defined(LWS_WITH_PLUGINS_API) struct lws_plugins_args { - struct lws_plugin **pplugin; - const char *_class; - const char *filter; - each_plugin_cb_t each; - void *each_user; + struct lws_plugin **pplugin; + const char *_class; + const char *filter; + each_plugin_cb_t each; + void *each_user; }; static int diff --git a/lib/plat/unix/CMakeLists.txt b/lib/plat/unix/CMakeLists.txt index 70a421745..e1079bedd 100644 --- a/lib/plat/unix/CMakeLists.txt +++ b/lib/plat/unix/CMakeLists.txt @@ -65,7 +65,7 @@ if (LWS_WITH_NETWORK) endif() endif() -if (LWS_WITH_PLUGINS OR LWS_WITH_EVLIB_PLUGINS) +if (LWS_WITH_PLUGINS_API) list(APPEND SOURCES plat/unix/unix-plugins.c) endif() diff --git a/lib/plat/unix/unix-plugins.c b/lib/plat/unix/unix-plugins.c index a68507740..c3d9663ba 100644 --- a/lib/plat/unix/unix-plugins.c +++ b/lib/plat/unix/unix-plugins.c @@ -29,10 +29,7 @@ #include #include - -#if defined(LWS_WITH_PLUGINS) || defined(LWS_WITH_EVLIB_PLUGINS) #include -#endif const lws_plugin_header_t * lws_plat_dlopen(struct lws_plugin **pplugin, const char *libpath, diff --git a/lib/plat/windows/windows-plugins.c b/lib/plat/windows/windows-plugins.c index 3ff412537..98e84593d 100644 --- a/lib/plat/windows/windows-plugins.c +++ b/lib/plat/windows/windows-plugins.c @@ -27,8 +27,11 @@ #endif #include "private-lib-core.h" -#if (defined(LWS_WITH_PLUGINS) || defined(LWS_WITH_EVLIB_PLUGINS)) && \ - (UV_VERSION_MAJOR > 0) +/* + * ie, if the plugins api needed at all + */ + +#if defined(LWS_WITH_PLUGINS_API) && (UV_VERSION_MAJOR > 0) const lws_plugin_header_t * lws_plat_dlopen(struct lws_plugin **pplugin, const char *libpath, @@ -105,6 +108,14 @@ lws_plat_destroy_dl(struct lws_plugin *p) return uv_dlclose(&p->u.lib); } +#endif + +/* + * Specifically for protocol plugins support + */ + +#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) + static int protocol_plugin_cb(struct lws_plugin *pin, void *each_user) { @@ -122,8 +133,7 @@ protocol_plugin_cb(struct lws_plugin *pin, void *each_user) int lws_plat_plugins_init(struct lws_context *context, const char * const *d) { -#if (defined(LWS_WITH_PLUGINS) || defined(LWS_WITH_EVLIB_PLUGINS)) && \ - (UV_VERSION_MAJOR > 0) +#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) if (info->plugin_dirs) { uv_loop_init(&context->uv.loop); lws_plugins_init(&context->plugin_list, info->plugin_dirs, @@ -138,8 +148,7 @@ lws_plat_plugins_init(struct lws_context *context, const char * const *d) int lws_plat_plugins_destroy(struct lws_context * context) { -#if (defined(LWS_WITH_PLUGINS) || defined(LWS_WITH_EVLIB_PLUGINS)) && \ - (UV_VERSION_MAJOR > 0) +#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV) && context->plugin_list) { lws_plugins_destroy(&context->plugin_list, NULL, NULL);