diff --git a/README.lwsws.md b/README.lwsws.md index 28cf5c14..63b02668 100644 --- a/README.lwsws.md +++ b/README.lwsws.md @@ -284,6 +284,19 @@ To help that happen conveniently, there are some new apis dumb increment, mirror and status protocol plugins are provided as examples. +Additional plugin search paths +------------------------------ + +Packages that have their own lws plugins can install them in their own +preferred dir and ask lwsws to scan there by using a config fragment +like this, in its own conf.d/ file managed by the other package + +{ + "global": { + "plugin-dir": "/usr/local/share/coherent-timeline/plugins" + } +} + lws-server-status plugin ------------------------ diff --git a/lib/libuv.c b/lib/libuv.c index b00d2a87..a230b57f 100644 --- a/lib/libuv.c +++ b/lib/libuv.c @@ -416,7 +416,7 @@ lws_libuv_closehandle(struct lws *wsi) #if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) LWS_VISIBLE int -lws_plat_plugins_init(struct lws_context * context, const char *d) +lws_plat_plugins_init(struct lws_context * context, const char * const *d) { struct lws_plugin_capability lcaps; struct lws_plugin *plugin; @@ -434,65 +434,71 @@ lws_plat_plugins_init(struct lws_context * context, const char *d) uv_loop_init(&loop); - if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) { - lwsl_err("Scandir on %s failed\n", d); - return 1; - } - lwsl_notice(" Plugins:\n"); - while (uv_fs_scandir_next(&req, &dent) != UV_EOF) { - if (strlen(dent.name) < 7) + while (d && *d) { + + lwsl_notice(" Scanning %s\n", *d); + m =uv_fs_scandir(&loop, &req, *d, 0, NULL); + if (m < 1) { + lwsl_err("Scandir on %s failed\n", *d); + return 1; + } + + while (uv_fs_scandir_next(&req, &dent) != UV_EOF) { + if (strlen(dent.name) < 7) + continue; + + lwsl_notice(" %s\n", dent.name); + + snprintf(path, sizeof(path) - 1, "%s/%s", *d, dent.name); + if (uv_dlopen(path, &lib)) { + uv_dlerror(&lib); + lwsl_err("Error loading DSO: %s\n", lib.errmsg); + goto bail; + } + /* we could open it, can we get his init function? */ + m = snprintf(path, sizeof(path) - 1, "init_%s", + dent.name + 3 /* snip lib... */); + path[m - 3] = '\0'; /* snip the .so */ + if (uv_dlsym(&lib, path, &v)) { + uv_dlerror(&lib); + lwsl_err("Failed to get init on %s: %s", + dent.name, lib.errmsg); + goto bail; + } + initfunc = (lws_plugin_init_func)v; + lcaps.api_magic = LWS_PLUGIN_API_MAGIC; + m = initfunc(context, &lcaps); + if (m) { + lwsl_err("Initializing %s failed %d\n", dent.name, m); + goto skip; + } + + plugin = lws_malloc(sizeof(*plugin)); + if (!plugin) { + lwsl_err("OOM\n"); + goto bail; + } + plugin->list = context->plugin_list; + context->plugin_list = plugin; + strncpy(plugin->name, dent.name, sizeof(plugin->name) - 1); + plugin->name[sizeof(plugin->name) - 1] = '\0'; + plugin->lib = lib; + plugin->caps = lcaps; + context->plugin_protocol_count += lcaps.count_protocols; + context->plugin_extension_count += lcaps.count_extensions; + continue; - lwsl_notice(" %s\n", dent.name); - - snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name); - if (uv_dlopen(path, &lib)) { - uv_dlerror(&lib); - lwsl_err("Error loading DSO: %s\n", lib.errmsg); - goto bail; - } - /* we could open it, can we get his init function? */ - m = snprintf(path, sizeof(path) - 1, "init_%s", - dent.name + 3 /* snip lib... */); - path[m - 3] = '\0'; /* snip the .so */ - if (uv_dlsym(&lib, path, &v)) { - uv_dlerror(&lib); - lwsl_err("Failed to get init on %s: %s", - dent.name, lib.errmsg); - goto bail; - } - initfunc = (lws_plugin_init_func)v; - lcaps.api_magic = LWS_PLUGIN_API_MAGIC; - m = initfunc(context, &lcaps); - if (m) { - lwsl_err("Initializing %s failed %d\n", dent.name, m); - goto skip; - } - - plugin = lws_malloc(sizeof(*plugin)); - if (!plugin) { - lwsl_err("OOM\n"); - goto bail; - } - plugin->list = context->plugin_list; - context->plugin_list = plugin; - strncpy(plugin->name, dent.name, sizeof(plugin->name) - 1); - plugin->name[sizeof(plugin->name) - 1] = '\0'; - plugin->lib = lib; - plugin->caps = lcaps; - context->plugin_protocol_count += lcaps.count_protocols; - context->plugin_extension_count += lcaps.count_extensions; - - continue; - skip: - uv_dlclose(&lib); + uv_dlclose(&lib); + } +bail: + uv_fs_req_cleanup(&req); + d++; } -bail: - uv_fs_req_cleanup(&req); uv_loop_close(&loop); return ret; diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 1f693461..82ed98d9 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -1478,8 +1478,8 @@ struct lws_http_mount { * @vhost_name: VHOST: name of vhost, must match external DNS name used to * access the site, like "warmcat.com" as it's used to match * Host: header and / or SNI name for SSL. - * @plugins_dir: CONTEXT: directory to scan for lws protocol plugins at - * context creation time + * @plugin_dirs: CONTEXT: NULL, or NULL-terminated array of directories to + * scan for lws protocol plugins at context creation time * @pvo: VHOST: pointer to optional linked list of per-vhost * options made accessible to protocols * @keepalive_timeout: VHOST: (default = 0 = 60s) seconds to allow remote @@ -1525,7 +1525,7 @@ struct lws_context_creation_info { unsigned int timeout_secs; /* VH */ const char *ecdh_curve; /* VH */ const char *vhost_name; /* VH */ - const char *plugins_dir; /* context */ + const char * const *plugin_dirs; /* context */ const struct lws_protocol_vhost_options *pvo; /* VH */ int keepalive_timeout; /* VH */ const char *log_filepath; /* VH */ diff --git a/lib/lws-plat-unix.c b/lib/lws-plat-unix.c index e4894fa0..98b6c588 100644 --- a/lib/lws-plat-unix.c +++ b/lib/lws-plat-unix.c @@ -306,7 +306,7 @@ static int filter(const struct dirent *ent) } LWS_VISIBLE int -lws_plat_plugins_init(struct lws_context * context, const char *d) +lws_plat_plugins_init(struct lws_context * context, const char * const *d) { struct lws_plugin_capability lcaps; struct lws_plugin *plugin; @@ -316,70 +316,73 @@ lws_plat_plugins_init(struct lws_context * context, const char *d) char path[256]; void *l; - - n = scandir(d, &namelist, filter, alphasort); - if (n < 0) { - lwsl_err("Scandir on %s failed\n", d); - return 1; - } - lwsl_notice(" Plugins:\n"); - for (i = 0; i < n; i++) { - if (strlen(namelist[i]->d_name) < 7) - goto inval; - - lwsl_notice(" %s\n", namelist[i]->d_name); - - snprintf(path, sizeof(path) - 1, "%s/%s", d, - namelist[i]->d_name); - l = dlopen(path, RTLD_NOW); - if (!l) { - lwsl_err("Error loading DSO: %s\n", dlerror()); - while (i++ < n) - free(namelist[i]); - goto bail; + while (d && *d) { + n = scandir(*d, &namelist, filter, alphasort); + if (n < 0) { + lwsl_err("Scandir on %s failed\n", *d); + return 1; } - /* we could open it, can we get his init function? */ - m = snprintf(path, sizeof(path) - 1, "init_%s", - namelist[i]->d_name + 3 /* snip lib... */); - path[m - 3] = '\0'; /* snip the .so */ - initfunc = dlsym(l, path); - if (!initfunc) { - lwsl_err("Failed to get init on %s: %s", - namelist[i]->d_name, dlerror()); + + for (i = 0; i < n; i++) { + if (strlen(namelist[i]->d_name) < 7) + goto inval; + + lwsl_notice(" %s\n", namelist[i]->d_name); + + snprintf(path, sizeof(path) - 1, "%s/%s", *d, + namelist[i]->d_name); + l = dlopen(path, RTLD_NOW); + if (!l) { + lwsl_err("Error loading DSO: %s\n", dlerror()); + while (i++ < n) + free(namelist[i]); + goto bail; + } + /* we could open it, can we get his init function? */ + m = snprintf(path, sizeof(path) - 1, "init_%s", + namelist[i]->d_name + 3 /* snip lib... */); + path[m - 3] = '\0'; /* snip the .so */ + initfunc = dlsym(l, path); + if (!initfunc) { + lwsl_err("Failed to get init on %s: %s", + namelist[i]->d_name, dlerror()); + dlclose(l); + } + lcaps.api_magic = LWS_PLUGIN_API_MAGIC; + m = initfunc(context, &lcaps); + if (m) { + lwsl_err("Initializing %s failed %d\n", + namelist[i]->d_name, m); + dlclose(l); + goto skip; + } + + plugin = lws_malloc(sizeof(*plugin)); + if (!plugin) { + lwsl_err("OOM\n"); + goto bail; + } + plugin->list = context->plugin_list; + context->plugin_list = plugin; + strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1); + plugin->name[sizeof(plugin->name) - 1] = '\0'; + plugin->l = l; + plugin->caps = lcaps; + context->plugin_protocol_count += lcaps.count_protocols; + context->plugin_extension_count += lcaps.count_extensions; + + free(namelist[i]); + continue; + + skip: dlclose(l); + inval: + free(namelist[i]); } - lcaps.api_magic = LWS_PLUGIN_API_MAGIC; - m = initfunc(context, &lcaps); - if (m) { - lwsl_err("Initializing %s failed %d\n", - namelist[i]->d_name, m); - dlclose(l); - goto skip; - } - - plugin = lws_malloc(sizeof(*plugin)); - if (!plugin) { - lwsl_err("OOM\n"); - goto bail; - } - plugin->list = context->plugin_list; - context->plugin_list = plugin; - strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1); - plugin->name[sizeof(plugin->name) - 1] = '\0'; - plugin->l = l; - plugin->caps = lcaps; - context->plugin_protocol_count += lcaps.count_protocols; - context->plugin_extension_count += lcaps.count_extensions; - - free(namelist[i]); - continue; - -skip: - dlclose(l); -inval: - free(namelist[i]); + free(namelist); + d++; } bail: @@ -707,8 +710,8 @@ lws_plat_init(struct lws_context *context, context->fops.write = _lws_plat_file_write; #ifdef LWS_WITH_PLUGINS - if (info->plugins_dir) - lws_plat_plugins_init(context, info->plugins_dir); + if (info->plugin_dirs) + lws_plat_plugins_init(context, info->plugin_dirs); #endif return 0; diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 91f4d757..df2bbf64 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -770,7 +770,7 @@ LWS_EXTERN void lws_libuv_closehandle(struct lws *wsi); LWS_VISIBLE LWS_EXTERN int -lws_plat_plugins_init(struct lws_context * context, const char *d); +lws_plat_plugins_init(struct lws_context * context, const char * const *d); LWS_VISIBLE LWS_EXTERN int lws_plat_plugins_destroy(struct lws_context * context); diff --git a/lwsws/conf.c b/lwsws/conf.c index 31d38f85..f222ed64 100644 --- a/lwsws/conf.c +++ b/lwsws/conf.c @@ -27,6 +27,7 @@ static const char * const paths_global[] = { "global.count-threads", "global.init-ssl", "global.server-string", + "global.plugin-dir" }; enum lejp_global_paths { @@ -35,6 +36,7 @@ enum lejp_global_paths { LEJPGP_COUNT_THREADS, LWJPGP_INIT_SSL, LEJPGP_SERVER_STRING, + LEJPGP_PLUGIN_DIR }; static const char * const paths_vhosts[] = { @@ -91,6 +93,8 @@ enum lejp_vhost_paths { LEJPVP_KEEPALIVE_TIMEOUT, }; +#define MAX_PLUGIN_DIRS 10 + struct jpargs { struct lws_context_creation_info *info; struct lws_context *context; @@ -101,6 +105,8 @@ struct jpargs { struct lws_protocol_vhost_options *pvo; struct lws_http_mount m; + const char **plugin_dirs; + int count_plugin_dirs; }; static void * @@ -154,6 +160,13 @@ lejp_globals_cb(struct lejp_ctx *ctx, char reason) case LEJPGP_SERVER_STRING: a->info->server_string = a->p; break; + case LEJPGP_PLUGIN_DIR: + if (a->count_plugin_dirs == MAX_PLUGIN_DIRS - 1) { + lwsl_err("Too many plugin dirs\n"); + return -1; + } + a->plugin_dirs[a->count_plugin_dirs++] = a->p; + break; default: return 0; @@ -537,12 +550,27 @@ lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d, char **cs, int *len) { struct jpargs a; + const char * const *old = info->plugin_dirs; + + memset(&a, 0, sizeof(a)); a.info = info; a.p = *cs; a.end = (a.p + *len) - 1; a.valid = 0; + lwsws_align(&a); + info->plugin_dirs = (void *)a.p; + a.plugin_dirs = (void *)a.p; /* writeable version */ + a.p += MAX_PLUGIN_DIRS * sizeof(void *); + + /* copy any default paths */ + + while (old && *old) { + a.plugin_dirs[a.count_plugin_dirs++] = *old; + old++; + } + if (lwsws_get_config(&a, "/etc/lwsws/conf", paths_global, ARRAY_SIZE(paths_global), lejp_globals_cb) > 1) return 1; @@ -550,6 +578,8 @@ lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d, ARRAY_SIZE(paths_global), lejp_globals_cb) > 1) return 1; + a.plugin_dirs[a.count_plugin_dirs] = NULL; + *cs = a.p; *len = a.end - a.p; @@ -563,6 +593,8 @@ lwsws_get_config_vhosts(struct lws_context *context, { struct jpargs a; + memset(&a, 0, sizeof(a)); + a.info = info; a.p = *cs; a.end = a.p + *len; diff --git a/lwsws/main.c b/lwsws/main.c index 8d84552b..2a7579fc 100644 --- a/lwsws/main.c +++ b/lwsws/main.c @@ -80,6 +80,11 @@ static const struct lws_extension exts[] = { { NULL, NULL, NULL /* terminator */ } }; +static const char * const plugin_dirs[] = { + INSTALL_DATADIR"/libwebsockets-test-server/plugins/", + NULL +}; + static struct option options[] = { { "help", no_argument, NULL, 'h' }, { "debug", required_argument, NULL, 'd' }, @@ -107,6 +112,8 @@ void signal_cb(uv_signal_t *watcher, int signum) } #endif + + int main(int argc, char **argv) { struct lws_context_creation_info info; @@ -184,8 +191,7 @@ int main(int argc, char **argv) LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_LIBUV; - info.plugins_dir = INSTALL_DATADIR"/libwebsockets-test-server/plugins/"; - + info.plugin_dirs = plugin_dirs; lwsl_notice("Using config dir: \"%s\"\n", config_dir); /* diff --git a/test-server/test-server-v2.0.c b/test-server/test-server-v2.0.c index 28a9264a..8702049b 100644 --- a/test-server/test-server-v2.0.c +++ b/test-server/test-server-v2.0.c @@ -155,6 +155,11 @@ static const struct option options[] = { { NULL, 0, 0, 0 } }; +static const char * const plugin_dirs[] = { + INSTALL_DATADIR"/libwebsockets-test-server/plugins/", + NULL +}; + int main(int argc, char **argv) { struct lws_context_creation_info info; @@ -344,7 +349,7 @@ int main(int argc, char **argv) "!AES256-SHA256"; /* tell lws to look for protocol plugins here */ - info.plugins_dir = INSTALL_DATADIR"/libwebsockets-test-server/plugins/"; + info.plugin_dirs = plugin_dirs; /* tell lws about our mount we want */ info.mounts = &mount;