From 7be18d63446503d938108b87aff0fee5cfd6b5f1 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 31 Oct 2008 06:29:55 +0100 Subject: [PATCH 1/7] Adding explicit delimiters to the config wordlist feature This greatly simplifies the task where we want to handle colon separated paths within the config. --- src/backend/config.h | 12 ++++++--- src/backend/config_wordlist.c | 49 ++++++++++++++--------------------- 2 files changed, 27 insertions(+), 34 deletions(-) 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))) From 48778cf3d7d35bd97fcf0ba89d1cca228948ea04 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 31 Oct 2008 06:42:53 +0100 Subject: [PATCH 2/7] WIP: new plugin.h header This ditches the old 'proof of concept' implementation, interfaces and plugins are internally separate now and publically only accessed over the 'interfaces' api. The interface registry now gets an additional 'plugin' member to backreference plugins from interfaces. --- src/backend/interfaceregistry.c | 13 ++-- src/backend/interfaceregistry.h | 9 ++- src/backend/plugin.h | 134 +++++++++----------------------- 3 files changed, 52 insertions(+), 104 deletions(-) diff --git a/src/backend/interfaceregistry.c b/src/backend/interfaceregistry.c index ffa979340..cc175dd0a 100644 --- a/src/backend/interfaceregistry.c +++ b/src/backend/interfaceregistry.c @@ -53,13 +53,15 @@ 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 +76,7 @@ lumiera_interfacenode_delete (LumieraInterfacenode self) if (self) { REQUIRE (self->refcnt == 0); + FIXME ("plugin handling"); lumiera_free (self->deps); lumiera_free (self); } @@ -115,7 +118,7 @@ lumiera_interfaceregistry_destroy (void) void -lumiera_interfaceregistry_register_interface (LumieraInterface self) +lumiera_interfaceregistry_register_interface (LumieraInterface self, LumieraPlugin plugin) { TRACE (interfaceregistry); REQUIRE (self); @@ -123,13 +126,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 +142,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; } } 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.h b/src/backend/plugin.h index 7e17afc18..6696e395f 100644 --- a/src/backend/plugin.h +++ b/src/backend/plugin.h @@ -21,111 +21,68 @@ #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); + + +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; - - /// 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); - +lumiera_pluginregistry_init (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. - */ -struct lumiera_interface* -lumiera_interface_open (const char* plugin, const char* name, size_t min_revision); - - -/** - * 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 + * destroy the plugin system, free all resources */ void -lumiera_interface_close (void* self); +lumiera_pluginregistry_destroy (void); + + +LumieraPlugin +lumiera_plugin_load (const char* plugin); + + +int +lumiera_plugin_register (LumieraPlugin); + + +/** + * 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. + */ +int +lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin), + int (*callback_register) (LumieraPlugin)); + + + + /** * Tries to unload a plugin. @@ -136,22 +93,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 */ From d510ade5c3cb48426da8370fe1407150f493f74e Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 31 Oct 2008 06:43:54 +0100 Subject: [PATCH 3/7] fix wordlist tests to use explicit delimiters --- tests/backend/test-config.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 From 10e22c74f7c8cc84e8d13cf762dcab77bce0d898 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 31 Oct 2008 06:44:27 +0100 Subject: [PATCH 4/7] fix test-interfaces for new plugin parameter --- tests/backend/test-interfaces.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 = From a21a6b5d3dd803d09e7a5ff567b781e73a06154f Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 3 Nov 2008 08:00:32 +0100 Subject: [PATCH 5/7] Add dependencies to test-interfaces looks like one needs to add all dependencies manually when using _DEPENDENCIES at least works by that, maybe investigate this later --- tests/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 257b310699e3baf4bf9b491d7588d26a888bcbb7 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 3 Nov 2008 08:03:48 +0100 Subject: [PATCH 6/7] add an error_peek function, fix prototypes lumiera_error_peek() lets one investigate the error state without resetting it. --- src/lib/error.c | 11 ++++++++++- src/lib/error.h | 14 +++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) 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 From 2342c3a87ad519b0879ed0c191260dd3fce9080b Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Mon, 3 Nov 2008 08:07:22 +0100 Subject: [PATCH 7/7] New Plugin implementation * plugin{.h,.c} are back but work in progress * the interfaceregistry also manages a pluginregistry, makes no much sense to isolate it yet, both share a mutex and are commonly used together --- src/backend/Makefile.am | 2 + src/backend/interfaceregistry.c | 29 ++- src/backend/plugin.c | 374 +++++++++++--------------------- src/backend/plugin.h | 18 +- 4 files changed, 160 insertions(+), 263 deletions(-) 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/interfaceregistry.c b/src/backend/interfaceregistry.c index cc175dd0a..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,17 +40,20 @@ */ 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 @@ -92,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)); } @@ -114,6 +125,13 @@ 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; } @@ -216,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; @@ -243,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/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 6696e395f..8c0e5e7ae 100644 --- a/src/backend/plugin.h +++ b/src/backend/plugin.h @@ -36,6 +36,7 @@ LUMIERA_ERROR_DECLARE(PLUGIN_DLOPEN); +LUMIERA_ERROR_DECLARE(PLUGIN_PLUGINPATH); NOBUG_DECLARE_FLAG (plugin); @@ -48,18 +49,11 @@ typedef struct lumiera_plugin_struct lumiera_plugin; typedef lumiera_plugin* LumieraPlugin; -/** - * Initialize the plugin system. - * always succeeds or aborts - */ -void -lumiera_pluginregistry_init (void); +int +lumiera_plugin_cmp_fn (const void* keya, const void* keyb); -/** - * destroy the plugin system, free all resources - */ -void -lumiera_pluginregistry_destroy (void); +const void* +lumiera_plugin_key_fn (const PSplaynode node); LumieraPlugin @@ -70,6 +64,8 @@ int lumiera_plugin_register (LumieraPlugin); + + /** * discover new plugins * traverses the configured plugin dirs and calls the callback_load function for any plugin