diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index da39e71c6..be9b9cd90 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -29,6 +29,7 @@ liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/filehandlecache.c \ $(liblumibackend_a_srcdir)/interface.c \ $(liblumibackend_a_srcdir)/interfaceregistry.c \ + $(liblumibackend_a_srcdir)/plugin.c \ $(liblumibackend_a_srcdir)/config.c \ $(liblumibackend_a_srcdir)/config_typed.c \ $(liblumibackend_a_srcdir)/config_wordlist.c \ @@ -47,6 +48,7 @@ noinst_HEADERS += \ $(liblumibackend_a_srcdir)/interface.h \ $(liblumibackend_a_srcdir)/interfaceregistry.h \ $(liblumibackend_a_srcdir)/interfacedescriptor.h \ + $(liblumibackend_a_srcdir)/plugin.h \ $(liblumibackend_a_srcdir)/config.h \ $(liblumibackend_a_srcdir)/configentry.h \ $(liblumibackend_a_srcdir)/configitem.h \ diff --git a/src/backend/config.h b/src/backend/config.h index 4e3acd9a9..445ed6e23 100644 --- a/src/backend/config.h +++ b/src/backend/config.h @@ -228,20 +228,22 @@ LUMIERA_CONFIG_TYPES * Get nth word of a wordlist. * @param key key under which this wordlist is stored * @param nth index of the word to get, starting with 0 + * @param delims a string literal listing all characters which are treated as delimiters * @return pointer to a tempbuf holding the nth word or NULL in case of error */ const char* -lumiera_config_wordlist_get_nth (const char* key, unsigned nth); +lumiera_config_wordlist_get_nth (const char* key, unsigned nth, const char* delims); /** * Find the index of a word in a wordlist. * @param key key under which this wordlist is stored * @param value word to find + * @param delims a string literal listing all characters which are treated as delimiters * @return index of the first occurence of the word or -1 in case of failure */ int -lumiera_config_wordlist_find (const char* key, const char* value); +lumiera_config_wordlist_find (const char* key, const char* value, const char* delims); /** @@ -254,20 +256,22 @@ lumiera_config_wordlist_find (const char* key, const char* value); * @param value word to be replaced * @param subst1 first replacement word * @param subst2 second replacement word + * @param delims a string literal listing all characters which are treated as delimiters * @return internal representation of the wordlist in a tmpbuf or NULL in case of an error */ const char* -lumiera_config_wordlist_replace (const char* key, const char* value, const char* subst1, const char* subst2); +lumiera_config_wordlist_replace (const char* key, const char* value, const char* subst1, const char* subst2, const char* delims); /** * Add a word to the end of a wordlist if it doesnt exist already * @param key key under which this wordlist is stored * @param value new word to add + * @param delims a string literal listing all characters which are treated as delimiters * @return internal representation of the wordlist in a tmpbuf or NULL in case of an error */ const char* -lumiera_config_wordlist_add (const char* key, const char* value); +lumiera_config_wordlist_add (const char* key, const char* value, const char* delims); // * {{{ lumiera_config_TYPE_set (const char* key, TYPE*value, const char* fmt) }}} // Highlevel interface for different types, fmt is a printf format specifier for the desired format, when NULL, defaults apply. diff --git a/src/backend/config_wordlist.c b/src/backend/config_wordlist.c index 6cd0389a7..47232f483 100644 --- a/src/backend/config_wordlist.c +++ b/src/backend/config_wordlist.c @@ -19,25 +19,20 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -//TODO: Support library includes// #include "lib/error.h" #include "lib/safeclib.h" -//TODO: Lumiera header includes// #include "backend/config.h" -//TODO: internal/static forward declarations// extern LumieraConfig lumiera_global_config; -//TODO: System includes// - /** * return nth word of a wordlist */ const char* -lumiera_config_wordlist_get_nth (const char* key, unsigned nth) +lumiera_config_wordlist_get_nth (const char* key, unsigned nth, const char* delims) { const char* value; size_t len; @@ -47,8 +42,8 @@ lumiera_config_wordlist_get_nth (const char* key, unsigned nth) for (;;) { - value += strspn (value, " \t,;"); - len = strcspn (value, " \t,;"); + value += strspn (value, delims); + len = strcspn (value, delims); if (!nth && *value) break; @@ -64,7 +59,7 @@ lumiera_config_wordlist_get_nth (const char* key, unsigned nth) int -lumiera_config_wordlist_find (const char* key, const char* value) +lumiera_config_wordlist_find (const char* key, const char* value, const char* delims) { const char* itr; size_t vlen = strlen (value); @@ -75,8 +70,8 @@ lumiera_config_wordlist_find (const char* key, const char* value) for (int idx = 0; *itr; itr += len, ++idx) { - itr += strspn (itr, " \t,;"); - len = strcspn (itr, " \t,;"); + itr += strspn (itr, delims); + len = strcspn (itr, delims); if (len == vlen && !strncmp (itr, value, vlen)) return idx; @@ -87,7 +82,7 @@ lumiera_config_wordlist_find (const char* key, const char* value) const char* -lumiera_config_wordlist_replace (const char* key, const char* value, const char* subst1, const char* subst2) +lumiera_config_wordlist_replace (const char* key, const char* value, const char* subst1, const char* subst2, const char* delims) { const char* wordlist; const char* str = NULL; @@ -106,28 +101,25 @@ lumiera_config_wordlist_replace (const char* key, const char* value, const char* for (const char* itr = start; *itr; itr += len) { const char* left_end = itr; - itr += strspn (itr, " \t,;"); - len = strcspn (itr, " \t,;"); + itr += strspn (itr, delims); + len = strcspn (itr, delims); if (len == vlen && !strncmp (itr, value, vlen)) { - TODO ("figure delimiter from original string out"); - const char* delim = " "; - /* step over the word */ itr += len; - itr += strspn (itr, " \t,;"); + itr += strspn (itr, delims); /* getting the delimiters right for the corner cases looks ugly, want to refactor it? just do it */ str = lumiera_tmpbuf_snprintf (SIZE_MAX, - "%.*s%.*s%s%s%s%s%s%s", + "%.*s%.*s%.1s%s%.1s%s%.1s%s", start - wordlist, wordlist, left_end - start, start, - (left_end - start && subst1 && *subst1) ? delim : "", + (left_end - start && subst1 && *subst1) ? delims : "", (subst1 && *subst1) ? subst1 : "", - ((left_end - start || (subst1 && *subst1)) && subst2 && *subst2) ? delim : "", + ((left_end - start || (subst1 && *subst1)) && subst2 && *subst2) ? delims : "", (subst2 && *subst2) ? subst2 : "", - ((left_end - start || (subst1 && *subst1) || (subst2 && *subst2)) && *itr) ? delim : "", + ((left_end - start || (subst1 && *subst1) || (subst2 && *subst2)) && *itr) ? delims : "", itr ); @@ -145,7 +137,7 @@ lumiera_config_wordlist_replace (const char* key, const char* value, const char* const char* -lumiera_config_wordlist_add (const char* key, const char* value) +lumiera_config_wordlist_add (const char* key, const char* value, const char* delims) { const char* wordlist = NULL; @@ -160,19 +152,16 @@ lumiera_config_wordlist_add (const char* key, const char* value) for (const char* itr = wordlist; *itr; itr += len) { - itr += strspn (itr, " \t,;"); - len = strcspn (itr, " \t,;"); + itr += strspn (itr, delims); + len = strcspn (itr, delims); if (len == vlen && !strncmp (itr, value, vlen)) goto end; } - TODO ("figure delimiter from original string out"); - const char* delim = " "; - - wordlist = lumiera_tmpbuf_snprintf (SIZE_MAX, "%s%s%s", + wordlist = lumiera_tmpbuf_snprintf (SIZE_MAX, "%s%.1s%s", wordlist, - wordlist[strspn (wordlist, " \t,;")]?delim:"", + wordlist[strspn (wordlist, delims)] ? delims : "", value); if (!lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, "=%s", wordlist))) diff --git a/src/backend/interfaceregistry.c b/src/backend/interfaceregistry.c index ffa979340..05e2224a1 100644 --- a/src/backend/interfaceregistry.c +++ b/src/backend/interfaceregistry.c @@ -29,6 +29,7 @@ +#include "backend/plugin.h" #include "backend/interfaceregistry.h" /** @@ -39,27 +40,32 @@ */ NOBUG_DEFINE_FLAG_PARENT (interface_all, backend); +NOBUG_DEFINE_FLAG_PARENT (plugin, interface_all); NOBUG_DEFINE_FLAG_PARENT (interfaceregistry, interface_all); NOBUG_DEFINE_FLAG_PARENT (interface, interface_all); PSplay lumiera_interfaceregistry; +PSplay lumiera_pluginregistry; lumiera_mutex lumiera_interface_mutex; static int -cmp_fn (const void* keya, const void* keyb); +lumiera_interface_cmp_fn (const void* keya, const void* keyb); static const void* -key_fn (const PSplaynode node); +lumiera_interface_key_fn (const PSplaynode node); + static LumieraInterfacenode -lumiera_interfacenode_new (LumieraInterface iface) +lumiera_interfacenode_new (LumieraInterface iface, LumieraPlugin plugin) { LumieraInterfacenode self = lumiera_malloc (sizeof (*self)); psplaynode_init (&self->node); self->interface = iface; self->refcnt = 0; + self->plugin = plugin; + FIXME ("plugin handling (refcnt, atime)"); self->lnk = NULL; self->deps_size = 0; self->deps = NULL; @@ -74,6 +80,7 @@ lumiera_interfacenode_delete (LumieraInterfacenode self) if (self) { REQUIRE (self->refcnt == 0); + FIXME ("plugin handling"); lumiera_free (self->deps); lumiera_free (self); } @@ -89,13 +96,20 @@ lumiera_interfaceregistry_init (void) NOBUG_INIT_FLAG (interface_all); NOBUG_INIT_FLAG (interfaceregistry); NOBUG_INIT_FLAG (interface); + NOBUG_INIT_FLAG (plugin); + TRACE (interfaceregistry); REQUIRE (!lumiera_interfaceregistry); + REQUIRE (!lumiera_pluginregistry); - lumiera_interfaceregistry = psplay_new (cmp_fn, key_fn, NULL); + lumiera_interfaceregistry = psplay_new (lumiera_interface_cmp_fn, lumiera_interface_key_fn, NULL); if (!lumiera_interfaceregistry) LUMIERA_DIE (ERRNO); + lumiera_pluginregistry = psplay_new (lumiera_plugin_cmp_fn, lumiera_plugin_key_fn, NULL); + if (!lumiera_pluginregistry) + LUMIERA_DIE (ERRNO); + lumiera_recmutex_init (&lumiera_interface_mutex, "interfaceregistry", &NOBUG_FLAG(interfaceregistry)); } @@ -111,11 +125,18 @@ lumiera_interfaceregistry_destroy (void) if (lumiera_interfaceregistry) psplay_destroy (lumiera_interfaceregistry); lumiera_interfaceregistry = NULL; + + TODO ("provide a delete function for the psplay tree to tear it down"); + REQUIRE (psplay_nelements (lumiera_pluginregistry) == 0, "plugins still loaded at destroy"); + + if (lumiera_pluginregistry) + psplay_delete (lumiera_pluginregistry); + lumiera_pluginregistry = NULL; } void -lumiera_interfaceregistry_register_interface (LumieraInterface self) +lumiera_interfaceregistry_register_interface (LumieraInterface self, LumieraPlugin plugin) { TRACE (interfaceregistry); REQUIRE (self); @@ -123,13 +144,13 @@ lumiera_interfaceregistry_register_interface (LumieraInterface self) LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) { TRACE (interfaceregistry, "interface %s, version %d, instance %s", self->interface, self->version, self->name); - psplay_insert (lumiera_interfaceregistry, &lumiera_interfacenode_new (self)->node, 100); + psplay_insert (lumiera_interfaceregistry, &lumiera_interfacenode_new (self, plugin)->node, 100); } } void -lumiera_interfaceregistry_bulkregister_interfaces (LumieraInterface* self) +lumiera_interfaceregistry_bulkregister_interfaces (LumieraInterface* self, LumieraPlugin plugin) { TRACE (interfaceregistry); REQUIRE (self); @@ -139,7 +160,7 @@ lumiera_interfaceregistry_bulkregister_interfaces (LumieraInterface* self) while (*self) { TRACE (interfaceregistry, "interface %s, version %d, instance %s", (*self)->interface, (*self)->version, (*self)->name); - psplay_insert (lumiera_interfaceregistry, &lumiera_interfacenode_new (*self)->node, 100); + psplay_insert (lumiera_interfaceregistry, &lumiera_interfacenode_new (*self, plugin)->node, 100); ++self; } } @@ -213,7 +234,7 @@ lumiera_interfaceregistry_interface_find (const char* interface, unsigned versio static int -cmp_fn (const void* keya, const void* keyb) +lumiera_interface_cmp_fn (const void* keya, const void* keyb) { const LumieraInterface a = (const LumieraInterface)keya; const LumieraInterface b = (const LumieraInterface)keyb; @@ -240,12 +261,11 @@ cmp_fn (const void* keya, const void* keyb) static const void* -key_fn (const PSplaynode node) +lumiera_interface_key_fn (const PSplaynode node) { return ((LumieraInterfacenode)node)->interface; } - /* // Local Variables: // mode: C diff --git a/src/backend/interfaceregistry.h b/src/backend/interfaceregistry.h index 02481072d..54ede293a 100644 --- a/src/backend/interfaceregistry.h +++ b/src/backend/interfaceregistry.h @@ -25,6 +25,7 @@ #include "lib/psplay.h" #include "backend/interface.h" +#include "backend/plugin.h" #include @@ -62,6 +63,10 @@ struct lumiera_interfacenode_struct /** reference counters and link used for internal reference management */ unsigned refcnt; + + /** backreference to its plugin if it comes from a plugin, else NULL */ + LumieraPlugin plugin; + /** temporary used to stack interfaces when recursively opening/closing them */ LumieraInterfacenode lnk; /** allocated size of the following deps table */ @@ -84,10 +89,10 @@ lumiera_interfaceregistry_destroy (void); void -lumiera_interfaceregistry_register_interface (LumieraInterface self); +lumiera_interfaceregistry_register_interface (LumieraInterface self, LumieraPlugin plugin); void -lumiera_interfaceregistry_bulkregister_interfaces (LumieraInterface* self); +lumiera_interfaceregistry_bulkregister_interfaces (LumieraInterface* self, LumieraPlugin plugin); void lumiera_interfaceregistry_remove_interface (LumieraInterface self); diff --git a/src/backend/plugin.c b/src/backend/plugin.c index f6ecd7649..1b708c6be 100644 --- a/src/backend/plugin.c +++ b/src/backend/plugin.c @@ -18,72 +18,72 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include -#include -#include -#include -#include -#include -#include -#include - #include "lib/safeclib.h" +#include "lib/psplay.h" +#include "lib/mutex.h" +#include "lib/error.h" +#include "backend/interfaceregistry.h" +#include "backend/config.h" #include "backend/plugin.h" +#include + +#include + /** * @file * Plugin loader. */ +extern PSplay lumiera_pluginregistry; /* TODO should be set by the build system to the actual plugin path */ -#define LUMIERA_PLUGIN_PATH "~/.lumiera/plugins:/usr/local/lib/lumiera/plugins:.libs" - -NOBUG_DEFINE_FLAG (lumiera_plugin); /* errors */ LUMIERA_ERROR_DEFINE(PLUGIN_DLOPEN, "Could not open plugin"); -LUMIERA_ERROR_DEFINE(PLUGIN_HOOK, "Hook function failed"); -LUMIERA_ERROR_DEFINE(PLUGIN_NFILE, "No such plugin"); -LUMIERA_ERROR_DEFINE(PLUGIN_NIFACE, "No such interface"); -LUMIERA_ERROR_DEFINE(PLUGIN_REVISION, "Interface revision too old"); /* supported (planned) plugin types and their file extensions */ +#define LUMIERA_PLUGIN_TYPES \ + LUMIERA_PLUGIN_TYPE(DYNLIB, ".so") + +/* + 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_NULL, - LUMIERA_PLUGIN_DYNLIB, - LUMIERA_PLUGIN_CSOURCE + LUMIERA_PLUGIN_TYPES + LUMIERA_PLUGIN_NONE }; +#undef LUMIERA_PLUGIN_TYPE -static const struct -{ - const char* const ext; - enum lumiera_plugin_type type; -} lumiera_plugin_extensions [] = +#define LUMIERA_PLUGIN_TYPE(type, ext) ext, +static const char* const lumiera_plugin_ext[] = { - {"so", LUMIERA_PLUGIN_DYNLIB}, - {"o", LUMIERA_PLUGIN_DYNLIB}, - {"c", LUMIERA_PLUGIN_CSOURCE}, - /* extend here */ - {NULL, LUMIERA_PLUGIN_NULL} + LUMIERA_PLUGIN_TYPES + NULL }; +#undef LUMIERA_PLUGIN_TYPE -struct lumiera_plugin + + +struct lumiera_plugin_struct { - /* short name as queried ("effects/audio/normalize") used for sorting/finding */ - const char* name; + psplaynode node; /* long names as looked up ("/usr/local/lib/lumiera/plugins/effects/audio/normalize.so") */ - const char* pathname; + const char* name; /* use count for all interfaces of this plugin */ - unsigned use_count; + unsigned refcnt; /* time when the last open or close action happened */ time_t last; @@ -96,242 +96,124 @@ struct lumiera_plugin }; -/* global plugin registry */ -void* lumiera_plugin_registry = NULL; -/* plugin operations are protected by one big mutex */ -pthread_mutex_t lumiera_plugin_mutex = PTHREAD_MUTEX_INITIALIZER; - -/** - * the compare function for the registry tree. - * Compares the names of two struct lumiera_plugin. - * @return 0 if a and b are equal, just like strcmp. */ -static int -lumiera_plugin_name_cmp (const void* a, const void* b) -{ - return strcmp (((struct lumiera_plugin*) a)->name, ((struct lumiera_plugin*) b)->name); -} - -void -lumiera_init_plugin (void) -{ - NOBUG_INIT_FLAG (lumiera_plugin); -} - -/** - * Find and set pathname for the plugin. - * Searches through given path for given plugin, trying to find the file's location in the filesystem. - * If found, self->pathname will be set to the found plugin file. - * @param self The lumiera_plugin to open look for. - * @param path The path to search trough (paths seperated by ":") - * @return 0 on success. -1 on error, or if plugin not found in path. - */ int -lumiera_plugin_lookup (struct lumiera_plugin* self, const char* path) +lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin), + int (*callback_register) (LumieraPlugin)) { - if (!path) - return -1; + TRACE (plugin); + REQUIRE (callback_load); + REQUIRE (callback_register); - if (strlen(path) > 1023) - return -1; /*TODO error handling*/ - - char tpath[1024]; - TODO("dunno if PATH_MAX may be undefined (in case arbitary lengths are supported), I check that later"); - char pathname[1024] = {0}; - char* tmp; - - strcpy(tpath, path); - - /*for each in path*/ - for (char* tok = strtok_r (tpath, ":", &tmp); tok; tok = strtok_r (NULL, ":", &tmp)) + /* construct glob trail {.so,.c,.foo} ... */ + static char* exts_globs = NULL; + if (!exts_globs) { - /*for each extension*/ - for (int i = 0; lumiera_plugin_extensions[i].ext; ++i) + size_t exts_sz = 4; /* / * { } \0 less one comma */ + const char*const* itr = lumiera_plugin_ext; + while (*itr) { - /* path/name.extension */ - int r = snprintf(pathname, 1024, "%s/%s.%s", tok, self->name, lumiera_plugin_extensions[i].ext); - if (r >= 1024) - return -1; /*TODO error handling, name too long*/ + exts_sz += strlen (*itr) + 1; + ++itr; + } - TRACE (lumiera_plugin, "trying %s", pathname); + exts_globs = lumiera_malloc (exts_sz); + *exts_globs = '\0'; - if (!access(pathname, R_OK)) + 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); + } + + const char* path; + unsigned i = 0; + int flags = GLOB_PERIOD|GLOB_BRACE|GLOB_TILDE_CHECK; + glob_t globs; + + while ((path = lumiera_config_wordlist_get_nth ("plugin.path", i, ":"))) + { + path = lumiera_tmpbuf_snprintf (SIZE_MAX,"%s%s", path, exts_globs); + TRACE (plugin, "globbing path '%s'", path); + int ret = glob (path, flags, NULL, &globs); + if (ret == GLOB_NOSPACE) + LUMIERA_DIE (NO_MEMORY); + + flags |= GLOB_APPEND; + ++i; + } + + LUMIERA_RECMUTEX_SECTION (plugin, &lumiera_interface_mutex) + { + for (char** itr = globs.gl_pathv; *itr; ++itr) + { + + if (!psplay_find (lumiera_pluginregistry, *itr, 100)) { - /* got it */ - TRACE (lumiera_plugin, "found %s", pathname); - self->pathname = lumiera_strndup (pathname, PATH_MAX); + TRACE (plugin, "found new plugin '%s'", *itr); - self->type = lumiera_plugin_extensions[i].type; - return 0; + LumieraPlugin plugin = callback_load (*itr); + if (plugin) + callback_register (plugin); } } } - return -1; /* plugin not found */ + + globfree (&globs); + + return !lumiera_error_peek (); } -struct lumiera_interface* -lumiera_interface_open (const char* name, const char* interface, size_t min_revision) + + + +LumieraPlugin +lumiera_plugin_load (const char* plugin) { - //REQUIRE (min_revision > sizeof(struct lumiera_interface), "try to use an empty interface eh?"); - REQUIRE (interface, "interface name must be given"); + TRACE (plugin); + UNIMPLEMENTED(); + (void) plugin; - pthread_mutex_lock (&lumiera_plugin_mutex); - - struct lumiera_plugin plugin; - struct lumiera_plugin** found; - - plugin.name = name; /* for searching */ - - found = tsearch (&plugin, &lumiera_plugin_registry, lumiera_plugin_name_cmp); - if (!found) - LUMIERA_DIE (NO_MEMORY); - - if (*found == &plugin) - { - NOTICE (lumiera_plugin, "new plugin"); - - /* now really create new item */ - *found = lumiera_malloc (sizeof (struct lumiera_plugin)); - - if (name) /* NULL is main app, no lookup needed */ - { - /*lookup for $LUMIERA_PLUGIN_PATH*/ - (*found)->name = lumiera_strndup (name, PATH_MAX); - - if (!!lumiera_plugin_lookup (*found, getenv("LUMIERA_PLUGIN_PATH")) -#ifdef LUMIERA_PLUGIN_PATH - /* else lookup for -DLUMIERA_PLUGIN_PATH */ - && !!lumiera_plugin_lookup (*found, LUMIERA_PLUGIN_PATH) -#endif - ) - { - LUMIERA_ERROR_SET (lumiera_plugin, PLUGIN_NFILE); - goto elookup; - } - } - else - { - (*found)->name = NULL; - (*found)->pathname = NULL; - } - - (*found)->use_count = 0; - - PLANNED("if .so like then dlopen; else if .c like tcc compile"); - TODO("factor dlopen and dlsym out"); - - TRACE(lumiera_plugin, "trying to open %s", (*found)->pathname); - - (*found)->handle = dlopen ((*found)->pathname, RTLD_LAZY|RTLD_LOCAL); - if (!(*found)->handle) - { - ERROR (lumiera_plugin, "dlopen failed: %s", dlerror()); - LUMIERA_ERROR_SET (lumiera_plugin, PLUGIN_DLOPEN); - goto edlopen; - } - - /* if the plugin defines a 'lumiera_plugin_init' function, we call it, must return 0 on success */ - int (*init)(void) = dlsym((*found)->handle, "lumiera_plugin_init"); - if (init && init()) - { - //ERROR (lumiera_plugin, "lumiera_plugin_init failed: %s: %s", name, interface); - LUMIERA_ERROR_SET (lumiera_plugin, PLUGIN_HOOK); - goto einit; - } - } - /* we have the plugin, now get the interface descriptor */ - struct lumiera_interface* ret; - - dlerror(); - ret = dlsym ((*found)->handle, interface); - - const char *dlerr = dlerror(); - TRACE(lumiera_plugin, "%s", dlerr); - TODO ("need some way to tell 'interface not provided by plugin', maybe lumiera_plugin_error()?"); - if (dlerr) - { - //ERROR (lumiera_plugin, "plugin %s doesnt provide interface %s", name, interface); - LUMIERA_ERROR_SET (lumiera_plugin, PLUGIN_NIFACE); - goto edlsym; - } - - /* is the interface older than required? */ - if (ret->size < min_revision) - { - ERROR (lumiera_plugin, "plugin %s provides older interface %s revision than required", name, interface); - LUMIERA_ERROR_SET (lumiera_plugin, PLUGIN_REVISION); - goto erevision; - } - - ret->plugin = *found; - - /* if the interface provides a 'open' function, call it now, must return 0 on success */ - if (ret->open && ret->open()) - { - ERROR (lumiera_plugin, "open hook indicated an error"); - LUMIERA_ERROR_SET (lumiera_plugin, PLUGIN_HOOK); - goto eopen; - } - - (*found)->use_count++; - (*found)->last = time (NULL); - ret->use_count++; - - pthread_mutex_unlock (&lumiera_plugin_mutex); - return ret; - - /* Error cleanup */ - einit: - dlclose((*found)->handle); - eopen: - erevision: - edlsym: - edlopen: - elookup: - free ((char*)(*found)->name); - free (*found); - *found = &plugin; - tdelete (&plugin, &lumiera_plugin_registry, lumiera_plugin_name_cmp); - pthread_mutex_unlock (&lumiera_plugin_mutex); return NULL; } -void -lumiera_interface_close (void* ptr) + +int +lumiera_plugin_register (LumieraPlugin plugin) { - TRACE (lumiera_plugin, "%p", ptr); - if(!ptr) - return; + TRACE (plugin); + UNIMPLEMENTED(); - struct lumiera_interface* self = (struct lumiera_interface*) ptr; - - pthread_mutex_lock (&lumiera_plugin_mutex); - - struct lumiera_plugin* plugin = self->plugin; - - plugin->use_count--; - self->use_count--; - - /* if the interface provides a 'close' function, call it now */ - if (self->close) - self->close(); - - /* plugin not longer in use, unload it */ - if (!plugin->use_count) + LUMIERA_RECMUTEX_SECTION (plugin, &lumiera_interface_mutex) { - TODO ("we dont want to close here, instead store time of recent use and make a expire run, already planned in my head"); + (void) plugin; - /* if the plugin defines a 'lumiera_plugin_destroy' function, we call it */ - int (*destroy)(void) = dlsym(plugin->handle, "lumiera_plugin_destroy"); - if (destroy) - destroy(); - - /* and now cleanup */ - tdelete (plugin, &lumiera_plugin_registry, lumiera_plugin_name_cmp); - free ((char*)plugin->name); - dlclose(plugin->handle); - free (plugin); } - pthread_mutex_unlock (&lumiera_plugin_mutex); + return 0; } + + + + + +int +lumiera_plugin_cmp_fn (const void* keya, const void* keyb) +{ + return strcmp ((const char*)keya, (const char*)keyb); +} + + +const void* +lumiera_plugin_key_fn (const PSplaynode node) +{ + return ((LumieraPlugin)node)->name; +} + diff --git a/src/backend/plugin.h b/src/backend/plugin.h index 7e17afc18..8c0e5e7ae 100644 --- a/src/backend/plugin.h +++ b/src/backend/plugin.h @@ -21,111 +21,64 @@ #ifndef LUMIERA_PLUGIN_H #define LUMIERA_PLUGIN_H -#ifdef __cplusplus -extern "C" { -#elif 0 -} /*eek, fixes emacs indenting for now*/ -#endif #include #include -#include "error.h" +#include "lib/error.h" /** * @file - * Plugin loader, header. + * Lumiera plugins defines 'interfaces' as shown in interface.h, the plugin system handles the loading + * of all kinds of plugins under the hood, invoked from the interface system. Anything defined here is + * called internally and should not be used by other parts of the application. */ -NOBUG_DECLARE_FLAG (lumiera_plugin); +LUMIERA_ERROR_DECLARE(PLUGIN_DLOPEN); +LUMIERA_ERROR_DECLARE(PLUGIN_PLUGINPATH); + + +NOBUG_DECLARE_FLAG (plugin); /* tool macros*/ -#define LUMIERA_INTERFACE_TYPE(name, version) struct lumiera_interface_##name##_##version -#define LUMIERA_INTERFACE_CAST(name, version) (LUMIERA_INTERFACE_TYPE(name, version)*) - -/* Interface definition */ -#define LUMIERA_INTERFACE(name, version, ...) \ -LUMIERA_INTERFACE_TYPE(name, version) \ -{ \ - struct lumiera_interface interface_header_; \ - __VA_ARGS__ \ -} - -#define LUMIERA_INTERFACE_PROTO(ret, name, params) ret (*name) params; - -/* Interface instantiation */ -#define LUMIERA_INTERFACE_IMPLEMENT(iname, version, name, open, close, ...) \ -LUMIERA_INTERFACE_TYPE(iname, version) name##_##version = \ -{ \ - { \ - version, \ - sizeof(LUMIERA_INTERFACE_TYPE(iname, version)), \ - NULL, \ - 0, \ - open, \ - close \ - }, \ - __VA_ARGS__ \ -} +struct lumiera_plugin_struct; +typedef struct lumiera_plugin_struct lumiera_plugin; +typedef lumiera_plugin* LumieraPlugin; -/* internally used */ -struct lumiera_plugin; -/** - * This is the header for any interface exported by plugins. - * Real interfaces append their functions at the end. There are few standard functions on each Interface - * Every function is required to be implemnted. - */ -struct lumiera_interface -{ - /// interface version number - unsigned version; - /// size of the full structure is used to determine the revision (adding a new function increments the size) - size_t size; - /// back reference to plugin - struct lumiera_plugin* plugin; - /// incremented for each user of this interface and decremented when closed - unsigned use_count; +int +lumiera_plugin_cmp_fn (const void* keya, const void* keyb); + +const void* +lumiera_plugin_key_fn (const PSplaynode node); + + +LumieraPlugin +lumiera_plugin_load (const char* plugin); + + +int +lumiera_plugin_register (LumieraPlugin); - /// called for each open of this interface - int (*open)(void); - /// called for each close of this interface - int (*close)(void); -}; -/** - * Initialize the plugin system. - * always succeeds or aborts - */ -void -lumiera_init_plugin (void); /** - * Make an interface available. - * To use an interface provided by a plugin it must be opened first. It is allowed to open an interface - * more than once. Each open must be paired with a close. - * @param name name of the plugin to use. - * @param interface name of the interface to open. - * @param min_revision the size of the interface structure is used as measure of a minimal required - * revision (new functions are appended at the end) - * @return handle to the interface or NULL in case of a error. The application shall cast this handle to - * the actual interface type. + * discover new plugins + * traverses the configured plugin dirs 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. */ -struct lumiera_interface* -lumiera_interface_open (const char* plugin, const char* name, size_t min_revision); +int +lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin), + int (*callback_register) (LumieraPlugin)); + + -/** - * Close an interface. Does not free associated resources - * Calling this function with self==NULL is legal. Every interface handle must be closed only once. - * @param ptr interface to be closed - */ -void -lumiera_interface_close (void* self); /** * Tries to unload a plugin. @@ -136,22 +89,5 @@ lumiera_interface_close (void* self); int lumiera_plugin_unload (const char* plugin); -/** - * Tries to unload plugins which are not in use. - * Calls lumiera_plugin_unload() for each Plugin which is not used for more than age seconds. - * This function might be infrequently called by the scheduler to remove things which are not needed. - * @param age timeout in seconds when to unload plugins - */ -void -lumiera_plugin_expire (time_t age); -LUMIERA_ERROR_DECLARE(PLUGIN_DLOPEN); -LUMIERA_ERROR_DECLARE(PLUGIN_HOOK); -LUMIERA_ERROR_DECLARE(PLUGIN_NFILE); -LUMIERA_ERROR_DECLARE(PLUGIN_NIFACE); -LUMIERA_ERROR_DECLARE(PLUGIN_REVISION); - -#ifdef __cplusplus -} /* extern "C" */ -#endif #endif /* LUMIERA_PLUGIN_H */ diff --git a/src/lib/error.c b/src/lib/error.c index 1d3fb50c3..6007dcbdb 100644 --- a/src/lib/error.c +++ b/src/lib/error.c @@ -60,7 +60,7 @@ lumiera_error_set (lumiera_err nerr) lumiera_err -lumiera_error () +lumiera_error (void) { pthread_once (&lumiera_error_initialized, lumiera_error_tls_init); @@ -69,3 +69,12 @@ lumiera_error () pthread_setspecific (lumiera_error_tls, NULL); return err; } + + +lumiera_err +lumiera_error_peek (void) +{ + pthread_once (&lumiera_error_initialized, lumiera_error_tls_init); + + return pthread_getspecific (lumiera_error_tls); +} diff --git a/src/lib/error.h b/src/lib/error.h index 5c4a8c145..5ad8a3142 100644 --- a/src/lib/error.h +++ b/src/lib/error.h @@ -88,7 +88,19 @@ lumiera_error_set (lumiera_err err); * @return pointer to any pending error of this thread, NULL if no error is pending */ lumiera_err -lumiera_error (); +lumiera_error (void); + + +/** + * Check current error state without clearing it + * Please avoid this function and use lumiera_error() if possible. Errors must be cleared else certain + * parts of the application refuse to cooperate with you. This shall only be used to decide if one + * wants to barf out of a loop or subroutine to deliver the error to a higher level. + * @return pointer to any pending error of this thread, NULL if no error is pending + */ +lumiera_err +lumiera_error_peek (void); + /* predefined errors diff --git a/tests/Makefile.am b/tests/Makefile.am index 66367ffb4..a88377b92 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -72,6 +72,6 @@ check_PROGRAMS += test-interfaces test_interfaces_SOURCES = $(tests_srcdir)/backend/test-interfaces.c test_interfaces_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror test_interfaces_LDADD = liblumibackend.a liblumiera.a $(NOBUGMT_LUMIERA_LIBS) -lm -test_interfaces_DEPENDENCIES = examplepluginc.la +test_interfaces_DEPENDENCIES = examplepluginc.la liblumibackend.a liblumiera.a TESTS = $(tests_srcdir)/test.sh diff --git a/tests/backend/test-config.c b/tests/backend/test-config.c index 506801454..120fc57d7 100644 --- a/tests/backend/test-config.c +++ b/tests/backend/test-config.c @@ -330,7 +330,7 @@ TEST ("wordlist_get_nth") if (!lumiera_config_wordlist_set (argv[2], &argv[3])) printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); - const char* word = lumiera_config_wordlist_get_nth (argv[2], atoi (argv[4])); + const char* word = lumiera_config_wordlist_get_nth (argv[2], atoi (argv[4]), " \t,;"); printf ("'%s'\n", word?word:"NULL"); @@ -349,7 +349,7 @@ TEST ("wordlist_find") if (!lumiera_config_wordlist_set (argv[2], &argv[3])) printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); - int n = lumiera_config_wordlist_find (argv[2], argv[4]); + int n = lumiera_config_wordlist_find (argv[2], argv[4], " \t,;"); printf ("'%d'\n", n); @@ -370,7 +370,7 @@ TEST ("wordlist_replace") if (!lumiera_config_wordlist_set (argv[2], &argv[3])) printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); - const char* wordlist = lumiera_config_wordlist_replace (argv[2], argv[4], *argv[5]?argv[5]:NULL, *argv[6]?argv[6]:NULL); + const char* wordlist = lumiera_config_wordlist_replace (argv[2], argv[4], *argv[5]?argv[5]:NULL, *argv[6]?argv[6]:NULL, " \t,;"); if (wordlist) printf ("'%s'\n", wordlist); @@ -393,13 +393,13 @@ TEST ("wordlist_add") if (!lumiera_config_wordlist_set (argv[2], &argv[3])) printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); - const char* wordlist = lumiera_config_wordlist_add (argv[2], argv[4]); + const char* wordlist = lumiera_config_wordlist_add (argv[2], argv[4], " \t,;"); if (wordlist) printf ("'%s'\n", wordlist); else printf ("%s\n", lumiera_error ()); - wordlist = lumiera_config_wordlist_add (argv[2], argv[5]); + wordlist = lumiera_config_wordlist_add (argv[2], argv[5], " \t,;"); if (wordlist) printf ("'%s'\n", wordlist); else diff --git a/tests/backend/test-interfaces.c b/tests/backend/test-interfaces.c index 8d7c377e8..110c6a855 100644 --- a/tests/backend/test-interfaces.c +++ b/tests/backend/test-interfaces.c @@ -298,7 +298,7 @@ TEST ("basic") { lumiera_interfaceregistry_init (); - lumiera_interfaceregistry_bulkregister_interfaces (interfaces_defined_here()); + lumiera_interfaceregistry_bulkregister_interfaces (interfaces_defined_here(), NULL); /* some ugly lowlevel handling tests */ @@ -321,7 +321,7 @@ TEST ("basic") TEST ("open_close") { lumiera_interfaceregistry_init (); - lumiera_interfaceregistry_bulkregister_interfaces (interfaces_defined_here()); + lumiera_interfaceregistry_bulkregister_interfaces (interfaces_defined_here(), NULL); LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_one, 0) handle = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_one, 0, 0, lumieraorg_first_test); @@ -338,7 +338,7 @@ TEST ("open_close") TEST ("dependencies_one") { lumiera_interfaceregistry_init (); - lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests(), NULL); LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_one); @@ -356,7 +356,7 @@ TEST ("dependencies_one") TEST ("dependencies_two") { lumiera_interfaceregistry_init (); - lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests(), NULL); LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_two); @@ -373,7 +373,7 @@ TEST ("dependencies_two") TEST ("dependencies_three") { lumiera_interfaceregistry_init (); - lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests(), NULL); LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_three); @@ -391,7 +391,7 @@ TEST ("dependencies_three") TEST ("dependencies_four") { lumiera_interfaceregistry_init (); - lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests(), NULL); LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_four); @@ -410,7 +410,7 @@ TEST ("dependencies_four") TEST ("dependencies_all") { lumiera_interfaceregistry_init (); - lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests(), NULL); TRACE (tests, "OPEN one"); LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle_one =