diff --git a/configure.ac b/configure.ac index ae9f0d79b..4f705e01b 100644 --- a/configure.ac +++ b/configure.ac @@ -87,6 +87,10 @@ AC_LANG_PUSH([C]) AC_CHECK_HEADER([execinfo.h], AC_DEFINE(HAVE_EXECINFO_H)) # there is a warning in nobug, disabled til fixed AC_CHECK_HEADER([valgrind/valgrind.h], AC_DEFINE(HAVE_VALGRIND_VALGRIND_H)) +AC_CHECK_LIB(dl, dlopen, [LUMIERA_PLUGIN_LIBS="$LUMIERA_PLUGIN_LIBS -ldl"], + [AC_MSG_ERROR([Dynamic linking not supported, report a bug])] +) + AC_LANG_POP([C]) @@ -160,6 +164,7 @@ AC_CHECK_LIB(Xv, XvQueryAdaptors, [LUMIERA_GUI_LIBS="$LUMIERA_GUI_LIBS -lXv"], # END X11 Dependancies +AC_SUBST(LUMIERA_PLUGIN_LIBS) AC_SUBST(LUMIERA_GUI_CFLAGS) AC_SUBST(LUMIERA_GUI_LIBS) diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index be9b9cd90..d8c0c159e 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -30,6 +30,7 @@ liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/interface.c \ $(liblumibackend_a_srcdir)/interfaceregistry.c \ $(liblumibackend_a_srcdir)/plugin.c \ + $(liblumibackend_a_srcdir)/plugin_dynlib.c \ $(liblumibackend_a_srcdir)/config.c \ $(liblumibackend_a_srcdir)/config_typed.c \ $(liblumibackend_a_srcdir)/config_wordlist.c \ diff --git a/src/backend/plugin.c b/src/backend/plugin.c index 1b708c6be..66ebd345c 100644 --- a/src/backend/plugin.c +++ b/src/backend/plugin.c @@ -18,6 +18,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + #include "lib/safeclib.h" #include "lib/psplay.h" #include "lib/mutex.h" @@ -41,40 +43,56 @@ extern PSplay lumiera_pluginregistry; /* TODO should be set by the build system to the actual plugin path */ /* errors */ +LUMIERA_ERROR_DEFINE(PLUGIN_INIT, "Initialization error"); LUMIERA_ERROR_DEFINE(PLUGIN_DLOPEN, "Could not open plugin"); +LUMIERA_ERROR_DEFINE(PLUGIN_WTF, "Not a Lumiera plugin"); +LUMIERA_ERROR_DEFINE(PLUGIN_REGISTER, "Could not register plugin"); +LUMIERA_ERROR_DEFINE(PLUGIN_VERSION, "Plugin Version unsupported"); -/* - supported (planned) plugin types and their file extensions -*/ +#define LUMIERA_PLUGIN_TYPE_PLANNED(name, ext) -#define LUMIERA_PLUGIN_TYPES \ - LUMIERA_PLUGIN_TYPE(DYNLIB, ".so") +/** + * Supported (and planned) plugin types and their file extensions + * This maps filename extensions to implementations (of the respective _load_NAME and _unload_NAME functions) + * So far we only support platform dynamic libraries, later we may add plugins implemented in lua + * and c source modules which get compiled on the fly. + */ +#define LUMIERA_PLUGIN_TYPES \ + LUMIERA_PLUGIN_TYPE_PLANNED(DYNLIB, ".so") \ + LUMIERA_PLUGIN_TYPE_PLANNED(DYNLIB, ".lum") \ + LUMIERA_PLUGIN_TYPE_PLANNED(LUA, ".lua") \ + LUMIERA_PLUGIN_TYPE_PLANNED(CSOURCE, ".c") -/* - only .so dynlibs for now, later we may support some more - LUMIERA_PLUGIN_TYPE(LUA, ".lua") - LUMIERA_PLUGIN_TYPE(CSOURCE, ".c") -*/ -#define LUMIERA_PLUGIN_TYPE(type, ext) LUMIERA_PLUGIN_##type, -enum lumiera_plugin_type - { - LUMIERA_PLUGIN_TYPES - LUMIERA_PLUGIN_NONE - }; +/** + * record the extension and a callback function for loading the associated plugin for each plugin type + */ +struct lumiera_plugintype_struct +{ + LumieraPlugin (*lumiera_plugin_load_fn)(const char*); + void (*lumiera_plugin_unload_fn)(LumieraPlugin); + const char* ext; +}; +typedef struct lumiera_plugintype_struct lumiera_plugintype; +typedef lumiera_plugintype* LumieraPlugintype; + +/* forward declare loader functions for all types */ +#define LUMIERA_PLUGIN_TYPE(type, ext) \ + LumieraPlugin lumiera_plugin_load_##type (const char*); \ + void lumiera_plugin_unload_##type (LumieraPlugin); +LUMIERA_PLUGIN_TYPES #undef LUMIERA_PLUGIN_TYPE -#define LUMIERA_PLUGIN_TYPE(type, ext) ext, -static const char* const lumiera_plugin_ext[] = +/* and now setup a table which will be used for dispatching loaders depending on the type of the plugin */ +#define LUMIERA_PLUGIN_TYPE(type, ext) {lumiera_plugin_load_##type, lumiera_plugin_unload_##type, ext}, +static lumiera_plugintype lumiera_plugin_types[] = { LUMIERA_PLUGIN_TYPES - NULL + {NULL, NULL, NULL} }; #undef LUMIERA_PLUGIN_TYPE - - struct lumiera_plugin_struct { psplaynode node; @@ -88,14 +106,46 @@ struct lumiera_plugin_struct /* time when the last open or close action happened */ time_t last; - /* kind of plugin */ - enum lumiera_plugin_type type; + /* when loading plugins en masse we do not want to fail completely if one doesnt cooperate, instead we record local errors here */ + lumiera_err error; - /* dlopen handle */ + /* the 'plugin' interface itself */ + LumieraInterface plugin; + + /* generic handle for the plugin, dlopen handle, etc */ void* handle; }; +LumieraPlugin +lumiera_plugin_new (const char* name) +{ + LumieraPlugin self = lumiera_malloc (sizeof (*self)); + + psplaynode_init (&self->node); + self->name = lumiera_strndup (name, SIZE_MAX); + self->refcnt = 0; + time (&self->last); + self->error = LUMIERA_ERROR_PLUGIN_INIT; + self->plugin = NULL; + self->handle = NULL; + return self; +} + + +LumieraPlugin +lumiera_plugin_init (LumieraPlugin self, void* handle, LumieraInterface plugin) +{ + self->error = lumiera_error (); + self->plugin = plugin; + self->handle = handle; + return self; +} + + + + +static char* init_exts_globs (); int lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin), @@ -108,30 +158,7 @@ lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin), /* construct glob trail {.so,.c,.foo} ... */ static char* exts_globs = NULL; if (!exts_globs) - { - size_t exts_sz = 4; /* / * { } \0 less one comma */ - const char*const* itr = lumiera_plugin_ext; - while (*itr) - { - exts_sz += strlen (*itr) + 1; - ++itr; - } - - exts_globs = lumiera_malloc (exts_sz); - *exts_globs = '\0'; - - itr = lumiera_plugin_ext; - strcat (exts_globs, "/*{"); - - while (*itr) - { - strcat (exts_globs, *itr); - strcat (exts_globs, ","); - ++itr; - } - exts_globs[exts_sz-2] = '}'; - TRACE (plugin, "initialized extension glob to '%s'", exts_globs); - } + exts_globs = init_exts_globs (&exts_globs); const char* path; unsigned i = 0; @@ -150,21 +177,18 @@ lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin), ++i; } - LUMIERA_RECMUTEX_SECTION (plugin, &lumiera_interface_mutex) - { - for (char** itr = globs.gl_pathv; *itr; ++itr) - { - - if (!psplay_find (lumiera_pluginregistry, *itr, 100)) - { - TRACE (plugin, "found new plugin '%s'", *itr); - - LumieraPlugin plugin = callback_load (*itr); - if (plugin) - callback_register (plugin); - } - } - } + if (globs.gl_pathc) + LUMIERA_RECMUTEX_SECTION (plugin, &lumiera_interface_mutex) + { + for (char** itr = globs.gl_pathv; *itr; ++itr) + { + if (!psplay_find (lumiera_pluginregistry, *itr, 100)) + { + TRACE (plugin, "found new plugin '%s'", *itr); + callback_register (callback_load (*itr)); + } + } + } globfree (&globs); @@ -172,37 +196,90 @@ lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin), } - - - LumieraPlugin lumiera_plugin_load (const char* plugin) { TRACE (plugin); - UNIMPLEMENTED(); - (void) plugin; + /* dispatch on ext, call the registered function */ + const char* ext = strrchr (plugin, '.'); + + LumieraPlugintype itr = lumiera_plugin_types; + while (itr->ext) + { + if (!strcmp (itr->ext, ext)) + return itr->lumiera_plugin_load_fn (plugin); + ++itr; + } return NULL; } - int lumiera_plugin_register (LumieraPlugin plugin) { TRACE (plugin); - UNIMPLEMENTED(); + if (!plugin) + return 1; LUMIERA_RECMUTEX_SECTION (plugin, &lumiera_interface_mutex) { - (void) plugin; - + if (psplay_insert (lumiera_pluginregistry, &plugin->node, 100)) + { + if (!plugin->error) + { + switch (lumiera_interface_version (plugin->plugin, "lumieraorg__plugin")) + { + case 0: + { + TRACE (plugin, "registering %s", plugin->name); + LUMIERA_INTERFACE_HANDLE(lumieraorg__plugin, 0) handle = + LUMIERA_INTERFACE_CAST(lumieraorg__plugin, 0) plugin->plugin; + lumiera_interfaceregistry_bulkregister_interfaces (handle->plugin_interfaces (), plugin); + } + break; + default: + LUMIERA_ERROR_SET (plugin, PLUGIN_VERSION); + } + } + } + else + { + LUMIERA_ERROR_SET (plugin, PLUGIN_REGISTER); + } } - return 0; + return !!lumiera_error_peek(); } +static char* init_exts_globs () +{ + char* exts_globs; + size_t exts_sz = 4; /* / * { } \0 less one comma */ + LumieraPlugintype itr = lumiera_plugin_types; + while (itr->ext) + { + exts_sz += strlen (itr->ext) + 1; + ++itr; + } + + exts_globs = lumiera_malloc (exts_sz); + *exts_globs = '\0'; + + itr = lumiera_plugin_types; + strcat (exts_globs, "/*{"); + + while (itr->ext) + { + strcat (exts_globs, itr->ext); + strcat (exts_globs, ","); + ++itr; + } + exts_globs[exts_sz-2] = '}'; + TRACE (plugin, "initialized extension glob to '%s'", exts_globs); + return exts_globs; +} int lumiera_plugin_cmp_fn (const void* keya, const void* keyb) diff --git a/src/backend/plugin.h b/src/backend/plugin.h index 8c0e5e7ae..03b963990 100644 --- a/src/backend/plugin.h +++ b/src/backend/plugin.h @@ -21,12 +21,14 @@ #ifndef LUMIERA_PLUGIN_H #define LUMIERA_PLUGIN_H +#include "lib/psplay.h" +#include "lib/error.h" + +#include "backend/interface.h" #include #include -#include "lib/error.h" - /** * @file * Lumiera plugins defines 'interfaces' as shown in interface.h, the plugin system handles the loading @@ -35,8 +37,11 @@ */ +LUMIERA_ERROR_DECLARE(PLUGIN_INIT); LUMIERA_ERROR_DECLARE(PLUGIN_DLOPEN); -LUMIERA_ERROR_DECLARE(PLUGIN_PLUGINPATH); +LUMIERA_ERROR_DECLARE(PLUGIN_WTF); +LUMIERA_ERROR_DECLARE(PLUGIN_REGISTER); +LUMIERA_ERROR_DECLARE(PLUGIN_VERSION); NOBUG_DECLARE_FLAG (plugin); @@ -47,6 +52,7 @@ NOBUG_DECLARE_FLAG (plugin); struct lumiera_plugin_struct; typedef struct lumiera_plugin_struct lumiera_plugin; typedef lumiera_plugin* LumieraPlugin; +enum lumiera_plugin_type; int @@ -55,6 +61,11 @@ lumiera_plugin_cmp_fn (const void* keya, const void* keyb); const void* lumiera_plugin_key_fn (const PSplaynode node); +LumieraPlugin +lumiera_plugin_new (const char* name); + +LumieraPlugin +lumiera_plugin_init (LumieraPlugin self, void* handle, LumieraInterface plugin); LumieraPlugin lumiera_plugin_load (const char* plugin); @@ -68,7 +79,7 @@ lumiera_plugin_register (LumieraPlugin); /** * discover new plugins - * traverses the configured plugin dirs and calls the callback_load function for any plugin + * traverses the configured plugin paths and calls the callback_load function for any plugin * not actually loaded. If callback_load returns a plugin (and not NULL) then this is feed to * the callback_register function. */