From 7a49246e65d6643bf03b9d36c1c9cd0d77f76811 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Thu, 15 Mar 2018 12:28:41 +0100 Subject: [PATCH 01/46] notification: refactoring & added elektraNotificationRegisterFloat --- doc/tutorials/notifications.md | 4 +- src/include/kdbnotification.h | 21 +- src/include/kdbnotificationinternal.h | 17 +- .../example/example_notification.c | 4 +- .../example/example_notification_async.c | 4 +- src/libs/notification/notification.c | 31 ++- .../notification/tests/testlib_notification.c | 6 +- src/plugins/internalnotification/README.md | 48 +---- .../internalnotification.c | 187 +++++++++--------- .../testmod_internalnotification.c | 76 ++++++- 10 files changed, 241 insertions(+), 157 deletions(-) diff --git a/doc/tutorials/notifications.md b/doc/tutorials/notifications.md index d17fbc98a77..fa96d6ebb9c 100644 --- a/doc/tutorials/notifications.md +++ b/doc/tutorials/notifications.md @@ -194,7 +194,7 @@ changed key needs further processing. #define ANSI_COLOR_RED "\x1b[31m" #define ANSI_COLOR_GREEN "\x1b[32m" -void setTerminalColor (Key * color) +void setTerminalColor (Key * color, void * context) { char * value = keyString (color); @@ -227,7 +227,7 @@ int main (void) } // Re-Initialize on key changes - int result = elektraNotificationRegisterCallback(repo, color, &setTerminalColor); + int result = elektraNotificationRegisterCallback(repo, color, &setTerminalColor, NULL); if (!result) { printf ("could not register callback!\n"); return -1; diff --git a/src/include/kdbnotification.h b/src/include/kdbnotification.h index 2fd51f348bb..6f92e3970ac 100644 --- a/src/include/kdbnotification.h +++ b/src/include/kdbnotification.h @@ -114,12 +114,26 @@ int elektraNotificationClose (KDB * kdb); */ int elektraNotificationRegisterInt (KDB * kdb, Key * key, int * variable); +/** + * Subscribe for automatic updates to a given float variable when the given + * key value is changed. + * + * @param handle plugin handle + * @param key key to watch for changes + * @param variable float variable + * + * @retval 1 on success + * @retval 0 on failure + */ +int elektraNotificationRegisterFloat (KDB * kdb, Key * key, float * variable); + /** * Callback function for key changes. * - * @param key changed key + * @param key changed key + * @param Context user supplied callback context */ -typedef void (*ElektraNotificationChangeCallback) (Key * key); +typedef void (*ElektraNotificationChangeCallback) (Key * key, void * context); /** * Subscribe for updates via callback when a given key value is changed. @@ -127,11 +141,12 @@ typedef void (*ElektraNotificationChangeCallback) (Key * key); * @param handle plugin handle * @param key key to watch for changes * @param callback callback function + * @param context user supplied context passed to callback function * * @retval 1 on success * @retval 0 on failure */ -int elektraNotificationRegisterCallback (KDB * kdb, Key * key, ElektraNotificationChangeCallback callback); +int elektraNotificationRegisterCallback (KDB * kdb, Key * key, ElektraNotificationChangeCallback callback, void * context); #ifdef __cplusplus diff --git a/src/include/kdbnotificationinternal.h b/src/include/kdbnotificationinternal.h index a14ac654bdb..a795fab2355 100644 --- a/src/include/kdbnotificationinternal.h +++ b/src/include/kdbnotificationinternal.h @@ -36,17 +36,32 @@ extern "C" { */ typedef int (*ElektraNotificationPluginRegisterInt) (Plugin * handle, Key * key, int * variable); +/** + * Subscribe for automatic updates to a given float variable when the given + * key value is changed. + * + * @param handle plugin handle + * @param key key to watch for changes + * @param variable float variable + * + * @retval 1 on success + * @retval 0 on failure + */ +typedef int (*ElektraNotificationPluginRegisterFloat) (Plugin * handle, Key * key, float * variable); + /** * Subscribe for updates via callback when a given key value is changed. * * @param handle plugin handle * @param key key to watch for changes * @param callback callback function + * @param context user supplied context passed to callback function * * @retval 1 on success * @retval 0 on failure */ -typedef int (*ElektraNotificationPluginRegisterCallback) (Plugin * handle, Key * key, ElektraNotificationChangeCallback callback); +typedef int (*ElektraNotificationPluginRegisterCallback) (Plugin * handle, Key * key, ElektraNotificationChangeCallback callback, + void * context); /** * Context for notification callbacks. diff --git a/src/libs/notification/example/example_notification.c b/src/libs/notification/example/example_notification.c index c00debaa04a..55ded07dbe8 100644 --- a/src/libs/notification/example/example_notification.c +++ b/src/libs/notification/example/example_notification.c @@ -23,7 +23,7 @@ static volatile int keepRunning = 0; #define ANSI_COLOR_GREEN "\x1b[32m" #define ANSI_COLOR_BLUE "\x1b[34m" -static void setTerminalColor (Key * color) +static void setTerminalColor (Key * color, void * context) { const char * value = keyString (color); printf ("Callback called. Changing color to %s\n", value); @@ -107,7 +107,7 @@ int main (void) } Key * callbackKeyToWatch = keyNew ("/sw/tests/example_notification/#0/current/color", KEY_END); - result = elektraNotificationRegisterCallback (kdb, callbackKeyToWatch, &setTerminalColor); + result = elektraNotificationRegisterCallback (kdb, callbackKeyToWatch, &setTerminalColor, NULL); if (!result) { printf ("could not register callback. aborting!"); diff --git a/src/libs/notification/example/example_notification_async.c b/src/libs/notification/example/example_notification_async.c index 256d39d5f0e..533e6ff8884 100644 --- a/src/libs/notification/example/example_notification_async.c +++ b/src/libs/notification/example/example_notification_async.c @@ -24,7 +24,7 @@ #define ANSI_COLOR_GREEN "\x1b[32m" #define ANSI_COLOR_BLUE "\x1b[34m" -static void setTerminalColor (Key * color) +static void setTerminalColor (Key * color, void * context ELEKTRA_UNUSED) { const char * value = keyString (color); printf ("Callback called. Changing color to %s\n", value); @@ -104,7 +104,7 @@ int main (void) } Key * callbackKeyToWatch = keyNew ("/sw/tests/example_notification/#0/current/color", KEY_END); - result = elektraNotificationRegisterCallback (kdb, callbackKeyToWatch, &setTerminalColor); + result = elektraNotificationRegisterCallback (kdb, callbackKeyToWatch, &setTerminalColor, NULL); if (!result) { printf ("could not register callback. aborting!"); diff --git a/src/libs/notification/notification.c b/src/libs/notification/notification.c index 8357b1c43f1..ca1c94c7079 100644 --- a/src/libs/notification/notification.c +++ b/src/libs/notification/notification.c @@ -877,7 +877,34 @@ int elektraNotificationRegisterInt (KDB * kdb, Key * key, int * variable) return registerFunc (notificationPlugin, key, variable); } -int elektraNotificationRegisterCallback (KDB * kdb, Key * key, ElektraNotificationChangeCallback callback) +int elektraNotificationRegisterFloat (KDB * kdb, Key * key, float * variable) +{ + if (!kdb || !key || !variable) + { + ELEKTRA_LOG_WARNING ("null pointer passed"); + return 0; + } + + // Find notification plugin + Plugin * notificationPlugin = getNotificationPlugin (kdb); + if (!notificationPlugin) + { + return 0; + } + + // Get register function from plugin + size_t func = elektraPluginGetFunction (notificationPlugin, "registerFloat"); + if (!func) + { + return 0; + } + + // Call register function + ElektraNotificationPluginRegisterFloat registerFunc = (ElektraNotificationPluginRegisterFloat) func; + return registerFunc (notificationPlugin, key, variable); +} + +int elektraNotificationRegisterCallback (KDB * kdb, Key * key, ElektraNotificationChangeCallback callback, void * context) { if (!kdb || !key || !callback) { @@ -901,5 +928,5 @@ int elektraNotificationRegisterCallback (KDB * kdb, Key * key, ElektraNotificati // Call register function ElektraNotificationPluginRegisterCallback registerFunc = (ElektraNotificationPluginRegisterCallback) func; - return registerFunc (notificationPlugin, key, callback); + return registerFunc (notificationPlugin, key, callback, context); } diff --git a/src/libs/notification/tests/testlib_notification.c b/src/libs/notification/tests/testlib_notification.c index 5847a6b2312..39889b1b94e 100644 --- a/src/libs/notification/tests/testlib_notification.c +++ b/src/libs/notification/tests/testlib_notification.c @@ -71,7 +71,7 @@ static void test_registerInt (void) keyDel (valueKey); } -static void testCallback (Key * key ELEKTRA_UNUSED) +static void testCallback (Key * key ELEKTRA_UNUSED, void * context ELEKTRA_UNUSED) { callback_called = 1; } @@ -86,11 +86,11 @@ static void test_registerCallback (void) KDB * kdb = kdbOpen (key); - succeed_if (elektraNotificationRegisterCallback (kdb, valueKey, testCallback) == 0, "register should fail before open"); + succeed_if (elektraNotificationRegisterCallback (kdb, valueKey, testCallback, NULL) == 0, "register should fail before open"); elektraNotificationOpen (kdb); - succeed_if (elektraNotificationRegisterCallback (kdb, valueKey, testCallback), "register failed"); + succeed_if (elektraNotificationRegisterCallback (kdb, valueKey, testCallback, NULL), "register failed"); // call kdbGet; value gets automatically updated KeySet * config = ksNew (0, KS_END); diff --git a/src/plugins/internalnotification/README.md b/src/plugins/internalnotification/README.md index 7b4c1924e2f..ddf432642d7 100644 --- a/src/plugins/internalnotification/README.md +++ b/src/plugins/internalnotification/README.md @@ -19,47 +19,9 @@ Application developers should use the instead of the functions exported by this plugin. The API is easier to use and decouples applications from this plugin. -## Exported Methods +## Exported Functions -This plugin exports the following functions. The functions addresses are -exported below `system/elektra/modules/internalnotification/exports/`. - -All functions have a similar signature: - -```C -int registerX (Plugin * handle, Key * key, ...); -``` - -If the given `key` is contained in a KeySet on a kdbGet or kdbSet operation a -action according to the function's description is executed. -Cascading keys as `key` names are also supported. - -*Parameters* - -- *handle* The internal plugin `handle` is exported as - `system/elektra/modules/internalnotification/exports/handle`. -- *key* Key to watch for changes. - -*Returns* - -1 on success, 0 otherwise - -Please note that the plugin API may change as this plugin is experimental. - -### int registerInt (Plugin * handle, Key * key, int * variable) - -The key's value is converted to integer and the registered variable is updated -with the new value. - -*Additional Parameters* - -- *variable* Pointer to the variable - -### int registerCallback (Plugin * handle, Key * key, ElektraNotificationChangeCallback callback) - -When the key changes the callback is called with the new key. - -*Additional Parameters* - -- *callback* Callback function with the signature - `void (*ElektraNotificationChangeCallback) (Key * key)`. +This plugin exports various functions starting with "register*" below +`system/elektra/modules/internalnotification/exports/`. +These functions should not be used directly. +Instead the notification API should be used. diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index e9a4349431d..e9e7be54415 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -18,10 +18,23 @@ #include #include -typedef enum { - TYPE_INT = 1 << 0, - TYPE_CALLBACK = 1 << 1, -} KeyRegistrationType; +/* + checklist for types + [ ] TYPE_BOOLEAN = 1 << 0, + [ ] TYPE_CHAR = 1 << 1, + [ ] TYPE_OCTET = 1 << 2, + [ ] TYPE_SHORT = 1 << 3, + [ ] TYPE_UNSIGNED_SHORT = 1 << 4, + [x] TYPE_INT = 1 << 5, + [ ] TYPE_LONG = 1 << 6, + [ ] TYPE_UNSIGNED_LONG = 1 << 7, + [ ] TYPE_LONG_LONG = 1 << 8, + [ ] TYPE_UNSIGNED_LONG_LONG = 1 << 9, + [x] TYPE_FLOAT = 1 << 10, + [ ] TYPE_DOUBLE = 1 << 11, + [ ] TYPE_LONG_DOUBLE = 1 << 12, + [x] TYPE_CALLBACK = 1 << 13, +*/ /** * Structure for registered key variable pairs @@ -30,13 +43,9 @@ typedef enum { struct _KeyRegistration { char * name; - KeyRegistrationType type; char * lastValue; - union - { - int * variable; - ElektraNotificationChangeCallback callback; - } ref; + ElektraNotificationChangeCallback callback; + void * context; struct _KeyRegistration * next; }; typedef struct _KeyRegistration KeyRegistration; @@ -153,6 +162,46 @@ static KeyRegistration * elektraInternalnotificationAddNewRegistration (PluginSt return item; } +static void updateRegistrationInt (Key * key, void * context) +{ + int * variable = (int *) context; + + // Convert string value to long + char * end; + errno = 0; + long int value = strtol (keyString (key), &end, 10); + // Update variable if conversion was successful and did not exceed integer range + if (*end == 0 && errno == 0 && value <= INT_MAX && value >= INT_MIN) + { + *(variable) = value; + } + else + { + ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%ld", keyString (key), *end, errno, + value); + } +} + +static void updateRegistrationFloat (Key * key, void * context) +{ + float * variable = (float *) context; + + // Convert string value to long + char * end; + errno = 0; + float value = strtof (keyString (key), &end); + // Update variable if conversion was successful + if (*end == 0 && errno == 0) + { + *(variable) = value; + } + else + { + ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%ld", keyString (key), *end, errno, + value); + } +} + /** * Updates all KeyRegistrations according to data from the given KeySet * @internal @@ -207,35 +256,12 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * if (changed) { - // Perform actions depending on type - switch (registeredKey->type) - { - case TYPE_INT: - ELEKTRA_LOG_DEBUG ("found registeredKey=%s; updating variable=%p with string value \"%s\"", - registeredKey->name, (void *) registeredKey->ref.variable, keyString (key)); - - // Convert string value to long - char * end; - errno = 0; - long int value = strtol (keyString (key), &end, 10); - // Update variable if conversion was successful and did not exceed integer range - if (*end == 0 && errno == 0 && value <= INT_MAX && value >= INT_MIN) - { - *(registeredKey->ref.variable) = value; - } - else - { - ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%ld", - keyString (key), *end, errno, value); - } - break; - case TYPE_CALLBACK: - ELEKTRA_LOG_DEBUG ("found registeredKey=%s; invoking callback", registeredKey->name); - ElektraNotificationChangeCallback callback = - *(ElektraNotificationChangeCallback) registeredKey->ref.callback; - callback (key); - break; - } + ELEKTRA_LOG_DEBUG ("found changed registeredKey=%s with string value \"%s\". using context or variable=%p", + registeredKey->name, registeredKey->context, keyString (key)); + + // Invoke callback + ElektraNotificationChangeCallback callback = *(ElektraNotificationChangeCallback) registeredKey->callback; + callback (key, registeredKey->context); } registeredKey = registeredKey->next; @@ -244,18 +270,7 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * /** - * Subscribe for automatic updates to a given integer variable when the given - * key value has changed. - * - * Implementation of ElektraNotificationPluginRegisterInt() - * @see kdbnotificationinternal.h - * - * @param handle plugin handle - * @param variable integer variable - * @param key key to watch for changes - * - * @retval 1 on success - * @retval 0 on failure + * @see kdbnotificationinternal.h ::elektraNotificationRegisterInt */ int elektraInternalnotificationRegisterInt (Plugin * handle, Key * key, int * variable) { @@ -267,43 +282,18 @@ int elektraInternalnotificationRegisterInt (Plugin * handle, Key * key, int * va { return 0; } - - // Copy key name - size_t nameBufferSize = keyGetNameSize (key); - char * nameBuffer = elektraMalloc (nameBufferSize); - if (nameBuffer == NULL) - { - return 0; - } - ssize_t result = keyGetName (key, nameBuffer, nameBufferSize); - if (result == 1 || result == -1) - { - return 0; - } - // Save key registration - registeredKey->name = nameBuffer; - registeredKey->type = TYPE_INT; - registeredKey->ref.variable = variable; + registeredKey->name = elektraStrDup (keyName (key)); + registeredKey->callback = updateRegistrationInt; + registeredKey->context = variable; return 1; } /** - * Subscribe for updates via callback when a given key value is changed. - * key value has changed. - * - * Implementation of ElektraNotificationPluginRegisterCallback() - * @see kdbnotificationinternal.h - * - * @param handle plugin handle - * @param key key to watch for changes - * @param callback callback function - * - * @retval 1 on success - * @retval 0 on failure + * @see kdbnotificationinternal.h ::ElektraNotificationPluginRegisterFloat */ -int elektraInternalnotificationRegisterCallback (Plugin * handle, Key * key, ElektraNotificationChangeCallback callback) +int elektraInternalnotificationRegisterFloat (Plugin * handle, Key * key, float * variable) { PluginState * pluginState = elektraPluginGetData (handle); ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); @@ -313,24 +303,31 @@ int elektraInternalnotificationRegisterCallback (Plugin * handle, Key * key, Ele { return 0; } + // Save key registration + registeredKey->name = elektraStrDup (keyName (key)); + registeredKey->callback = updateRegistrationFloat; + registeredKey->context = variable; - // Copy key name - size_t nameBufferSize = keyGetNameSize (key); - char * nameBuffer = elektraMalloc (nameBufferSize); - if (nameBuffer == NULL) - { - return 0; - } - ssize_t result = keyGetName (key, nameBuffer, nameBufferSize); - if (result == 1 || result == -1) + return 1; +} + +/** + * @see kdbnotificationinternal.h ::ElektraNotificationPluginRegisterCallback + */ +int elektraInternalnotificationRegisterCallback (Plugin * handle, Key * key, ElektraNotificationChangeCallback callback, void * context) +{ + PluginState * pluginState = elektraPluginGetData (handle); + ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); + + KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration (pluginState); + if (registeredKey == NULL) { return 0; } - // Save key registration - registeredKey->name = nameBuffer; - registeredKey->type = TYPE_CALLBACK; - registeredKey->ref.callback = callback; + registeredKey->name = elektraStrDup (keyName (key)); + registeredKey->callback = callback; + registeredKey->context = context; return 1; } @@ -370,6 +367,8 @@ int elektraInternalnotificationGet (Plugin * handle, KeySet * returned, Key * pa // Export register* functions keyNew ("system/elektra/modules/internalnotification/exports/registerInt", KEY_FUNC, elektraInternalnotificationRegisterInt, KEY_END), + keyNew ("system/elektra/modules/internalnotification/exports/registerFloat", KEY_FUNC, + elektraInternalnotificationRegisterFloat, KEY_END), keyNew ("system/elektra/modules/internalnotification/exports/registerCallback", KEY_FUNC, elektraInternalnotificationRegisterCallback, KEY_END), diff --git a/src/plugins/internalnotification/testmod_internalnotification.c b/src/plugins/internalnotification/testmod_internalnotification.c index a8c4062b4ed..75878e3f231 100644 --- a/src/plugins/internalnotification/testmod_internalnotification.c +++ b/src/plugins/internalnotification/testmod_internalnotification.c @@ -23,6 +23,8 @@ int callback_called; char * callback_keyValue; char * callback_keyName; +#define CALLBACK_CONTEXT_MAGIC_NUMBER ((void *) 1234) + static int internalnotificationRegisterInt (Plugin * plugin, Key * key, int * variable) { size_t address = elektraPluginGetFunction (plugin, "registerInt"); @@ -31,12 +33,20 @@ static int internalnotificationRegisterInt (Plugin * plugin, Key * key, int * va return ((ElektraNotificationPluginRegisterInt) address) (plugin, key, variable); } -static int internalnotificationRegisterCallback (Plugin * plugin, Key * key, ElektraNotificationChangeCallback callback) +static int internalnotificationRegisterFloat (Plugin * plugin, Key * key, float * variable) +{ + size_t address = elektraPluginGetFunction (plugin, "registerFloat"); + + // Register key with plugin + return ((ElektraNotificationPluginRegisterFloat) address) (plugin, key, variable); +} + +static int internalnotificationRegisterCallback (Plugin * plugin, Key * key, ElektraNotificationChangeCallback callback, void * context) { size_t address = elektraPluginGetFunction (plugin, "registerCallback"); // Register key with plugin - return ((ElektraNotificationPluginRegisterCallback) address) (plugin, key, callback); + return ((ElektraNotificationPluginRegisterCallback) address) (plugin, key, callback, context); } static int digits (long long number) @@ -302,8 +312,60 @@ static void test_intNoUpdateWithValueExceedingIntMin (void) PLUGIN_CLOSE (); } -static void test_callback (Key * key) + +static void test_floatUpdateWithCascadingKey (void) +{ + printf ("test update with cascading key registered\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * registeredKey = keyNew ("/test/internalnotification/value", KEY_END); + float value = 0; + succeed_if (internalnotificationRegisterFloat (plugin, registeredKey, &value) == 1, + "call to elektraInternalnotificationRegisterFloat was not successful"); + + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, "2.3", KEY_END); + KeySet * ks = ksNew (1, valueKey, KS_END); + + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); + + succeed_if (value >= 2.295 && value <= 2.305, "registered value was not updated"); + + keyDel (registeredKey); + ksDel (ks); + PLUGIN_CLOSE (); +} + +static void test_floatNoUpdateWithInvalidValue (void) { + printf ("test no update with invalid value\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); + KeySet * ks = ksNew (1, valueKey, KS_END); + + float value = 0.0; + succeed_if (internalnotificationRegisterFloat (plugin, valueKey, &value) == 1, + "call to elektraInternalnotificationRegisterFloat was not successful"); + + keySetString (valueKey, "4.a"); + + + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); + + succeed_if ((int) value == 0, "registered value was updated"); + + ksDel (ks); + PLUGIN_CLOSE (); +} + + +static void test_callback (Key * key, void * context) +{ + succeed_if (context == CALLBACK_CONTEXT_MAGIC_NUMBER, "callback context was not passed"); callback_called = 1; callback_keyValue = (char *) keyValue (key); callback_keyName = (char *) keyName (key); @@ -320,7 +382,7 @@ static void test_callbackCalledWithKey (void) Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, value, KEY_END); KeySet * ks = ksNew (1, valueKey, KS_END); - succeed_if (internalnotificationRegisterCallback (plugin, valueKey, test_callback) == 1, + succeed_if (internalnotificationRegisterCallback (plugin, valueKey, test_callback, CALLBACK_CONTEXT_MAGIC_NUMBER) == 1, "call to elektraInternalnotificationRegisterCallback was not successful"); elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); @@ -344,7 +406,7 @@ static void test_callbackCalledWithChangeDetection (void) Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, value, KEY_END); KeySet * ks = ksNew (1, valueKey, KS_END); - succeed_if (internalnotificationRegisterCallback (plugin, valueKey, test_callback) == 1, + succeed_if (internalnotificationRegisterCallback (plugin, valueKey, test_callback, CALLBACK_CONTEXT_MAGIC_NUMBER) == 1, "call to elektraInternalnotificationRegisterCallback was not successful"); elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); @@ -378,6 +440,10 @@ int main (int argc, char ** argv) test_intUpdateWithValueNotYetExceedingIntMin (); test_intNoUpdateWithValueExceedingIntMin (); + printf ("\nregisterFloat\n-----------\n"); + test_floatUpdateWithCascadingKey (); + test_floatNoUpdateWithInvalidValue (); + printf ("\nregisterCallback\n----------------\n"); test_callbackCalledWithKey (); test_callbackCalledWithChangeDetection (); From f2d079bf948396fa457a783b29437595a6873d69 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Fri, 16 Mar 2018 15:27:51 +0100 Subject: [PATCH 02/46] notification: added register functions for long, unsigned long & double --- src/include/kdbnotification.h | 39 ++++ src/include/kdbnotificationinternal.h | 39 ++++ .../example/example_notification.c | 3 +- src/libs/notification/notification.c | 81 ++++++++ .../internalnotification/CMakeLists.txt | 12 +- src/plugins/internalnotification/convert.c | 114 +++++++++++ .../internalnotification.c | 140 +++++++------ .../internalnotification.h | 7 + .../testmod_internalnotification.c | 188 ++++++++++++++++++ 9 files changed, 554 insertions(+), 69 deletions(-) create mode 100644 src/plugins/internalnotification/convert.c diff --git a/src/include/kdbnotification.h b/src/include/kdbnotification.h index 6f92e3970ac..16bfac68b48 100644 --- a/src/include/kdbnotification.h +++ b/src/include/kdbnotification.h @@ -114,6 +114,32 @@ int elektraNotificationClose (KDB * kdb); */ int elektraNotificationRegisterInt (KDB * kdb, Key * key, int * variable); +/** + * Subscribe for automatic updates to a given long variable when the given + * key value is changed. + * + * @param handle plugin handle + * @param key key to watch for changes + * @param variable long variable + * + * @retval 1 on success + * @retval 0 on failure + */ +int elektraNotificationRegisterLong (KDB * kdb, Key * key, long * variable); + +/** + * Subscribe for automatic updates to a given unsigned long variable when the given + * key value is changed. + * + * @param handle plugin handle + * @param key key to watch for changes + * @param variable unsigned long variable + * + * @retval 1 on success + * @retval 0 on failure + */ +int elektraNotificationRegisterUnsignedLong (KDB * kdb, Key * key, unsigned long * variable); + /** * Subscribe for automatic updates to a given float variable when the given * key value is changed. @@ -127,6 +153,19 @@ int elektraNotificationRegisterInt (KDB * kdb, Key * key, int * variable); */ int elektraNotificationRegisterFloat (KDB * kdb, Key * key, float * variable); +/** + * Subscribe for automatic updates to a given double variable when the given + * key value is changed. + * + * @param handle plugin handle + * @param key key to watch for changes + * @param variable double variable + * + * @retval 1 on success + * @retval 0 on failure + */ +int elektraNotificationRegisterDouble (KDB * kdb, Key * key, double * variable); + /** * Callback function for key changes. * diff --git a/src/include/kdbnotificationinternal.h b/src/include/kdbnotificationinternal.h index a795fab2355..eee307962ac 100644 --- a/src/include/kdbnotificationinternal.h +++ b/src/include/kdbnotificationinternal.h @@ -36,6 +36,32 @@ extern "C" { */ typedef int (*ElektraNotificationPluginRegisterInt) (Plugin * handle, Key * key, int * variable); +/** + * Subscribe for automatic updates to a given long variable when the given + * key value is changed. + * + * @param handle plugin handle + * @param key key to watch for changes + * @param variable long variable + * + * @retval 1 on success + * @retval 0 on failure + */ +typedef int (*ElektraNotificationPluginRegisterLong) (Plugin * handle, Key * key, long * variable); + +/** + * Subscribe for automatic updates to a given unsigned long variable when the given + * key value is changed. + * + * @param handle plugin handle + * @param key key to watch for changes + * @param variable unsigned long variable + * + * @retval 1 on success + * @retval 0 on failure + */ +typedef int (*ElektraNotificationPluginRegisterUnsignedLong) (Plugin * handle, Key * key, unsigned long * variable); + /** * Subscribe for automatic updates to a given float variable when the given * key value is changed. @@ -49,6 +75,19 @@ typedef int (*ElektraNotificationPluginRegisterInt) (Plugin * handle, Key * key, */ typedef int (*ElektraNotificationPluginRegisterFloat) (Plugin * handle, Key * key, float * variable); +/** + * Subscribe for automatic updates to a given double variable when the given + * key value is changed. + * + * @param handle plugin handle + * @param key key to watch for changes + * @param variable double variable + * + * @retval 1 on success + * @retval 0 on failure + */ +typedef int (*ElektraNotificationPluginRegisterDouble) (Plugin * handle, Key * key, double * variable); + /** * Subscribe for updates via callback when a given key value is changed. * diff --git a/src/libs/notification/example/example_notification.c b/src/libs/notification/example/example_notification.c index 55ded07dbe8..5d7d2d48846 100644 --- a/src/libs/notification/example/example_notification.c +++ b/src/libs/notification/example/example_notification.c @@ -8,6 +8,7 @@ */ #include +#include // ELEKTRA_UNUSED #include #include @@ -23,7 +24,7 @@ static volatile int keepRunning = 0; #define ANSI_COLOR_GREEN "\x1b[32m" #define ANSI_COLOR_BLUE "\x1b[34m" -static void setTerminalColor (Key * color, void * context) +static void setTerminalColor (Key * color, void * context ELEKTRA_UNUSED) { const char * value = keyString (color); printf ("Callback called. Changing color to %s\n", value); diff --git a/src/libs/notification/notification.c b/src/libs/notification/notification.c index ca1c94c7079..acac335b51a 100644 --- a/src/libs/notification/notification.c +++ b/src/libs/notification/notification.c @@ -877,6 +877,60 @@ int elektraNotificationRegisterInt (KDB * kdb, Key * key, int * variable) return registerFunc (notificationPlugin, key, variable); } +int elektraNotificationRegisterLong (KDB * kdb, Key * key, long * variable) +{ + if (!kdb || !key || !variable) + { + ELEKTRA_LOG_WARNING ("null pointer passed"); + return 0; + } + + // Find notification plugin + Plugin * notificationPlugin = getNotificationPlugin (kdb); + if (!notificationPlugin) + { + return 0; + } + + // Get register function from plugin + size_t func = elektraPluginGetFunction (notificationPlugin, "registerLong"); + if (!func) + { + return 0; + } + + // Call register function + ElektraNotificationPluginRegisterLong registerFunc = (ElektraNotificationPluginRegisterLong)func; + return registerFunc (notificationPlugin, key, variable); +} + +int elektraNotificationRegisterUnsignedLong (KDB * kdb, Key * key, unsigned long * variable) +{ + if (!kdb || !key || !variable) + { + ELEKTRA_LOG_WARNING ("null pointer passed"); + return 0; + } + + // Find notification plugin + Plugin * notificationPlugin = getNotificationPlugin (kdb); + if (!notificationPlugin) + { + return 0; + } + + // Get register function from plugin + size_t func = elektraPluginGetFunction (notificationPlugin, "registerUnsignedLong"); + if (!func) + { + return 0; + } + + // Call register function + ElektraNotificationPluginRegisterUnsignedLong registerFunc = (ElektraNotificationPluginRegisterUnsignedLong)func; + return registerFunc (notificationPlugin, key, variable); +} + int elektraNotificationRegisterFloat (KDB * kdb, Key * key, float * variable) { if (!kdb || !key || !variable) @@ -904,6 +958,33 @@ int elektraNotificationRegisterFloat (KDB * kdb, Key * key, float * variable) return registerFunc (notificationPlugin, key, variable); } +int elektraNotificationRegisterDouble (KDB * kdb, Key * key, double * variable) +{ + if (!kdb || !key || !variable) + { + ELEKTRA_LOG_WARNING ("null pointer passed"); + return 0; + } + + // Find notification plugin + Plugin * notificationPlugin = getNotificationPlugin (kdb); + if (!notificationPlugin) + { + return 0; + } + + // Get register function from plugin + size_t func = elektraPluginGetFunction (notificationPlugin, "registerDouble"); + if (!func) + { + return 0; + } + + // Call register function + ElektraNotificationPluginRegisterDouble registerFunc = (ElektraNotificationPluginRegisterDouble)func; + return registerFunc (notificationPlugin, key, variable); +} + int elektraNotificationRegisterCallback (KDB * kdb, Key * key, ElektraNotificationChangeCallback callback, void * context) { if (!kdb || !key || !callback) diff --git a/src/plugins/internalnotification/CMakeLists.txt b/src/plugins/internalnotification/CMakeLists.txt index ef7c56e245d..2878feec8a6 100644 --- a/src/plugins/internalnotification/CMakeLists.txt +++ b/src/plugins/internalnotification/CMakeLists.txt @@ -1,7 +1,11 @@ include (LibAddMacros) add_plugin (internalnotification - SOURCES internalnotification.h - internalnotification.c - ADD_TEST - LINK_ELEKTRA elektra-kdb) + SOURCES + internalnotification.h + internalnotification.c + convert.c + ADD_TEST + LINK_ELEKTRA + elektra-kdb + ) diff --git a/src/plugins/internalnotification/convert.c b/src/plugins/internalnotification/convert.c new file mode 100644 index 00000000000..42292c1ef20 --- /dev/null +++ b/src/plugins/internalnotification/convert.c @@ -0,0 +1,114 @@ +/** + * @file + * + * @brief Source for internalnotification plugin + * + * @copyright BSD License (see doc/COPYING or http://www.libelektra.org) + * + */ + +#include "internalnotification.h" + +#include // errno +#include // ELEKTRA_LOG macros +#include // strto* functions + +void elektraInternalnotificationConvertInt (Key * key, void * context) +{ + int * variable = (int *)context; + + // Convert string value to long + char * end; + errno = 0; + long int value = strtol (keyString (key), &end, 10); + // Update variable if conversion was successful and did not exceed integer range + if (*end == 0 && errno == 0 && value <= INT_MAX && value >= INT_MIN) + { + *(variable) = value; + } + else + { + ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%ld", keyString (key), *end, errno, + value); + } +} + +void elektraInternalnotificationConvertLong (Key * key, void * context) +{ + long * variable = (long *)context; + + // Convert string value to long + char * end; + errno = 0; + long value = strtol (keyString (key), &end, 10); + // Update variable if conversion was successful and did not exceed integer range + if (*end == 0 && errno == 0) + { + *(variable) = value; + } + else + { + ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%ld", keyString (key), *end, errno, + value); + } +} + +void elektraInternalnotificationConvertUnsignedLong (Key * key, void * context) +{ + unsigned long * variable = (unsigned long *)context; + + // Convert string value to unsigned long + char * end; + errno = 0; + unsigned long value = strtoul (keyString (key), &end, 10); + // Update variable if conversion was successful and did not exceed integer range + if (*end == 0 && errno == 0) + { + *(variable) = value; + } + else + { + ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%ld", keyString (key), *end, errno, + value); + } +} + +void elektraInternalnotificationConvertFloat (Key * key, void * context) +{ + float * variable = (float *)context; + + // Convert string value to long + char * end; + errno = 0; + float value = strtof (keyString (key), &end); + // Update variable if conversion was successful + if (*end == 0 && errno == 0) + { + *(variable) = value; + } + else + { + ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%f", keyString (key), *end, errno, + value); + } +} + +void elektraInternalnotificationConvertDouble (Key * key, void * context) +{ + double * variable = (double *)context; + + // Convert string value to long + char * end; + errno = 0; + double value = strtod (keyString (key), &end); + // Update variable if conversion was successful + if (*end == 0 && errno == 0) + { + *(variable) = value; + } + else + { + ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%f", keyString (key), *end, errno, + value); + } +} diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index e9e7be54415..b3829b6134e 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -9,9 +9,6 @@ #include "internalnotification.h" -#include -#include - #include #include #include @@ -26,12 +23,12 @@ [ ] TYPE_SHORT = 1 << 3, [ ] TYPE_UNSIGNED_SHORT = 1 << 4, [x] TYPE_INT = 1 << 5, - [ ] TYPE_LONG = 1 << 6, - [ ] TYPE_UNSIGNED_LONG = 1 << 7, + [x] TYPE_LONG = 1 << 6, + [x] TYPE_UNSIGNED_LONG = 1 << 7, [ ] TYPE_LONG_LONG = 1 << 8, [ ] TYPE_UNSIGNED_LONG_LONG = 1 << 9, [x] TYPE_FLOAT = 1 << 10, - [ ] TYPE_DOUBLE = 1 << 11, + [x] TYPE_DOUBLE = 1 << 11, [ ] TYPE_LONG_DOUBLE = 1 << 12, [x] TYPE_CALLBACK = 1 << 13, */ @@ -138,14 +135,19 @@ static void elektraInternalnotificationDoUpdate (Key * changedKey, ElektraNotifi * * @return pointer to created KeyRegistration structure or NULL if memory allocation failed */ -static KeyRegistration * elektraInternalnotificationAddNewRegistration (PluginState * pluginState) +static KeyRegistration * elektraInternalnotificationAddNewRegistration (PluginState * pluginState, Key * key, + ElektraNotificationChangeCallback callback, void * context) { - KeyRegistration * item = elektraCalloc (sizeof *item); + KeyRegistration * item = elektraMalloc (sizeof *item); if (item == NULL) { return NULL; } item->next = NULL; + item->lastValue = NULL; + item->name = elektraStrDup (keyName (key)); + item->callback = callback; + item->context = context; if (pluginState->head == NULL) { @@ -162,46 +164,6 @@ static KeyRegistration * elektraInternalnotificationAddNewRegistration (PluginSt return item; } -static void updateRegistrationInt (Key * key, void * context) -{ - int * variable = (int *) context; - - // Convert string value to long - char * end; - errno = 0; - long int value = strtol (keyString (key), &end, 10); - // Update variable if conversion was successful and did not exceed integer range - if (*end == 0 && errno == 0 && value <= INT_MAX && value >= INT_MIN) - { - *(variable) = value; - } - else - { - ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%ld", keyString (key), *end, errno, - value); - } -} - -static void updateRegistrationFloat (Key * key, void * context) -{ - float * variable = (float *) context; - - // Convert string value to long - char * end; - errno = 0; - float value = strtof (keyString (key), &end); - // Update variable if conversion was successful - if (*end == 0 && errno == 0) - { - *(variable) = value; - } - else - { - ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%ld", keyString (key), *end, errno, - value); - } -} - /** * Updates all KeyRegistrations according to data from the given KeySet * @internal @@ -257,7 +219,7 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * if (changed) { ELEKTRA_LOG_DEBUG ("found changed registeredKey=%s with string value \"%s\". using context or variable=%p", - registeredKey->name, registeredKey->context, keyString (key)); + registeredKey->name, keyString (key), registeredKey->context); // Invoke callback ElektraNotificationChangeCallback callback = *(ElektraNotificationChangeCallback) registeredKey->callback; @@ -277,15 +239,48 @@ int elektraInternalnotificationRegisterInt (Plugin * handle, Key * key, int * va PluginState * pluginState = elektraPluginGetData (handle); ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); - KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration (pluginState); + KeyRegistration * registeredKey = + elektraInternalnotificationAddNewRegistration (pluginState, key, elektraInternalnotificationConvertInt, variable); + if (registeredKey == NULL) + { + return 0; + } + + return 1; +} + +/** + * @see kdbnotificationinternal.h ::elektraNotificationRegisterLong + */ +int elektraInternalnotificationRegisterLong (Plugin * handle, Key * key, long * variable) +{ + PluginState * pluginState = elektraPluginGetData (handle); + ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); + + KeyRegistration * registeredKey = + elektraInternalnotificationAddNewRegistration (pluginState, key, elektraInternalnotificationConvertLong, variable); + if (registeredKey == NULL) + { + return 0; + } + + return 1; +} + +/** + * @see kdbnotificationinternal.h ::elektraNotificationRegisterUnsignedLong + */ +int elektraInternalnotificationRegisterUnsignedLong (Plugin * handle, Key * key, unsigned long * variable) +{ + PluginState * pluginState = elektraPluginGetData (handle); + ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); + + KeyRegistration * registeredKey = + elektraInternalnotificationAddNewRegistration (pluginState, key, elektraInternalnotificationConvertUnsignedLong, variable); if (registeredKey == NULL) { return 0; } - // Save key registration - registeredKey->name = elektraStrDup (keyName (key)); - registeredKey->callback = updateRegistrationInt; - registeredKey->context = variable; return 1; } @@ -298,15 +293,30 @@ int elektraInternalnotificationRegisterFloat (Plugin * handle, Key * key, float PluginState * pluginState = elektraPluginGetData (handle); ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); - KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration (pluginState); + KeyRegistration * registeredKey = + elektraInternalnotificationAddNewRegistration (pluginState, key, elektraInternalnotificationConvertFloat, variable); + if (registeredKey == NULL) + { + return 0; + } + + return 1; +} + +/** + * @see kdbnotificationinternal.h ::ElektraNotificationPluginRegisterDouble + */ +int elektraInternalnotificationRegisterDouble (Plugin * handle, Key * key, double * variable) +{ + PluginState * pluginState = elektraPluginGetData (handle); + ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); + + KeyRegistration * registeredKey = + elektraInternalnotificationAddNewRegistration (pluginState, key, elektraInternalnotificationConvertDouble, variable); if (registeredKey == NULL) { return 0; } - // Save key registration - registeredKey->name = elektraStrDup (keyName (key)); - registeredKey->callback = updateRegistrationFloat; - registeredKey->context = variable; return 1; } @@ -319,15 +329,11 @@ int elektraInternalnotificationRegisterCallback (Plugin * handle, Key * key, Ele PluginState * pluginState = elektraPluginGetData (handle); ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); - KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration (pluginState); + KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration (pluginState, key, callback, context); if (registeredKey == NULL) { return 0; } - // Save key registration - registeredKey->name = elektraStrDup (keyName (key)); - registeredKey->callback = callback; - registeredKey->context = context; return 1; } @@ -367,8 +373,14 @@ int elektraInternalnotificationGet (Plugin * handle, KeySet * returned, Key * pa // Export register* functions keyNew ("system/elektra/modules/internalnotification/exports/registerInt", KEY_FUNC, elektraInternalnotificationRegisterInt, KEY_END), + keyNew ("system/elektra/modules/internalnotification/exports/registerLong", KEY_FUNC, + elektraInternalnotificationRegisterLong, KEY_END), + keyNew ("system/elektra/modules/internalnotification/exports/registerUnsignedLong", KEY_FUNC, + elektraInternalnotificationRegisterUnsignedLong, KEY_END), keyNew ("system/elektra/modules/internalnotification/exports/registerFloat", KEY_FUNC, elektraInternalnotificationRegisterFloat, KEY_END), + keyNew ("system/elektra/modules/internalnotification/exports/registerDouble", KEY_FUNC, + elektraInternalnotificationRegisterDouble, KEY_END), keyNew ("system/elektra/modules/internalnotification/exports/registerCallback", KEY_FUNC, elektraInternalnotificationRegisterCallback, KEY_END), diff --git a/src/plugins/internalnotification/internalnotification.h b/src/plugins/internalnotification/internalnotification.h index 1af7469e837..9f9ff3f2a9c 100644 --- a/src/plugins/internalnotification/internalnotification.h +++ b/src/plugins/internalnotification/internalnotification.h @@ -24,4 +24,11 @@ Plugin * ELEKTRA_PLUGIN_EXPORT (internalnotification); // Not exported by plugin; used for testing void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * keySet); +// Conversion functions +void elektraInternalnotificationConvertInt (Key * key, void * context); +void elektraInternalnotificationConvertLong (Key * key, void * context); +void elektraInternalnotificationConvertUnsignedLong (Key * key, void * context); +void elektraInternalnotificationConvertFloat (Key * key, void * context); +void elektraInternalnotificationConvertDouble (Key * key, void * context); + #endif diff --git a/src/plugins/internalnotification/testmod_internalnotification.c b/src/plugins/internalnotification/testmod_internalnotification.c index 75878e3f231..6fccbd11eca 100644 --- a/src/plugins/internalnotification/testmod_internalnotification.c +++ b/src/plugins/internalnotification/testmod_internalnotification.c @@ -28,22 +28,52 @@ char * callback_keyName; static int internalnotificationRegisterInt (Plugin * plugin, Key * key, int * variable) { size_t address = elektraPluginGetFunction (plugin, "registerInt"); + if (!address) yield_error ("function not exported"); // Register key with plugin return ((ElektraNotificationPluginRegisterInt) address) (plugin, key, variable); } +static int internalnotificationRegisterLong (Plugin * plugin, Key * key, long * variable) +{ + size_t address = elektraPluginGetFunction (plugin, "registerLong"); + if (!address) yield_error ("function not exported"); + + // Register key with plugin + return ((ElektraNotificationPluginRegisterLong)address) (plugin, key, variable); +} + +static int internalnotificationRegisterUnsignedLong (Plugin * plugin, Key * key, unsigned long * variable) +{ + size_t address = elektraPluginGetFunction (plugin, "registerUnsignedLong"); + if (!address) yield_error ("function not exported"); + + // Register key with plugin + return ((ElektraNotificationPluginRegisterUnsignedLong)address) (plugin, key, variable); +} + static int internalnotificationRegisterFloat (Plugin * plugin, Key * key, float * variable) { size_t address = elektraPluginGetFunction (plugin, "registerFloat"); + if (!address) yield_error ("function not exported"); // Register key with plugin return ((ElektraNotificationPluginRegisterFloat) address) (plugin, key, variable); } +static int internalnotificationRegisterDouble (Plugin * plugin, Key * key, double * variable) +{ + size_t address = elektraPluginGetFunction (plugin, "registerDouble"); + if (!address) yield_error ("function not exported"); + + // Register key with plugin + return ((ElektraNotificationPluginRegisterDouble)address) (plugin, key, variable); +} + static int internalnotificationRegisterCallback (Plugin * plugin, Key * key, ElektraNotificationChangeCallback callback, void * context) { size_t address = elektraPluginGetFunction (plugin, "registerCallback"); + if (!address) yield_error ("function not exported"); // Register key with plugin return ((ElektraNotificationPluginRegisterCallback) address) (plugin, key, callback, context); @@ -353,6 +383,53 @@ static void test_floatNoUpdateWithInvalidValue (void) keySetString (valueKey, "4.a"); + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); + + succeed_if ((int)value == 0, "registered value was updated"); + + ksDel (ks); + PLUGIN_CLOSE (); +} + +static void test_doubleUpdateWithCascadingKey (void) +{ + printf ("test update with cascading key registered\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * registeredKey = keyNew ("/test/internalnotification/value", KEY_END); + double value = 0; + succeed_if (internalnotificationRegisterDouble (plugin, registeredKey, &value) == 1, + "call to elektraInternalnotificationRegisterFloat was not successful"); + + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, "1.00000001", KEY_END); + KeySet * ks = ksNew (1, valueKey, KS_END); + + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); + + succeed_if (value >= 1 + 1e-9 && value <= 1 + 1e-7, "registered value was not updated"); + + keyDel (registeredKey); + ksDel (ks); + PLUGIN_CLOSE (); +} + +static void test_doubleNoUpdateWithInvalidValue (void) +{ + printf ("test no update with invalid value\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); + KeySet * ks = ksNew (1, valueKey, KS_END); + + double value = 0.0; + succeed_if (internalnotificationRegisterDouble (plugin, valueKey, &value) == 1, + "call to elektraInternalnotificationRegisterFloat was not successful"); + + keySetString (valueKey, "4.a"); elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); @@ -362,6 +439,105 @@ static void test_floatNoUpdateWithInvalidValue (void) PLUGIN_CLOSE (); } +static void test_longUpdateWithCascadingKey (void) +{ + printf ("test update with cascading key registered\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * registeredKey = keyNew ("/test/internalnotification/value", KEY_END); + long value = 0; + succeed_if (internalnotificationRegisterLong (plugin, registeredKey, &value) == 1, + "call to elektraInternalnotificationRegisterFloat was not successful"); + + char * valueStr = convertLongLongToString (LONG_MAX); + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, valueStr, KEY_END); + KeySet * ks = ksNew (1, valueKey, KS_END); + + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); + + succeed_if (value == LONG_MAX, "registered value was not updated"); + + elektraFree (valueStr); + keyDel (registeredKey); + ksDel (ks); + PLUGIN_CLOSE (); +} + +static void test_longNoUpdateWithInvalidValue (void) +{ + printf ("test no update with invalid value\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); + KeySet * ks = ksNew (1, valueKey, KS_END); + + long value = 0; + succeed_if (internalnotificationRegisterLong (plugin, valueKey, &value) == 1, + "call to elektraInternalnotificationRegisterFloat was not successful"); + + keySetString (valueKey, "5000abc000"); + + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); + + succeed_if (value == 0, "registered value was updated"); + + ksDel (ks); + PLUGIN_CLOSE (); +} + +static void test_unsignedLongUpdateWithCascadingKey (void) +{ + printf ("test update with cascading key registered\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * registeredKey = keyNew ("/test/internalnotification/value", KEY_END); + unsigned long value = 0; + succeed_if (internalnotificationRegisterUnsignedLong (plugin, registeredKey, &value) == 1, + "call to elektraInternalnotificationRegisterFloat was not successful"); + + char * valueStr = convertLongLongToString (ULONG_MAX); + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, valueStr, KEY_END); + KeySet * ks = ksNew (1, valueKey, KS_END); + + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); + + succeed_if (value == ULONG_MAX, "registered value was not updated"); + + elektraFree (valueStr); + keyDel (registeredKey); + ksDel (ks); + PLUGIN_CLOSE (); +} + +static void test_unsignedLongNoUpdateWithInvalidValue (void) +{ + printf ("test no update with invalid value\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); + KeySet * ks = ksNew (1, valueKey, KS_END); + + unsigned long value = 0; + succeed_if (internalnotificationRegisterUnsignedLong (plugin, valueKey, &value) == 1, + "call to elektraInternalnotificationRegisterFloat was not successful"); + + keySetString (valueKey, "AA446744073709551615"); + + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); + + succeed_if (value == 0, "registered value was updated"); + + ksDel (ks); + PLUGIN_CLOSE (); +} static void test_callback (Key * key, void * context) { @@ -440,10 +616,22 @@ int main (int argc, char ** argv) test_intUpdateWithValueNotYetExceedingIntMin (); test_intNoUpdateWithValueExceedingIntMin (); + printf ("\nregisterLong\n-----------\n"); + test_longUpdateWithCascadingKey (); + test_longNoUpdateWithInvalidValue (); + + printf ("\nregisterUnsignedLong\n-----------\n"); + test_unsignedLongUpdateWithCascadingKey (); + test_unsignedLongNoUpdateWithInvalidValue (); + printf ("\nregisterFloat\n-----------\n"); test_floatUpdateWithCascadingKey (); test_floatNoUpdateWithInvalidValue (); + printf ("\nregisterDouble\n-----------\n"); + test_doubleUpdateWithCascadingKey (); + test_doubleNoUpdateWithInvalidValue (); + printf ("\nregisterCallback\n----------------\n"); test_callbackCalledWithKey (); test_callbackCalledWithChangeDetection (); From 7b168f9ee49ac5b7f4a20a99de8ad6503345e0ae Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 18 Mar 2018 18:17:40 +0100 Subject: [PATCH 03/46] notification: add support for kdbtypes --- doc/Doxyfile | 2 +- src/include/kdbnotification.h | 86 ++--- src/include/kdbnotificationinternal.h | 93 ++--- src/libs/notification/notification.c | 153 +------- .../internalnotification/CMakeLists.txt | 1 - src/plugins/internalnotification/convert.c | 114 ------ .../internalnotification.c | 145 +++---- .../internalnotification.h | 54 ++- .../testmod_internalnotification.c | 361 ++++++------------ 9 files changed, 288 insertions(+), 721 deletions(-) delete mode 100644 src/plugins/internalnotification/convert.c diff --git a/doc/Doxyfile b/doc/Doxyfile index 993ce30011f..d8c073993e2 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -2039,7 +2039,7 @@ PREDEFINED = "ELEKTRA_UNUSED=" # definition found in the source code. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = "ELEKTRA_NOTIFICATION_TYPE_DECLARATION" # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have diff --git a/src/include/kdbnotification.h b/src/include/kdbnotification.h index 16bfac68b48..a5dc41eb01c 100644 --- a/src/include/kdbnotification.h +++ b/src/include/kdbnotification.h @@ -13,6 +13,7 @@ #define KDB_NOTIFICATION_H_ #include "kdb.h" +#include "kdbtypes.h" /** * @defgroup kdbnotification Notification @@ -76,6 +77,7 @@ extern "C" { #endif /** + * @ingroup kdbnotification * Initialize the notification system for the given KDB instance. * * Asynchronous receiving of notifications requires an I/O binding. @@ -90,6 +92,7 @@ extern "C" { int elektraNotificationOpen (KDB * kdb); /** + * @ingroup kdbnotification * Stop the notification system for the given KDB instance. * * May only be called after elektraNotificationOpen() was called for given KDB @@ -101,72 +104,50 @@ int elektraNotificationOpen (KDB * kdb); */ int elektraNotificationClose (KDB * kdb); -/** - * Subscribe for automatic updates to a given integer variable when the given - * key value is changed. - * - * @param handle plugin handle - * @param key key to watch for changes - * @param variable integer variable - * - * @retval 1 on success - * @retval 0 on failure - */ -int elektraNotificationRegisterInt (KDB * kdb, Key * key, int * variable); +#define ELEKTRA_NOTIFICATION_REGISTER_NAME(TYPE_NAME) elektraNotificationRegister##TYPE_NAME -/** - * Subscribe for automatic updates to a given long variable when the given - * key value is changed. - * - * @param handle plugin handle - * @param key key to watch for changes - * @param variable long variable - * - * @retval 1 on success - * @retval 0 on failure - */ -int elektraNotificationRegisterLong (KDB * kdb, Key * key, long * variable); +#define ELEKTRA_NOTIFICATION_REGISTER_SIGNATURE(TYPE, TYPE_NAME) \ + /** @copydoc elektraNotificationRegisterInt */ \ + int ELEKTRA_NOTIFICATION_REGISTER_NAME (TYPE_NAME) (KDB * kdb, Key * key, TYPE * variable) -/** - * Subscribe for automatic updates to a given unsigned long variable when the given - * key value is changed. - * - * @param handle plugin handle - * @param key key to watch for changes - * @param variable unsigned long variable - * - * @retval 1 on success - * @retval 0 on failure - */ -int elektraNotificationRegisterUnsignedLong (KDB * kdb, Key * key, unsigned long * variable); +#define ELEKTRA_NOTIFICATION_TYPE_DECLARATION(TYPE, TYPE_NAME) ELEKTRA_NOTIFICATION_REGISTER_SIGNATURE (TYPE, TYPE_NAME); /** - * Subscribe for automatic updates to a given float variable when the given - * key value is changed. + * @ingroup kdbnotification + * @brief Subscribe for automatic updates to a given variable when the given key value is changed. * * @param handle plugin handle * @param key key to watch for changes - * @param variable float variable + * @param variable variable * * @retval 1 on success * @retval 0 on failure + * @{ */ -int elektraNotificationRegisterFloat (KDB * kdb, Key * key, float * variable); +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (int, Int) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (unsigned int, UnsignedInt) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (long, Long) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (unsigned long, UnsignedLong) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (float, Float) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (double, Double) + +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_boolean_t, KdbBoolean) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_char_t, KdbChar) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_octet_t, KdbOctet) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_short_t, KdbShort) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_unsigned_short_t, KdbUnsignedShort) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_long_t, KdbLong) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_unsigned_long_t, KdbUnsignedLong) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_long_long_t, KdbLongLong) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_unsigned_long_long_t, KdbUnsignedLongLong) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_float_t, KdbFloat) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_double_t, KdbDouble) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_long_double_t, KdbLongDouble) +/** @} */ -/** - * Subscribe for automatic updates to a given double variable when the given - * key value is changed. - * - * @param handle plugin handle - * @param key key to watch for changes - * @param variable double variable - * - * @retval 1 on success - * @retval 0 on failure - */ -int elektraNotificationRegisterDouble (KDB * kdb, Key * key, double * variable); /** + * @ingroup kdbnotification * Callback function for key changes. * * @param key changed key @@ -175,6 +156,7 @@ int elektraNotificationRegisterDouble (KDB * kdb, Key * key, double * variable); typedef void (*ElektraNotificationChangeCallback) (Key * key, void * context); /** + * @ingroup kdbnotification * Subscribe for updates via callback when a given key value is changed. * * @param handle plugin handle diff --git a/src/include/kdbnotificationinternal.h b/src/include/kdbnotificationinternal.h index eee307962ac..41c8ca40c6f 100644 --- a/src/include/kdbnotificationinternal.h +++ b/src/include/kdbnotificationinternal.h @@ -3,7 +3,7 @@ * @ingroup kdbnotification * * @brief Elektra-Notification structures and declarations for developing - * notification and transport plugins. + * notification and transport plugins. Only available in Elektra's source. * * Only used by elektra-notification library, notification plugins (e.g. * internalnotification) and transport plugins. @@ -23,70 +23,33 @@ namespace ckdb extern "C" { #endif -/** - * Subscribe for automatic updates to a given integer variable when the given - * key value is changed. - * - * @param handle plugin handle - * @param key key to watch for changes - * @param variable integer variable - * - * @retval 1 on success - * @retval 0 on failure - */ -typedef int (*ElektraNotificationPluginRegisterInt) (Plugin * handle, Key * key, int * variable); - -/** - * Subscribe for automatic updates to a given long variable when the given - * key value is changed. - * - * @param handle plugin handle - * @param key key to watch for changes - * @param variable long variable - * - * @retval 1 on success - * @retval 0 on failure - */ -typedef int (*ElektraNotificationPluginRegisterLong) (Plugin * handle, Key * key, long * variable); - -/** - * Subscribe for automatic updates to a given unsigned long variable when the given - * key value is changed. - * - * @param handle plugin handle - * @param key key to watch for changes - * @param variable unsigned long variable - * - * @retval 1 on success - * @retval 0 on failure - */ -typedef int (*ElektraNotificationPluginRegisterUnsignedLong) (Plugin * handle, Key * key, unsigned long * variable); - -/** - * Subscribe for automatic updates to a given float variable when the given - * key value is changed. - * - * @param handle plugin handle - * @param key key to watch for changes - * @param variable float variable - * - * @retval 1 on success - * @retval 0 on failure - */ -typedef int (*ElektraNotificationPluginRegisterFloat) (Plugin * handle, Key * key, float * variable); - -/** - * Subscribe for automatic updates to a given double variable when the given - * key value is changed. - * - * @param handle plugin handle - * @param key key to watch for changes - * @param variable double variable - * - * @retval 1 on success - * @retval 0 on failure - */ -typedef int (*ElektraNotificationPluginRegisterDouble) (Plugin * handle, Key * key, double * variable); +#define ELEKTRA_NOTIFICATION_REGISTERFUNC_TYPEDEF(FUNC_TYPE_NAME, TYPE) \ + typedef int (*FUNC_TYPE_NAME) (Plugin * handle, Key * key, TYPE * variable); + +#define ELEKTRA_NOTIFICATION_TYPE_DEFINITION(TYPE, TYPE_NAME) \ + ELEKTRA_NOTIFICATION_REGISTER_SIGNATURE (TYPE, TYPE_NAME) \ + { \ + if (!kdb || !key || !variable) \ + { \ + ELEKTRA_LOG_WARNING ("null pointer passed"); \ + return 0; \ + } \ + /* get notification plugins */ \ + Plugin * notificationPlugin = getNotificationPlugin (kdb); \ + if (!notificationPlugin) \ + { \ + return 0; \ + } \ + /* get register function from plugin */ \ + size_t func = elektraPluginGetFunction (notificationPlugin, "register" #TYPE_NAME); \ + if (!func) \ + { \ + return 0; \ + } \ + ELEKTRA_NOTIFICATION_REGISTERFUNC_TYPEDEF (RegisterFuncType, TYPE) \ + RegisterFuncType registerFunc = (RegisterFuncType)func; \ + return registerFunc (notificationPlugin, key, variable); \ + } /** * Subscribe for updates via callback when a given key value is changed. diff --git a/src/libs/notification/notification.c b/src/libs/notification/notification.c index acac335b51a..18b5bd29ccc 100644 --- a/src/libs/notification/notification.c +++ b/src/libs/notification/notification.c @@ -850,140 +850,25 @@ static Plugin * getNotificationPlugin (KDB * kdb) } } -int elektraNotificationRegisterInt (KDB * kdb, Key * key, int * variable) -{ - if (!kdb || !key || !variable) - { - ELEKTRA_LOG_WARNING ("null pointer passed"); - return 0; - } - - // Find notification plugin - Plugin * notificationPlugin = getNotificationPlugin (kdb); - if (!notificationPlugin) - { - return 0; - } - - // Get register function from plugin - size_t func = elektraPluginGetFunction (notificationPlugin, "registerInt"); - if (!func) - { - return 0; - } - - // Call register function - ElektraNotificationPluginRegisterInt registerFunc = (ElektraNotificationPluginRegisterInt) func; - return registerFunc (notificationPlugin, key, variable); -} - -int elektraNotificationRegisterLong (KDB * kdb, Key * key, long * variable) -{ - if (!kdb || !key || !variable) - { - ELEKTRA_LOG_WARNING ("null pointer passed"); - return 0; - } - - // Find notification plugin - Plugin * notificationPlugin = getNotificationPlugin (kdb); - if (!notificationPlugin) - { - return 0; - } - - // Get register function from plugin - size_t func = elektraPluginGetFunction (notificationPlugin, "registerLong"); - if (!func) - { - return 0; - } - - // Call register function - ElektraNotificationPluginRegisterLong registerFunc = (ElektraNotificationPluginRegisterLong)func; - return registerFunc (notificationPlugin, key, variable); -} - -int elektraNotificationRegisterUnsignedLong (KDB * kdb, Key * key, unsigned long * variable) -{ - if (!kdb || !key || !variable) - { - ELEKTRA_LOG_WARNING ("null pointer passed"); - return 0; - } - - // Find notification plugin - Plugin * notificationPlugin = getNotificationPlugin (kdb); - if (!notificationPlugin) - { - return 0; - } - - // Get register function from plugin - size_t func = elektraPluginGetFunction (notificationPlugin, "registerUnsignedLong"); - if (!func) - { - return 0; - } - - // Call register function - ElektraNotificationPluginRegisterUnsignedLong registerFunc = (ElektraNotificationPluginRegisterUnsignedLong)func; - return registerFunc (notificationPlugin, key, variable); -} - -int elektraNotificationRegisterFloat (KDB * kdb, Key * key, float * variable) -{ - if (!kdb || !key || !variable) - { - ELEKTRA_LOG_WARNING ("null pointer passed"); - return 0; - } - - // Find notification plugin - Plugin * notificationPlugin = getNotificationPlugin (kdb); - if (!notificationPlugin) - { - return 0; - } - - // Get register function from plugin - size_t func = elektraPluginGetFunction (notificationPlugin, "registerFloat"); - if (!func) - { - return 0; - } - - // Call register function - ElektraNotificationPluginRegisterFloat registerFunc = (ElektraNotificationPluginRegisterFloat) func; - return registerFunc (notificationPlugin, key, variable); -} - -int elektraNotificationRegisterDouble (KDB * kdb, Key * key, double * variable) -{ - if (!kdb || !key || !variable) - { - ELEKTRA_LOG_WARNING ("null pointer passed"); - return 0; - } - - // Find notification plugin - Plugin * notificationPlugin = getNotificationPlugin (kdb); - if (!notificationPlugin) - { - return 0; - } - - // Get register function from plugin - size_t func = elektraPluginGetFunction (notificationPlugin, "registerDouble"); - if (!func) - { - return 0; - } - - // Call register function - ElektraNotificationPluginRegisterDouble registerFunc = (ElektraNotificationPluginRegisterDouble)func; - return registerFunc (notificationPlugin, key, variable); -} +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (int, Int) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (unsigned int, UnsignedInt) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (long, Long) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (unsigned long, UnsignedLong) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (float, Float) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (double, Double) + +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_boolean_t, KdbBoolean) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_char_t, KdbChar) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_octet_t, KdbOctet) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_short_t, KdbShort) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_unsigned_short_t, KdbUnsignedShort) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_long_t, KdbLong) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_unsigned_long_t, KdbUnsignedLong) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_long_long_t, KdbLongLong) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_unsigned_long_long_t, KdbUnsignedLongLong) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_float_t, KdbFloat) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_double_t, KdbDouble) +ELEKTRA_NOTIFICATION_TYPE_DEFINITION (kdb_long_double_t, KdbLongDouble) int elektraNotificationRegisterCallback (KDB * kdb, Key * key, ElektraNotificationChangeCallback callback, void * context) { diff --git a/src/plugins/internalnotification/CMakeLists.txt b/src/plugins/internalnotification/CMakeLists.txt index 2878feec8a6..262897b336b 100644 --- a/src/plugins/internalnotification/CMakeLists.txt +++ b/src/plugins/internalnotification/CMakeLists.txt @@ -4,7 +4,6 @@ add_plugin (internalnotification SOURCES internalnotification.h internalnotification.c - convert.c ADD_TEST LINK_ELEKTRA elektra-kdb diff --git a/src/plugins/internalnotification/convert.c b/src/plugins/internalnotification/convert.c deleted file mode 100644 index 42292c1ef20..00000000000 --- a/src/plugins/internalnotification/convert.c +++ /dev/null @@ -1,114 +0,0 @@ -/** - * @file - * - * @brief Source for internalnotification plugin - * - * @copyright BSD License (see doc/COPYING or http://www.libelektra.org) - * - */ - -#include "internalnotification.h" - -#include // errno -#include // ELEKTRA_LOG macros -#include // strto* functions - -void elektraInternalnotificationConvertInt (Key * key, void * context) -{ - int * variable = (int *)context; - - // Convert string value to long - char * end; - errno = 0; - long int value = strtol (keyString (key), &end, 10); - // Update variable if conversion was successful and did not exceed integer range - if (*end == 0 && errno == 0 && value <= INT_MAX && value >= INT_MIN) - { - *(variable) = value; - } - else - { - ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%ld", keyString (key), *end, errno, - value); - } -} - -void elektraInternalnotificationConvertLong (Key * key, void * context) -{ - long * variable = (long *)context; - - // Convert string value to long - char * end; - errno = 0; - long value = strtol (keyString (key), &end, 10); - // Update variable if conversion was successful and did not exceed integer range - if (*end == 0 && errno == 0) - { - *(variable) = value; - } - else - { - ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%ld", keyString (key), *end, errno, - value); - } -} - -void elektraInternalnotificationConvertUnsignedLong (Key * key, void * context) -{ - unsigned long * variable = (unsigned long *)context; - - // Convert string value to unsigned long - char * end; - errno = 0; - unsigned long value = strtoul (keyString (key), &end, 10); - // Update variable if conversion was successful and did not exceed integer range - if (*end == 0 && errno == 0) - { - *(variable) = value; - } - else - { - ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%ld", keyString (key), *end, errno, - value); - } -} - -void elektraInternalnotificationConvertFloat (Key * key, void * context) -{ - float * variable = (float *)context; - - // Convert string value to long - char * end; - errno = 0; - float value = strtof (keyString (key), &end); - // Update variable if conversion was successful - if (*end == 0 && errno == 0) - { - *(variable) = value; - } - else - { - ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%f", keyString (key), *end, errno, - value); - } -} - -void elektraInternalnotificationConvertDouble (Key * key, void * context) -{ - double * variable = (double *)context; - - // Convert string value to long - char * end; - errno = 0; - double value = strtod (keyString (key), &end); - // Update variable if conversion was successful - if (*end == 0 && errno == 0) - { - *(variable) = value; - } - else - { - ELEKTRA_LOG_WARNING ("conversion failed! keyString=\"%s\" *end=%c, errno=%d, value=%f", keyString (key), *end, errno, - value); - } -} diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index b3829b6134e..e3a98d4556b 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -15,6 +15,8 @@ #include #include +#include // errno +#include // strto* functions /* checklist for types [ ] TYPE_BOOLEAN = 1 << 0, @@ -230,96 +232,37 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * } } - -/** - * @see kdbnotificationinternal.h ::elektraNotificationRegisterInt - */ -int elektraInternalnotificationRegisterInt (Plugin * handle, Key * key, int * variable) -{ - PluginState * pluginState = elektraPluginGetData (handle); - ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); - - KeyRegistration * registeredKey = - elektraInternalnotificationAddNewRegistration (pluginState, key, elektraInternalnotificationConvertInt, variable); - if (registeredKey == NULL) - { - return 0; - } - - return 1; -} - -/** - * @see kdbnotificationinternal.h ::elektraNotificationRegisterLong - */ -int elektraInternalnotificationRegisterLong (Plugin * handle, Key * key, long * variable) -{ - PluginState * pluginState = elektraPluginGetData (handle); - ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); - - KeyRegistration * registeredKey = - elektraInternalnotificationAddNewRegistration (pluginState, key, elektraInternalnotificationConvertLong, variable); - if (registeredKey == NULL) - { - return 0; - } - - return 1; -} - -/** - * @see kdbnotificationinternal.h ::elektraNotificationRegisterUnsignedLong - */ -int elektraInternalnotificationRegisterUnsignedLong (Plugin * handle, Key * key, unsigned long * variable) -{ - PluginState * pluginState = elektraPluginGetData (handle); - ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); - - KeyRegistration * registeredKey = - elektraInternalnotificationAddNewRegistration (pluginState, key, elektraInternalnotificationConvertUnsignedLong, variable); - if (registeredKey == NULL) - { - return 0; - } - - return 1; -} - -/** - * @see kdbnotificationinternal.h ::ElektraNotificationPluginRegisterFloat - */ -int elektraInternalnotificationRegisterFloat (Plugin * handle, Key * key, float * variable) -{ - PluginState * pluginState = elektraPluginGetData (handle); - ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); - - KeyRegistration * registeredKey = - elektraInternalnotificationAddNewRegistration (pluginState, key, elektraInternalnotificationConvertFloat, variable); - if (registeredKey == NULL) - { - return 0; - } - - return 1; -} - -/** - * @see kdbnotificationinternal.h ::ElektraNotificationPluginRegisterDouble - */ -int elektraInternalnotificationRegisterDouble (Plugin * handle, Key * key, double * variable) -{ - PluginState * pluginState = elektraPluginGetData (handle); - ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); - - KeyRegistration * registeredKey = - elektraInternalnotificationAddNewRegistration (pluginState, key, elektraInternalnotificationConvertDouble, variable); - if (registeredKey == NULL) - { - return 0; - } - - return 1; -} +// Generate register and conversion functions +INTERNALNOTIFICATION_TYPE (int, long int, Int, (strtol (string, &end, 10)), + INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= INT_MAX && value >= INT_MIN)) +INTERNALNOTIFICATION_TYPE (unsigned int, unsigned long int, UnsignedInt, (strtoul (string, &end, 10)), + INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= UINT_MAX)) + +INTERNALNOTIFICATION_TYPE (long, long, Long, (strtol (string, &end, 10)), INTERNALNOTIFICATION_CHECK_CONVERSION) +INTERNALNOTIFICATION_TYPE (unsigned long, unsigned long, UnsignedLong, (strtoul (string, &end, 10)), INTERNALNOTIFICATION_CHECK_CONVERSION) + +INTERNALNOTIFICATION_TYPE (float, float, Float, (strtof (string, &end)), INTERNALNOTIFICATION_CHECK_CONVERSION) +INTERNALNOTIFICATION_TYPE (double, double, Double, (strtod (string, &end)), INTERNALNOTIFICATION_CHECK_CONVERSION) + +INTERNALNOTIFICATION_TYPE (kdb_boolean_t, int, KdbBoolean, (!strcmp (string, "1")), 1) +INTERNALNOTIFICATION_TYPE (kdb_char_t, kdb_char_t, KdbChar, (string[0]), 1) +INTERNALNOTIFICATION_TYPE (kdb_octet_t, unsigned int, KdbOctet, (strtoul (string, &end, 10)), + INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= 255)) +INTERNALNOTIFICATION_TYPE (kdb_short_t, int, KdbShort, (strtol (string, &end, 10)), + INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= SHRT_MAX && value >= SHRT_MIN)) +INTERNALNOTIFICATION_TYPE (kdb_unsigned_short_t, unsigned int, KdbUnsignedShort, (strtoul (string, &end, 10)), + INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= USHRT_MAX)) +INTERNALNOTIFICATION_TYPE (kdb_long_t, kdb_long_t, KdbLong, (strtol (string, &end, 10)), INTERNALNOTIFICATION_CHECK_CONVERSION) +INTERNALNOTIFICATION_TYPE (kdb_unsigned_long_t, kdb_unsigned_long_t, KdbUnsignedLong, (strtoul (string, &end, 10)), + INTERNALNOTIFICATION_CHECK_CONVERSION) +INTERNALNOTIFICATION_TYPE (kdb_long_long_t, kdb_long_long_t, KdbLongLong, (ELEKTRA_LONG_LONG_S (string, &end, 10)), + INTERNALNOTIFICATION_CHECK_CONVERSION) +INTERNALNOTIFICATION_TYPE (kdb_unsigned_long_long_t, kdb_unsigned_long_long_t, KdbUnsignedLongLong, + (ELEKTRA_UNSIGNED_LONG_LONG_S (string, &end, 10)), INTERNALNOTIFICATION_CHECK_CONVERSION) +INTERNALNOTIFICATION_TYPE (kdb_float_t, kdb_float_t, KdbFloat, (strtof (string, &end)), INTERNALNOTIFICATION_CHECK_CONVERSION) +INTERNALNOTIFICATION_TYPE (kdb_double_t, kdb_double_t, KdbDouble, (strtod (string, &end)), INTERNALNOTIFICATION_CHECK_CONVERSION) +INTERNALNOTIFICATION_TYPE (kdb_long_double_t, kdb_long_double_t, KdbLongDouble, (strtold (string, &end)), + INTERNALNOTIFICATION_CHECK_CONVERSION) /** * @see kdbnotificationinternal.h ::ElektraNotificationPluginRegisterCallback @@ -371,16 +314,18 @@ int elektraInternalnotificationGet (Plugin * handle, KeySet * returned, Key * pa elektraInternalnotificationDoUpdate, KEY_END), // Export register* functions - keyNew ("system/elektra/modules/internalnotification/exports/registerInt", KEY_FUNC, - elektraInternalnotificationRegisterInt, KEY_END), - keyNew ("system/elektra/modules/internalnotification/exports/registerLong", KEY_FUNC, - elektraInternalnotificationRegisterLong, KEY_END), - keyNew ("system/elektra/modules/internalnotification/exports/registerUnsignedLong", KEY_FUNC, - elektraInternalnotificationRegisterUnsignedLong, KEY_END), - keyNew ("system/elektra/modules/internalnotification/exports/registerFloat", KEY_FUNC, - elektraInternalnotificationRegisterFloat, KEY_END), - keyNew ("system/elektra/modules/internalnotification/exports/registerDouble", KEY_FUNC, - elektraInternalnotificationRegisterDouble, KEY_END), + INTERNALNOTIFICATION_EXPORT_FUNCTION (Int), INTERNALNOTIFICATION_EXPORT_FUNCTION (UnsignedInt), + INTERNALNOTIFICATION_EXPORT_FUNCTION (Long), INTERNALNOTIFICATION_EXPORT_FUNCTION (UnsignedLong), + INTERNALNOTIFICATION_EXPORT_FUNCTION (Float), INTERNALNOTIFICATION_EXPORT_FUNCTION (Double), + + // Export register* functions for kdb_*_t types + INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbBoolean), INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbChar), + INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbOctet), INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbShort), + INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbUnsignedShort), INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbLong), + INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbUnsignedLong), INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbLongLong), + INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbUnsignedLongLong), INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbFloat), + INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbDouble), INTERNALNOTIFICATION_EXPORT_FUNCTION (KdbLongDouble), + keyNew ("system/elektra/modules/internalnotification/exports/registerCallback", KEY_FUNC, elektraInternalnotificationRegisterCallback, KEY_END), diff --git a/src/plugins/internalnotification/internalnotification.h b/src/plugins/internalnotification/internalnotification.h index 9f9ff3f2a9c..60c77d4cb9e 100644 --- a/src/plugins/internalnotification/internalnotification.h +++ b/src/plugins/internalnotification/internalnotification.h @@ -24,11 +24,53 @@ Plugin * ELEKTRA_PLUGIN_EXPORT (internalnotification); // Not exported by plugin; used for testing void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * keySet); -// Conversion functions -void elektraInternalnotificationConvertInt (Key * key, void * context); -void elektraInternalnotificationConvertLong (Key * key, void * context); -void elektraInternalnotificationConvertUnsignedLong (Key * key, void * context); -void elektraInternalnotificationConvertFloat (Key * key, void * context); -void elektraInternalnotificationConvertDouble (Key * key, void * context); +#define INTERNALNOTIFICATION_REGISTER_NAME(TYPE_NAME) elektraInternalnotificationRegister##TYPE_NAME +#define INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME(TYPE_NAME) elektraInternalnotificationConvert##TYPE_NAME + +#define INTERNALNOTIFICATION_EXPORT_FUNCTION(TYPE_NAME) \ + keyNew ("system/elektra/modules/internalnotification/exports/register" #TYPE_NAME, KEY_FUNC, \ + INTERNALNOTIFICATION_REGISTER_NAME (TYPE_NAME), KEY_END) + +#define INTERNALNOTIFICATION_REGISTER_SIGNATURE(TYPE, TYPE_NAME) \ + int INTERNALNOTIFICATION_REGISTER_NAME (TYPE_NAME) (Plugin * handle, Key * key, TYPE * variable) + +#define INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE(TYPE_NAME) \ + void INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME (TYPE_NAME) (Key * key, void * context) + +#define INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE(CHECK_RANGE) (*end == 0 && errno == 0 && CHECK_RANGE) +#define INTERNALNOTIFICATION_CHECK_CONVERSION (*end == 0 && errno == 0) + +#define INTERNALNOTIFICATION_TYPE(TYPE, VALUE_TYPE, TYPE_NAME, TO_VALUE, CHECK_CONVERSION) \ + INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE (TYPE_NAME) \ + { \ + TYPE * variable = (TYPE *)context; \ + char * end ELEKTRA_UNUSED; \ + const char * string = keyValue (key); \ + errno = 0; \ + /* convert string to target type */ \ + VALUE_TYPE value = TO_VALUE; \ + /* only update if conversion was successful */ \ + if (CHECK_CONVERSION) \ + { \ + *(variable) = value; \ + } \ + else \ + { \ + ELEKTRA_LOG_WARNING ("conversion failed! string=%s, stopped=%c errno=%d", keyString (key), *end, errno); \ + } \ + } \ + \ + INTERNALNOTIFICATION_REGISTER_SIGNATURE (TYPE, TYPE_NAME) \ + { \ + PluginState * pluginState = elektraPluginGetData (handle); \ + ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); \ + KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration ( \ + pluginState, key, INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME (TYPE_NAME), variable); \ + if (registeredKey == NULL) \ + { \ + return 0; \ + } \ + return 1; \ + } #endif diff --git a/src/plugins/internalnotification/testmod_internalnotification.c b/src/plugins/internalnotification/testmod_internalnotification.c index 6fccbd11eca..bbe9c3102b2 100644 --- a/src/plugins/internalnotification/testmod_internalnotification.c +++ b/src/plugins/internalnotification/testmod_internalnotification.c @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -25,49 +26,76 @@ char * callback_keyName; #define CALLBACK_CONTEXT_MAGIC_NUMBER ((void *) 1234) -static int internalnotificationRegisterInt (Plugin * plugin, Key * key, int * variable) -{ - size_t address = elektraPluginGetFunction (plugin, "registerInt"); - if (!address) yield_error ("function not exported"); - - // Register key with plugin - return ((ElektraNotificationPluginRegisterInt) address) (plugin, key, variable); -} - -static int internalnotificationRegisterLong (Plugin * plugin, Key * key, long * variable) -{ - size_t address = elektraPluginGetFunction (plugin, "registerLong"); - if (!address) yield_error ("function not exported"); - - // Register key with plugin - return ((ElektraNotificationPluginRegisterLong)address) (plugin, key, variable); -} - -static int internalnotificationRegisterUnsignedLong (Plugin * plugin, Key * key, unsigned long * variable) -{ - size_t address = elektraPluginGetFunction (plugin, "registerUnsignedLong"); - if (!address) yield_error ("function not exported"); +#define REGISTER_FUNC_NAME(TYPE_NAME) internalnotificationRegister##TYPE_NAME +#define TEST_CASE_UPDATE_NAME(TYPE_NAME) test_update##TYPE_NAME +#define TEST_CASE_NO_UPDATE_NAME(TYPE_NAME) test_noUpdate##TYPE_NAME + +#define RUN_TYPE_TESTS(TYPE_NAME) \ + printf ("\n" #TYPE_NAME "\n----------------\n"); \ + TEST_CASE_UPDATE_NAME (TYPE_NAME) (); \ + TEST_CASE_NO_UPDATE_NAME (TYPE_NAME) (); + +#define REGISTER_FUNC_DEFINITION(TYPE, TYPE_NAME) \ + static int REGISTER_FUNC_NAME (TYPE_NAME) (Plugin * plugin, Key * key, TYPE * variable) \ + { \ + size_t address = elektraPluginGetFunction (plugin, "register" #TYPE_NAME); \ + if (!address) yield_error ("function not exported"); \ + \ + /* register key with plugin */ \ + ELEKTRA_NOTIFICATION_REGISTERFUNC_TYPEDEF (RegisterFuncType, TYPE) \ + return ((RegisterFuncType) address) (plugin, key, variable); \ + } - // Register key with plugin - return ((ElektraNotificationPluginRegisterUnsignedLong)address) (plugin, key, variable); -} +#define CREATE_UPDATE_TEST_CASE(TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE, CHECK_VALUE) \ + static void TEST_CASE_UPDATE_NAME (TYPE_NAME) (void) \ + { \ + printf (#TYPE ": test update\n"); \ + KeySet * conf = ksNew (0, KS_END); \ + PLUGIN_OPEN ("internalnotification"); \ + Key * registeredKey = keyNew ("/test/internalnotification/value", KEY_END); \ + TYPE value = 0; \ + succeed_if (REGISTER_FUNC_NAME (TYPE_NAME) (plugin, registeredKey, &value) == 1, "registration was not successful"); \ + char * valueStr = elektraFormat (FORMAT_STRING, TEST_VALUE); \ + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, valueStr, KEY_END); \ + KeySet * ks = ksNew (1, valueKey, KS_END); \ + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); \ + succeed_if (CHECK_VALUE, "registered value was not updated"); \ + free (valueStr); \ + keyDel (registeredKey); \ + ksDel (ks); \ + PLUGIN_CLOSE (); \ + } -static int internalnotificationRegisterFloat (Plugin * plugin, Key * key, float * variable) -{ - size_t address = elektraPluginGetFunction (plugin, "registerFloat"); - if (!address) yield_error ("function not exported"); +#define CREATE_TYPE_TESTS(TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE, CHECK_VALUE, INVALID_VALUE, CHECK_INVALID) \ + REGISTER_FUNC_DEFINITION (TYPE, TYPE_NAME) \ + \ + CREATE_UPDATE_TEST_CASE (TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE, CHECK_VALUE) \ + \ + static void TEST_CASE_NO_UPDATE_NAME (TYPE_NAME) (void) \ + { \ + printf (#TYPE ": test no update with invalid value\n"); \ + KeySet * conf = ksNew (0, KS_END); \ + PLUGIN_OPEN ("internalnotification"); \ + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); \ + KeySet * ks = ksNew (1, valueKey, KS_END); \ + TYPE value = 0; \ + succeed_if (REGISTER_FUNC_NAME (TYPE_NAME) (plugin, valueKey, &value) == 1, "registration was not successful"); \ + keySetString (valueKey, INVALID_VALUE); \ + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); \ + succeed_if (CHECK_INVALID, "registered value was updated"); \ + ksDel (ks); \ + PLUGIN_CLOSE (); \ + } - // Register key with plugin - return ((ElektraNotificationPluginRegisterFloat) address) (plugin, key, variable); -} -static int internalnotificationRegisterDouble (Plugin * plugin, Key * key, double * variable) +static int internalnotificationRegisterInt (Plugin * plugin, Key * key, int * variable) { - size_t address = elektraPluginGetFunction (plugin, "registerDouble"); + size_t address = elektraPluginGetFunction (plugin, "registerInt"); if (!address) yield_error ("function not exported"); // Register key with plugin - return ((ElektraNotificationPluginRegisterDouble)address) (plugin, key, variable); + ELEKTRA_NOTIFICATION_REGISTERFUNC_TYPEDEF (RegisterFuncType, int) + return ((RegisterFuncType) address) (plugin, key, variable); } static int internalnotificationRegisterCallback (Plugin * plugin, Key * key, ElektraNotificationChangeCallback callback, void * context) @@ -342,203 +370,6 @@ static void test_intNoUpdateWithValueExceedingIntMin (void) PLUGIN_CLOSE (); } - -static void test_floatUpdateWithCascadingKey (void) -{ - printf ("test update with cascading key registered\n"); - - KeySet * conf = ksNew (0, KS_END); - PLUGIN_OPEN ("internalnotification"); - - Key * registeredKey = keyNew ("/test/internalnotification/value", KEY_END); - float value = 0; - succeed_if (internalnotificationRegisterFloat (plugin, registeredKey, &value) == 1, - "call to elektraInternalnotificationRegisterFloat was not successful"); - - Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, "2.3", KEY_END); - KeySet * ks = ksNew (1, valueKey, KS_END); - - elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); - - succeed_if (value >= 2.295 && value <= 2.305, "registered value was not updated"); - - keyDel (registeredKey); - ksDel (ks); - PLUGIN_CLOSE (); -} - -static void test_floatNoUpdateWithInvalidValue (void) -{ - printf ("test no update with invalid value\n"); - - KeySet * conf = ksNew (0, KS_END); - PLUGIN_OPEN ("internalnotification"); - - Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); - KeySet * ks = ksNew (1, valueKey, KS_END); - - float value = 0.0; - succeed_if (internalnotificationRegisterFloat (plugin, valueKey, &value) == 1, - "call to elektraInternalnotificationRegisterFloat was not successful"); - - keySetString (valueKey, "4.a"); - - elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); - - succeed_if ((int)value == 0, "registered value was updated"); - - ksDel (ks); - PLUGIN_CLOSE (); -} - -static void test_doubleUpdateWithCascadingKey (void) -{ - printf ("test update with cascading key registered\n"); - - KeySet * conf = ksNew (0, KS_END); - PLUGIN_OPEN ("internalnotification"); - - Key * registeredKey = keyNew ("/test/internalnotification/value", KEY_END); - double value = 0; - succeed_if (internalnotificationRegisterDouble (plugin, registeredKey, &value) == 1, - "call to elektraInternalnotificationRegisterFloat was not successful"); - - Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, "1.00000001", KEY_END); - KeySet * ks = ksNew (1, valueKey, KS_END); - - elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); - - succeed_if (value >= 1 + 1e-9 && value <= 1 + 1e-7, "registered value was not updated"); - - keyDel (registeredKey); - ksDel (ks); - PLUGIN_CLOSE (); -} - -static void test_doubleNoUpdateWithInvalidValue (void) -{ - printf ("test no update with invalid value\n"); - - KeySet * conf = ksNew (0, KS_END); - PLUGIN_OPEN ("internalnotification"); - - Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); - KeySet * ks = ksNew (1, valueKey, KS_END); - - double value = 0.0; - succeed_if (internalnotificationRegisterDouble (plugin, valueKey, &value) == 1, - "call to elektraInternalnotificationRegisterFloat was not successful"); - - keySetString (valueKey, "4.a"); - - elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); - - succeed_if ((int) value == 0, "registered value was updated"); - - ksDel (ks); - PLUGIN_CLOSE (); -} - -static void test_longUpdateWithCascadingKey (void) -{ - printf ("test update with cascading key registered\n"); - - KeySet * conf = ksNew (0, KS_END); - PLUGIN_OPEN ("internalnotification"); - - Key * registeredKey = keyNew ("/test/internalnotification/value", KEY_END); - long value = 0; - succeed_if (internalnotificationRegisterLong (plugin, registeredKey, &value) == 1, - "call to elektraInternalnotificationRegisterFloat was not successful"); - - char * valueStr = convertLongLongToString (LONG_MAX); - Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, valueStr, KEY_END); - KeySet * ks = ksNew (1, valueKey, KS_END); - - elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); - - succeed_if (value == LONG_MAX, "registered value was not updated"); - - elektraFree (valueStr); - keyDel (registeredKey); - ksDel (ks); - PLUGIN_CLOSE (); -} - -static void test_longNoUpdateWithInvalidValue (void) -{ - printf ("test no update with invalid value\n"); - - KeySet * conf = ksNew (0, KS_END); - PLUGIN_OPEN ("internalnotification"); - - Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); - KeySet * ks = ksNew (1, valueKey, KS_END); - - long value = 0; - succeed_if (internalnotificationRegisterLong (plugin, valueKey, &value) == 1, - "call to elektraInternalnotificationRegisterFloat was not successful"); - - keySetString (valueKey, "5000abc000"); - - elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); - - succeed_if (value == 0, "registered value was updated"); - - ksDel (ks); - PLUGIN_CLOSE (); -} - -static void test_unsignedLongUpdateWithCascadingKey (void) -{ - printf ("test update with cascading key registered\n"); - - KeySet * conf = ksNew (0, KS_END); - PLUGIN_OPEN ("internalnotification"); - - Key * registeredKey = keyNew ("/test/internalnotification/value", KEY_END); - unsigned long value = 0; - succeed_if (internalnotificationRegisterUnsignedLong (plugin, registeredKey, &value) == 1, - "call to elektraInternalnotificationRegisterFloat was not successful"); - - char * valueStr = convertLongLongToString (ULONG_MAX); - Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, valueStr, KEY_END); - KeySet * ks = ksNew (1, valueKey, KS_END); - - elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); - - succeed_if (value == ULONG_MAX, "registered value was not updated"); - - elektraFree (valueStr); - keyDel (registeredKey); - ksDel (ks); - PLUGIN_CLOSE (); -} - -static void test_unsignedLongNoUpdateWithInvalidValue (void) -{ - printf ("test no update with invalid value\n"); - - KeySet * conf = ksNew (0, KS_END); - PLUGIN_OPEN ("internalnotification"); - - Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); - KeySet * ks = ksNew (1, valueKey, KS_END); - - unsigned long value = 0; - succeed_if (internalnotificationRegisterUnsignedLong (plugin, valueKey, &value) == 1, - "call to elektraInternalnotificationRegisterFloat was not successful"); - - keySetString (valueKey, "AA446744073709551615"); - - elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); - - succeed_if (value == 0, "registered value was updated"); - - ksDel (ks); - PLUGIN_CLOSE (); -} - static void test_callback (Key * key, void * context) { succeed_if (context == CALLBACK_CONTEXT_MAGIC_NUMBER, "callback context was not passed"); @@ -597,6 +428,32 @@ static void test_callbackCalledWithChangeDetection (void) PLUGIN_CLOSE (); } +CREATE_TYPE_TESTS (unsigned int, UnsignedInt, "%u", UINT_MAX, (value == UINT_MAX), "-1", value == 0) +CREATE_TYPE_TESTS (long, Long, "%ld", LONG_MAX, (value == LONG_MAX), "5000abc000", value == 0) +CREATE_TYPE_TESTS (unsigned long, UnsignedLong, "%lu", ULONG_MAX, (value == ULONG_MAX), "AA446744073709551615", value == 0) + +CREATE_TYPE_TESTS (float, Float, "%f", 2.3, (value >= 2.295 && value <= 2.305), "4.a", ((int) value == 0)) +CREATE_TYPE_TESTS (double, Double, "%1.8f", 1.00000001, (value >= 1 + 1e-9 && value <= 1 + 1e-7), "4.a", ((int) value == 0)) + +REGISTER_FUNC_DEFINITION (kdb_boolean_t, KdbBoolean) +CREATE_UPDATE_TEST_CASE (kdb_boolean_t, KdbBoolean, "%d", 1, (value)) + +REGISTER_FUNC_DEFINITION (kdb_char_t, KdbChar) +CREATE_UPDATE_TEST_CASE (kdb_char_t, KdbChar, "abc%d", 1, (value == 'a')) + +CREATE_TYPE_TESTS (kdb_octet_t, KdbOctet, "%d", 255, (value == 255), "4a", value == 0) +CREATE_TYPE_TESTS (kdb_short_t, KdbShort, "%d", SHRT_MIN, (value == SHRT_MIN), "-55ABC", value == 0) +CREATE_TYPE_TESTS (kdb_unsigned_short_t, KdbUnsignedShort, "%d", USHRT_MAX, (value == USHRT_MAX), "55ABC", value == 0) +CREATE_TYPE_TESTS (kdb_long_t, KdbLong, "%d", INT_MIN, (value == INT_MIN), "B5C", value == 0) +CREATE_TYPE_TESTS (kdb_unsigned_long_t, KdbUnsignedLong, "%d", UINT_MAX, (value == UINT_MAX), "B5C", value == 0) +CREATE_TYPE_TESTS (kdb_long_long_t, KdbLongLong, ELEKTRA_LONG_LONG_F, LONG_MIN, (value == LONG_MIN), "50000asasd", value == 0) +CREATE_TYPE_TESTS (kdb_unsigned_long_long_t, KdbUnsignedLongLong, ELEKTRA_UNSIGNED_LONG_LONG_F, ULONG_MAX, (value == ULONG_MAX), "-B5C", + value == 0) +CREATE_TYPE_TESTS (kdb_float_t, KdbFloat, "%f", 2.3, (value >= 2.295 && value <= 2.305), "4.a", ((int) value == 0)) +CREATE_TYPE_TESTS (kdb_double_t, KdbDouble, "%1.8f", 1.00000001, (value >= 1 + 1e-9 && value <= 1 + 1e-7), "4.a", ((int) value == 0)) +CREATE_TYPE_TESTS (kdb_long_double_t, KdbLongDouble, "%1.8f", 1.00000001, (value >= 1 + 1e-9 && value <= 1 + 1e-7), "4.a", + ((int) value == 0)) + int main (int argc, char ** argv) { printf ("INTERNALNOTIFICATION TESTS\n"); @@ -616,26 +473,34 @@ int main (int argc, char ** argv) test_intUpdateWithValueNotYetExceedingIntMin (); test_intNoUpdateWithValueExceedingIntMin (); - printf ("\nregisterLong\n-----------\n"); - test_longUpdateWithCascadingKey (); - test_longNoUpdateWithInvalidValue (); - - printf ("\nregisterUnsignedLong\n-----------\n"); - test_unsignedLongUpdateWithCascadingKey (); - test_unsignedLongNoUpdateWithInvalidValue (); - - printf ("\nregisterFloat\n-----------\n"); - test_floatUpdateWithCascadingKey (); - test_floatNoUpdateWithInvalidValue (); - - printf ("\nregisterDouble\n-----------\n"); - test_doubleUpdateWithCascadingKey (); - test_doubleNoUpdateWithInvalidValue (); - printf ("\nregisterCallback\n----------------\n"); test_callbackCalledWithKey (); test_callbackCalledWithChangeDetection (); + RUN_TYPE_TESTS (UnsignedInt) + RUN_TYPE_TESTS (Long) + RUN_TYPE_TESTS (UnsignedLong) + + RUN_TYPE_TESTS (Float) + RUN_TYPE_TESTS (Double) + + printf ("\nKdbBoolean\n----------------\n"); + TEST_CASE_UPDATE_NAME (KdbBoolean) (); + + printf ("\nKdbChar\n----------------\n"); + TEST_CASE_UPDATE_NAME (KdbChar) (); + + RUN_TYPE_TESTS (KdbOctet) + RUN_TYPE_TESTS (KdbShort) + RUN_TYPE_TESTS (KdbUnsignedShort) + RUN_TYPE_TESTS (KdbLong) + RUN_TYPE_TESTS (KdbUnsignedLong) + RUN_TYPE_TESTS (KdbLongLong) + RUN_TYPE_TESTS (KdbUnsignedLongLong) + RUN_TYPE_TESTS (KdbFloat) + RUN_TYPE_TESTS (KdbDouble) + RUN_TYPE_TESTS (KdbLongDouble) + printf ("\ntestmod_internalnotification RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError); return nbError; From 8c1cb441950b303877b230304d3d7fb60b35b79d Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Tue, 20 Mar 2018 19:33:56 +0100 Subject: [PATCH 04/46] kdbtypes.h: add definitions for C99 --- src/include/kdbtypes.h | 27 ++++++++++++++++++++++++++- src/plugins/boolean/boolean.c | 29 ++++++++++++++--------------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/include/kdbtypes.h b/src/include/kdbtypes.h index ad107626595..a408763a63f 100644 --- a/src/include/kdbtypes.h +++ b/src/include/kdbtypes.h @@ -48,8 +48,31 @@ using octet_t = uint8_t; // default: 0 // for C (and C++) -typedef unsigned char kdb_boolean_t; typedef unsigned char kdb_char_t; + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +// for C99+ +#include +#include + +typedef uint8_t kdb_octet_t; +typedef bool kdb_boolean_t; +typedef int16_t kdb_short_t; +typedef int32_t kdb_long_t; +typedef int64_t kdb_long_long_t; +typedef uint16_t kdb_unsigned_short_t; +typedef uint32_t kdb_unsigned_long_t; +typedef uint64_t kdb_unsigned_long_long_t; + +#define ELEKTRA_LONG_F "%d" +#define ELEKTRA_UNSIGNED_LONG_F "%u" +#define ELEKTRA_LONG_LONG_F "%ld" +#define ELEKTRA_LONG_LONG_S strtoll +#define ELEKTRA_UNSIGNED_LONG_LONG_F "%lu" +#define ELEKTRA_UNSIGNED_LONG_LONG_S strtoull + +#else // for C89 +typedef unsigned char kdb_boolean_t; typedef unsigned char kdb_octet_t; typedef signed short kdb_short_t; typedef unsigned short kdb_unsigned_short_t; @@ -86,6 +109,8 @@ typedef long long kdb_long_long_t; typedef unsigned long long kdb_unsigned_long_long_t; #endif +#endif // for C89 + typedef float kdb_float_t; typedef double kdb_double_t; diff --git a/src/plugins/boolean/boolean.c b/src/plugins/boolean/boolean.c index 040e19521e5..68bff80d1de 100644 --- a/src/plugins/boolean/boolean.c +++ b/src/plugins/boolean/boolean.c @@ -29,8 +29,8 @@ typedef enum { typedef struct { - char * true; - char * false; + char * trueValue; + char * falseValue; InvalidAction invalid; char ** trueValues; char ** falseValues; @@ -115,14 +115,14 @@ static void normalize (Key * key, Key * parentKey, BoolData * data) if (isTrue (value, trueStrings)) { keySetMeta (key, "origvalue", keyString (key)); - ELEKTRA_LOG_DEBUG ("Convert “%s” to “%s”", value, data->true); - keySetString (key, data->true); + ELEKTRA_LOG_DEBUG ("Convert “%s” to “%s”", value, data->trueValue); + keySetString (key, data->trueValue); } else if (isFalse (value, falseStrings)) { keySetMeta (key, "origvalue", keyString (key)); - ELEKTRA_LOG_DEBUG ("Convert “%s” to “%s”", value, data->false); - keySetString (key, data->false); + ELEKTRA_LOG_DEBUG ("Convert “%s” to “%s”", value, data->falseValue); + keySetString (key, data->falseValue); } else { @@ -135,20 +135,20 @@ static void normalize (Key * key, Key * parentKey, BoolData * data) { ELEKTRA_ADD_WARNINGF (ELEKTRA_WARNING_INVALID_BOOL, parentKey, "Key %s with value %s is not a valid boolean. Defaulting to %s.", keyName (key), - keyString (key), data->true); + keyString (key), data->trueValue); } keySetMeta (key, "origvalue", keyString (key)); - keySetString (key, data->true); + keySetString (key, data->trueValue); break; case FALSE: if (data->invalid & WARNING) { ELEKTRA_ADD_WARNINGF (ELEKTRA_WARNING_INVALID_BOOL, parentKey, "Key %s with value %s is not a valid boolean. Defaulting to %s.", keyName (key), - keyString (key), data->false); + keyString (key), data->falseValue); } keySetMeta (key, "origvalue", keyString (key)); - keySetString (key, data->false); + keySetString (key, data->falseValue); break; case WARNING: break; @@ -228,8 +228,8 @@ static void parseConfig (KeySet * config, BoolData * data) else data->invalid |= WARNING; } - data->true = (char *) trueValue; - data->false = (char *) falseValue; + data->trueValue = (char *) trueValue; + data->falseValue = (char *) falseValue; Key * validTrueKey = ksLookupByName (config, "/true", 0); Key * validFalseKey = ksLookupByName (config, "/false", 0); if (validTrueKey) @@ -323,8 +323,8 @@ int elektraBooleanSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA parseConfig (config, data); elektraPluginSetData (handle, data); } - const char * trueValue = data->true; - const char * falseValue = data->false; + const char * trueValue = data->trueValue; + const char * falseValue = data->falseValue; ksRewind (returned); Key * key; @@ -360,4 +360,3 @@ Plugin * ELEKTRA_PLUGIN_EXPORT (boolean) ELEKTRA_PLUGIN_SET, &elektraBooleanSet, ELEKTRA_PLUGIN_END); } - From 18be80e6708dfb0d78ed2b399d2cc5476fb39231 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Wed, 21 Mar 2018 10:04:40 +0100 Subject: [PATCH 05/46] kdbtypes.h: fix formatting/converter defintions for C99 --- src/include/kdbtypes.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/include/kdbtypes.h b/src/include/kdbtypes.h index a408763a63f..712eeda59d3 100644 --- a/src/include/kdbtypes.h +++ b/src/include/kdbtypes.h @@ -64,12 +64,25 @@ typedef uint16_t kdb_unsigned_short_t; typedef uint32_t kdb_unsigned_long_t; typedef uint64_t kdb_unsigned_long_long_t; +#if SIZEOF_LONG == 4 +#define ELEKTRA_LONG_F "%ld" +#define ELEKTRA_UNSIGNED_LONG_F "%lu" +#elif SIZEOF_INT == 4 #define ELEKTRA_LONG_F "%d" #define ELEKTRA_UNSIGNED_LONG_F "%u" +#endif + +#if SIZEOF_LONG == 8 #define ELEKTRA_LONG_LONG_F "%ld" -#define ELEKTRA_LONG_LONG_S strtoll +#define ELEKTRA_LONG_LONG_S strtol #define ELEKTRA_UNSIGNED_LONG_LONG_F "%lu" +#define ELEKTRA_UNSIGNED_LONG_LONG_S strtoul +#elif defined(HAVE_SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG == 8) +#define ELEKTRA_LONG_LONG_F "%lld" +#define ELEKTRA_LONG_LONG_S strtoll +#define ELEKTRA_UNSIGNED_LONG_LONG_F "%llu" #define ELEKTRA_UNSIGNED_LONG_LONG_S strtoull +#endif #else // for C89 typedef unsigned char kdb_boolean_t; From 64e2fbb10724e0da80f6fc564b5506cac34d59e8 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Wed, 21 Mar 2018 14:27:17 +0100 Subject: [PATCH 06/46] kdbtypes.h: fix for divergent int64_t definition on macOS --- src/include/kdbtypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/kdbtypes.h b/src/include/kdbtypes.h index 712eeda59d3..73ac3050cbd 100644 --- a/src/include/kdbtypes.h +++ b/src/include/kdbtypes.h @@ -72,7 +72,7 @@ typedef uint64_t kdb_unsigned_long_long_t; #define ELEKTRA_UNSIGNED_LONG_F "%u" #endif -#if SIZEOF_LONG == 8 +#if SIZEOF_LONG == 8 && !defined(__APPLE__) #define ELEKTRA_LONG_LONG_F "%ld" #define ELEKTRA_LONG_LONG_S strtol #define ELEKTRA_UNSIGNED_LONG_LONG_F "%lu" From 9239562b59b7b80c54813a0adf5ee57f99d15c0e Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Tue, 20 Mar 2018 19:33:56 +0100 Subject: [PATCH 07/46] notification: fix formatting --- src/include/kdbnotificationinternal.h | 2 +- src/plugins/internalnotification/internalnotification.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/kdbnotificationinternal.h b/src/include/kdbnotificationinternal.h index 41c8ca40c6f..487a6fe4d02 100644 --- a/src/include/kdbnotificationinternal.h +++ b/src/include/kdbnotificationinternal.h @@ -47,7 +47,7 @@ extern "C" { return 0; \ } \ ELEKTRA_NOTIFICATION_REGISTERFUNC_TYPEDEF (RegisterFuncType, TYPE) \ - RegisterFuncType registerFunc = (RegisterFuncType)func; \ + RegisterFuncType registerFunc = (RegisterFuncType) func; \ return registerFunc (notificationPlugin, key, variable); \ } diff --git a/src/plugins/internalnotification/internalnotification.h b/src/plugins/internalnotification/internalnotification.h index 60c77d4cb9e..1d6d3394c7b 100644 --- a/src/plugins/internalnotification/internalnotification.h +++ b/src/plugins/internalnotification/internalnotification.h @@ -43,7 +43,7 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define INTERNALNOTIFICATION_TYPE(TYPE, VALUE_TYPE, TYPE_NAME, TO_VALUE, CHECK_CONVERSION) \ INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE (TYPE_NAME) \ { \ - TYPE * variable = (TYPE *)context; \ + TYPE * variable = (TYPE *) context; \ char * end ELEKTRA_UNUSED; \ const char * string = keyValue (key); \ errno = 0; \ From afd2bb0a30826475a9af64550e5e276e8732d7ac Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Tue, 20 Mar 2018 19:33:56 +0100 Subject: [PATCH 08/46] notification: added news --- doc/news/_preparation_next_release.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/doc/news/_preparation_next_release.md b/doc/news/_preparation_next_release.md index 8bafd4e7854..be1d855aae5 100644 --- a/doc/news/_preparation_next_release.md +++ b/doc/news/_preparation_next_release.md @@ -362,7 +362,14 @@ Thanks to Daniel Bugl. - replaced strdup with elektraStrDup (for C99 compatibility) *(Markus Raab)* - You can now remove the basename of a key via the C++ API by calling `key.delBaseName()`. *(René Schwaiger)* - The function `elektraArrayGetNextKey` now uses `NULL` instead of the empty string as init value for the returned key. *(René Schwaiger)* - +- The + [notification API](https://doc.libelektra.org/api/current/html/group__kdbnotification.html) + was extended. + The API now supports contexts for callbacks, the types `int`, `unsigned int`, + `long`, `unsigned long`, `float` and `double`. + It also supports all of Elektra's `kdb_*_t` types defined in `kdbtypes.h`. +- <> +- <> - <> ### pluginprocess @@ -477,6 +484,18 @@ Thanks to Daniel Bugl. [Markdown Shell Recorder]: https://master.libelektra.org/tests/shell/shell_recorder/tutorial_wrapper [Shell Recorder]: (https://master.libelektra.org/tests/shell/shell_recorder) +## Compatibility + +As always, the ABI and API of kdb.h is fully compatible, i.e. programs +compiled against an older 0.8 version of Elektra will continue to work +(ABI) and you will be able to recompile programs without errors (API). + +- <> +- <> +- <> +- `kdbtypes.h` now comes with support for C99 types +- We added the private headerfiles `kdbnotificationinternal.h`, `kdbioplugin.h`. + ## Build ### CMake From d1b26a254fae46f095f382a51fdca377a541d2bb Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Thu, 22 Mar 2018 17:46:02 +0100 Subject: [PATCH 09/46] notification: added elektraNotificationRegisterCallbackSameOrBelow() --- src/include/kdbnotification.h | 14 ++ src/include/kdbnotificationinternal.h | 14 ++ src/libs/notification/notification.c | 27 +++ .../internalnotification.c | 202 +++++++++++++----- 4 files changed, 203 insertions(+), 54 deletions(-) diff --git a/src/include/kdbnotification.h b/src/include/kdbnotification.h index a5dc41eb01c..0e6d2f904f6 100644 --- a/src/include/kdbnotification.h +++ b/src/include/kdbnotification.h @@ -169,6 +169,20 @@ typedef void (*ElektraNotificationChangeCallback) (Key * key, void * context); */ int elektraNotificationRegisterCallback (KDB * kdb, Key * key, ElektraNotificationChangeCallback callback, void * context); +/** + * @ingroup kdbnotification + * Subscribe for updates via callback when a given key or a key below changed. + * + * @param handle plugin handle + * @param key key to watch for changes + * @param callback callback function + * @param context user supplied context passed to callback function + * + * @retval 1 on success + * @retval 0 on failure + */ +int elektraNotificationRegisterCallbackSameOrBelow (KDB * kdb, Key * key, ElektraNotificationChangeCallback callback, void * context); + #ifdef __cplusplus } diff --git a/src/include/kdbnotificationinternal.h b/src/include/kdbnotificationinternal.h index 487a6fe4d02..1d161ebc7ac 100644 --- a/src/include/kdbnotificationinternal.h +++ b/src/include/kdbnotificationinternal.h @@ -65,6 +65,20 @@ extern "C" { typedef int (*ElektraNotificationPluginRegisterCallback) (Plugin * handle, Key * key, ElektraNotificationChangeCallback callback, void * context); +/** + * Subscribe for updates via callback when a given key or a key below is changed. + * + * @param handle plugin handle + * @param key key to watch for changes + * @param callback callback function + * @param context user supplied context passed to callback function + * + * @retval 1 on success + * @retval 0 on failure + */ +typedef int (*ElektraNotificationPluginRegisterCallbackSameOrBelow) (Plugin * handle, Key * key, ElektraNotificationChangeCallback callback, + void * context); + /** * Context for notification callbacks. */ diff --git a/src/libs/notification/notification.c b/src/libs/notification/notification.c index 18b5bd29ccc..12709f16fe0 100644 --- a/src/libs/notification/notification.c +++ b/src/libs/notification/notification.c @@ -896,3 +896,30 @@ int elektraNotificationRegisterCallback (KDB * kdb, Key * key, ElektraNotificati ElektraNotificationPluginRegisterCallback registerFunc = (ElektraNotificationPluginRegisterCallback) func; return registerFunc (notificationPlugin, key, callback, context); } + +int elektraNotificationRegisterCallbackSameOrBelow (KDB * kdb, Key * key, ElektraNotificationChangeCallback callback, void * context) +{ + if (!kdb || !key || !callback) + { + ELEKTRA_LOG_WARNING ("null pointer passed"); + return 0; + } + + // Find notification plugin + Plugin * notificationPlugin = getNotificationPlugin (kdb); + if (!notificationPlugin) + { + return 0; + } + + // Get register function from plugin + size_t func = elektraPluginGetFunction (notificationPlugin, "registerCallbackSameOrBelow"); + if (!func) + { + return 0; + } + + // Call register function + ElektraNotificationPluginRegisterCallbackSameOrBelow registerFunc = (ElektraNotificationPluginRegisterCallbackSameOrBelow) func; + return registerFunc (notificationPlugin, key, callback, context); +} diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index e3a98d4556b..029b7051cb3 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -17,23 +17,6 @@ #include // errno #include // strto* functions -/* - checklist for types - [ ] TYPE_BOOLEAN = 1 << 0, - [ ] TYPE_CHAR = 1 << 1, - [ ] TYPE_OCTET = 1 << 2, - [ ] TYPE_SHORT = 1 << 3, - [ ] TYPE_UNSIGNED_SHORT = 1 << 4, - [x] TYPE_INT = 1 << 5, - [x] TYPE_LONG = 1 << 6, - [x] TYPE_UNSIGNED_LONG = 1 << 7, - [ ] TYPE_LONG_LONG = 1 << 8, - [ ] TYPE_UNSIGNED_LONG_LONG = 1 << 9, - [x] TYPE_FLOAT = 1 << 10, - [x] TYPE_DOUBLE = 1 << 11, - [ ] TYPE_LONG_DOUBLE = 1 << 12, - [x] TYPE_CALLBACK = 1 << 13, -*/ /** * Structure for registered key variable pairs @@ -43,6 +26,7 @@ struct _KeyRegistration { char * name; char * lastValue; + int sameOrBelow; ElektraNotificationChangeCallback callback; void * context; struct _KeyRegistration * next; @@ -62,20 +46,66 @@ typedef struct _PluginState PluginState; /** * @internal - * Convert key name into cascading key name for comparison. + * Check if two keys have the same name. + * If one of the keys is cascading only the cascading names are compared. * - * Key name is not modified, nothing to free + * @param key key + * @param check check + * @retval 1 if keys have the same name + * @retval 0 otherwise + */ +static int checkKeyIsSame (Key * key, Key * check) +{ + int result = 0; + if (keyGetNamespace (check) == KEY_NS_CASCADING || keyGetNamespace (key) == KEY_NS_CASCADING) + { + const char * cascadingCheck = strrchr (keyName (check), '/'); + const char * cascadingKey = strrchr (keyName (key), '/'); + if (cascadingCheck != NULL && cascadingKey != NULL) + { + result = elektraStrCmp (cascadingKey, cascadingCheck) == 0; + } + else + { + if (cascadingCheck == NULL) + { + ELEKTRA_LOG_WARNING ("invalid key given: '%s' is not a valid key", cascadingCheck); + } + if (cascadingKey == NULL) + { + ELEKTRA_LOG_WARNING ("invalid key given: '%s' is not a valid key", cascadingKey); + } + } + } + else + { + result = elektraStrCmp (keyName (check), keyName (key)) == 0; + } + return result; +} + +/** + * @internal + * Check if a key has the same name or is below a given key. * - * @param keyName key name - * @return pointer to cascading name + * @param key key + * @param check check + * @retval 1 if key has the same name or is below + * @retval 0 otherwise */ -static const char * toCascadingName (const char * keyName) +static int checkKeyIsBelowOrSame (Key * key, Key * check) { - while (keyName[0] != '/') + int result = 0; + if (keyIsBelow (key, check)) { - keyName++; + result = 1; } - return keyName; + else + { + result = checkKeyIsSame (key, check); + } + + return result; } /** @@ -103,21 +133,21 @@ static void elektraInternalnotificationDoUpdate (Key * changedKey, ElektraNotifi KeyRegistration * keyRegistration = pluginState->head; while (keyRegistration != NULL) { - Key * registereKey = keyNew (keyRegistration->name, KEY_END); + Key * registeredKey = keyNew (keyRegistration->name, KEY_END); - if (keyIsBelow (changedKey, registereKey)) + if (keyRegistration->sameOrBelow) { - kdbChanged |= 1; + // check if changed key is same or below registered key + kdbChanged |= checkKeyIsBelowOrSame (registeredKey, changedKey); } - else if (keyGetNamespace (registereKey) == KEY_NS_CASCADING || keyGetNamespace (changedKey) == KEY_NS_CASCADING) + else { - const char * cascadingRegisterdKey = toCascadingName (keyRegistration->name); - const char * cascadingChangedKey = toCascadingName (keyName (changedKey)); - kdbChanged |= elektraStrCmp (cascadingChangedKey, cascadingRegisterdKey) == 0; + // check if changed key is same as registered key + kdbChanged |= checkKeyIsSame (changedKey, registeredKey); } keyRegistration = keyRegistration->next; - keyDel (registereKey); + keyDel (registeredKey); } if (kdbChanged) @@ -150,6 +180,7 @@ static KeyRegistration * elektraInternalnotificationAddNewRegistration (PluginSt item->name = elektraStrDup (keyName (key)); item->callback = callback; item->context = context; + item->sameOrBelow = 0; if (pluginState->head == NULL) { @@ -166,6 +197,31 @@ static KeyRegistration * elektraInternalnotificationAddNewRegistration (PluginSt return item; } +/** + * @internal + * Check if a key set contains a key that is same or below a given key. + * + * @param key key + * @param ks key set + * @retval 1 if the key set contains the key + * @retval 0 otherwise + */ +static int keySetContainsSameOrBelow (Key * check, KeySet * ks) +{ + int result = 0; + Key * current; + ksRewind (ks); + while ((current = ksNext (ks)) != NULL) + { + if (checkKeyIsBelowOrSame (check, current)) + { + result = 1; + break; + } + } + return result; +} + /** * Updates all KeyRegistrations according to data from the given KeySet * @internal @@ -183,37 +239,54 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * KeyRegistration * registeredKey = pluginState->head; while (registeredKey != NULL) { - Key * key = ksLookupByName (keySet, registeredKey->name, 0); - if (key == NULL) - { - registeredKey = registeredKey->next; - continue; - } - - // Detect changes for string keys int changed = 0; - if (!keyIsString (key)) + Key * key; + if (registeredKey->sameOrBelow) { - // always notify for binary keys - changed = 1; + Key * checkKey = keyNew (registeredKey->name, KEY_END); + if (keySetContainsSameOrBelow (checkKey, keySet)) + { + changed = 1; + key = checkKey; + } + else + { + keyDel (checkKey); + } } else { - const char * currentValue = keyString (key); - changed = registeredKey->lastValue == NULL || strcmp (currentValue, registeredKey->lastValue) != 0; + key = ksLookupByName (keySet, registeredKey->name, 0); + if (key == NULL) + { + registeredKey = registeredKey->next; + continue; + } - if (changed) + // Detect changes for string keys + if (!keyIsString (key)) + { + // always notify for binary keys + changed = 1; + } + else { - // Save last value - char * buffer = elektraStrDup (currentValue); - if (buffer) + const char * currentValue = keyString (key); + changed = registeredKey->lastValue == NULL || strcmp (currentValue, registeredKey->lastValue) != 0; + + if (changed) { - if (registeredKey->lastValue != NULL) + // Save last value + char * buffer = elektraStrDup (currentValue); + if (buffer) { - // Free previous value - elektraFree (registeredKey->lastValue); + if (registeredKey->lastValue != NULL) + { + // Free previous value + elektraFree (registeredKey->lastValue); + } + registeredKey->lastValue = buffer; } - registeredKey->lastValue = buffer; } } } @@ -281,6 +354,25 @@ int elektraInternalnotificationRegisterCallback (Plugin * handle, Key * key, Ele return 1; } +/** + * @see kdbnotificationinternal.h ::ElektraNotificationPluginRegisterCallbackSameOrBelow + */ +int elektraInternalnotificationRegisterCallbackSameOrBelow (Plugin * handle, Key * key, ElektraNotificationChangeCallback callback, + void * context) +{ + PluginState * pluginState = elektraPluginGetData (handle); + ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); + + KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration (pluginState, key, callback, context); + if (registeredKey == NULL) + { + return 0; + } + registeredKey->sameOrBelow = 1; + + return 1; +} + /** * Updates registrations with current data from storage. * Part of elektra plugin contract. @@ -328,6 +420,8 @@ int elektraInternalnotificationGet (Plugin * handle, KeySet * returned, Key * pa keyNew ("system/elektra/modules/internalnotification/exports/registerCallback", KEY_FUNC, elektraInternalnotificationRegisterCallback, KEY_END), + keyNew ("system/elektra/modules/internalnotification/exports/registerCallbackSameOrBelow", KEY_FUNC, + elektraInternalnotificationRegisterCallbackSameOrBelow, KEY_END), #include ELEKTRA_README (internalnotification) From 0e5be96e9b87976299fbf8f23a697a7048bca67b Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Thu, 22 Mar 2018 22:57:30 +0100 Subject: [PATCH 10/46] internalnotification: added tests for update function --- src/include/kdbnotificationinternal.h | 12 +- src/libs/notification/notification.c | 11 ++ .../internalnotification.c | 17 +- .../internalnotification.h | 2 + .../testmod_internalnotification.c | 159 ++++++++++++++++++ 5 files changed, 189 insertions(+), 12 deletions(-) diff --git a/src/include/kdbnotificationinternal.h b/src/include/kdbnotificationinternal.h index 1d161ebc7ac..a89c267a04d 100644 --- a/src/include/kdbnotificationinternal.h +++ b/src/include/kdbnotificationinternal.h @@ -114,6 +114,14 @@ typedef void (*ElektraNotificationOpenNotification) (Plugin * handle, KeySet * p */ typedef void (*ElektraNotificationCloseNotification) (Plugin * handle, KeySet * parameters); +/** + * Used by notification plugins to get values from the key database. + * + * @param kdb kdb handle + * @param changedKey which key was updated + */ +typedef void (*ElektraNotificationKdbUpdate) (KDB * kdb, Key * changedKey); + /** * Private struct with information about for ElektraNotificationCallback. * @internal @@ -122,7 +130,9 @@ typedef void (*ElektraNotificationCloseNotification) (Plugin * handle, KeySet * */ struct _ElektraNotificationCallbackContext { - KDB * kdb; /*!< The pointer the KDB handle.*/ + KDB * kdb; /*!< The pointer to kdb handle.*/ + + ElektraNotificationKdbUpdate kdbUpdate; /*!< The pointer to the update function.*/ Plugin * notificationPlugin; /*!< Notification plugin handle.*/ }; diff --git a/src/libs/notification/notification.c b/src/libs/notification/notification.c index 12709f16fe0..88f704875eb 100644 --- a/src/libs/notification/notification.c +++ b/src/libs/notification/notification.c @@ -719,6 +719,16 @@ static void pluginsCloseNotification (KDB * kdb) } } +/** + * @see kdbnotificationinternal.h ::ElektraNotificationKdbUpdate + */ +static void elektraNotificationKdbUpdate (KDB * kdb, Key * changedKey) +{ + KeySet * ks = ksNew (0, KS_END); + kdbGet (kdb, ks, changedKey); + ksDel (ks); +} + int elektraNotificationOpen (KDB * kdb) { // Make sure kdb is not null @@ -760,6 +770,7 @@ int elektraNotificationOpen (KDB * kdb) return 0; } context->kdb = kdb; + context->kdbUpdate = &elektraNotificationKdbUpdate; context->notificationPlugin = notificationPlugin; // Get notification callback from notification plugin diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index 029b7051cb3..12175c7c087 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -118,12 +118,11 @@ static int checkKeyIsBelowOrSame (Key * key, Key * check) * @param key changed key * @param context callback context */ -static void elektraInternalnotificationDoUpdate (Key * changedKey, ElektraNotificationCallbackContext * context) +void elektraInternalnotificationDoUpdate (Key * changedKey, ElektraNotificationCallbackContext * context) { ELEKTRA_NOT_NULL (changedKey); ELEKTRA_NOT_NULL (context); - KDB * kdb = context->kdb; Plugin * plugin = context->notificationPlugin; PluginState * pluginState = elektraPluginGetData (plugin); @@ -135,16 +134,14 @@ static void elektraInternalnotificationDoUpdate (Key * changedKey, ElektraNotifi { Key * registeredKey = keyNew (keyRegistration->name, KEY_END); + // check if registered key is same or below changed/commit key + kdbChanged |= checkKeyIsBelowOrSame (changedKey, registeredKey); + if (keyRegistration->sameOrBelow) { - // check if changed key is same or below registered key + // check if registered key is also above changed/commit key kdbChanged |= checkKeyIsBelowOrSame (registeredKey, changedKey); } - else - { - // check if changed key is same as registered key - kdbChanged |= checkKeyIsSame (changedKey, registeredKey); - } keyRegistration = keyRegistration->next; keyDel (registeredKey); @@ -152,9 +149,7 @@ static void elektraInternalnotificationDoUpdate (Key * changedKey, ElektraNotifi if (kdbChanged) { - KeySet * ks = ksNew (0, KS_END); - kdbGet (kdb, ks, changedKey); - ksDel (ks); + context->kdbUpdate (context->kdb, changedKey); } keyDel (changedKey); } diff --git a/src/plugins/internalnotification/internalnotification.h b/src/plugins/internalnotification/internalnotification.h index 1d6d3394c7b..d7c28e3e5a3 100644 --- a/src/plugins/internalnotification/internalnotification.h +++ b/src/plugins/internalnotification/internalnotification.h @@ -11,6 +11,7 @@ #define ELEKTRA_PLUGIN_INTERNALNOTIFICATION_H #include +#include #include @@ -23,6 +24,7 @@ Plugin * ELEKTRA_PLUGIN_EXPORT (internalnotification); // Not exported by plugin; used for testing void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * keySet); +void elektraInternalnotificationDoUpdate (Key * changedKey, ElektraNotificationCallbackContext * context); #define INTERNALNOTIFICATION_REGISTER_NAME(TYPE_NAME) elektraInternalnotificationRegister##TYPE_NAME #define INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME(TYPE_NAME) elektraInternalnotificationConvert##TYPE_NAME diff --git a/src/plugins/internalnotification/testmod_internalnotification.c b/src/plugins/internalnotification/testmod_internalnotification.c index bbe9c3102b2..87ef92ad133 100644 --- a/src/plugins/internalnotification/testmod_internalnotification.c +++ b/src/plugins/internalnotification/testmod_internalnotification.c @@ -24,6 +24,8 @@ int callback_called; char * callback_keyValue; char * callback_keyName; +int doUpdate_callback_called; + #define CALLBACK_CONTEXT_MAGIC_NUMBER ((void *) 1234) #define REGISTER_FUNC_NAME(TYPE_NAME) internalnotificationRegister##TYPE_NAME @@ -107,6 +109,16 @@ static int internalnotificationRegisterCallback (Plugin * plugin, Key * key, Ele return ((ElektraNotificationPluginRegisterCallback) address) (plugin, key, callback, context); } +static int internalnotificationRegisterCallbackSameOrBelow (Plugin * plugin, Key * key, ElektraNotificationChangeCallback callback, + void * context) +{ + size_t address = elektraPluginGetFunction (plugin, "registerCallbackSameOrBelow"); + if (!address) yield_error ("function not exported"); + + // Register key with plugin + return ((ElektraNotificationPluginRegisterCallbackSameOrBelow) address) (plugin, key, callback, context); +} + static int digits (long long number) { int digits = 0; @@ -428,6 +440,146 @@ static void test_callbackCalledWithChangeDetection (void) PLUGIN_CLOSE (); } +static void test_doUpdate_callback (KDB * kdb ELEKTRA_UNUSED, Key * changedKey ELEKTRA_UNUSED) +{ + doUpdate_callback_called = 1; +} + +static void test_doUpdateShouldUpdateKey (void) +{ + printf ("test doUpdate should update same key\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * changedKey = keyNew ("user/test/internalnotification/value", KEY_END); + + succeed_if (internalnotificationRegisterCallback (plugin, changedKey, test_callback, NULL) == 1, + "call to elektraInternalnotificationRegisterCallback was not successful"); + + ElektraNotificationCallbackContext * context = elektraMalloc (sizeof *context); + context->kdbUpdate = NULL; + context->kdbUpdate = test_doUpdate_callback; + context->notificationPlugin = plugin; + + doUpdate_callback_called = 0; + elektraInternalnotificationDoUpdate (changedKey, context); + + succeed_if (doUpdate_callback_called, "did not call callback for registered key"); + + elektraFree (context); + PLUGIN_CLOSE (); +} + +static void test_doUpdateShouldUpdateKeyBelow (void) +{ + printf ("test doUpdate should update key below changed key\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * changedKey = keyNew ("user/test/internalnotification", KEY_END); + + Key * registeredKey = keyNew ("user/test/internalnotification/value", KEY_END); + succeed_if (internalnotificationRegisterCallback (plugin, registeredKey, test_callback, NULL) == 1, + "call to elektraInternalnotificationRegisterCallback was not successful"); + + ElektraNotificationCallbackContext * context = elektraMalloc (sizeof *context); + context->kdbUpdate = NULL; + context->kdbUpdate = test_doUpdate_callback; + context->notificationPlugin = plugin; + + doUpdate_callback_called = 0; + elektraInternalnotificationDoUpdate (changedKey, context); + + succeed_if (doUpdate_callback_called, "did not call callback for registered key"); + + elektraFree (context); + keyDel (registeredKey); + PLUGIN_CLOSE (); +} + +static void test_doUpdateShouldNotUpdateKeyAbove (void) +{ + printf ("test doUpdate should not update key above changed key\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * changedKey = keyNew ("user/test/internalnotification/value", KEY_END); + + Key * registeredKey = keyNew ("user/test/internalnotification", KEY_END); + succeed_if (internalnotificationRegisterCallback (plugin, registeredKey, test_callback, NULL) == 1, + "call to elektraInternalnotificationRegisterCallback was not successful"); + + ElektraNotificationCallbackContext * context = elektraMalloc (sizeof *context); + context->kdbUpdate = NULL; + context->kdbUpdate = test_doUpdate_callback; + context->notificationPlugin = plugin; + + doUpdate_callback_called = 0; + elektraInternalnotificationDoUpdate (changedKey, context); + + succeed_if (doUpdate_callback_called == 0, "did call callback for key above"); + + elektraFree (context); + keyDel (registeredKey); + PLUGIN_CLOSE (); +} + +static void test_doUpdateShouldUpdateKeyAbove (void) +{ + printf ("test doUpdate should update key above changed key for sameOrBelow callbacks\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * changedKey = keyNew ("user/test/internalnotification/value", KEY_END); + + Key * registeredKey = keyNew ("user/test/internalnotification", KEY_END); + succeed_if (internalnotificationRegisterCallbackSameOrBelow (plugin, registeredKey, test_callback, NULL) == 1, + "call to internalnotificationRegisterCallbackSameOrBelow was not successful"); + + ElektraNotificationCallbackContext * context = elektraMalloc (sizeof *context); + context->kdbUpdate = NULL; + context->kdbUpdate = test_doUpdate_callback; + context->notificationPlugin = plugin; + + doUpdate_callback_called = 0; + elektraInternalnotificationDoUpdate (changedKey, context); + + succeed_if (doUpdate_callback_called, "did not call callback for key above"); + + elektraFree (context); + keyDel (registeredKey); + PLUGIN_CLOSE (); +} + +static void test_doUpdateShouldNotUpdateUnregisteredKey (void) +{ + printf ("test doUpdate should not update unregistered key\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * changedKey = keyNew ("user/test/internalnotification/value", KEY_END); + + // No key registration made + + ElektraNotificationCallbackContext * context = elektraMalloc (sizeof *context); + context->kdbUpdate = NULL; + context->kdbUpdate = test_doUpdate_callback; + context->notificationPlugin = plugin; + + doUpdate_callback_called = 0; + elektraInternalnotificationDoUpdate (changedKey, context); + + succeed_if (doUpdate_callback_called == 0, "did call callback for unregistered key"); + + elektraFree (context); + PLUGIN_CLOSE (); +} + CREATE_TYPE_TESTS (unsigned int, UnsignedInt, "%u", UINT_MAX, (value == UINT_MAX), "-1", value == 0) CREATE_TYPE_TESTS (long, Long, "%ld", LONG_MAX, (value == LONG_MAX), "5000abc000", value == 0) CREATE_TYPE_TESTS (unsigned long, UnsignedLong, "%lu", ULONG_MAX, (value == ULONG_MAX), "AA446744073709551615", value == 0) @@ -501,6 +653,13 @@ int main (int argc, char ** argv) RUN_TYPE_TESTS (KdbDouble) RUN_TYPE_TESTS (KdbLongDouble) + printf ("\nelektraInternalnotificationDoUpdate\n-----------------------------------\n"); + test_doUpdateShouldUpdateKey (); + test_doUpdateShouldUpdateKeyBelow (); + test_doUpdateShouldNotUpdateKeyAbove (); + test_doUpdateShouldNotUpdateUnregisteredKey (); + test_doUpdateShouldUpdateKeyAbove (); + printf ("\ntestmod_internalnotification RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError); return nbError; From e0f29762fd66d710d0e8a4527b11bd3b9513308c Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sat, 24 Mar 2018 11:56:44 +0100 Subject: [PATCH 11/46] list: remove unused declarations --- src/plugins/list/list.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/list/list.h b/src/plugins/list/list.h index 73f2ec87635..cce67148e4f 100644 --- a/src/plugins/list/list.h +++ b/src/plugins/list/list.h @@ -21,9 +21,7 @@ int elektraListSet (Plugin * handle, KeySet * ks, Key * parentKey); int elektraListError (Plugin * handle, KeySet * ks, Key * parentKey); int elektraListAddPlugin (Plugin * handle, KeySet * pluginConfig); int elektraListEditPlugin (Plugin * handle, KeySet * pluginConfig); -void elektraListSetIoBinding (Plugin * handle, ElektraIoInterface * binding); -void elektraListOpenNotification (Plugin * handle, ElektraNotificationCallback callback, ElektraNotificationCallbackContext * context); -void elektraListCloseNotification (Plugin * handle); + Plugin * ELEKTRA_PLUGIN_EXPORT (list); #endif From 56543aedee742f18adc2cfbf15c1dc0808c79b9e Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sat, 24 Mar 2018 11:31:41 +0100 Subject: [PATCH 12/46] decision "deferred plugin calls": minor changes --- doc/decisions/deferred_plugin_calls.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/decisions/deferred_plugin_calls.md b/doc/decisions/deferred_plugin_calls.md index 0d79775031e..9fc323b79fd 100644 --- a/doc/decisions/deferred_plugin_calls.md +++ b/doc/decisions/deferred_plugin_calls.md @@ -67,14 +67,14 @@ These plugins also "hide" functions exported by encapsulated plugins. Encapsulating plugins export a function called `deferredCall` with the declaration -`void ElektraDeferredCall (Plugin * plugin, char * name, KeySet * parameters)`. +`void elektraDeferredCall (Plugin * plugin, char * name, KeySet * parameters)`. Encapsulating plugins shall save multiple deferred calls and call the exported functions specified by `name` passing the `parameters` KeySet when a plugin is initialized in the same order as received. Plugins that support deferred calls shall have the following declaration for their functions -`void ElektraDeferredCallable (Plugin * plugin, KeySet * parameters)`. +`void somePluginFunction (Plugin * plugin, KeySet * parameters)`. The calling developer is responsible for ensuring that the called functions have a compatible declaration. Encapsulated plugins that do not export a specified function name are omitted. @@ -89,15 +89,15 @@ breaking callers. The called function receive their parameters via a KeySet. While called functions could return data using the `parameters` KeySet (or a -seperate KeySet) there is no defined moment when the data can be collected. +separate KeySet) there is no defined moment when the data can be collected. Defining such a moment would break the lazy-loading constraint. It is recommended to use callbacks passed as `parameters`. The callback function declaration is not affected by this decision. ## Related decisions -- Elektra's invoke functionality will be extended to also allow use of deferred -calls with new functions: +- Elektra's invoke functionality will be extended to also allow us to use + deferred calls with new functions: - `int elektraInvokeFunctionDeferred (ElektraInvokeHandle * handle, const char * elektraPluginFunctionName, KeySet * ks)` which defers a call if the plugin exports `deferredCall`. From e5c70533b6ecceb70b0dd5c0ffef855bf3c22313 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 25 Mar 2018 16:43:23 +0200 Subject: [PATCH 13/46] notification: cleanup & use supermacros --- doc/tutorials/notifications.md | 16 +- .../internalnotification.c | 194 +++++++++++----- .../internalnotification.h | 47 +--- .../internalnotification/macros/addType.h | 71 ++++++ .../macros/testCreateTypeTests.h | 89 ++++++++ .../testmod_internalnotification.c | 211 +++++++++++------- 6 files changed, 434 insertions(+), 194 deletions(-) create mode 100644 src/plugins/internalnotification/macros/addType.h create mode 100644 src/plugins/internalnotification/macros/testCreateTypeTests.h diff --git a/doc/tutorials/notifications.md b/doc/tutorials/notifications.md index fa96d6ebb9c..577b4cd9f9d 100644 --- a/doc/tutorials/notifications.md +++ b/doc/tutorials/notifications.md @@ -2,17 +2,7 @@ ## Preface -**Not all of the features described in this document are implemented yet.** - -Development state: - - - [x] internalnotification plugin (support for int and callback) - - [x] notification wrapper (support for int and callback) - - [X] transport plugin dbus - - [ ] transport plugin zeromq - - [ ] transport plugin redis - - [ ] internalnotification plugin & notfication wrapper (support for Elektra's basic types) - +**The features described in this document are experimental.** This document explains how notifications are implemented in Elektra and how they can be used by application developers. @@ -194,8 +184,10 @@ changed key needs further processing. #define ANSI_COLOR_RED "\x1b[31m" #define ANSI_COLOR_GREEN "\x1b[32m" -void setTerminalColor (Key * color, void * context) +void setTerminalColor (Key * color, void * context ELEKTRA_UNUSED) { + // context contains whatever was passed as 4th parameter + // to elektraNotificationRegisterCallback() char * value = keyString (color); if (strcmp (value, "red") == 0) diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index 12175c7c087..cf7fa35d3cc 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -203,18 +203,16 @@ static KeyRegistration * elektraInternalnotificationAddNewRegistration (PluginSt */ static int keySetContainsSameOrBelow (Key * check, KeySet * ks) { - int result = 0; Key * current; ksRewind (ks); while ((current = ksNext (ks)) != NULL) { if (checkKeyIsBelowOrSame (check, current)) { - result = 1; - break; + return 1; } } - return result; + return 0; } /** @@ -252,35 +250,32 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * else { key = ksLookupByName (keySet, registeredKey->name, 0); - if (key == NULL) + if (key != NULL) { - registeredKey = registeredKey->next; - continue; - } - - // Detect changes for string keys - if (!keyIsString (key)) - { - // always notify for binary keys - changed = 1; - } - else - { - const char * currentValue = keyString (key); - changed = registeredKey->lastValue == NULL || strcmp (currentValue, registeredKey->lastValue) != 0; - - if (changed) + // Detect changes for string keys + if (!keyIsString (key)) { - // Save last value - char * buffer = elektraStrDup (currentValue); - if (buffer) + // always notify for binary keys + changed = 1; + } + else + { + const char * currentValue = keyString (key); + changed = registeredKey->lastValue == NULL || strcmp (currentValue, registeredKey->lastValue) != 0; + + if (changed) { - if (registeredKey->lastValue != NULL) + // Save last value + char * buffer = elektraStrDup (currentValue); + if (buffer) { - // Free previous value - elektraFree (registeredKey->lastValue); + if (registeredKey->lastValue != NULL) + { + // Free previous value + elektraFree (registeredKey->lastValue); + } + registeredKey->lastValue = buffer; } - registeredKey->lastValue = buffer; } } } @@ -296,41 +291,124 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * callback (key, registeredKey->context); } + // proceed with next registered key registeredKey = registeredKey->next; } } // Generate register and conversion functions -INTERNALNOTIFICATION_TYPE (int, long int, Int, (strtol (string, &end, 10)), - INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= INT_MAX && value >= INT_MIN)) -INTERNALNOTIFICATION_TYPE (unsigned int, unsigned long int, UnsignedInt, (strtoul (string, &end, 10)), - INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= UINT_MAX)) - -INTERNALNOTIFICATION_TYPE (long, long, Long, (strtol (string, &end, 10)), INTERNALNOTIFICATION_CHECK_CONVERSION) -INTERNALNOTIFICATION_TYPE (unsigned long, unsigned long, UnsignedLong, (strtoul (string, &end, 10)), INTERNALNOTIFICATION_CHECK_CONVERSION) - -INTERNALNOTIFICATION_TYPE (float, float, Float, (strtof (string, &end)), INTERNALNOTIFICATION_CHECK_CONVERSION) -INTERNALNOTIFICATION_TYPE (double, double, Double, (strtod (string, &end)), INTERNALNOTIFICATION_CHECK_CONVERSION) - -INTERNALNOTIFICATION_TYPE (kdb_boolean_t, int, KdbBoolean, (!strcmp (string, "1")), 1) -INTERNALNOTIFICATION_TYPE (kdb_char_t, kdb_char_t, KdbChar, (string[0]), 1) -INTERNALNOTIFICATION_TYPE (kdb_octet_t, unsigned int, KdbOctet, (strtoul (string, &end, 10)), - INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= 255)) -INTERNALNOTIFICATION_TYPE (kdb_short_t, int, KdbShort, (strtol (string, &end, 10)), - INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= SHRT_MAX && value >= SHRT_MIN)) -INTERNALNOTIFICATION_TYPE (kdb_unsigned_short_t, unsigned int, KdbUnsignedShort, (strtoul (string, &end, 10)), - INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= USHRT_MAX)) -INTERNALNOTIFICATION_TYPE (kdb_long_t, kdb_long_t, KdbLong, (strtol (string, &end, 10)), INTERNALNOTIFICATION_CHECK_CONVERSION) -INTERNALNOTIFICATION_TYPE (kdb_unsigned_long_t, kdb_unsigned_long_t, KdbUnsignedLong, (strtoul (string, &end, 10)), - INTERNALNOTIFICATION_CHECK_CONVERSION) -INTERNALNOTIFICATION_TYPE (kdb_long_long_t, kdb_long_long_t, KdbLongLong, (ELEKTRA_LONG_LONG_S (string, &end, 10)), - INTERNALNOTIFICATION_CHECK_CONVERSION) -INTERNALNOTIFICATION_TYPE (kdb_unsigned_long_long_t, kdb_unsigned_long_long_t, KdbUnsignedLongLong, - (ELEKTRA_UNSIGNED_LONG_LONG_S (string, &end, 10)), INTERNALNOTIFICATION_CHECK_CONVERSION) -INTERNALNOTIFICATION_TYPE (kdb_float_t, kdb_float_t, KdbFloat, (strtof (string, &end)), INTERNALNOTIFICATION_CHECK_CONVERSION) -INTERNALNOTIFICATION_TYPE (kdb_double_t, kdb_double_t, KdbDouble, (strtod (string, &end)), INTERNALNOTIFICATION_CHECK_CONVERSION) -INTERNALNOTIFICATION_TYPE (kdb_long_double_t, kdb_long_double_t, KdbLongDouble, (strtold (string, &end)), - INTERNALNOTIFICATION_CHECK_CONVERSION) +// for built-in C types +#define TYPE int +#define VALUE_TYPE long int +#define TYPE_NAME Int +#define TO_VALUE (strtol (string, &end, 10)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= INT_MAX && value >= INT_MIN) +#include "macros/addType.h" + +#define TYPE unsigned int +#define VALUE_TYPE unsigned long int +#define TYPE_NAME UnsignedInt +#define TO_VALUE (strtoul (string, &end, 10)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= UINT_MAX) +#include "macros/addType.h" + +#define TYPE long +#define TYPE_NAME Long +#define TO_VALUE (strtol (string, &end, 10)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#include "macros/addType.h" + +#define TYPE unsigned long +#define TYPE_NAME UnsignedLong +#define TO_VALUE (strtoul (string, &end, 10)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#include "macros/addType.h" + +#define TYPE float +#define TYPE_NAME Float +#define TO_VALUE (strtof (string, &end)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#include "macros/addType.h" + +#define TYPE double +#define TYPE_NAME Double +#define TO_VALUE (strtod (string, &end)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#include "macros/addType.h" + +// for kdb_*_t Types +#define TYPE kdb_boolean_t +#define TYPE_NAME KdbBoolean +#define TO_VALUE (!strcmp (string, "1")) +#include "macros/addType.h" + +#define TYPE kdb_char_t +#define TYPE_NAME KdbChar +#define TO_VALUE (string[0]) +#include "macros/addType.h" + +#define TYPE kdb_octet_t +#define VALUE_TYPE unsigned int +#define TYPE_NAME KdbOctet +#define TO_VALUE (strtoul (string, &end, 10)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= 255) +#include "macros/addType.h" + +#define TYPE kdb_short_t +#define VALUE_TYPE int +#define TYPE_NAME KdbShort +#define TO_VALUE (strtol (string, &end, 10)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= SHRT_MAX && value >= SHRT_MIN) +#include "macros/addType.h" + +#define TYPE kdb_unsigned_short_t +#define VALUE_TYPE unsigned int +#define TYPE_NAME KdbUnsignedShort +#define TO_VALUE (strtoul (string, &end, 10)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= USHRT_MAX) +#include "macros/addType.h" + +#define TYPE kdb_long_t +#define TYPE_NAME KdbLong +#define TO_VALUE (strtol (string, &end, 10)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#include "macros/addType.h" + +#define TYPE kdb_unsigned_long_t +#define TYPE_NAME KdbUnsignedLong +#define TO_VALUE (strtoul (string, &end, 10)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#include "macros/addType.h" + +#define TYPE kdb_long_long_t +#define TYPE_NAME KdbLongLong +#define TO_VALUE (ELEKTRA_LONG_LONG_S (string, &end, 10)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#include "macros/addType.h" + +#define TYPE kdb_unsigned_long_long_t +#define TYPE_NAME KdbUnsignedLongLong +#define TO_VALUE (ELEKTRA_UNSIGNED_LONG_LONG_S (string, &end, 10)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#include "macros/addType.h" + +#define TYPE kdb_float_t +#define TYPE_NAME KdbFloat +#define TO_VALUE (strtof (string, &end)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#include "macros/addType.h" + +#define TYPE kdb_double_t +#define TYPE_NAME KdbDouble +#define TO_VALUE (strtod (string, &end)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#include "macros/addType.h" + +#define TYPE kdb_long_double_t +#define TYPE_NAME KdbLongDouble +#define TO_VALUE (strtold (string, &end)) +#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#include "macros/addType.h" /** * @see kdbnotificationinternal.h ::ElektraNotificationPluginRegisterCallback diff --git a/src/plugins/internalnotification/internalnotification.h b/src/plugins/internalnotification/internalnotification.h index d7c28e3e5a3..4125e80c39c 100644 --- a/src/plugins/internalnotification/internalnotification.h +++ b/src/plugins/internalnotification/internalnotification.h @@ -14,7 +14,6 @@ #include #include - int elektraInternalnotificationGet (Plugin * handle, KeySet * ks, Key * parentKey); int elektraInternalnotificationSet (Plugin * handle, KeySet * ks, Key * parentKey); int elektraInternalnotificationClose (Plugin * handle, Key * errorKey); @@ -26,53 +25,13 @@ Plugin * ELEKTRA_PLUGIN_EXPORT (internalnotification); void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * keySet); void elektraInternalnotificationDoUpdate (Key * changedKey, ElektraNotificationCallbackContext * context); +#define INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE(CHECK_RANGE) (*end == 0 && errno == 0 && CHECK_RANGE) +#define INTERNALNOTIFICATION_CHECK_CONVERSION (*end == 0 && errno == 0) + #define INTERNALNOTIFICATION_REGISTER_NAME(TYPE_NAME) elektraInternalnotificationRegister##TYPE_NAME -#define INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME(TYPE_NAME) elektraInternalnotificationConvert##TYPE_NAME #define INTERNALNOTIFICATION_EXPORT_FUNCTION(TYPE_NAME) \ keyNew ("system/elektra/modules/internalnotification/exports/register" #TYPE_NAME, KEY_FUNC, \ INTERNALNOTIFICATION_REGISTER_NAME (TYPE_NAME), KEY_END) -#define INTERNALNOTIFICATION_REGISTER_SIGNATURE(TYPE, TYPE_NAME) \ - int INTERNALNOTIFICATION_REGISTER_NAME (TYPE_NAME) (Plugin * handle, Key * key, TYPE * variable) - -#define INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE(TYPE_NAME) \ - void INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME (TYPE_NAME) (Key * key, void * context) - -#define INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE(CHECK_RANGE) (*end == 0 && errno == 0 && CHECK_RANGE) -#define INTERNALNOTIFICATION_CHECK_CONVERSION (*end == 0 && errno == 0) - -#define INTERNALNOTIFICATION_TYPE(TYPE, VALUE_TYPE, TYPE_NAME, TO_VALUE, CHECK_CONVERSION) \ - INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE (TYPE_NAME) \ - { \ - TYPE * variable = (TYPE *) context; \ - char * end ELEKTRA_UNUSED; \ - const char * string = keyValue (key); \ - errno = 0; \ - /* convert string to target type */ \ - VALUE_TYPE value = TO_VALUE; \ - /* only update if conversion was successful */ \ - if (CHECK_CONVERSION) \ - { \ - *(variable) = value; \ - } \ - else \ - { \ - ELEKTRA_LOG_WARNING ("conversion failed! string=%s, stopped=%c errno=%d", keyString (key), *end, errno); \ - } \ - } \ - \ - INTERNALNOTIFICATION_REGISTER_SIGNATURE (TYPE, TYPE_NAME) \ - { \ - PluginState * pluginState = elektraPluginGetData (handle); \ - ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); \ - KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration ( \ - pluginState, key, INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME (TYPE_NAME), variable); \ - if (registeredKey == NULL) \ - { \ - return 0; \ - } \ - return 1; \ - } - #endif diff --git a/src/plugins/internalnotification/macros/addType.h b/src/plugins/internalnotification/macros/addType.h new file mode 100644 index 00000000000..10cee5c6440 --- /dev/null +++ b/src/plugins/internalnotification/macros/addType.h @@ -0,0 +1,71 @@ +#ifndef TYPE +#error "You have to #define TYPE, TYPE_NAME and TO_VALUE before including the addType supermacro" +#endif +#ifndef VALUE_TYPE +// use type as default if not set +#define VALUE_TYPE TYPE +#endif +#ifndef TYPE_NAME +#error "You have to #define TYPE, TYPE_NAME and TO_VALUE before including the addType supermacro" +#endif +#ifndef TO_VALUE +#error "You have to #define TYPE, TYPE_NAME and TO_VALUE before including the addType supermacro" +#endif +#ifndef CHECK_CONVERSION +#define CHECK_CONVERSION 1 +#endif + +#define CONCAT(X, Y) CONCAT2 (X, Y) +#define CONCAT2(X, Y) X##Y + +#define INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME(TYPE_NAME) CONCAT (elektraInternalnotificationConvert, TYPE_NAME) + +#define INTERNALNOTIFICATION_REGISTER_SIGNATURE(TYPE, TYPE_NAME) \ + int INTERNALNOTIFICATION_REGISTER_NAME (TYPE_NAME) (Plugin * handle, Key * key, TYPE * variable) + +#define INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE(TYPE_NAME) \ + void INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME (TYPE_NAME) (Key * key, void * context) + +INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE (TYPE_NAME) +{ + TYPE * variable = (TYPE *) context; + char * end ELEKTRA_UNUSED; + const char * string = keyValue (key); + errno = 0; + /* convert string to target type */ + VALUE_TYPE value = TO_VALUE; + /* only update if conversion was successful */ + if (CHECK_CONVERSION) + { + *(variable) = value; + } + else + { + ELEKTRA_LOG_WARNING ("conversion failed! string=%s, stopped=%c errno=%d", keyString (key), *end, errno); + } +} + +INTERNALNOTIFICATION_REGISTER_SIGNATURE (TYPE, TYPE_NAME) +{ + PluginState * pluginState = elektraPluginGetData (handle); + ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); + KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration ( + pluginState, key, INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME (TYPE_NAME), variable); + if (registeredKey == NULL) + { + return 0; + } + return 1; +} + +#undef CONCAT +#undef CONCAT2 +#undef INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME +#undef INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE +#undef INTERNALNOTIFICATION_REGISTER_SIGNATURE + +#undef TYPE +#undef VALUE_TYPE +#undef TYPE_NAME +#undef TO_VALUE +#undef CHECK_CONVERSION diff --git a/src/plugins/internalnotification/macros/testCreateTypeTests.h b/src/plugins/internalnotification/macros/testCreateTypeTests.h new file mode 100644 index 00000000000..9b20d39b8e2 --- /dev/null +++ b/src/plugins/internalnotification/macros/testCreateTypeTests.h @@ -0,0 +1,89 @@ +#ifndef TYPE +#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the testCreateTypeRegister supermacro" +#endif +#ifndef TYPE_NAME +#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the testCreateTypeRegister supermacro" +#endif +#ifndef FORMAT_STRING +#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the testCreateTypeRegister supermacro" +#endif +#ifndef TEST_VALUE +#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the testCreateTypeRegister supermacro" +#endif +#ifndef CHECK_VALUE +#define CHECK_VALUE (value == TEST_VALUE) +#endif +#ifndef CHECK_INVALID +#define CHECK_INVALID (value == 0) +#endif + +#define CONCAT(X, Y) CONCAT2 (X, Y) +#define CONCAT2(X, Y) X##Y + +#define STRINGIFY(X) STRINGIFY2 (X) +#define STRINGIFY2(X) #X + +#define REGISTER_FUNC_NAME(TYPE_NAME) CONCAT (internalnotificationRegister, TYPE_NAME) + +#define TEST_CASE_UPDATE_SIGNATURE(TYPE_NAME) static void TEST_CASE_UPDATE_NAME (TYPE_NAME) (void) +#define TEST_CASE_NO_UPDATE_SIGNATURE(TYPE_NAME) static void TEST_CASE_NO_UPDATE_NAME (TYPE_NAME) (void) + +static int REGISTER_FUNC_NAME (TYPE_NAME) (Plugin * plugin, Key * key, TYPE * variable) +{ + size_t address = elektraPluginGetFunction (plugin, STRINGIFY (CONCAT (register, TYPE_NAME))); + if (!address) yield_error ("function not exported"); + + /* register key with plugin */ + ELEKTRA_NOTIFICATION_REGISTERFUNC_TYPEDEF (RegisterFuncType, TYPE) + return ((RegisterFuncType) address) (plugin, key, variable); +} + +TEST_CASE_UPDATE_SIGNATURE (TYPE_NAME) +{ + printf ("test update\n"); + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + Key * registeredKey = keyNew ("/test/internalnotification/value", KEY_END); + TYPE value = 0; + succeed_if (REGISTER_FUNC_NAME (TYPE_NAME) (plugin, registeredKey, &value) == 1, "registration was not successful"); + char * valueStr = elektraFormat (FORMAT_STRING, TEST_VALUE); + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, valueStr, KEY_END); + KeySet * ks = ksNew (1, valueKey, KS_END); + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); + succeed_if (CHECK_VALUE, "registered value was not updated"); + free (valueStr); + keyDel (registeredKey); + ksDel (ks); + PLUGIN_CLOSE (); +} + +#ifdef INVALID_VALUE +TEST_CASE_NO_UPDATE_SIGNATURE (TYPE_NAME) +{ + printf ("test no update with invalid value\n"); + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); + KeySet * ks = ksNew (1, valueKey, KS_END); + TYPE value = 0; + succeed_if (REGISTER_FUNC_NAME (TYPE_NAME) (plugin, valueKey, &value) == 1, "registration was not successful"); + keySetString (valueKey, INVALID_VALUE); + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); + succeed_if (CHECK_INVALID, "registered value was updated"); + ksDel (ks); + PLUGIN_CLOSE (); +} +#endif + +#undef CONCAT +#undef CONCAT2 +#undef STRINGIFY +#undef STRINGIFY2 + +#undef TYPE +#undef TYPE_NAME +#undef FORMAT_STRING +#undef TEST_VALUE +#undef CHECK_VALUE +#undef CHECK_INVALID +#undef INVALID_VALUE diff --git a/src/plugins/internalnotification/testmod_internalnotification.c b/src/plugins/internalnotification/testmod_internalnotification.c index 87ef92ad133..cc1e4406a8a 100644 --- a/src/plugins/internalnotification/testmod_internalnotification.c +++ b/src/plugins/internalnotification/testmod_internalnotification.c @@ -28,7 +28,6 @@ int doUpdate_callback_called; #define CALLBACK_CONTEXT_MAGIC_NUMBER ((void *) 1234) -#define REGISTER_FUNC_NAME(TYPE_NAME) internalnotificationRegister##TYPE_NAME #define TEST_CASE_UPDATE_NAME(TYPE_NAME) test_update##TYPE_NAME #define TEST_CASE_NO_UPDATE_NAME(TYPE_NAME) test_noUpdate##TYPE_NAME @@ -37,59 +36,6 @@ int doUpdate_callback_called; TEST_CASE_UPDATE_NAME (TYPE_NAME) (); \ TEST_CASE_NO_UPDATE_NAME (TYPE_NAME) (); -#define REGISTER_FUNC_DEFINITION(TYPE, TYPE_NAME) \ - static int REGISTER_FUNC_NAME (TYPE_NAME) (Plugin * plugin, Key * key, TYPE * variable) \ - { \ - size_t address = elektraPluginGetFunction (plugin, "register" #TYPE_NAME); \ - if (!address) yield_error ("function not exported"); \ - \ - /* register key with plugin */ \ - ELEKTRA_NOTIFICATION_REGISTERFUNC_TYPEDEF (RegisterFuncType, TYPE) \ - return ((RegisterFuncType) address) (plugin, key, variable); \ - } - -#define CREATE_UPDATE_TEST_CASE(TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE, CHECK_VALUE) \ - static void TEST_CASE_UPDATE_NAME (TYPE_NAME) (void) \ - { \ - printf (#TYPE ": test update\n"); \ - KeySet * conf = ksNew (0, KS_END); \ - PLUGIN_OPEN ("internalnotification"); \ - Key * registeredKey = keyNew ("/test/internalnotification/value", KEY_END); \ - TYPE value = 0; \ - succeed_if (REGISTER_FUNC_NAME (TYPE_NAME) (plugin, registeredKey, &value) == 1, "registration was not successful"); \ - char * valueStr = elektraFormat (FORMAT_STRING, TEST_VALUE); \ - Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_VALUE, valueStr, KEY_END); \ - KeySet * ks = ksNew (1, valueKey, KS_END); \ - elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); \ - succeed_if (CHECK_VALUE, "registered value was not updated"); \ - free (valueStr); \ - keyDel (registeredKey); \ - ksDel (ks); \ - PLUGIN_CLOSE (); \ - } - -#define CREATE_TYPE_TESTS(TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE, CHECK_VALUE, INVALID_VALUE, CHECK_INVALID) \ - REGISTER_FUNC_DEFINITION (TYPE, TYPE_NAME) \ - \ - CREATE_UPDATE_TEST_CASE (TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE, CHECK_VALUE) \ - \ - static void TEST_CASE_NO_UPDATE_NAME (TYPE_NAME) (void) \ - { \ - printf (#TYPE ": test no update with invalid value\n"); \ - KeySet * conf = ksNew (0, KS_END); \ - PLUGIN_OPEN ("internalnotification"); \ - Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); \ - KeySet * ks = ksNew (1, valueKey, KS_END); \ - TYPE value = 0; \ - succeed_if (REGISTER_FUNC_NAME (TYPE_NAME) (plugin, valueKey, &value) == 1, "registration was not successful"); \ - keySetString (valueKey, INVALID_VALUE); \ - elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); \ - succeed_if (CHECK_INVALID, "registered value was updated"); \ - ksDel (ks); \ - PLUGIN_CLOSE (); \ - } - - static int internalnotificationRegisterInt (Plugin * plugin, Key * key, int * variable) { size_t address = elektraPluginGetFunction (plugin, "registerInt"); @@ -580,31 +526,136 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) PLUGIN_CLOSE (); } -CREATE_TYPE_TESTS (unsigned int, UnsignedInt, "%u", UINT_MAX, (value == UINT_MAX), "-1", value == 0) -CREATE_TYPE_TESTS (long, Long, "%ld", LONG_MAX, (value == LONG_MAX), "5000abc000", value == 0) -CREATE_TYPE_TESTS (unsigned long, UnsignedLong, "%lu", ULONG_MAX, (value == ULONG_MAX), "AA446744073709551615", value == 0) - -CREATE_TYPE_TESTS (float, Float, "%f", 2.3, (value >= 2.295 && value <= 2.305), "4.a", ((int) value == 0)) -CREATE_TYPE_TESTS (double, Double, "%1.8f", 1.00000001, (value >= 1 + 1e-9 && value <= 1 + 1e-7), "4.a", ((int) value == 0)) - -REGISTER_FUNC_DEFINITION (kdb_boolean_t, KdbBoolean) -CREATE_UPDATE_TEST_CASE (kdb_boolean_t, KdbBoolean, "%d", 1, (value)) - -REGISTER_FUNC_DEFINITION (kdb_char_t, KdbChar) -CREATE_UPDATE_TEST_CASE (kdb_char_t, KdbChar, "abc%d", 1, (value == 'a')) - -CREATE_TYPE_TESTS (kdb_octet_t, KdbOctet, "%d", 255, (value == 255), "4a", value == 0) -CREATE_TYPE_TESTS (kdb_short_t, KdbShort, "%d", SHRT_MIN, (value == SHRT_MIN), "-55ABC", value == 0) -CREATE_TYPE_TESTS (kdb_unsigned_short_t, KdbUnsignedShort, "%d", USHRT_MAX, (value == USHRT_MAX), "55ABC", value == 0) -CREATE_TYPE_TESTS (kdb_long_t, KdbLong, "%d", INT_MIN, (value == INT_MIN), "B5C", value == 0) -CREATE_TYPE_TESTS (kdb_unsigned_long_t, KdbUnsignedLong, "%d", UINT_MAX, (value == UINT_MAX), "B5C", value == 0) -CREATE_TYPE_TESTS (kdb_long_long_t, KdbLongLong, ELEKTRA_LONG_LONG_F, LONG_MIN, (value == LONG_MIN), "50000asasd", value == 0) -CREATE_TYPE_TESTS (kdb_unsigned_long_long_t, KdbUnsignedLongLong, ELEKTRA_UNSIGNED_LONG_LONG_F, ULONG_MAX, (value == ULONG_MAX), "-B5C", - value == 0) -CREATE_TYPE_TESTS (kdb_float_t, KdbFloat, "%f", 2.3, (value >= 2.295 && value <= 2.305), "4.a", ((int) value == 0)) -CREATE_TYPE_TESTS (kdb_double_t, KdbDouble, "%1.8f", 1.00000001, (value >= 1 + 1e-9 && value <= 1 + 1e-7), "4.a", ((int) value == 0)) -CREATE_TYPE_TESTS (kdb_long_double_t, KdbLongDouble, "%1.8f", 1.00000001, (value >= 1 + 1e-9 && value <= 1 + 1e-7), "4.a", - ((int) value == 0)) +// Generate test cases for C built-in types +#define TYPE unsigned int +#define TYPE_NAME UnsignedInt +#define FORMAT_STRING "%u" +#define TEST_VALUE UINT_MAX +#define INVALID_VALUE "-1" +#include "macros/testCreateTypeTests.h" + +#define TYPE long +#define TYPE_NAME Long +#define FORMAT_STRING "%ld" +#define TEST_VALUE LONG_MAX +#define INVALID_VALUE "5000abc000" +#include "macros/testCreateTypeTests.h" + +#define TYPE unsigned long +#define TYPE_NAME UnsignedLong +#define FORMAT_STRING "%lu" +#define TEST_VALUE ULONG_MAX +#define INVALID_VALUE "AA446744073709551615" +#include "macros/testCreateTypeTests.h" + +#define TYPE float +#define TYPE_NAME Float +#define FORMAT_STRING "%f" +#define TEST_VALUE 2.3 +#define CHECK_VALUE (value >= 2.295 && value <= 2.305) +#define INVALID_VALUE "4.a" +#define CHECK_INVALID ((int) value == 0) +#include "macros/testCreateTypeTests.h" + +#define TYPE double +#define TYPE_NAME Double +#define FORMAT_STRING "%1.8f" +#define TEST_VALUE 1.00000001 +#define CHECK_VALUE (value >= 1 + 1e-9 && value <= 1 + 1e-7) +#define INVALID_VALUE "4.a" +#define CHECK_INVALID ((int) value == 0) +#include "macros/testCreateTypeTests.h" + +// for kdb_*_t types +#define TYPE kdb_boolean_t +#define TYPE_NAME KdbBoolean +#define FORMAT_STRING "%d" +#define TEST_VALUE 1 +#define CHECK_VALUE (value) +#include "macros/testCreateTypeTests.h" + +#define TYPE kdb_char_t +#define TYPE_NAME KdbChar +#define FORMAT_STRING "abc%d" +#define TEST_VALUE 1 +#define CHECK_VALUE (value == 'a') +#include "macros/testCreateTypeTests.h" + +#define TYPE kdb_octet_t +#define TYPE_NAME KdbOctet +#define FORMAT_STRING "%d" +#define TEST_VALUE 255 +#define INVALID_VALUE "4a" +#include "macros/testCreateTypeTests.h" + +#define TYPE kdb_short_t +#define TYPE_NAME KdbShort +#define FORMAT_STRING "%d" +#define TEST_VALUE SHRT_MIN +#define INVALID_VALUE "-55ABC" +#include "macros/testCreateTypeTests.h" + +#define TYPE kdb_unsigned_short_t +#define TYPE_NAME KdbUnsignedShort +#define FORMAT_STRING "%d" +#define TEST_VALUE USHRT_MAX +#define INVALID_VALUE "55ABC" +#include "macros/testCreateTypeTests.h" + +#define TYPE kdb_long_t +#define TYPE_NAME KdbLong +#define FORMAT_STRING "%d" +#define TEST_VALUE INT_MIN +#define INVALID_VALUE "B5C" +#include "macros/testCreateTypeTests.h" + +#define TYPE kdb_unsigned_long_t +#define TYPE_NAME KdbUnsignedLong +#define FORMAT_STRING "%d" +#define TEST_VALUE UINT_MAX +#define INVALID_VALUE "B5C" +#include "macros/testCreateTypeTests.h" + +#define TYPE kdb_long_long_t +#define TYPE_NAME KdbLongLong +#define FORMAT_STRING ELEKTRA_LONG_LONG_F +#define TEST_VALUE LONG_MIN +#define INVALID_VALUE "50000asasd" +#include "macros/testCreateTypeTests.h" + +#define TYPE kdb_unsigned_long_long_t +#define TYPE_NAME KdbUnsignedLongLong +#define FORMAT_STRING ELEKTRA_UNSIGNED_LONG_LONG_F +#define TEST_VALUE ULONG_MAX +#define INVALID_VALUE "-B5C" +#include "macros/testCreateTypeTests.h" + +#define TYPE kdb_float_t +#define TYPE_NAME KdbFloat +#define FORMAT_STRING "%f" +#define TEST_VALUE 2.3 +#define CHECK_VALUE (value >= 2.295 && value <= 2.305) +#define INVALID_VALUE "4.a" +#define CHECK_INVALID ((int) value == 0) +#include "macros/testCreateTypeTests.h" + +#define TYPE kdb_double_t +#define TYPE_NAME KdbDouble +#define FORMAT_STRING "%1.8f" +#define TEST_VALUE 1.00000001 +#define CHECK_VALUE (value >= 1 + 1e-9 && value <= 1 + 1e-7) +#define INVALID_VALUE "4.a" +#define CHECK_INVALID ((int) value == 0) +#include "macros/testCreateTypeTests.h" + +#define TYPE kdb_long_double_t +#define TYPE_NAME KdbLongDouble +#define FORMAT_STRING "%1.8f" +#define TEST_VALUE 1.00000001 +#define CHECK_VALUE (value >= 1 + 1e-9 && value <= 1 + 1e-7) +#define INVALID_VALUE "4.a" +#define CHECK_INVALID ((int) value == 0) +#include "macros/testCreateTypeTests.h" int main (int argc, char ** argv) { @@ -660,7 +711,7 @@ int main (int argc, char ** argv) test_doUpdateShouldNotUpdateUnregisteredKey (); test_doUpdateShouldUpdateKeyAbove (); - printf ("\ntestmod_internalnotification RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError); + print_result ("testmod_internalnotification"); return nbError; } From 054e65d30e5b7c7e5404d9393fcbb556d17ee8b9 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 25 Mar 2018 17:06:22 +0200 Subject: [PATCH 14/46] kdbtypes.h: use PRI-macros for C99 --- src/include/kdbtypes.h | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/include/kdbtypes.h b/src/include/kdbtypes.h index 73ac3050cbd..7eff72d76ad 100644 --- a/src/include/kdbtypes.h +++ b/src/include/kdbtypes.h @@ -64,25 +64,12 @@ typedef uint16_t kdb_unsigned_short_t; typedef uint32_t kdb_unsigned_long_t; typedef uint64_t kdb_unsigned_long_long_t; -#if SIZEOF_LONG == 4 -#define ELEKTRA_LONG_F "%ld" -#define ELEKTRA_UNSIGNED_LONG_F "%lu" -#elif SIZEOF_INT == 4 -#define ELEKTRA_LONG_F "%d" -#define ELEKTRA_UNSIGNED_LONG_F "%u" -#endif - -#if SIZEOF_LONG == 8 && !defined(__APPLE__) -#define ELEKTRA_LONG_LONG_F "%ld" -#define ELEKTRA_LONG_LONG_S strtol -#define ELEKTRA_UNSIGNED_LONG_LONG_F "%lu" -#define ELEKTRA_UNSIGNED_LONG_LONG_S strtoul -#elif defined(HAVE_SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG == 8) -#define ELEKTRA_LONG_LONG_F "%lld" +#define ELEKTRA_LONG_F "%" PRIi32 +#define ELEKTRA_UNSIGNED_LONG_F "%" PRIu32 +#define ELEKTRA_LONG_LONG_F "%" PRIi64 #define ELEKTRA_LONG_LONG_S strtoll -#define ELEKTRA_UNSIGNED_LONG_LONG_F "%llu" +#define ELEKTRA_UNSIGNED_LONG_LONG_F "%" PRIu64 #define ELEKTRA_UNSIGNED_LONG_LONG_S strtoull -#endif #else // for C89 typedef unsigned char kdb_boolean_t; From 9cbdc94b4d2a94ddad59bbf9338b58eb32360e7c Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 25 Mar 2018 21:22:38 +0200 Subject: [PATCH 15/46] notification: add documentation to supermacros --- .../internalnotification.c | 36 +++++++++---------- .../macros/{addType.h => add_type.h} | 33 ++++++++++++++--- ...tCreateTypeTests.h => create_type_tests.h} | 36 +++++++++++++------ .../testmod_internalnotification.c | 34 +++++++++--------- 4 files changed, 89 insertions(+), 50 deletions(-) rename src/plugins/internalnotification/macros/{addType.h => add_type.h} (55%) rename src/plugins/internalnotification/macros/{testCreateTypeTests.h => create_type_tests.h} (65%) diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index cf7fa35d3cc..e1ed26dbb6e 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -303,112 +303,112 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define TYPE_NAME Int #define TO_VALUE (strtol (string, &end, 10)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= INT_MAX && value >= INT_MIN) -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE unsigned int #define VALUE_TYPE unsigned long int #define TYPE_NAME UnsignedInt #define TO_VALUE (strtoul (string, &end, 10)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= UINT_MAX) -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE long #define TYPE_NAME Long #define TO_VALUE (strtol (string, &end, 10)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE unsigned long #define TYPE_NAME UnsignedLong #define TO_VALUE (strtoul (string, &end, 10)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE float #define TYPE_NAME Float #define TO_VALUE (strtof (string, &end)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE double #define TYPE_NAME Double #define TO_VALUE (strtod (string, &end)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION -#include "macros/addType.h" +#include "macros/add_type.h" // for kdb_*_t Types #define TYPE kdb_boolean_t #define TYPE_NAME KdbBoolean #define TO_VALUE (!strcmp (string, "1")) -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE kdb_char_t #define TYPE_NAME KdbChar #define TO_VALUE (string[0]) -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE kdb_octet_t #define VALUE_TYPE unsigned int #define TYPE_NAME KdbOctet #define TO_VALUE (strtoul (string, &end, 10)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= 255) -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE kdb_short_t #define VALUE_TYPE int #define TYPE_NAME KdbShort #define TO_VALUE (strtol (string, &end, 10)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= SHRT_MAX && value >= SHRT_MIN) -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE kdb_unsigned_short_t #define VALUE_TYPE unsigned int #define TYPE_NAME KdbUnsignedShort #define TO_VALUE (strtoul (string, &end, 10)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= USHRT_MAX) -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE kdb_long_t #define TYPE_NAME KdbLong #define TO_VALUE (strtol (string, &end, 10)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE kdb_unsigned_long_t #define TYPE_NAME KdbUnsignedLong #define TO_VALUE (strtoul (string, &end, 10)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE kdb_long_long_t #define TYPE_NAME KdbLongLong #define TO_VALUE (ELEKTRA_LONG_LONG_S (string, &end, 10)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE kdb_unsigned_long_long_t #define TYPE_NAME KdbUnsignedLongLong #define TO_VALUE (ELEKTRA_UNSIGNED_LONG_LONG_S (string, &end, 10)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE kdb_float_t #define TYPE_NAME KdbFloat #define TO_VALUE (strtof (string, &end)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE kdb_double_t #define TYPE_NAME KdbDouble #define TO_VALUE (strtod (string, &end)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION -#include "macros/addType.h" +#include "macros/add_type.h" #define TYPE kdb_long_double_t #define TYPE_NAME KdbLongDouble #define TO_VALUE (strtold (string, &end)) #define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION -#include "macros/addType.h" +#include "macros/add_type.h" /** * @see kdbnotificationinternal.h ::ElektraNotificationPluginRegisterCallback diff --git a/src/plugins/internalnotification/macros/addType.h b/src/plugins/internalnotification/macros/add_type.h similarity index 55% rename from src/plugins/internalnotification/macros/addType.h rename to src/plugins/internalnotification/macros/add_type.h index 10cee5c6440..432f01d8397 100644 --- a/src/plugins/internalnotification/macros/addType.h +++ b/src/plugins/internalnotification/macros/add_type.h @@ -1,3 +1,26 @@ +/** + * Add a type to the internalnotification plugin. + * + * Additional required steps: + * - Export the register function using INTERNALNOTIFICATION_EXPORT_FUNCTION in elektraInternalnotificationGet() + * - Update testmod_internalnotification.c: Generate additional test cases using the create_type_tests supermacro + * - Update kdbnotification.h: add a ELEKTRA_NOTIFICATION_TYPE_DECLARATION + * - Update libs/notification/notification.c: add a ELEKTRA_NOTIFICATION_TYPE_DEFINITION + * + * This supermacro creates the following functions: + * - void elektraInternalnotificationConvertTYPE_NAME (Key * key, void * context) + * - int elektraInternalnotificationRegisterTYPE_NAME (Plugin * handle, Key * key, TYPE * variable) + * + * @param TYPE valid C type (e.g. int or kdb_short_t) + * @param TYPE_NAME name suffix for the functions (e.g. Int or UnsignedLong) + * @param VALUE_TYPE optional, defaults to TYPE. Ideally a larger type assigned to variable `value` for + * checking the range before the variable is updated + * @param TO_VALUE expression for converting `string` (variable containing the key value) to VALUE_TYPE + * @param CHECK_CONVERSION optional, defaults to true. A boolean expression. Allows to check the range after + * conversion. Use INTERNALNOTIFICATION_CHECK_CONVERSION to check if a conversion using + * strto*()-functions was successful and INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (RANGE) + * to check additionally for a specified range. + */ #ifndef TYPE #error "You have to #define TYPE, TYPE_NAME and TO_VALUE before including the addType supermacro" #endif @@ -15,10 +38,10 @@ #define CHECK_CONVERSION 1 #endif -#define CONCAT(X, Y) CONCAT2 (X, Y) -#define CONCAT2(X, Y) X##Y +#define ELEKTRA_CONCAT(X, Y) ELEKTRA_CONCAT2 (X, Y) +#define ELEKTRA_CONCAT2(X, Y) X##Y -#define INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME(TYPE_NAME) CONCAT (elektraInternalnotificationConvert, TYPE_NAME) +#define INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME(TYPE_NAME) ELEKTRA_CONCAT (elektraInternalnotificationConvert, TYPE_NAME) #define INTERNALNOTIFICATION_REGISTER_SIGNATURE(TYPE, TYPE_NAME) \ int INTERNALNOTIFICATION_REGISTER_NAME (TYPE_NAME) (Plugin * handle, Key * key, TYPE * variable) @@ -58,8 +81,8 @@ INTERNALNOTIFICATION_REGISTER_SIGNATURE (TYPE, TYPE_NAME) return 1; } -#undef CONCAT -#undef CONCAT2 +#undef ELEKTRA_CONCAT +#undef ELEKTRA_CONCAT2 #undef INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME #undef INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE #undef INTERNALNOTIFICATION_REGISTER_SIGNATURE diff --git a/src/plugins/internalnotification/macros/testCreateTypeTests.h b/src/plugins/internalnotification/macros/create_type_tests.h similarity index 65% rename from src/plugins/internalnotification/macros/testCreateTypeTests.h rename to src/plugins/internalnotification/macros/create_type_tests.h index 9b20d39b8e2..3e2aaddce23 100644 --- a/src/plugins/internalnotification/macros/testCreateTypeTests.h +++ b/src/plugins/internalnotification/macros/create_type_tests.h @@ -1,3 +1,19 @@ +/** + * Create test cases for internalnotification type. + * + * This supermacro creates the following functions: + * - int internalnotificationRegisterTYPE_NAME (Plugin * plugin, Key * key, TYPE * variable) + * - static void test_updateTYPE_NAME (void) + * - static void test_noUpdateTYPE_NAME (void) (only if INVALID_VALUE is defined) + * + * @param TYPE valid C type (e.g. int or kdb_short_t) + * @param TYPE_NAME name suffix for the functions (e.g. Int or UnsignedLong) + * @param TEST_VALUE value of type TYPE. Used for the "update" test case + * @param FORMAT_STRING format to convert TEST_VALUE to string (passed to elektraFormat()) + * @param CHECK_VALUE optional, default is (value == TEST_VALUE). Boolean expression to check if `value` equals the test value + * @param INVALID_VALUE optional. Value of type string. Used for the no update test case. If not defined, "no update" test case is omitted + * @param CHECK_INVALID optioal, defaults to (value == 0). Check if the variable `value` has not been updated. Value should be 0. + */ #ifndef TYPE #error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the testCreateTypeRegister supermacro" #endif @@ -17,20 +33,20 @@ #define CHECK_INVALID (value == 0) #endif -#define CONCAT(X, Y) CONCAT2 (X, Y) -#define CONCAT2(X, Y) X##Y +#define ELEKTRA_CONCAT(X, Y) ELEKTRA_CONCAT2 (X, Y) +#define ELEKTRA_CONCAT2(X, Y) X##Y -#define STRINGIFY(X) STRINGIFY2 (X) -#define STRINGIFY2(X) #X +#define ELEKTRA_STRINGIFY(X) ELEKTRA_STRINGIFY2 (X) +#define ELEKTRA_STRINGIFY2(X) #X -#define REGISTER_FUNC_NAME(TYPE_NAME) CONCAT (internalnotificationRegister, TYPE_NAME) +#define REGISTER_FUNC_NAME(TYPE_NAME) ELEKTRA_CONCAT (internalnotificationRegister, TYPE_NAME) #define TEST_CASE_UPDATE_SIGNATURE(TYPE_NAME) static void TEST_CASE_UPDATE_NAME (TYPE_NAME) (void) #define TEST_CASE_NO_UPDATE_SIGNATURE(TYPE_NAME) static void TEST_CASE_NO_UPDATE_NAME (TYPE_NAME) (void) static int REGISTER_FUNC_NAME (TYPE_NAME) (Plugin * plugin, Key * key, TYPE * variable) { - size_t address = elektraPluginGetFunction (plugin, STRINGIFY (CONCAT (register, TYPE_NAME))); + size_t address = elektraPluginGetFunction (plugin, ELEKTRA_STRINGIFY (ELEKTRA_CONCAT (register, TYPE_NAME))); if (!address) yield_error ("function not exported"); /* register key with plugin */ @@ -75,10 +91,10 @@ TEST_CASE_NO_UPDATE_SIGNATURE (TYPE_NAME) } #endif -#undef CONCAT -#undef CONCAT2 -#undef STRINGIFY -#undef STRINGIFY2 +#undef ELEKTRA_CONCAT +#undef ELEKTRA_CONCAT2 +#undef ELEKTRA_STRINGIFY +#undef ELEKTRA_STRINGIFY2 #undef TYPE #undef TYPE_NAME diff --git a/src/plugins/internalnotification/testmod_internalnotification.c b/src/plugins/internalnotification/testmod_internalnotification.c index cc1e4406a8a..1d12b96da2f 100644 --- a/src/plugins/internalnotification/testmod_internalnotification.c +++ b/src/plugins/internalnotification/testmod_internalnotification.c @@ -532,21 +532,21 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define FORMAT_STRING "%u" #define TEST_VALUE UINT_MAX #define INVALID_VALUE "-1" -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE long #define TYPE_NAME Long #define FORMAT_STRING "%ld" #define TEST_VALUE LONG_MAX #define INVALID_VALUE "5000abc000" -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE unsigned long #define TYPE_NAME UnsignedLong #define FORMAT_STRING "%lu" #define TEST_VALUE ULONG_MAX #define INVALID_VALUE "AA446744073709551615" -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE float #define TYPE_NAME Float @@ -555,7 +555,7 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define CHECK_VALUE (value >= 2.295 && value <= 2.305) #define INVALID_VALUE "4.a" #define CHECK_INVALID ((int) value == 0) -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE double #define TYPE_NAME Double @@ -564,7 +564,7 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define CHECK_VALUE (value >= 1 + 1e-9 && value <= 1 + 1e-7) #define INVALID_VALUE "4.a" #define CHECK_INVALID ((int) value == 0) -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" // for kdb_*_t types #define TYPE kdb_boolean_t @@ -572,63 +572,63 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define FORMAT_STRING "%d" #define TEST_VALUE 1 #define CHECK_VALUE (value) -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE kdb_char_t #define TYPE_NAME KdbChar #define FORMAT_STRING "abc%d" #define TEST_VALUE 1 #define CHECK_VALUE (value == 'a') -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE kdb_octet_t #define TYPE_NAME KdbOctet #define FORMAT_STRING "%d" #define TEST_VALUE 255 #define INVALID_VALUE "4a" -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE kdb_short_t #define TYPE_NAME KdbShort #define FORMAT_STRING "%d" #define TEST_VALUE SHRT_MIN #define INVALID_VALUE "-55ABC" -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE kdb_unsigned_short_t #define TYPE_NAME KdbUnsignedShort #define FORMAT_STRING "%d" #define TEST_VALUE USHRT_MAX #define INVALID_VALUE "55ABC" -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE kdb_long_t #define TYPE_NAME KdbLong #define FORMAT_STRING "%d" #define TEST_VALUE INT_MIN #define INVALID_VALUE "B5C" -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE kdb_unsigned_long_t #define TYPE_NAME KdbUnsignedLong #define FORMAT_STRING "%d" #define TEST_VALUE UINT_MAX #define INVALID_VALUE "B5C" -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE kdb_long_long_t #define TYPE_NAME KdbLongLong #define FORMAT_STRING ELEKTRA_LONG_LONG_F #define TEST_VALUE LONG_MIN #define INVALID_VALUE "50000asasd" -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE kdb_unsigned_long_long_t #define TYPE_NAME KdbUnsignedLongLong #define FORMAT_STRING ELEKTRA_UNSIGNED_LONG_LONG_F #define TEST_VALUE ULONG_MAX #define INVALID_VALUE "-B5C" -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE kdb_float_t #define TYPE_NAME KdbFloat @@ -637,7 +637,7 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define CHECK_VALUE (value >= 2.295 && value <= 2.305) #define INVALID_VALUE "4.a" #define CHECK_INVALID ((int) value == 0) -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE kdb_double_t #define TYPE_NAME KdbDouble @@ -646,7 +646,7 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define CHECK_VALUE (value >= 1 + 1e-9 && value <= 1 + 1e-7) #define INVALID_VALUE "4.a" #define CHECK_INVALID ((int) value == 0) -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" #define TYPE kdb_long_double_t #define TYPE_NAME KdbLongDouble @@ -655,7 +655,7 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define CHECK_VALUE (value >= 1 + 1e-9 && value <= 1 + 1e-7) #define INVALID_VALUE "4.a" #define CHECK_INVALID ((int) value == 0) -#include "macros/testCreateTypeTests.h" +#include "macros/create_type_tests.h" int main (int argc, char ** argv) { From 2111f8e43b23ba1cd5b3f42a4c67ff8d03854874 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 25 Mar 2018 22:40:01 +0200 Subject: [PATCH 16/46] notification: add elektraNotificationSetConversionErrorCallback() & refactoring --- src/include/kdbinvoke.h | 1 + src/include/kdbnotification.h | 11 ++- src/include/kdbnotificationinternal.h | 15 +++- src/libs/invoke/invoke.c | 77 ++++++++++++++----- src/libs/io/CMakeLists.txt | 5 +- src/libs/io/io.c | 18 +---- src/libs/notification/CMakeLists.txt | 2 +- src/libs/notification/notification.c | 63 ++++++++------- .../internalnotification.c | 40 +++++++++- .../internalnotification.h | 13 ++++ .../internalnotification/macros/add_type.h | 19 ++++- .../macros/create_type_tests.h | 5 -- .../testmod_internalnotification.c | 60 +++++++++++++-- 13 files changed, 238 insertions(+), 91 deletions(-) diff --git a/src/include/kdbinvoke.h b/src/include/kdbinvoke.h index 423a2d59b01..c5cb231e516 100644 --- a/src/include/kdbinvoke.h +++ b/src/include/kdbinvoke.h @@ -44,6 +44,7 @@ int elektraDeferredCallAdd (ElektraDeferredCallList * list, const char * name, K ElektraDeferredCallList * elektraDeferredCallCreateList (void); void elektraDeferredCallDeleteList (ElektraDeferredCallList * list); void elektraDeferredCallsExecute (Plugin * plugin, ElektraDeferredCallList * list); +int elektraDeferredCall (Plugin * handle, const char * elektraPluginFunctionName, KeySet * parameters); #ifdef __cplusplus } diff --git a/src/include/kdbnotification.h b/src/include/kdbnotification.h index 0e6d2f904f6..fd7622c39a0 100644 --- a/src/include/kdbnotification.h +++ b/src/include/kdbnotification.h @@ -146,12 +146,21 @@ ELEKTRA_NOTIFICATION_TYPE_DECLARATION (kdb_long_double_t, KdbLongDouble) /** @} */ +/** + * @ingroup kdbnotification + * Callback function called when string to number conversion failed. + * + * @param key key with invalid value + * @param context user supplied callback context + */ +typedef void (*ElektraNotificationConversionErrorCallback) (Key * key, void * context); + /** * @ingroup kdbnotification * Callback function for key changes. * * @param key changed key - * @param Context user supplied callback context + * @param context user supplied callback context */ typedef void (*ElektraNotificationChangeCallback) (Key * key, void * context); diff --git a/src/include/kdbnotificationinternal.h b/src/include/kdbnotificationinternal.h index a89c267a04d..0ecce0c75b8 100644 --- a/src/include/kdbnotificationinternal.h +++ b/src/include/kdbnotificationinternal.h @@ -53,6 +53,7 @@ extern "C" { /** * Subscribe for updates via callback when a given key value is changed. + * Exported as "registerCallback" by notification plugins. * * @param handle plugin handle * @param key key to watch for changes @@ -67,6 +68,7 @@ typedef int (*ElektraNotificationPluginRegisterCallback) (Plugin * handle, Key * /** * Subscribe for updates via callback when a given key or a key below is changed. + * Exported as "registerCallbackSameOrBelow" by notification plugins. * * @param handle plugin handle * @param key key to watch for changes @@ -79,6 +81,17 @@ typedef int (*ElektraNotificationPluginRegisterCallback) (Plugin * handle, Key * typedef int (*ElektraNotificationPluginRegisterCallbackSameOrBelow) (Plugin * handle, Key * key, ElektraNotificationChangeCallback callback, void * context); +/** + * Allow setting a callback that is called when a value conversion failed. + * Exported as "setConversionErrorCallback" notification plugins. + * + * @param kdb kdb handle + * @param callback callback + * @param context context + */ +typedef void (*ElektraNotificationSetConversionErrorCallback) (Plugin * handle, ElektraNotificationConversionErrorCallback callback, + void * context); + /** * Context for notification callbacks. */ @@ -100,7 +113,7 @@ typedef void (*ElektraNotificationCallback) (Key * key, ElektraNotificationCallb * Exported as "openNotification" by transport plugins. * * @param handle plugin handle - * @param parameters contains the keys "/callback" (ElektraNotificationCallback * ) and "/context" (ElektraNotificationCallbackContext *). + * @param parameters contains the keys "/callback" (ElektraNotificationCallback) and "/context" (ElektraNotificationCallbackContext *). */ typedef void (*ElektraNotificationOpenNotification) (Plugin * handle, KeySet * parameters); diff --git a/src/libs/invoke/invoke.c b/src/libs/invoke/invoke.c index 9e139aaee9f..b98be04427d 100644 --- a/src/libs/invoke/invoke.c +++ b/src/libs/invoke/invoke.c @@ -5,6 +5,7 @@ * * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) */ +#include #include #include #include // for elektraPluginOpen/Close @@ -337,7 +338,7 @@ void elektraInvokeClose (ElektraInvokeHandle * handle, Key * errorKey) } /** - * Invokes a deferable function on a plugin handle. + * Invokes a deferable function on an invoke handle. * If the function is exported by the plugin it is directly invoked, * if the plugin supports deferring calls, the call is deferred. * @@ -345,23 +346,67 @@ void elektraInvokeClose (ElektraInvokeHandle * handle, Key * errorKey) * * @param handle invoke handle * @param elektraPluginFunctionName function name - * @param ks parameter key set + * @param parameters parameter key set * @retval 0 on success - * @retval 1 when function not exported and deferring unsupported by plugin + * @retval 1 when function not exported and deferring is unsupported by plugin */ int elektraInvokeCallDeferable (ElektraInvokeHandle * handle, const char * elektraPluginFunctionName, KeySet * parameters) { - ElektraDeferredCallable direct = *(ElektraDeferredCallable *) elektraInvokeGetFunction (handle, elektraPluginFunctionName); + if (!handle) + { + return 1; + } + return elektraDeferredCall (handle->plugin, elektraPluginFunctionName, parameters); +} + +/** + * Execute deferred calls from list on given invoke handle. + * + * Used internally by plugins holding invoke handles. + * + * @param handle invoke handle + * @param list list + */ +void elektraInvokeExecuteDeferredCalls (ElektraInvokeHandle * handle, ElektraDeferredCallList * list) +{ + if (!handle) + { + return; + } + elektraDeferredCallsExecute (handle->plugin, list); +} + +/** + * Call a deferable function on a plugin handle. + * If the function is exported by the plugin it is directly invoked, + * if the plugin supports deferring calls, the call is deferred. + * + * The parameters key set can be freed afterwards. + * + * @param handle invoke handle + * @param elektraPluginFunctionName function name + * @param parameters parameter key set + * @retval 0 on success + * @retval 1 when function not exported and deferring is unsupported by plugin + */ +int elektraDeferredCall (Plugin * handle, const char * elektraPluginFunctionName, KeySet * parameters) +{ + ELEKTRA_NOT_NULL (handle); + ELEKTRA_NOT_NULL (elektraPluginFunctionName); + + size_t direct = elektraPluginGetFunction (handle, elektraPluginFunctionName); if (direct) { - direct (handle->plugin, parameters); + ElektraDeferredCallable directFn = (ElektraDeferredCallable) direct; + directFn (handle, parameters); } else { - ElektraDeferredCall deferredCall = *(ElektraDeferredCall *) elektraInvokeGetFunction (handle, "deferredCall"); + size_t deferredCall = elektraPluginGetFunction (handle, "deferredCall"); if (deferredCall) { - deferredCall (handle->plugin, elektraPluginFunctionName, parameters); + ElektraDeferredCall deferredCallFn = (ElektraDeferredCall) deferredCall; + deferredCallFn (handle, elektraPluginFunctionName, parameters); } else { @@ -374,19 +419,6 @@ int elektraInvokeCallDeferable (ElektraInvokeHandle * handle, const char * elekt return 0; } -/** - * Execute deferred calls from list on given invoke handle. - * - * Used internally by plugins holding invoke handles. - * - * @param handle invoke handle - * @param list list - */ -void elektraInvokeExecuteDeferredCalls (ElektraInvokeHandle * handle, ElektraDeferredCallList * list) -{ - elektraDeferredCallsExecute (handle->plugin, list); -} - /** * Add a new deferred call to the deferred call list. * @@ -400,6 +432,8 @@ void elektraInvokeExecuteDeferredCalls (ElektraInvokeHandle * handle, ElektraDef */ int elektraDeferredCallAdd (ElektraDeferredCallList * list, const char * name, KeySet * parameters) { + ELEKTRA_NOT_NULL (list); + ELEKTRA_NOT_NULL (name); _ElektraDeferredCall * item = elektraMalloc (sizeof *item); if (item == NULL) { @@ -453,6 +487,7 @@ ElektraDeferredCallList * elektraDeferredCallCreateList (void) */ void elektraDeferredCallDeleteList (ElektraDeferredCallList * list) { + ELEKTRA_NOT_NULL (list); _ElektraDeferredCall * item = list->head; while (item != NULL) { @@ -478,6 +513,8 @@ void elektraDeferredCallDeleteList (ElektraDeferredCallList * list) */ void elektraDeferredCallsExecute (Plugin * plugin, ElektraDeferredCallList * list) { + ELEKTRA_NOT_NULL (plugin); + ELEKTRA_NOT_NULL (list); _ElektraDeferredCall * item = list->head; while (item != NULL) { diff --git a/src/libs/io/CMakeLists.txt b/src/libs/io/CMakeLists.txt index e21e67182b7..7cbfaf72be2 100644 --- a/src/libs/io/CMakeLists.txt +++ b/src/libs/io/CMakeLists.txt @@ -2,7 +2,10 @@ set (SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/io.c") set (LIBRARY_NAME elektra-io) -add_lib (io SOURCES ${SOURCES} LINK_ELEKTRA elektra-kdb) +add_lib (io + SOURCES ${SOURCES} + LINK_ELEKTRA elektra-kdb elektra-invoke +) configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/${LIBRARY_NAME}.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}.pc" @ONLY) diff --git a/src/libs/io/io.c b/src/libs/io/io.c index 99bb3ca0a41..8ec853534da 100644 --- a/src/libs/io/io.c +++ b/src/libs/io/io.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -34,22 +35,7 @@ void elektraIoSetBinding (KDB * kdb, ElektraIoInterface * ioBinding) continue; } - size_t func = elektraPluginGetFunction (plugin, "setIoBinding"); - if (func) - { - ElektraIoPluginSetBinding setIoBinding = (ElektraIoPluginSetBinding) func; - setIoBinding (plugin, parameters); - } - else - { - func = elektraPluginGetFunction (plugin, "deferredCall"); - if (func) - { - typedef void (*DeferFunctionCall) (Plugin * handle, char * name, KeySet * parameters); - DeferFunctionCall defer = (DeferFunctionCall) func; - defer (plugin, "setIoBinding", parameters); - } - } + elektraDeferredCall (plugin, "setIoBinding", parameters); } } diff --git a/src/libs/notification/CMakeLists.txt b/src/libs/notification/CMakeLists.txt index e7707232a76..6392037a046 100644 --- a/src/libs/notification/CMakeLists.txt +++ b/src/libs/notification/CMakeLists.txt @@ -10,7 +10,7 @@ else () set (LIBRARY_NAME elektra-notification) - add_lib (notification SOURCES ${SOURCES} LINK_ELEKTRA elektra-kdb elektra-ease) + add_lib (notification SOURCES ${SOURCES} LINK_ELEKTRA elektra-kdb elektra-ease elektra-invoke) configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/${LIBRARY_NAME}.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}.pc" @ONLY) diff --git a/src/libs/notification/notification.c b/src/libs/notification/notification.c index 88f704875eb..b914f32fc44 100644 --- a/src/libs/notification/notification.c +++ b/src/libs/notification/notification.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -662,22 +663,7 @@ static void pluginsOpenNotification (KDB * kdb, ElektraNotificationCallback call } - size_t func = elektraPluginGetFunction (plugin, "openNotification"); - if (func) - { - ElektraNotificationOpenNotification openNotification = (ElektraNotificationOpenNotification) func; - openNotification (plugin, parameters); - } - else - { - func = elektraPluginGetFunction (plugin, "deferredCall"); - if (func) - { - typedef void (*DeferFunctionCall) (Plugin * handle, char * name, KeySet * parameters); - DeferFunctionCall defer = (DeferFunctionCall) func; - defer (plugin, "openNotification", parameters); - } - } + elektraDeferredCall (plugin, "openNotification", parameters); } } @@ -699,22 +685,7 @@ static void pluginsCloseNotification (KDB * kdb) continue; } - size_t func = elektraPluginGetFunction (plugin, "closeNotification"); - if (func) - { - ElektraNotificationCloseNotification closeNotification = (ElektraNotificationCloseNotification) func; - closeNotification (plugin, NULL); - } - else - { - func = elektraPluginGetFunction (plugin, "deferredCall"); - if (func) - { - typedef void (*DeferFunctionCall) (Plugin * handle, char * name, KeySet * parameters); - DeferFunctionCall defer = (DeferFunctionCall) func; - defer (plugin, "closeNotification", NULL); - } - } + elektraDeferredCall (plugin, "closeNotification", NULL); } } } @@ -934,3 +905,31 @@ int elektraNotificationRegisterCallbackSameOrBelow (KDB * kdb, Key * key, Elektr ElektraNotificationPluginRegisterCallbackSameOrBelow registerFunc = (ElektraNotificationPluginRegisterCallbackSameOrBelow) func; return registerFunc (notificationPlugin, key, callback, context); } + +int elektraNotificationSetConversionErrorCallback (KDB * kdb, ElektraNotificationConversionErrorCallback callback, void * context) +{ + if (!kdb || !callback) + { + ELEKTRA_LOG_WARNING ("null pointer passed"); + return 0; + } + + // Find notification plugin + Plugin * notificationPlugin = getNotificationPlugin (kdb); + if (!notificationPlugin) + { + return 0; + } + + // Get register function from plugin + size_t func = elektraPluginGetFunction (notificationPlugin, "setConversionErrorCallback"); + if (!func) + { + return 0; + } + + // Call register function + ElektraNotificationSetConversionErrorCallback setCallbackFunc = (ElektraNotificationSetConversionErrorCallback) func; + setCallbackFunc (notificationPlugin, callback, context); + return 1; +} diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index e1ed26dbb6e..4dbaa4783fb 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -27,6 +27,7 @@ struct _KeyRegistration char * name; char * lastValue; int sameOrBelow; + int freeContext; ElektraNotificationChangeCallback callback; void * context; struct _KeyRegistration * next; @@ -41,9 +42,26 @@ struct _PluginState { KeyRegistration * head; KeyRegistration * last; + ElektraNotificationConversionErrorCallback conversionErrorCallback; + void * conversionErrorCallbackContext; }; typedef struct _PluginState PluginState; +/** + * @see kdbnotificationinternal.h ::ElektraNotificationSetConversionErrorCallback + */ +static void elektraInternalnotificationSetConversionErrorCallback (Plugin * handle, ElektraNotificationConversionErrorCallback callback, + void * context) +{ + ELEKTRA_NOT_NULL (handle); + ELEKTRA_NOT_NULL (callback); + PluginState * data = elektraPluginGetData (handle); + ELEKTRA_NOT_NULL (data); + + data->conversionErrorCallback = callback; + data->conversionErrorCallbackContext = context; +} + /** * @internal * Check if two keys have the same name. @@ -158,12 +176,17 @@ void elektraInternalnotificationDoUpdate (Key * changedKey, ElektraNotificationC * Creates a new KeyRegistration structure and appends it at the end of the registration list * @internal * - * @param pluginState internal plugin data structure + * @param pluginState internal plugin data structure + * @param key key + * @param callback callback for changes + * @param context context for callback + * @param freeContext context needs to be freed on close * * @return pointer to created KeyRegistration structure or NULL if memory allocation failed */ static KeyRegistration * elektraInternalnotificationAddNewRegistration (PluginState * pluginState, Key * key, - ElektraNotificationChangeCallback callback, void * context) + ElektraNotificationChangeCallback callback, void * context, + int freeContext) { KeyRegistration * item = elektraMalloc (sizeof *item); if (item == NULL) @@ -176,6 +199,7 @@ static KeyRegistration * elektraInternalnotificationAddNewRegistration (PluginSt item->callback = callback; item->context = context; item->sameOrBelow = 0; + item->freeContext = freeContext; if (pluginState->head == NULL) { @@ -418,7 +442,7 @@ int elektraInternalnotificationRegisterCallback (Plugin * handle, Key * key, Ele PluginState * pluginState = elektraPluginGetData (handle); ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); - KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration (pluginState, key, callback, context); + KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration (pluginState, key, callback, context, 0); if (registeredKey == NULL) { return 0; @@ -436,7 +460,7 @@ int elektraInternalnotificationRegisterCallbackSameOrBelow (Plugin * handle, Key PluginState * pluginState = elektraPluginGetData (handle); ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); - KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration (pluginState, key, callback, context); + KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration (pluginState, key, callback, context, 0); if (registeredKey == NULL) { return 0; @@ -495,6 +519,8 @@ int elektraInternalnotificationGet (Plugin * handle, KeySet * returned, Key * pa elektraInternalnotificationRegisterCallback, KEY_END), keyNew ("system/elektra/modules/internalnotification/exports/registerCallbackSameOrBelow", KEY_FUNC, elektraInternalnotificationRegisterCallbackSameOrBelow, KEY_END), + keyNew ("system/elektra/modules/internalnotification/exports/setConversionErrorCallback", KEY_FUNC, + elektraInternalnotificationSetConversionErrorCallback, KEY_END), #include ELEKTRA_README (internalnotification) @@ -553,6 +579,8 @@ int elektraInternalnotificationOpen (Plugin * handle, Key * parentKey ELEKTRA_UN // Initialize list pointers for registered keys pluginState->head = NULL; pluginState->last = NULL; + pluginState->conversionErrorCallback = NULL; + pluginState->conversionErrorCallbackContext = NULL; } return 1; @@ -584,6 +612,10 @@ int elektraInternalnotificationClose (Plugin * handle, Key * parentKey ELEKTRA_U { elektraFree (current->lastValue); } + if (current->freeContext) + { + elektraFree (current->context); + } elektraFree (current); current = next; diff --git a/src/plugins/internalnotification/internalnotification.h b/src/plugins/internalnotification/internalnotification.h index 4125e80c39c..a854b1a8eef 100644 --- a/src/plugins/internalnotification/internalnotification.h +++ b/src/plugins/internalnotification/internalnotification.h @@ -34,4 +34,17 @@ void elektraInternalnotificationDoUpdate (Key * changedKey, ElektraNotificationC keyNew ("system/elektra/modules/internalnotification/exports/register" #TYPE_NAME, KEY_FUNC, \ INTERNALNOTIFICATION_REGISTER_NAME (TYPE_NAME), KEY_END) +/** + * Structure containing conversion context + * @internal + */ +struct _ElektraInternalnotificationConversionContext +{ + void * variable; + ElektraNotificationConversionErrorCallback errorCallback; + void * errorCallbackContext; +}; +typedef struct _ElektraInternalnotificationConversionContext _ElektraInternalnotificationConversionContext; + + #endif diff --git a/src/plugins/internalnotification/macros/add_type.h b/src/plugins/internalnotification/macros/add_type.h index 432f01d8397..ac38a3fd54f 100644 --- a/src/plugins/internalnotification/macros/add_type.h +++ b/src/plugins/internalnotification/macros/add_type.h @@ -51,7 +51,8 @@ INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE (TYPE_NAME) { - TYPE * variable = (TYPE *) context; + _ElektraInternalnotificationConversionContext * ctx = (_ElektraInternalnotificationConversionContext *) context; + TYPE * variable = (TYPE *) ctx->variable; char * end ELEKTRA_UNUSED; const char * string = keyValue (key); errno = 0; @@ -65,6 +66,10 @@ INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE (TYPE_NAME) else { ELEKTRA_LOG_WARNING ("conversion failed! string=%s, stopped=%c errno=%d", keyString (key), *end, errno); + if (ctx->errorCallback) + { + ctx->errorCallback (key, ctx->errorCallbackContext); + } } } @@ -72,8 +77,18 @@ INTERNALNOTIFICATION_REGISTER_SIGNATURE (TYPE, TYPE_NAME) { PluginState * pluginState = elektraPluginGetData (handle); ELEKTRA_ASSERT (pluginState != NULL, "plugin state was not initialized properly"); + + _ElektraInternalnotificationConversionContext * context = elektraMalloc (sizeof *context); + if (context == NULL) + { + return 0; + } + context->errorCallback = pluginState->conversionErrorCallback; + context->errorCallbackContext = pluginState->conversionErrorCallbackContext; + context->variable = variable; + KeyRegistration * registeredKey = elektraInternalnotificationAddNewRegistration ( - pluginState, key, INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME (TYPE_NAME), variable); + pluginState, key, INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME (TYPE_NAME), context, 1); if (registeredKey == NULL) { return 0; diff --git a/src/plugins/internalnotification/macros/create_type_tests.h b/src/plugins/internalnotification/macros/create_type_tests.h index 3e2aaddce23..8ca4233c386 100644 --- a/src/plugins/internalnotification/macros/create_type_tests.h +++ b/src/plugins/internalnotification/macros/create_type_tests.h @@ -36,9 +36,6 @@ #define ELEKTRA_CONCAT(X, Y) ELEKTRA_CONCAT2 (X, Y) #define ELEKTRA_CONCAT2(X, Y) X##Y -#define ELEKTRA_STRINGIFY(X) ELEKTRA_STRINGIFY2 (X) -#define ELEKTRA_STRINGIFY2(X) #X - #define REGISTER_FUNC_NAME(TYPE_NAME) ELEKTRA_CONCAT (internalnotificationRegister, TYPE_NAME) #define TEST_CASE_UPDATE_SIGNATURE(TYPE_NAME) static void TEST_CASE_UPDATE_NAME (TYPE_NAME) (void) @@ -93,8 +90,6 @@ TEST_CASE_NO_UPDATE_SIGNATURE (TYPE_NAME) #undef ELEKTRA_CONCAT #undef ELEKTRA_CONCAT2 -#undef ELEKTRA_STRINGIFY -#undef ELEKTRA_STRINGIFY2 #undef TYPE #undef TYPE_NAME diff --git a/src/plugins/internalnotification/testmod_internalnotification.c b/src/plugins/internalnotification/testmod_internalnotification.c index 1d12b96da2f..a89fdec32b1 100644 --- a/src/plugins/internalnotification/testmod_internalnotification.c +++ b/src/plugins/internalnotification/testmod_internalnotification.c @@ -36,6 +36,14 @@ int doUpdate_callback_called; TEST_CASE_UPDATE_NAME (TYPE_NAME) (); \ TEST_CASE_NO_UPDATE_NAME (TYPE_NAME) (); +static void test_callback (Key * key, void * context) +{ + succeed_if (context == CALLBACK_CONTEXT_MAGIC_NUMBER, "callback context was not passed"); + callback_called = 1; + callback_keyValue = (char *) keyValue (key); + callback_keyName = (char *) keyName (key); +} + static int internalnotificationRegisterInt (Plugin * plugin, Key * key, int * variable) { size_t address = elektraPluginGetFunction (plugin, "registerInt"); @@ -46,6 +54,17 @@ static int internalnotificationRegisterInt (Plugin * plugin, Key * key, int * va return ((RegisterFuncType) address) (plugin, key, variable); } +static int internalnotificationSetConversionErrorCallback (Plugin * plugin, ElektraNotificationConversionErrorCallback callback, + void * context) +{ + size_t address = elektraPluginGetFunction (plugin, "setConversionErrorCallback"); + if (!address) yield_error ("function not exported"); + + // Register key with plugin + ((ElektraNotificationSetConversionErrorCallback) address) (plugin, callback, context); + return 1; +} + static int internalnotificationRegisterCallback (Plugin * plugin, Key * key, ElektraNotificationChangeCallback callback, void * context) { size_t address = elektraPluginGetFunction (plugin, "registerCallback"); @@ -216,6 +235,38 @@ static void test_intNoUpdateWithInvalidValue (void) PLUGIN_CLOSE (); } +static void test_conversionError (void) +{ + printf ("test conversion error callback is called on invalid value\n"); + + KeySet * conf = ksNew (0, KS_END); + PLUGIN_OPEN ("internalnotification"); + + Key * valueKey = keyNew ("user/test/internalnotification/value", KEY_END); + KeySet * ks = ksNew (1, valueKey, KS_END); + + succeed_if (internalnotificationSetConversionErrorCallback (plugin, test_callback, CALLBACK_CONTEXT_MAGIC_NUMBER) == 1, + "call to elektraInternalnotificationSetConversionErrorCallback was not successful"); + + int value = 123; + succeed_if (internalnotificationRegisterInt (plugin, valueKey, &value) == 1, + "call to elektraInternalnotificationRegisterInt was not successful"); + + keySetString (valueKey, "42abcd"); + + callback_called = 0; + callback_keyName = NULL; + callback_keyValue = NULL; + elektraInternalnotificationUpdateRegisteredKeys (plugin, ks); + + succeed_if (value == 123, "registered value was updated"); + succeed_if (callback_called, "conversion error callback was not called"); + succeed_if_same_string (keyName (valueKey), callback_keyName) succeed_if_same_string (keyString (valueKey), callback_keyValue) + + ksDel (ks); + PLUGIN_CLOSE (); +} + static void test_intUpdateWithValueNotYetExceedingIntMax (void) { printf ("test update with value = INT_MAX\n"); @@ -328,14 +379,6 @@ static void test_intNoUpdateWithValueExceedingIntMin (void) PLUGIN_CLOSE (); } -static void test_callback (Key * key, void * context) -{ - succeed_if (context == CALLBACK_CONTEXT_MAGIC_NUMBER, "callback context was not passed"); - callback_called = 1; - callback_keyValue = (char *) keyValue (key); - callback_keyName = (char *) keyName (key); -} - static void test_callbackCalledWithKey (void) { printf ("test callback is called with changed key\n"); @@ -667,6 +710,7 @@ int main (int argc, char ** argv) test_basics (); test_updateOnKdbGet (); test_updateOnKdbSet (); + test_conversionError (); printf ("\nregisterInt\n-----------\n"); test_intUpdateWithCascadingKey (); From 29a87d7b88d82e315f1b52ecefd7821de84e0a61 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Wed, 28 Mar 2018 16:26:30 +0200 Subject: [PATCH 17/46] notification: clarify docs --- src/include/kdbioprivate.h | 2 +- src/include/kdbnotification.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/include/kdbioprivate.h b/src/include/kdbioprivate.h index 686443fcec8..ce8ccfcee75 100644 --- a/src/include/kdbioprivate.h +++ b/src/include/kdbioprivate.h @@ -197,7 +197,7 @@ typedef struct _ElektraIoInterface /** * Free memory used by I/O binding. * - * All added file descriptors and timers have to be removed before calling this function. + * All added operations have to be removed before calling this function. * * @param binding I/O binding * @return 0 on success, any other value on error diff --git a/src/include/kdbnotification.h b/src/include/kdbnotification.h index fd7622c39a0..5f2a3f19732 100644 --- a/src/include/kdbnotification.h +++ b/src/include/kdbnotification.h @@ -116,6 +116,8 @@ int elektraNotificationClose (KDB * kdb); * @ingroup kdbnotification * @brief Subscribe for automatic updates to a given variable when the given key value is changed. * + * On kdbGet iff the key is present and its content is valid, the registered variable is updated. + * * @param handle plugin handle * @param key key to watch for changes * @param variable variable From 21150cf734e6ff6e0e6f754503246d19c95b1f4f Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Wed, 28 Mar 2018 17:28:26 +0200 Subject: [PATCH 18/46] type conversion: added supermacro for type to value conversion functions --- src/include/macros/type_create_to_value.h | 98 +++++++++++++++++++ .../internalnotification.c | 32 +++--- .../internalnotification.h | 3 - .../internalnotification/macros/add_type.h | 22 ++--- 4 files changed, 123 insertions(+), 32 deletions(-) create mode 100644 src/include/macros/type_create_to_value.h diff --git a/src/include/macros/type_create_to_value.h b/src/include/macros/type_create_to_value.h new file mode 100644 index 00000000000..5181e2a624e --- /dev/null +++ b/src/include/macros/type_create_to_value.h @@ -0,0 +1,98 @@ +/** + * Create key to type conversion function. + * + * This supermacro creates the following functions: + * - int NAME_MACRO (TYPE_NAME) (Key * key, TYPE * variable) + * + * @param TYPE valid C type (e.g. int or kdb_short_t) + * @param TYPE_NAME name suffix for the functions (e.g. Int or UnsignedLong) + * @param VALUE_TYPE optional, defaults to TYPE. Ideally a larger type assigned to variable `value` for + * checking the range before the variable is updated + * @param TO_VALUE expression for converting `string` (variable containing the key value) to VALUE_TYPE + * @param CHECK_CONVERSION optional, defaults to true. A boolean expression. Allows to check the range after + * conversion. Use ELEKTRA_TYPE_CHECK_CONVERSION to check if a conversion using + * strto*()-functions was successful and ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (RANGE) + * to check additionally for a specified range. + * @param DISABLE_UNDEF_PARAMETERS define to disable undefining of parameters after the macro. Use if parameters + * are used within another supermacro. + */ +#ifndef TYPE +#error "You have to #define TYPE, TYPE_NAME, TO_VALUE and NAME_MACRO before including the type_create_to_value supermacro" +#endif +#ifndef VALUE_TYPE +// use type as default if not set +#define VALUE_TYPE TYPE +#endif +#ifndef TYPE_NAME +#error "You have to #define TYPE, TYPE_NAME, TO_VALUE and NAME_MACRO before including the type_create_to_value supermacro" +#endif +#ifndef NAME_MACRO +#error "You have to #define TYPE, TYPE_NAME, TO_VALUE and NAME_MACRO before including the type_create_to_value supermacro" +#endif +#ifndef TO_VALUE +#error "You have to #define TYPE, TYPE_NAME, TO_VALUE and NAME_MACRO before including the type_create_to_value supermacro" +#endif +#ifndef CHECK_CONVERSION +#define CHECK_CONVERSION 1 +#endif + +#ifndef ELEKTRA_TYPE_CONVERSION_MACROS +#define ELEKTRA_TYPE_CONVERSION_MACROS +#define ELEKTRA_TYPE_CHECK_CONVERSION (*end == 0 && errno == 0) +#define ELEKTRA_TYPE_CHECK_CONVERSION_RANGE(CHECK_RANGE) (ELEKTRA_TYPE_CHECK_CONVERSION && CHECK_RANGE) +#endif + +#include // errno + +#define TYPE_CONVERSION_SIGNATURE(TYPE, TYPE_NAME, NAME_MACRO) int NAME_MACRO (TYPE_NAME) (Key * key, TYPE * variable) + +/** + * Convert string to TYPE. + * + * The variable is only changed if no conversion error occured + * + * Example: + * int variable = 1234; + * if (!NAME_MACRO (TYPE_NAME) (key, &variable)) + * { + * // conversion failed + * // variable == 1234 + * } + * // variable was changed + * + * @param key key + * @param variable pointer to variable + * @retval 1 on success + * @retval 0 on conversion error + */ +TYPE_CONVERSION_SIGNATURE (TYPE, TYPE_NAME, NAME_MACRO) +{ + char * end ELEKTRA_UNUSED; + const char * string = keyValue (key); + errno = 0; + // convert string to target type + VALUE_TYPE value = TO_VALUE; + if (CHECK_CONVERSION) + { + // only update if conversion was successful + *(variable) = value; + return 1; + } + else + { + ELEKTRA_LOG_WARNING ("type conversion failed! string=%s, stopped=%c errno=%d", keyString (key), *end, errno); + return 0; + } +} + +#undef TYPE_CONVERSION_SIGNATURE + +#ifndef DISABLE_UNDEF_PARAMETERS +#undef TYPE +#undef VALUE_TYPE +#undef TYPE_NAME +#undef NAME_MACRO +#undef TO_VALUE +#undef CHECK_CONVERSION +#endif +#undef DISABLE_UNDEF_PARAMETERS diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index 4dbaa4783fb..7b9dad15cab 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -326,38 +326,38 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define VALUE_TYPE long int #define TYPE_NAME Int #define TO_VALUE (strtol (string, &end, 10)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= INT_MAX && value >= INT_MIN) +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (value <= INT_MAX && value >= INT_MIN) #include "macros/add_type.h" #define TYPE unsigned int #define VALUE_TYPE unsigned long int #define TYPE_NAME UnsignedInt #define TO_VALUE (strtoul (string, &end, 10)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= UINT_MAX) +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (value <= UINT_MAX) #include "macros/add_type.h" #define TYPE long #define TYPE_NAME Long #define TO_VALUE (strtol (string, &end, 10)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE unsigned long #define TYPE_NAME UnsignedLong #define TO_VALUE (strtoul (string, &end, 10)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE float #define TYPE_NAME Float #define TO_VALUE (strtof (string, &end)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE double #define TYPE_NAME Double #define TO_VALUE (strtod (string, &end)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" // for kdb_*_t Types @@ -375,63 +375,63 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define VALUE_TYPE unsigned int #define TYPE_NAME KdbOctet #define TO_VALUE (strtoul (string, &end, 10)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= 255) +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (value <= 255) #include "macros/add_type.h" #define TYPE kdb_short_t #define VALUE_TYPE int #define TYPE_NAME KdbShort #define TO_VALUE (strtol (string, &end, 10)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= SHRT_MAX && value >= SHRT_MIN) +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (value <= SHRT_MAX && value >= SHRT_MIN) #include "macros/add_type.h" #define TYPE kdb_unsigned_short_t #define VALUE_TYPE unsigned int #define TYPE_NAME KdbUnsignedShort #define TO_VALUE (strtoul (string, &end, 10)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE (value <= USHRT_MAX) +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (value <= USHRT_MAX) #include "macros/add_type.h" #define TYPE kdb_long_t #define TYPE_NAME KdbLong #define TO_VALUE (strtol (string, &end, 10)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE kdb_unsigned_long_t #define TYPE_NAME KdbUnsignedLong #define TO_VALUE (strtoul (string, &end, 10)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE kdb_long_long_t #define TYPE_NAME KdbLongLong #define TO_VALUE (ELEKTRA_LONG_LONG_S (string, &end, 10)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE kdb_unsigned_long_long_t #define TYPE_NAME KdbUnsignedLongLong #define TO_VALUE (ELEKTRA_UNSIGNED_LONG_LONG_S (string, &end, 10)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE kdb_float_t #define TYPE_NAME KdbFloat #define TO_VALUE (strtof (string, &end)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE kdb_double_t #define TYPE_NAME KdbDouble #define TO_VALUE (strtod (string, &end)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE kdb_long_double_t #define TYPE_NAME KdbLongDouble #define TO_VALUE (strtold (string, &end)) -#define CHECK_CONVERSION INTERNALNOTIFICATION_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" /** diff --git a/src/plugins/internalnotification/internalnotification.h b/src/plugins/internalnotification/internalnotification.h index a854b1a8eef..bb4da6cb099 100644 --- a/src/plugins/internalnotification/internalnotification.h +++ b/src/plugins/internalnotification/internalnotification.h @@ -25,9 +25,6 @@ Plugin * ELEKTRA_PLUGIN_EXPORT (internalnotification); void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * keySet); void elektraInternalnotificationDoUpdate (Key * changedKey, ElektraNotificationCallbackContext * context); -#define INTERNALNOTIFICATION_CHECK_CONVERSION_RANGE(CHECK_RANGE) (*end == 0 && errno == 0 && CHECK_RANGE) -#define INTERNALNOTIFICATION_CHECK_CONVERSION (*end == 0 && errno == 0) - #define INTERNALNOTIFICATION_REGISTER_NAME(TYPE_NAME) elektraInternalnotificationRegister##TYPE_NAME #define INTERNALNOTIFICATION_EXPORT_FUNCTION(TYPE_NAME) \ diff --git a/src/plugins/internalnotification/macros/add_type.h b/src/plugins/internalnotification/macros/add_type.h index ac38a3fd54f..3391b9d2657 100644 --- a/src/plugins/internalnotification/macros/add_type.h +++ b/src/plugins/internalnotification/macros/add_type.h @@ -41,7 +41,8 @@ #define ELEKTRA_CONCAT(X, Y) ELEKTRA_CONCAT2 (X, Y) #define ELEKTRA_CONCAT2(X, Y) X##Y -#define INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME(TYPE_NAME) ELEKTRA_CONCAT (elektraInternalnotificationConvert, TYPE_NAME) +#define INTERNALNOTIFICATION_CONVERSION_FUNCTION_NAME(TYPE_NAME) ELEKTRA_CONCAT (elektraInternalnotificationConvert, TYPE_NAME) +#define INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME(TYPE_NAME) ELEKTRA_CONCAT (elektraInternalnotificationConvertCallback, TYPE_NAME) #define INTERNALNOTIFICATION_REGISTER_SIGNATURE(TYPE, TYPE_NAME) \ int INTERNALNOTIFICATION_REGISTER_NAME (TYPE_NAME) (Plugin * handle, Key * key, TYPE * variable) @@ -49,23 +50,16 @@ #define INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE(TYPE_NAME) \ void INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME (TYPE_NAME) (Key * key, void * context) +#define DISABLE_UNDEF_PARAMETERS +#define NAME_MACRO INTERNALNOTIFICATION_CONVERSION_FUNCTION_NAME +#include + INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE (TYPE_NAME) { _ElektraInternalnotificationConversionContext * ctx = (_ElektraInternalnotificationConversionContext *) context; TYPE * variable = (TYPE *) ctx->variable; - char * end ELEKTRA_UNUSED; - const char * string = keyValue (key); - errno = 0; - /* convert string to target type */ - VALUE_TYPE value = TO_VALUE; - /* only update if conversion was successful */ - if (CHECK_CONVERSION) - { - *(variable) = value; - } - else + if (!INTERNALNOTIFICATION_CONVERSION_FUNCTION_NAME (TYPE_NAME) (key, variable)) { - ELEKTRA_LOG_WARNING ("conversion failed! string=%s, stopped=%c errno=%d", keyString (key), *end, errno); if (ctx->errorCallback) { ctx->errorCallback (key, ctx->errorCallbackContext); @@ -99,8 +93,10 @@ INTERNALNOTIFICATION_REGISTER_SIGNATURE (TYPE, TYPE_NAME) #undef ELEKTRA_CONCAT #undef ELEKTRA_CONCAT2 #undef INTERNALNOTIFICATION_CONVERSION_CALLBACK_NAME +#undef INTERNALNOTIFICATION_CONVERSION_FUNCTION_NAME_SIGNATURE #undef INTERNALNOTIFICATION_CONVERSION_CALLBACK_SIGNATURE #undef INTERNALNOTIFICATION_REGISTER_SIGNATURE +#undef NAME_MACRO #undef TYPE #undef VALUE_TYPE From 1f5f9f073a5457cc6ae24126c28ce95f519af88c Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Thu, 29 Mar 2018 21:11:48 +0200 Subject: [PATCH 19/46] kdbtypes.h: explicitly include inttypes.h --- src/include/kdbtypes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/include/kdbtypes.h b/src/include/kdbtypes.h index 7eff72d76ad..d14607f44d2 100644 --- a/src/include/kdbtypes.h +++ b/src/include/kdbtypes.h @@ -52,6 +52,7 @@ typedef unsigned char kdb_char_t; #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // for C99+ +#include #include #include From 6b2b8b77ff7e3fe546548e731eeeefc49ae59a4c Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Fri, 6 Apr 2018 20:11:05 +0200 Subject: [PATCH 20/46] notification: fix for #1883 --- .../example/example_notification_async.c | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/libs/notification/example/example_notification_async.c b/src/libs/notification/example/example_notification_async.c index 533e6ff8884..15ada7ab9fc 100644 --- a/src/libs/notification/example/example_notification_async.c +++ b/src/libs/notification/example/example_notification_async.c @@ -18,6 +18,16 @@ #include // signal() #include // printf() & co +ElektraIoTimerOperation * timer; +uv_async_t wakeup; + +#ifdef HAVE_LIBUV0 +static void wakeupCallback (uv_async_t * async ELEKTRA_UNUSED, int unknown ELEKTRA_UNUSED) +{ + // nothing to do; callback required for libuv 0.x +} +#endif + // from https://en.wikipedia.org/wiki/ANSI_escape_code#Colors #define ANSI_COLOR_RESET "\x1b[0m" #define ANSI_COLOR_RED "\x1b[31m" @@ -57,13 +67,16 @@ static void onSIGNAL (int signal) { if (signal == SIGINT) { + elektraIoBindingRemoveTimer (timer); uv_stop (uv_default_loop ()); + // Without this call the loop would be "sleeping" until the next timer interval + uv_async_send (&wakeup); } } -static void printVariable (ElektraIoTimerOperation * timer) +static void printVariable (ElektraIoTimerOperation * timerOp) { - int value = *(int *) elektraIoTimerGetData (timer); + int value = *(int *) elektraIoTimerGetData (timerOp); printf ("\nMy integer value is %d\n", value); } @@ -112,7 +125,7 @@ int main (void) } // Setup timer that repeatedly prints the variable - ElektraIoTimerOperation * timer = elektraIoNewTimerOperation (2000, 1, printVariable, &value); + timer = elektraIoNewTimerOperation (2000, 1, printVariable, &value); elektraIoBindingAddTimer (binding, timer); kdbGet (kdb, config, key); @@ -121,21 +134,28 @@ int main (void) printf ("- Set \"%s\" to red, blue or green to change the text color\n", keyName (callbackKeyToWatch)); printf ("- Set \"%s\" to any integer value\n", keyName (intKeyToWatch)); printf ("Send SIGINT (Ctl+C) to exit.\n\n"); + printVariable (timer); + + // This allows us to wake the loop from our signal handler +#ifdef HAVE_LIBUV1 + uv_async_init (loop, &wakeup, NULL); +#else + uv_async_init (loop, &wakeup, wakeupCallback); +#endif uv_run (loop, UV_RUN_DEFAULT); // Cleanup resetTerminalColor (); - elektraIoBindingRemoveTimer (timer); elektraFree (timer); elektraNotificationClose (kdb); kdbClose (kdb, key); elektraIoBindingCleanup (binding); - uv_run (loop, UV_RUN_ONCE); // allow cleanup + uv_run (loop, UV_RUN_NOWAIT); #ifdef HAVE_LIBUV1 uv_loop_close (uv_default_loop ()); -#elif HAVE_LIBUV0 +#else uv_loop_delete (uv_default_loop ()); #endif From 489727190edde67ad76e1849cb4840c767ad3e6e Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Fri, 6 Apr 2018 21:04:38 +0200 Subject: [PATCH 21/46] notification: improve example documentation --- src/libs/notification/example/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/notification/example/README.md b/src/libs/notification/example/README.md index d654e7581ae..4458d3836fd 100644 --- a/src/libs/notification/example/README.md +++ b/src/libs/notification/example/README.md @@ -1,6 +1,7 @@ # Example Applications for Notifications -This folder contains two example notifications: +This folder contains example notifications which demonstrate how +notifications and the I/O binding API are used in applications: - example_notification: Repeatedly calls kdbGet, does not require transport plugins - example_notification_async: Uses asynchronous I/O. Requires transport plugins @@ -25,4 +26,4 @@ Usage: Make sure that the required transport plugins are mounted (e.g. for D-Bus): -> kdb global-mount dbus dbusrecv +> kdb global-mount dbus announce=once dbusrecv From aaac0cb22dae75206a074da332265540b3a3e6e5 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Fri, 6 Apr 2018 22:47:12 +0200 Subject: [PATCH 22/46] notification: update tutorial --- doc/tutorials/notifications.md | 253 ++++++++++-------- .../example/example_notification_async.c | 13 +- 2 files changed, 154 insertions(+), 112 deletions(-) diff --git a/doc/tutorials/notifications.md b/doc/tutorials/notifications.md index 577b4cd9f9d..d69c589f357 100644 --- a/doc/tutorials/notifications.md +++ b/doc/tutorials/notifications.md @@ -11,28 +11,32 @@ they can be used by application developers. Elektra's notification feature consists of several components. While sending and receiving notifications is implemented by plugins, -applications use APIs provided by wrappers in order to use different plugins. +applications use the notification API in order to use different plugins. -A -[wrapper for notifications](https://doc.libelektra.org/api/current/html/group__kdbnotification.html) -provides the API for receiving and handling notifications. -A [wrapper for I/O operations](https://doc.libelektra.org/api/current/html/group__kdbio.html) +The +[notification API](https://doc.libelektra.org/api/current/html/group__kdbnotification.html) +implemented by the `elektra-notification` library allows receiving and handling +notifications. +An [I/O abstraction layer](https://doc.libelektra.org/api/current/html/group__kdbio.html) allows asynchronous notification processing by compatible plugins. -The I/O wrapper consists of an *interface* used by transport plugins and -multiple implementations of that interface called *I/O bindings*. -A I/O binding implements the actual I/O management functions for a specific -I/O management library. +The abstraction layer consists of an *interface* used by transport plugins and +different implementations of that interface called *I/O bindings*. +An I/O binding implements the actual I/O management functions for a specific +event loop API. Applications typically use one I/O binding but can also use none or multiple I/O bindings. +For more on I/O bindings see the +[API documenation](https://doc.libelektra.org/api/current/html/group__kdbio.html). -Transport plugins exchange notifications via different protocols like D-Bus, -Redis and ZeroMQ. -For each type of transport there are two types of plugins: One for sending and -one for receiving notifications. +Transport plugins exchange notifications via different protocols like D-Bus or +ZeroMQ. +For each type of transport there are typically two types of plugins: One for +sending and one for receiving notifications. Developers do not interact with those plugins directly. The underlying transports are transparent to them. -An internal-notification plugin implements notification handling functions and +The "internalnotification" plugin implements notification handling functions and feeds back configuration changes from within the application. +It is only used internally by the `elektra-notification` library. ![Overview](../images/notifications.svg) @@ -43,88 +47,87 @@ notifications but not whether notifications are sent or which transport is used. How notifications are sent is specified in the *notification configuration* by the system operator. -## Notification configuration +## Notification Configuration System operators can mount the desired transport plugins and configure them -(e.g. set channel, host, port and credentials) either globally or when mounting -a configuration file. +(e.g. set channel, host, port and credentials) globally. They need to mount both sending and receiving plugins in order to use a transport. ```sh -kdb mount file.ini system/example ini dbussend dbusreceive +kdb global-mount dbus announce=once dbusrecv ``` +Plugins usable as transport plugin are marked with `transport` on the +[plugin page](https://www.libelektra.org/plugins/readme#notification-and-logging). + ## How to integrate an I/O binding and send notifications asynchronously Developers do not need to change their programs in order to start sending notifications. -However without the integration of an I/O binding notifications are sent -synchronously which will block normal program execution. +However without the integration of an I/O binding notifications *may* be sent +synchronously which would block normal program execution. For programs without time constraints (e.g. CLI programs) this may not be important, but for GUIs or network services this will have negative impact. -Since many different I/O management libraries exist (e.g. libuv, glib or libev) -the transport plugins use the I/O interface for their I/O operations. -Each I/O management library needs its own I/O binding. -Developers can also create their own I/O binding for the I/O management library -of their choice. -This is described in the last section. +The "zeromqsend" and "dbus" plugins do not block program execution for sending +as sending is handled asynchronously be the underlying libraries. -Each I/O binding has its own initialization function that creates a new -I/O binding and connects it to the I/O management library. -For this tutorial we will assume that libuv is used. -For details on how to use a specific binding please look at the bindings' -README.md in `src/bindings/io/`. +The following is a basic example of an application using Elektra extended by +the initialization of an I/O binding. ```C #include #include #include -#include #include void main (void) { - KDB* repo; + KDB* repo; - // .. open KDB + // Open KDB + Key * key = keyNew ("/sw/myorg/myapp/#0/current", KEY_END); + KDB * kdb = kdbOpen (key); - // Create libuv event loop - uv_loop_t * loop = uv_default_loop (); + // Create libuv event loop + uv_loop_t * loop = uv_default_loop (); - // Initialize I/O binding tied to event loop - ElektraIoInterface * binding = elektraIoUvNew (loop); + // Initialize I/O binding tied to event loop + ElektraIoInterface * binding = elektraIoUvNew (loop); // Use I/O binding for our kdb instance elektraIoSetBinding (kdb, binding); - // Initialize notification wrapper - elektraNotificationOpen (kdb); + // Normal application setup code ... - // Start the event loop - uv_run (loop, UV_RUN_DEFAULT); + // Start the event loop + uv_run (loop, UV_RUN_DEFAULT); - // Cleanup - elektraNotificationClose (kdb); + // Cleanup + kdbClose (kdb, key); elektraIoBindingCleanup (binding); - uv_loop_close (loop); -} - -void someFunction (void) -{ - // notifications are sent asynchronously on kdbSet - // over configured transports using our uv event loop - kdbSet (repo, key, parentKey); + uv_loop_close (loop); } ``` ## How to receive notifications -We extend the example from the previous section where we already created our -I/O binding and initialized the notification-wrapper. +Since many different I/O management libraries exist (e.g. libuv, glib or libev) +the transport plugins use the I/O interface for their I/O operations. +Each I/O management library needs its own I/O binding. +Developers can also create their own I/O binding for the I/O management library +of their choice. +This is described in the last section. + +Each I/O binding has its own initialization function that creates a new +I/O binding and connects it to the I/O management library. +For this tutorial we will assume that libuv 1.x is used. +For details on how to use a specific binding please look at available I/O +bindings on the [bindings page](https://www.libelektra.org/bindings/readme). + In order to handle change notifications a developer can either register a variable or a callback. @@ -132,36 +135,75 @@ variable or a callback. Values of registered variables are automatically updated when the value of the assigned key has changed. -In the following example we will register an integer variable: +In the following example we will register an integer variable. -```C -KDB * repo; +The following examples are shortened for tangibility. The complete example is available in +[src/libs/notification/example/example_notification_async.c](https://github.com/ElektraInitiative/libelektra/blob/master/src/libs/notification/example/example_notification_async.c). -// ... initialization of KDB and I/O binding +```C +#include +#include +#include +#include -Key * key = keyNew ("/sw/myorg/myprogram/#0/current/value", KEY_END); -int keyValue; +#include -int result = elektraNotificationRegisterInt (repo, key, &keyValue); -if (!result) +static void printVariable (ElektraIoTimerOperation * timerOp) { - printf ("could not register variable!\n"); - return; + int value = *(int *) elektraIoTimerGetData (timerOp); + printf ("\nMy integer value is %d\n", value); } -// repeatedly print variable -while (1) { - printf ("value is %d\n", keyValue); +void main (void) +{ + KDB* repo; + + // Open KDB + Key * key = keyNew ("/sw/myorg/myapp/#0/current", KEY_END); + KDB * kdb = kdbOpen (key); + + // Create libuv event loop + uv_loop_t * loop = uv_default_loop (); + + // Initialize I/O binding tied to event loop + ElektraIoInterface * binding = elektraIoUvNew (loop); + + // Use I/O binding for our kdb instance + elektraIoSetBinding (kdb, binding); + + // Initialize notification wrapper + elektraNotificationOpen (kdb); + + // Register "value" for updates + Key * registeredKey = keyNew ("/sw/myorg/myapp/#0/current/value", KEY_END); + int value; + elektraNotificationRegisterInt (repo, registeredKey, &value); + + // Create a timer to repeatedly print "value" + ElektraIoTimerOperation * timer = elektraIoNewTimerOperation (2000, 1, printVariable, &value); + elektraIoBindingAddTimer (binding, timer); + + // Get configuration + KeySet * config = ksNew(0, KS_END); + kdbGet (kdb, config, key); + printVariable (timer); // "value" was automatically updated + + // Start the event loop + uv_run (loop, UV_RUN_DEFAULT); - sleep(2); + // Cleanup + elektraNotificationClose (kdb); + kdbClose (kdb, key); + elektraIoBindingRemoveTimer (timer); + elektraIoBindingCleanup (binding); + uv_loop_close (loop); } ``` -After calling `elektraNotificationRegisterInt` the variable `keyValue` will be +After calling `elektraNotificationRegisterInt()` the variable `value` will be automatically updated if the key in the program above is changed by another program (e.g. by using the `kdb` CLI command). -For automatic updates to work transport plugins have to be in place either at -a mountpoint above the configuration or mounted globally. +For automatic updates to work transport plugins have to be mounted globally. ### Callbacks @@ -188,47 +230,48 @@ void setTerminalColor (Key * color, void * context ELEKTRA_UNUSED) { // context contains whatever was passed as 4th parameter // to elektraNotificationRegisterCallback() - char * value = keyString (color); - - if (strcmp (value, "red") == 0) - { - printf (ANSI_COLOR_RED); - } - if (strcmp (value, "green") == 0) - { - printf (ANSI_COLOR_GREEN); - } + char * value = keyString (color); + + if (strcmp (value, "red") == 0) + { + printf (ANSI_COLOR_RED); + } + if (strcmp (value, "green") == 0) + { + printf (ANSI_COLOR_GREEN); + } } int main (void) { - KDB * repo; - - // ... initialization of KDB, I/O binding and notifications - - Key * configBase = keyNew ("/sw/myorg/myprogram/#0/current", KEY_END); - Key * color = keyNew ("/sw/myorg/myprogram/#0/current/color", KEY_END); - - // Retrieve key from kdb - KeySet * ks = ksNew (10, KS_END); - kdbGet (repo, ks, configBase); - Key * key = ksLookup (ks, color, 0); - if (key) { - // Initialization - setTerminalColor (key); - } - - // Re-Initialize on key changes - int result = elektraNotificationRegisterCallback(repo, color, &setTerminalColor, NULL); - if (!result) { - printf ("could not register callback!\n"); - return -1; - } - - // ... start loop, etc. + KDB * repo; + + // ... initialization of KDB, I/O binding and notifications + + Key * color = keyNew ("/sw/myorg/myapp/#0/current/color", KEY_END); + + // Re-Initialize on key changes + elektraNotificationRegisterCallback(repo, color, &setTerminalColor, NULL); + + // ... start loop, etc. } ``` +## Guidelines + +TODO + +## Logging + +In order to analyze application behavior the [logging plugins](https://www.libelektra.org/plugins/readme#notification-and-logging) +can be used with the `get=on` option when mounting: + +```sh +kdb global-mount syslog get=on +``` + +Now both reading from and writing to Elektra's key database is logged. + ## How to create your own I/O Binding Developers can create their own bindings if the I/O management library of their diff --git a/src/libs/notification/example/example_notification_async.c b/src/libs/notification/example/example_notification_async.c index 15ada7ab9fc..e37b0c09ad5 100644 --- a/src/libs/notification/example/example_notification_async.c +++ b/src/libs/notification/example/example_notification_async.c @@ -18,7 +18,6 @@ #include // signal() #include // printf() & co -ElektraIoTimerOperation * timer; uv_async_t wakeup; #ifdef HAVE_LIBUV0 @@ -67,7 +66,6 @@ static void onSIGNAL (int signal) { if (signal == SIGINT) { - elektraIoBindingRemoveTimer (timer); uv_stop (uv_default_loop ()); // Without this call the loop would be "sleeping" until the next timer interval uv_async_send (&wakeup); @@ -84,7 +82,6 @@ int main (void) { // Cleanup on SIGINT signal (SIGINT, onSIGNAL); - signal (SIGQUIT, onSIGNAL); KeySet * config = ksNew (20, KS_END); @@ -125,16 +122,17 @@ int main (void) } // Setup timer that repeatedly prints the variable - timer = elektraIoNewTimerOperation (2000, 1, printVariable, &value); + ElektraIoTimerOperation * timer = elektraIoNewTimerOperation (2000, 1, printVariable, &value); elektraIoBindingAddTimer (binding, timer); - kdbGet (kdb, config, key); - printf ("Asynchronous Notification Example Application\n"); printf ("- Set \"%s\" to red, blue or green to change the text color\n", keyName (callbackKeyToWatch)); printf ("- Set \"%s\" to any integer value\n", keyName (intKeyToWatch)); printf ("Send SIGINT (Ctl+C) to exit.\n\n"); - printVariable (timer); + + // Get configuration + kdbGet (kdb, config, key); + printVariable (timer); // "value" was automatically updated // This allows us to wake the loop from our signal handler #ifdef HAVE_LIBUV1 @@ -147,6 +145,7 @@ int main (void) // Cleanup resetTerminalColor (); + elektraIoBindingRemoveTimer (timer); elektraFree (timer); elektraNotificationClose (kdb); kdbClose (kdb, key); From 6f102e5331ff89fe36a875fa03eae37e6177c2bf Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 15 Apr 2018 11:30:11 +0200 Subject: [PATCH 23/46] notifications: add emergent misbehavior guidlines to tutorial --- doc/images/notifications.svg | 309 ++++++++++++--------------------- doc/tutorials/notifications.md | 138 ++++++++++++++- 2 files changed, 247 insertions(+), 200 deletions(-) diff --git a/doc/images/notifications.svg b/doc/images/notifications.svg index 9c6412f86be..9daaf5f5e16 100644 --- a/doc/images/notifications.svg +++ b/doc/images/notifications.svg @@ -10,8 +10,8 @@ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="143.07768mm" - height="92.272842mm" - viewBox="0 0 143.07768 92.272842" + height="82.787338mm" + viewBox="0 0 143.07768 82.787338" version="1.1" id="svg8" inkscape:version="0.92.1 r15371" @@ -128,15 +128,15 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="0.98994949" - inkscape:cx="352.43636" - inkscape:cy="316.21597" + inkscape:zoom="1.979899" + inkscape:cx="270.38302" + inkscape:cy="145.14869" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="true" inkscape:snap-bbox="false" - inkscape:measure-start="225,624" - inkscape:measure-end="348,747" + inkscape:measure-start="264.383,186.383" + inkscape:measure-end="264.383,132.383" inkscape:snap-global="true" inkscape:snap-text-baseline="true" showguides="false" @@ -153,8 +153,8 @@ type="xygrid" id="grid4508" empspacing="3" - originx="0.10133949" - originy="-201.51114" /> + originx="0.10133948" + originy="-209.44865" /> @@ -172,14 +172,21 @@ inkscape:label="Ebene 1" inkscape:groupmode="layer" id="layer1" - transform="translate(0.1013395,-3.2160262)"> + transform="translate(0.1013395,-4.7640087)"> + + width="34.924999" + height="11.112499" + x="101.59999" + y="8.0750084" + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.15000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.27200001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> - + y="54.112507" /> Application - Internal-NotificationPlugin - - Redis - I/O - Library(e.g. glib, libuv) + x="119.06995" + y="15.011325" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93888889px;line-height:0.64999998;font-family:'Latin Modern Roman';-inkscape-font-specification:'Latin Modern Roman, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0.26458332;" + id="tspan4584">Event Loop I/O-Wrapper Interface - + id="tspan4584-2">I/O Interface NotificationWrapper - - DBus - + id="tspan4676">Library ZeroMQ - I/O-Wrapper Binding + id="tspan4584-2-27">I/O Binding Transport Plugins + + - - - - + y="68.400063" + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.21698041;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> Emergent BehaviorLibrary + id="tspan5063">Logging Plugins - libelektra + id="tspan4563-6-2">Elektra + d="m 119.79282,27.890472 1.92881,-3.10419 h -1.07156 v -4.856499 h 1.07156 l -1.92881,-3.104181 -1.92881,3.104181 h 1.07156 v 4.856499 h -1.07156 z" + id="path4542-6-5-0-0-7-9-4" + sodipodi:nodetypes="ccccccccccc" /> + + Notification API diff --git a/doc/tutorials/notifications.md b/doc/tutorials/notifications.md index d69c589f357..9a71abe5945 100644 --- a/doc/tutorials/notifications.md +++ b/doc/tutorials/notifications.md @@ -59,7 +59,7 @@ transport. kdb global-mount dbus announce=once dbusrecv ``` -Plugins usable as transport plugin are marked with `transport` on the +Plugins usable as transport plugin are marked with `notification` on the [plugin page](https://www.libelektra.org/plugins/readme#notification-and-logging). ## How to integrate an I/O binding and send notifications asynchronously @@ -257,9 +257,139 @@ int main (void) } ``` -## Guidelines - -TODO +## Emergent Behavior Guidelines + +When applications react to configuration changes made by other applications this +can lead to *emergent behavior*. +We speak of emergent behavior when the parts of a system are functioning as +designed but an unintended, unexpected or unanticipated behavior at system level +occurs. + +For example, take the following sequence of events: + +1. application `A` changes its configuration +2. application `B` receives a notification about the change from `A` and updates its configuration + + Given these two steps the sequence could be a case of *wanted* emergent behavior: + Maybe application `B` keeps track of the number of global configuration + changes. + Now consider adding the following events to the sequence: + +3. application `A` receives a notification about the change from `B` and changes its configuration +4. *continue at step 2* + +These additional two steps cause an infinite cycle of configuration updates +which introduced *unwanted* behavior. + +When designing a system it is desirable to use components with predictable and +well-defined behavior. +As a system grows larger and gets more *complex* unpredictable behavior +emerges that was neither intended by the system designer nor by the designer of +the components. +This system behavior is called *emergent behavior* if it cannot be explained +from its components but only from analysis of the whole system. + +Emergent behavior can be beneficial for a system, for example, useful cooperation +in an ant colony but it also has disadvantages. +Systems that bear *unwanted* emergent behavior are difficult to manage and +experience failures in the worst case. +This kind of unwanted emergent behavior is called +[*emergent misbehavior*](http://www.hpl.hp.com/techreports/2006/HPL-2006-2.html). +Examples of emergent misbehavior are traffic jams or the +[Millenium Footbridge](https://researchcourse.pbworks.com/f/structural+engineering.pdf) +[incident in London](https://www.sciencedaily.com/releases/2005/11/051103080801.htm). + +An evaluation of Elektra's notification feature shows that it can exhibit the +following symptoms: + +- **Synchronization** occurs due to the shared notification medium. + Multiple applications receive a notification at the same time and execute + their configuration update logic. + In turn shared resources like hard disk or CPU become overutilized. +- **Oscillation** occurs when applications are reacting to configuration changes + by other applications. +- In the worst case oscillation results in **livelock** when the frequency of + configuration updates becomes so high that applications are only executing + configuration update logic. +- **Phase change** is a sudden change of the system behavior in reaction to a + minor change (e.g. incremental change of a configuration setting). + Phase change is introduced by application logic. + +Building on these findings we will now present guidelines for preventing +emergent misbehavior when using the notification API and callbacks in particular. + +### Guideline 1: Avoid callbacks +> Most of the guidelines are related to callbacks. +> With normal use of the notification API emergent behavior should not occur. + +Callbacks couple an application temporally to configuration changes of other +applications or instances of the same application. +This observation is the basis for [Guidelines 1](#guideline-1-avoid-callbacks), +[2](#guideline-2-wait-before-reacting-to-changes) and [3](#guideline-3-avoid-updates-as-reaction-to-change). +While it is possible with registered variables to check for configuration +changes at regular time intervals and react to changes the coupling is not as +tight as with callbacks. + +### Guideline 2: Wait before reacting to changes +> Waiting decouples an application from changes and reduces the risk for unwanted ***synchronization***. + +In applications where applying changes has impact on resource usage (e.g. CPU or +disk) applying a time delay as suggested by this Guideline is a +sensible choice. +But this guideline is not only limited to these applications. + +Generally waiting before reacting to changes reduces the risk for unwanted +synchronization by decoupling the application temporally. +Waiting can be implemented using random time delays which further promotes +decoupling since applications react at different points in time to changes. +Waiting can also be implemented using a flag: +Callbacks set the flag and when the control flow is in the main loop again, the +pending updates are applied and the flag is cleared. + +### Guideline 3: Avoid updates as reaction to change +> Avoid changing the configuration as reaction to a change especially in callbacks. +> This reduces the risk for unwanted ***oscillation***. + +While this guideline does not forbid updating the key database using `kdbSet()` +in a callback it advises to avoid it. +If we recall the example from before we see how updating as reaction to change +leads to unwanted oscillation. +If necessary, the function `kdbSet()` should be temporally decoupled as +suggested in [Guideline 2](#guideline-2-wait-before-reacting-to-changes). + +### Guideline 4: Do not use notifications for synchronization +> Applications should not use notifications for synchronization as this can lead to ***phase change***. + +This guideline limits the use of the notification API to notifications about +configuration changes. +There are better suited techiques for different use cases. +Applications should not keep track of changes and change their behavior on +certain conditions. + +For example, this happens when applications synchronize themselves at startup by +incrementing a counter in the key database. +When a certain limit of application instances is reached the applications +proceed with different behavior. +If this behavior affects other applications phase change has occured. + +### Guideline 5: Apply changes immediately +> Call `kdbSet()` to save updated configuration immediately after a change occured. +> This reduces conflicting changes in the key database. + +When a configuration setting is updated within an application this guideline +suggests to write the change immediately to the key database using `kdbSet()`. +This ensures that other applications have the same view of the key database and +operate on current settings. + +### Guideline 6: Be careful on what to call inside callbacks +> Notification callbacks are called from within Elektra. +> Calling `kdbClose()`, `elektraNotificationClose()` or `elektraSetIoBinding()` in a callback will lead to undefined behavior or an application crash. + +Closing and cleaning up the KDB handle will cause an application crash because +the control flow returns from the callback to now removed code. +While this can be considered an implementation detail it aligns with +[Guideline 2](#guidline-2-wait-before-reacting-to-changes) since reinitialization of KDB +uses more resources than other operations like `kdbGet()` or `kdbSet()`. ## Logging From 37e05277bf2ca5e3c12d357490f79cb06bab1bdd Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Tue, 15 May 2018 19:46:37 +0200 Subject: [PATCH 24/46] notification: minor documentation changes --- doc/tutorials/notifications.md | 4 ++-- src/libs/notification/tests/testlib_notification.c | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/tutorials/notifications.md b/doc/tutorials/notifications.md index 9a71abe5945..55ba0ca8c56 100644 --- a/doc/tutorials/notifications.md +++ b/doc/tutorials/notifications.md @@ -278,8 +278,8 @@ For example, take the following sequence of events: 3. application `A` receives a notification about the change from `B` and changes its configuration 4. *continue at step 2* -These additional two steps cause an infinite cycle of configuration updates -which introduced *unwanted* behavior. +The additional step causes an infinite cycle of configuration updates +which introduces *unwanted* behavior. When designing a system it is desirable to use components with predictable and well-defined behavior. diff --git a/src/libs/notification/tests/testlib_notification.c b/src/libs/notification/tests/testlib_notification.c index 39889b1b94e..968a8a23d52 100644 --- a/src/libs/notification/tests/testlib_notification.c +++ b/src/libs/notification/tests/testlib_notification.c @@ -20,7 +20,6 @@ static void test_openclose (void) { printf ("test open & close\n"); - // TODO test with ASAN and with & without cascading key Key * key = keyNew ("system/sw/tests/testlib_notification", KEY_END); KDB * kdb = kdbOpen (key); exit_if_fail (kdb, "opening kdb failed"); From f0626e9b7ff4a4cad049e5d9ea7a71dee738be5e Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Fri, 18 May 2018 22:22:31 +0200 Subject: [PATCH 25/46] internalnotification: redo cmake formatting --- src/plugins/internalnotification/CMakeLists.txt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/plugins/internalnotification/CMakeLists.txt b/src/plugins/internalnotification/CMakeLists.txt index 262897b336b..c512e38d078 100644 --- a/src/plugins/internalnotification/CMakeLists.txt +++ b/src/plugins/internalnotification/CMakeLists.txt @@ -1,10 +1,3 @@ include (LibAddMacros) -add_plugin (internalnotification - SOURCES - internalnotification.h - internalnotification.c - ADD_TEST - LINK_ELEKTRA - elektra-kdb - ) +add_plugin (internalnotification SOURCES internalnotification.h internalnotification.c ADD_TEST LINK_ELEKTRA elektra-kdb) From 76cc60620b101fe1d30af86dc42c844f5057b370 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Fri, 18 May 2018 23:11:53 +0200 Subject: [PATCH 26/46] io: redo cmake formatting --- src/libs/io/CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libs/io/CMakeLists.txt b/src/libs/io/CMakeLists.txt index 7cbfaf72be2..110d9a77a50 100644 --- a/src/libs/io/CMakeLists.txt +++ b/src/libs/io/CMakeLists.txt @@ -2,10 +2,7 @@ set (SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/io.c") set (LIBRARY_NAME elektra-io) -add_lib (io - SOURCES ${SOURCES} - LINK_ELEKTRA elektra-kdb elektra-invoke -) +add_lib (io SOURCES ${SOURCES} LINK_ELEKTRA elektra-kdb elektra-invoke) configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/${LIBRARY_NAME}.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}.pc" @ONLY) From fe7f9b271bd4206833f96378a9702b2067896ff1 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 27 May 2018 21:48:45 +0200 Subject: [PATCH 27/46] notification: add names to news --- doc/news/_preparation_next_release.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/news/_preparation_next_release.md b/doc/news/_preparation_next_release.md index be1d855aae5..2e14a6afa1f 100644 --- a/doc/news/_preparation_next_release.md +++ b/doc/news/_preparation_next_release.md @@ -367,7 +367,7 @@ Thanks to Daniel Bugl. was extended. The API now supports contexts for callbacks, the types `int`, `unsigned int`, `long`, `unsigned long`, `float` and `double`. - It also supports all of Elektra's `kdb_*_t` types defined in `kdbtypes.h`. + It also supports all of Elektra's `kdb_*_t` types defined in `kdbtypes.h`. *(Thomas Wahringer)* - <> - <> - <> @@ -494,7 +494,7 @@ compiled against an older 0.8 version of Elektra will continue to work - <> - <> - `kdbtypes.h` now comes with support for C99 types -- We added the private headerfiles `kdbnotificationinternal.h`, `kdbioplugin.h`. +- We added the private headerfiles `kdbnotificationinternal.h`, `kdbioplugin.h`. *(Thomas Wahringer)* ## Build From acc58b7e99f01c9f54beac0e204e4873b3d587f2 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Tue, 29 May 2018 23:20:50 +0200 Subject: [PATCH 28/46] I/O binding headers: move to "kdbio" directory --- doc/news/_preparation_next_release.md | 2 ++ doc/tutorials/notifications.md | 4 ++-- src/bindings/io/ev/README.md | 2 +- src/bindings/io/ev/example/exampleio_ev.c | 2 +- src/bindings/io/ev/testio_ev.c | 2 +- src/bindings/io/glib/README.md | 2 +- src/bindings/io/glib/example/exampleio_glib.c | 2 +- src/bindings/io/glib/testio_glib.c | 2 +- src/bindings/io/uv/README.md | 2 +- src/bindings/io/uv/example/exampleio_uv.c | 2 +- src/bindings/io/uv/testio_uv.c | 2 +- src/include/CMakeLists.txt | 15 --------------- src/include/kdbio/CMakeLists.txt | 14 ++++++++++++++ .../adapters/dbus.h} | 0 src/include/{kdbio_ev.h => kdbio/ev.h} | 0 src/include/{kdbio_glib.h => kdbio/glib.h} | 0 src/include/{kdbio_uv.h => kdbio/uv.h} | 0 src/libs/io/adapter/dbus/dbus.c | 2 +- .../example/example_notification_async.c | 2 +- src/plugins/dbus/dbus.h | 2 +- src/plugins/dbusrecv/dbusrecv.h | 2 +- src/plugins/dbusrecv/testmod_dbusrecv.c | 2 +- 22 files changed, 32 insertions(+), 31 deletions(-) create mode 100644 src/include/kdbio/CMakeLists.txt rename src/include/{kdbio_adapter_dbus.h => kdbio/adapters/dbus.h} (100%) rename src/include/{kdbio_ev.h => kdbio/ev.h} (100%) rename src/include/{kdbio_glib.h => kdbio/glib.h} (100%) rename src/include/{kdbio_uv.h => kdbio/uv.h} (100%) diff --git a/doc/news/_preparation_next_release.md b/doc/news/_preparation_next_release.md index 2e14a6afa1f..a055d59620c 100644 --- a/doc/news/_preparation_next_release.md +++ b/doc/news/_preparation_next_release.md @@ -495,6 +495,8 @@ compiled against an older 0.8 version of Elektra will continue to work - <> - `kdbtypes.h` now comes with support for C99 types - We added the private headerfiles `kdbnotificationinternal.h`, `kdbioplugin.h`. *(Thomas Wahringer)* +- The I/O binding header files have been moved a new directory called `kdbio`. + For example, instead of including `elektra/kdbio_ev.h` users of the binding now include `elektra/kdbio/ev.h`. *(Thomas Wahringer)* ## Build diff --git a/doc/tutorials/notifications.md b/doc/tutorials/notifications.md index 55ba0ca8c56..9ae2892b0a0 100644 --- a/doc/tutorials/notifications.md +++ b/doc/tutorials/notifications.md @@ -80,7 +80,7 @@ the initialization of an I/O binding. ```C #include #include -#include +#include #include @@ -143,7 +143,7 @@ The following examples are shortened for tangibility. The complete example is av ```C #include #include -#include +#include #include #include diff --git a/src/bindings/io/ev/README.md b/src/bindings/io/ev/README.md index 06d88dda4a1..d3f32f89483 100644 --- a/src/bindings/io/ev/README.md +++ b/src/bindings/io/ev/README.md @@ -37,7 +37,7 @@ Populated I/O interface ```C #include #include -#include +#include #include diff --git a/src/bindings/io/ev/example/exampleio_ev.c b/src/bindings/io/ev/example/exampleio_ev.c index 0cb750b9802..576d3da55f5 100644 --- a/src/bindings/io/ev/example/exampleio_ev.c +++ b/src/bindings/io/ev/example/exampleio_ev.c @@ -26,7 +26,7 @@ #include // assertions (ELEKTRA_NOT_NULL) #include // malloc & free #include // I/O binding functions (elektraIo*) -#include // I/O binding constructor for ev (elektraIoEvNew) +#include // I/O binding constructor for ev (elektraIoEvNew) #include // ev functions diff --git a/src/bindings/io/ev/testio_ev.c b/src/bindings/io/ev/testio_ev.c index d42bc8032b6..4a5cd27a794 100644 --- a/src/bindings/io/ev/testio_ev.c +++ b/src/bindings/io/ev/testio_ev.c @@ -16,7 +16,7 @@ #include -#include +#include static ElektraIoInterface * createBinding (void) { diff --git a/src/bindings/io/glib/README.md b/src/bindings/io/glib/README.md index d1d604f9743..86a95fa2a75 100644 --- a/src/bindings/io/glib/README.md +++ b/src/bindings/io/glib/README.md @@ -39,7 +39,7 @@ Populated I/O interface #include #include -#include +#include #include diff --git a/src/bindings/io/glib/example/exampleio_glib.c b/src/bindings/io/glib/example/exampleio_glib.c index fc87edadb3e..274fe4617a3 100644 --- a/src/bindings/io/glib/example/exampleio_glib.c +++ b/src/bindings/io/glib/example/exampleio_glib.c @@ -26,7 +26,7 @@ #include // assertions (ELEKTRA_NOT_NULL) #include // malloc & free #include // I/O binding functions (elektraIo*) -#include // I/O binding constructor for glib (elektraIoGlibNew) +#include // I/O binding constructor for glib (elektraIoGlibNew) #include // glib functions diff --git a/src/bindings/io/glib/testio_glib.c b/src/bindings/io/glib/testio_glib.c index c34aae062ee..5a287e28bc5 100644 --- a/src/bindings/io/glib/testio_glib.c +++ b/src/bindings/io/glib/testio_glib.c @@ -16,7 +16,7 @@ #include -#include +#include GMainLoop * loop; GMainContext * context; diff --git a/src/bindings/io/uv/README.md b/src/bindings/io/uv/README.md index df008f2babc..a74a34601b3 100644 --- a/src/bindings/io/uv/README.md +++ b/src/bindings/io/uv/README.md @@ -38,7 +38,7 @@ Populated I/O interface #include #include -#include +#include #include diff --git a/src/bindings/io/uv/example/exampleio_uv.c b/src/bindings/io/uv/example/exampleio_uv.c index f603ab8f094..f30e0b578dc 100644 --- a/src/bindings/io/uv/example/exampleio_uv.c +++ b/src/bindings/io/uv/example/exampleio_uv.c @@ -26,7 +26,7 @@ #include // assertions (ELEKTRA_NOT_NULL) #include // malloc & free #include // I/O binding functions (elektraIo*) -#include // I/O binding constructor for uv (elektraIoUvNew) +#include // I/O binding constructor for uv (elektraIoUvNew) #include // uv functions diff --git a/src/bindings/io/uv/testio_uv.c b/src/bindings/io/uv/testio_uv.c index 29fe584920d..55b97dc812f 100644 --- a/src/bindings/io/uv/testio_uv.c +++ b/src/bindings/io/uv/testio_uv.c @@ -16,7 +16,7 @@ #include -#include +#include static ElektraIoInterface * createBinding (void) { diff --git a/src/include/CMakeLists.txt b/src/include/CMakeLists.txt index 972b2519151..6f2db14c551 100644 --- a/src/include/CMakeLists.txt +++ b/src/include/CMakeLists.txt @@ -106,21 +106,6 @@ install (FILES "${CMAKE_CURRENT_BINARY_DIR}/kdbconfig.h" "${CMAKE_CURRENT_BINARY_DIR}/kdbversion.h" DESTINATION include/${TARGET_INCLUDE_FOLDER}) -check_binding_included ("io_uv" IO_UV_INCLUDED SUBDIRECTORY "io/uv" SILENT) -if (IO_UV_INCLUDED) - install (FILES kdbio_uv.h DESTINATION include/${TARGET_INCLUDE_FOLDER}) -endif () - -check_binding_included ("io_glib" IO_GLIB_INCLUDED SUBDIRECTORY "io/glib" SILENT) -if (IO_GLIB_INCLUDED) - install (FILES kdbio_glib.h DESTINATION include/${TARGET_INCLUDE_FOLDER}) -endif () - -check_binding_included ("io_ev" IO_EV_INCLUDED SUBDIRECTORY "io/ev" SILENT) -if (IO_EV_INCLUDED) - install (FILES kdbio_ev.h DESTINATION include/${TARGET_INCLUDE_FOLDER}) -endif () - add_custom_target (elektra_config_headers ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/kdb.h diff --git a/src/include/kdbio/CMakeLists.txt b/src/include/kdbio/CMakeLists.txt new file mode 100644 index 00000000000..b71fabcffa7 --- /dev/null +++ b/src/include/kdbio/CMakeLists.txt @@ -0,0 +1,14 @@ +check_binding_included ("io_uv" IO_UV_INCLUDED SUBDIRECTORY "io/uv" SILENT) +if (IO_UV_INCLUDED) + install (FILES uv.h DESTINATION include/${TARGET_INCLUDE_FOLDER}/kdbio) +endif () + +check_binding_included ("io_glib" IO_GLIB_INCLUDED SUBDIRECTORY "io/glib" SILENT) +if (IO_GLIB_INCLUDED) + install (FILES glib.h DESTINATION include/${TARGET_INCLUDE_FOLDER}/kdbio) +endif () + +check_binding_included ("io_ev" IO_EV_INCLUDED SUBDIRECTORY "io/ev" SILENT) +if (IO_EV_INCLUDED) + install (FILES ev.h DESTINATION include/${TARGET_INCLUDE_FOLDER}/kdbio) +endif () diff --git a/src/include/kdbio_adapter_dbus.h b/src/include/kdbio/adapters/dbus.h similarity index 100% rename from src/include/kdbio_adapter_dbus.h rename to src/include/kdbio/adapters/dbus.h diff --git a/src/include/kdbio_ev.h b/src/include/kdbio/ev.h similarity index 100% rename from src/include/kdbio_ev.h rename to src/include/kdbio/ev.h diff --git a/src/include/kdbio_glib.h b/src/include/kdbio/glib.h similarity index 100% rename from src/include/kdbio_glib.h rename to src/include/kdbio/glib.h diff --git a/src/include/kdbio_uv.h b/src/include/kdbio/uv.h similarity index 100% rename from src/include/kdbio_uv.h rename to src/include/kdbio/uv.h diff --git a/src/libs/io/adapter/dbus/dbus.c b/src/libs/io/adapter/dbus/dbus.c index 2427ca54a5c..9034afdf586 100644 --- a/src/libs/io/adapter/dbus/dbus.c +++ b/src/libs/io/adapter/dbus/dbus.c @@ -6,7 +6,7 @@ * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) */ #include -#include +#include #include #include diff --git a/src/libs/notification/example/example_notification_async.c b/src/libs/notification/example/example_notification_async.c index e37b0c09ad5..2744f63d984 100644 --- a/src/libs/notification/example/example_notification_async.c +++ b/src/libs/notification/example/example_notification_async.c @@ -10,7 +10,7 @@ #include #include // elektraFree #include // I/O binding functions (elektraIo*) -#include // I/O binding constructor for uv (elektraIoUvNew) +#include // I/O binding constructor for uv (elektraIoUvNew) #include // notification functions #include // uv functions diff --git a/src/plugins/dbus/dbus.h b/src/plugins/dbus/dbus.h index 30f1a8e1695..f7ee0f3a743 100644 --- a/src/plugins/dbus/dbus.h +++ b/src/plugins/dbus/dbus.h @@ -18,7 +18,7 @@ #include // elektraIoDbus*() -#include +#include /** * @internal diff --git a/src/plugins/dbusrecv/dbusrecv.h b/src/plugins/dbusrecv/dbusrecv.h index b6eafc2d8c7..fe8ebd4d374 100644 --- a/src/plugins/dbusrecv/dbusrecv.h +++ b/src/plugins/dbusrecv/dbusrecv.h @@ -19,7 +19,7 @@ #include // elektraIoDbus*() -#include +#include /** * @internal diff --git a/src/plugins/dbusrecv/testmod_dbusrecv.c b/src/plugins/dbusrecv/testmod_dbusrecv.c index 8b433f33ce5..f819f055fdf 100644 --- a/src/plugins/dbusrecv/testmod_dbusrecv.c +++ b/src/plugins/dbusrecv/testmod_dbusrecv.c @@ -10,7 +10,7 @@ #include // printf() & co -#include +#include #include #include From c5e641c36444dce8781975d639fee28357bf7ff6 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sat, 9 Jun 2018 16:45:28 +0200 Subject: [PATCH 29/46] notification: add notification_reload example; move notification examples to /examples --- doc/tutorials/notifications.md | 117 ++++++++++- examples/CMakeLists.txt | 76 ++++++- .../notificationAsync.c | 14 +- .../notificationPolling.c | 12 +- examples/notificationReload.c | 187 ++++++++++++++++++ src/libs/README.md | 20 +- src/libs/notification/CMakeLists.txt | 2 - src/libs/notification/example/README.md | 29 --- .../internalnotification.c | 4 + 9 files changed, 418 insertions(+), 43 deletions(-) rename src/libs/notification/example/example_notification_async.c => examples/notificationAsync.c (89%) rename src/libs/notification/example/example_notification.c => examples/notificationPolling.c (85%) create mode 100644 examples/notificationReload.c delete mode 100644 src/libs/notification/example/README.md diff --git a/doc/tutorials/notifications.md b/doc/tutorials/notifications.md index 9ae2892b0a0..d7f1ab8ce25 100644 --- a/doc/tutorials/notifications.md +++ b/doc/tutorials/notifications.md @@ -257,6 +257,116 @@ int main (void) } ``` +### How-To: Reload KDB when Elektra's configuration has changed + +This section shows how the notification feature can be used to reload an +application's KDB instance when Elektra's configuration has changed. +This enables applications to apply changes to mount points or globally mounted +plugins without restarting. + +**Step 1: Register for changes to Elektra's configuration** + +To achieve reloading on Elektra configuration changes we register for changes +below the key `system/elektra` using +`elektraNotificationRegisterCallbackSameOrBelow()`. + +```C +Key * elektraKey = keyNew ("system/elektra", KEY_END); +elektraNotificationRegisterCallbackSameOrBelow (kdb, elektraKey, elektraChangedCallback, NULL)) +keyDel (elektraKey); +``` + +**Step 2: Create a function for reloading KDB** + +Since our application needs to repeatedly initialize KDB on +configuration changes we need to create a function which cleans +up and reinitializates KDB. + +```C +void initKdb (void) +{ + if (kdb != NULL) + { + // Cleanup notifications and close KDB + elektraNotificationClose (kdb); + kdbClose (kdb, parentKey); + } + + kdb = kdbOpen (parentKey); + elektraIoSetBinding (kdb, binding); + elektraNotificationOpen (kdb); + + // registration code from snippet before + Key * elektraKey = keyNew ("system/elektra", KEY_END); + elektraNotificationRegisterCallbackSameOrBelow (kdb, elektraKey, elektraChangedCallback, NULL); + keyDel (elektraKey); + + // TODO: add application specific registrations + + // Get configuration + kdbGet (kdb, config, parentKey); +} +``` + +**Step 3: Handle configuration changes** + +The last step is to connect the registration for changes to the +(re-)initialization function. +Directly calling the function is discouraged due to tight coupling (see +guidelines in the next section) and also results in an application crash since +the notification API is closed while processing notification callbacks. +Therefore we suggest to add a timer operation with a sufficiently small interval +which is enabled only on configuration changes. +This timer will then call the initialization function. + +First, we create the timer in the main loop setup of the application. + +```C +// (global declaration) +ElektraIoTimerOperation * reload; + +// main loop setup (e.g. main()) +reload = elektraIoNewTimerOperation (100, 0, initKdb, NULL); +elektraIoBindingAddTimer (binding, reload); +``` + +Now we add the callback function for the registration from step 1: + +```C +void elektraChangedCallback (Key * changedKey, void * context) +{ + // Enable operation to reload KDB as soon as possible + elektraIoTimerSetEnabled (reload, 1); + elektraIoBindingUpdateTimer (reload); +} +``` + +Finally we disable the timer in the initialization function: + +```C +void initKdb (void) +{ + // Stop reload task + elektraIoTimerSetEnabled (reload, 0); + elektraIoBindingUpdateTimer (reload); + + if (kdb != NULL) + { + // Cleanup notifications and close KDB + elektraNotificationClose (kdb); + kdbClose (kdb, parentKey); + } + + // ... +} +``` + +By following these three steps any application can react to changes to Elektra's +configuration. +The snippets above omit error handling for brevity. The complete code including +error handling is available in the +["notification reload" example](https://www.libelektra.org/examples/notificationreload). + ## Emergent Behavior Guidelines When applications react to configuration changes made by other applications this @@ -331,7 +441,7 @@ changes at regular time intervals and react to changes the coupling is not as tight as with callbacks. ### Guideline 2: Wait before reacting to changes -> Waiting decouples an application from changes and reduces the risk for unwanted ***synchronization***. +> Waiting after receiving a notification decouples an application from changes and reduces the risk for unwanted ***synchronization***. In applications where applying changes has impact on resource usage (e.g. CPU or disk) applying a time delay as suggested by this Guideline is a @@ -347,7 +457,7 @@ Callbacks set the flag and when the control flow is in the main loop again, the pending updates are applied and the flag is cleared. ### Guideline 3: Avoid updates as reaction to change -> Avoid changing the configuration as reaction to a change especially in callbacks. +> Avoid changing the configuration as reaction to a change. > This reduces the risk for unwanted ***oscillation***. While this guideline does not forbid updating the key database using `kdbSet()` @@ -357,6 +467,9 @@ leads to unwanted oscillation. If necessary, the function `kdbSet()` should be temporally decoupled as suggested in [Guideline 2](#guideline-2-wait-before-reacting-to-changes). +This guideline applies especially to callbacks but is also relevant when variables +are polled for changes by the application. + ### Guideline 4: Do not use notifications for synchronization > Applications should not use notifications for synchronization as this can lead to ***phase change***. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7267a03f916..ec687a009d9 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,5 @@ include (LibAddMacros) +include (LibAddBinding) # don't call add_headers in a loop add_headers (HDR_FILES) @@ -20,7 +21,9 @@ file (GLOB TESTS *.c) foreach (file ${TESTS}) get_filename_component (name ${file} NAME_WE) - do_example (${name}) + if (NOT "${name}" MATCHES "^notification") + do_example (${name}) + endif () endforeach (file ${TESTS}) target_link_elektra (cascading elektra-kdb) @@ -31,3 +34,74 @@ target_link_elektra (kdbintro elektra-kdb) target_link_elektra (kdbopen elektra-kdb) target_link_elektra (kdbset elektra-kdb) target_link_elektra (set_key elektra-kdb) + +# Notification examples + +# Cannot build examples without notification library which requires the internalnotification plugin +list (FIND ADDED_PLUGINS "internalnotification" FOUND_NAME) +if (FOUND_NAME GREATER -1) + + # Build notification polling example + set (EXAMPLE notificationPolling) + + set (SRC_FILES notificationPolling.c) + set (SOURCES ${SRC_FILES} ${HDR_FILES}) + + add_executable (${EXAMPLE} ${SOURCES}) + add_dependencies (${EXAMPLE} kdberrors_generated) + + target_link_elektra (${EXAMPLE} elektra-kdb elektra-notification) + + # TODO resolve https://github.com/ElektraInitiative/libelektra/issues/2007 + check_binding_was_added ("io_uv" IS_INCLUDED) + if (IS_INCLUDED) + + # Build notification async example + set (EXAMPLE notificationAsync) + + set (SRC_FILES notificationAsync.c) + set (SOURCES ${SRC_FILES} ${HDR_FILES}) + if (BUILD_FULL OR BUILD_STATIC) + list (APPEND SOURCES $) # add sources for elektra-io-uv for static and full builds + endif () + + add_executable (${EXAMPLE} ${SOURCES}) + add_dependencies (${EXAMPLE} kdberrors_generated) + + target_link_elektra (${EXAMPLE} elektra-kdb elektra-notification elektra-io elektra-io-uv) + if (BUILD_FULL OR BUILD_STATIC) + target_link_libraries (${EXAMPLE} ${LIBUV_LDFLAGS}) + endif () + + if (LIBUV_VERSION VERSION_LESS "1.0") + target_compile_definitions (${EXAMPLE} PRIVATE "HAVE_LIBUV0") + else () + target_compile_definitions (${EXAMPLE} PRIVATE "HAVE_LIBUV1") + endif () + + endif () + + check_binding_was_added ("io_glib" IS_INCLUDED) + if (IS_INCLUDED) + + # Build notification reload example + set (EXAMPLE notificationReload) + + set (SRC_FILES notificationReload.c) + set (SOURCES ${SRC_FILES} ${HDR_FILES}) + if (BUILD_FULL OR BUILD_STATIC) + list (APPEND SOURCES $) # add sources for elektra-io-uv for static and full + # builds + endif () + + add_executable (${EXAMPLE} ${SOURCES}) + add_dependencies (${EXAMPLE} kdberrors_generated) + + target_link_elektra (${EXAMPLE} elektra-kdb elektra-notification elektra-io elektra-io-glib) + if (BUILD_FULL OR BUILD_STATIC) + target_link_libraries (${EXAMPLE} ${GLIB_LIBRARIES}) + endif () + + endif () + +endif () diff --git a/src/libs/notification/example/example_notification_async.c b/examples/notificationAsync.c similarity index 89% rename from src/libs/notification/example/example_notification_async.c rename to examples/notificationAsync.c index 2744f63d984..99f588333ae 100644 --- a/src/libs/notification/example/example_notification_async.c +++ b/examples/notificationAsync.c @@ -1,7 +1,17 @@ /** * @file * - * @brief Implementation of notification functions as defined in kdbnotification.h + * @brief Example for notification library which repeatedly reads some keys and + * reacts to them + * + * Requires: + * - io_uv binding + * - Transport plugins (e.g. kdb global-mount dbus announce=once dbusrecv) + * + * Ideas for this example: + * - /sw/tests/example_notification/#0/current/value: Set to any integer value + * - /sw/tests/example_notification/#0/current/color: Set the text color. Possible + * values are "red", "green" and "blue". * * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) * @@ -100,7 +110,7 @@ int main (void) int result = elektraNotificationOpen (kdb); if (!result) { - printf ("could init notification. aborting\n"); + printf ("could not init notification. aborting\n"); return -1; } diff --git a/src/libs/notification/example/example_notification.c b/examples/notificationPolling.c similarity index 85% rename from src/libs/notification/example/example_notification.c rename to examples/notificationPolling.c index 5d7d2d48846..fc0c3e2b21d 100644 --- a/src/libs/notification/example/example_notification.c +++ b/examples/notificationPolling.c @@ -1,7 +1,13 @@ /** * @file * - * @brief Implementation of notification functions as defined in kdbnotification.h + * @brief Example for notification library which repeatedly reads some keys and + * reacts to them; see "example_notification_async" for an example without polling + * + * Ideas for this example: + * - /sw/tests/example_notification/#0/current/value: Set to any integer value + * - /sw/tests/example_notification/#0/current/color: Set the text color. Possible + * values are "red", "green" and "blue". * * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) * @@ -94,7 +100,7 @@ int main (void) int result = elektraNotificationOpen (kdb); if (!result) { - printf ("could init notification. aborting\n"); + printf ("could not init notification. aborting\n"); return -1; } @@ -121,7 +127,7 @@ int main (void) while (!keepRunning) { // After this kdbGet the integer variable is updated and the callback was called. - // TODO remove polling or make it optional when "transport plugins" are available + // see "example_notification_async" for an example without polling kdbGet (kdb, config, key); // Print values diff --git a/examples/notificationReload.c b/examples/notificationReload.c new file mode 100644 index 00000000000..22d4fc0a429 --- /dev/null +++ b/examples/notificationReload.c @@ -0,0 +1,187 @@ +/** + * @file + * + * @brief Example for notification library which reloads KDB when Elektra's + * configuration (e.g. mount points or global plugins) has changed. + * + * Requires: + * - io_glib binding + * - Transport plugins (e.g. kdb global-mount zeromqsend zeromqrecv && kdb run-hub-zeromq) + * + * Ideas for this example: + * - /sw/tests/example_notification/#0/current/value: Set to any integer value + * - Try to add additional transport plugins and remove the original pair afterwards + * - Mount a file which sets the key above to a different value and unmount it + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + * + */ + +#include +#include // elektraFree +#include // I/O binding functions (elektraIo*) +#include // I/O binding constructor for glib (elektraIoGlibNew) +#include // notification functions + +#include // g_unix_signal_add() +#include // glib functions + +#include // signal() +#include // printf() & co +#include // exit() + +GMainLoop * loop; + +KDB * kdb; +Key * parentKey; +KeySet * config; +ElektraIoInterface * binding; +Key * intKeyToWatch; +int valueToPrint = 0; + +ElektraIoTimerOperation * timer; +ElektraIoTimerOperation * reload; + +static void elektraChangedCallback (Key * changedKey ELEKTRA_UNUSED, void * context ELEKTRA_UNUSED); + +/** + * Initializes KDB on first call and performs cleanup before initialization on + * subsequent calls. + * + * @param timerOp unused + */ +static void initKdb (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED) +{ + int didReload = 0; + + // Stop reload task + elektraIoTimerSetEnabled (reload, 0); + elektraIoBindingUpdateTimer (reload); + + if (kdb != NULL) + { + // Cleanup notifications and close KDB + elektraNotificationClose (kdb); + kdbClose (kdb, parentKey); + didReload = 1; + } + + kdb = kdbOpen (parentKey); + if (kdb == NULL) + { + printf ("could not open KDB. aborting\n"); + exit (-1); + } + + elektraIoSetBinding (kdb, binding); + + int result = elektraNotificationOpen (kdb); + if (!result) + { + printf ("could not init notification. aborting\n"); + exit (-1); + } + + result = elektraNotificationRegisterInt (kdb, intKeyToWatch, &valueToPrint); + if (!result) + { + printf ("could not register variable. aborting\n"); + exit (-1); + } + + Key * elektraKey = keyNew ("system/elektra", KEY_END); + if (!elektraNotificationRegisterCallbackSameOrBelow (kdb, elektraKey, elektraChangedCallback, NULL)) + { + printf ("could not register for changes to Elektra's configuration. aborting\n"); + exit (-1); + } + keyDel (elektraKey); + + // Get configuration + kdbGet (kdb, config, parentKey); + + if (didReload) + { + printf ("KDB reloaded.\n"); + } +} + +static gboolean onSIGNAL (gpointer user_data ELEKTRA_UNUSED) +{ + // Cleanup + elektraIoBindingRemoveTimer (timer); + elektraFree (timer); + elektraIoBindingRemoveTimer (reload); + elektraFree (reload); + elektraNotificationClose (kdb); + kdbClose (kdb, parentKey); + elektraIoBindingCleanup (binding); + + g_main_loop_quit (loop); + return FALSE; +} + +/** + * This function is called whenever Elektra's configuration has changed. + * Since cannot call elektraNotificationClose() here we start a timer operation + * which allows us to reload KDB in the next main loop iteration. + * + * @param changedKey unused + * @param context unused + */ +static void elektraChangedCallback (Key * changedKey ELEKTRA_UNUSED, void * context ELEKTRA_UNUSED) +{ + printf ("\nElektra's configuration has changed.\n"); + + // Enable operation to reload KDB as soon as possible + elektraIoTimerSetEnabled (reload, 1); + elektraIoBindingUpdateTimer (reload); +} + +static void printVariable (ElektraIoTimerOperation * timerOp) +{ + int value = *(int *) elektraIoTimerGetData (timerOp); + printf ("\nMy integer value is %d\n", value); +} + +int main (void) +{ + // Create glib main loop + GMainContext * context = NULL; // use default context + loop = g_main_loop_new (context, 0); + binding = elektraIoGlibNew (context); + + // Signal Handling + g_unix_signal_add (SIGINT, onSIGNAL, NULL); + + config = ksNew (20, KS_END); + parentKey = keyNew ("/sw/tests/example_notification/#0/current", KEY_END); + intKeyToWatch = keyNew ("/sw/tests/example_notification/#0/current/value", KEY_END); + + // Setup timer that repeatedly prints the variable + timer = elektraIoNewTimerOperation (2000, 1, printVariable, &valueToPrint); + elektraIoBindingAddTimer (binding, timer); + + // Setup timer for reloading Elektra's configuration + reload = elektraIoNewTimerOperation (100, 0, initKdb, NULL); + elektraIoBindingAddTimer (binding, reload); + + printf ("Reloading Notification Example Application\n"); + printf ("- Set \"%s\" to any integer value\n", keyName (intKeyToWatch)); + printf ("- Try to add additional transport plugins and remove the original pair afterwards\n"); + printf ("- Mount a file which sets the key above to a different value and unmount it\n"); + printf ("Send SIGINT (Ctl+C) to exit.\n\n"); + + // Initialize KDB + initKdb (reload); + printVariable (timer); // "value" was automatically updated + + g_main_loop_run (loop); + + g_main_loop_unref (loop); + + ksDel (config); + keyDel (intKeyToWatch); + keyDel (parentKey); + printf ("cleanup done!\n"); +} diff --git a/src/libs/README.md b/src/libs/README.md index fc809be8986..ce9c8aceb9e 100644 --- a/src/libs/README.md +++ b/src/libs/README.md @@ -31,13 +31,13 @@ Applications and plugins can choose to not link against it if they want to stay libelektra-pluginprocess.so **[libpluginprocess](pluginprocess/)** contains functions aiding in executing plugins in a separate -process and communicating with those child processes. This child process is forked from Elektra's +process and communicating with those child processes. This child process is forked from Elektra's main process each time such plugin is used and gets closed again afterwards. It uses a simple -communication protocol based on a KeySet that gets serialized through a pipe via the dump plugin to +communication protocol based on a KeySet that gets serialized through a pipe via the dump plugin to orchestrate the processes. -This is useful for plugins which cause memory leaks to be isolated in an own process. Furthermore -this is useful for runtimes or libraries that cannot be reinitialized in the same process after they +This is useful for plugins which cause memory leaks to be isolated in an own process. Furthermore +this is useful for runtimes or libraries that cannot be reinitialized in the same process after they have been used. ### Libproposal @@ -82,3 +82,15 @@ data structures. libelektra-invoke.so **[libinvoke](invoke/)** provides a simple API allowing us to call functions exported by plugins. + +### IO + + libelektra-io.so + +**[io](io/)** provides the common [I/O binding API](https://doc.libelektra.org/api/current/html/group__kdbio.html). + +### Notification + + libelektra-notification.so + +**[notification](notification/)** provides the [notification API](https://doc.libelektra.org/api/current/html/group__kdbnotification.html). diff --git a/src/libs/notification/CMakeLists.txt b/src/libs/notification/CMakeLists.txt index 6392037a046..b0e43bed8e8 100644 --- a/src/libs/notification/CMakeLists.txt +++ b/src/libs/notification/CMakeLists.txt @@ -16,8 +16,6 @@ else () install (FILES "${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}.pc" DESTINATION lib${LIB_SUFFIX}/${TARGET_PKGCONFIG_FOLDER}) - add_subdirectory (example) - if (ENABLE_TESTING) add_subdirectory (tests) endif (ENABLE_TESTING) diff --git a/src/libs/notification/example/README.md b/src/libs/notification/example/README.md deleted file mode 100644 index 4458d3836fd..00000000000 --- a/src/libs/notification/example/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Example Applications for Notifications - -This folder contains example notifications which demonstrate how -notifications and the I/O binding API are used in applications: - -- example_notification: Repeatedly calls kdbGet, does not require transport plugins -- example_notification_async: Uses asynchronous I/O. Requires transport plugins - -Both applications use the same keys: - -- /sw/tests/example_notification/#0/current/value: Set to any integer value -- /sw/tests/example_notification/#0/current/color: Set the text color. Valid - values are "red", "green" and "blue". - -## "example_notification" - -Is always built with the notification library. - -## "example_notification_async" - -Requires: - -- Binding: `io_uv` - -Usage: - -Make sure that the required transport plugins are mounted (e.g. for D-Bus): - -> kdb global-mount dbus announce=once dbusrecv diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index 7b9dad15cab..83e553b77db 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -313,6 +313,10 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * // Invoke callback ElektraNotificationChangeCallback callback = *(ElektraNotificationChangeCallback) registeredKey->callback; callback (key, registeredKey->context); + if (registeredKey->sameOrBelow) + { + keyDel (key); + } } // proceed with next registered key From 8ab97414a42b36540134dc9f3c672b5b2e36913d Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 17 Jun 2018 18:45:55 +0200 Subject: [PATCH 30/46] I/O binding headers: fix paths & install headers --- src/include/CMakeLists.txt | 2 ++ src/include/kdbio/ev.h | 2 +- src/include/kdbio/glib.h | 2 +- src/include/kdbio/uv.h | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/include/CMakeLists.txt b/src/include/CMakeLists.txt index 6f2db14c551..49d3e88586b 100644 --- a/src/include/CMakeLists.txt +++ b/src/include/CMakeLists.txt @@ -111,3 +111,5 @@ add_custom_target (elektra_config_headers DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/kdb.h ${CMAKE_CURRENT_BINARY_DIR}/kdbconfig.h ${CMAKE_CURRENT_BINARY_DIR}/kdbversion.h) + +add_subdirectory (kdbio) diff --git a/src/include/kdbio/ev.h b/src/include/kdbio/ev.h index 20ea1ff2972..64361feb1a9 100644 --- a/src/include/kdbio/ev.h +++ b/src/include/kdbio/ev.h @@ -8,7 +8,7 @@ #ifndef KDB_IOWRAPPER_EV_H_ #define KDB_IOWRAPPER_EV_H_ -#include "kdbio.h" +#include "../kdbio.h" #include /** diff --git a/src/include/kdbio/glib.h b/src/include/kdbio/glib.h index 47086bec1a2..b62ef7d86cb 100644 --- a/src/include/kdbio/glib.h +++ b/src/include/kdbio/glib.h @@ -8,7 +8,7 @@ #ifndef KDB_IOWRAPPER_GLIB_H_ #define KDB_IOWRAPPER_GLIB_H_ -#include "kdbio.h" +#include "../kdbio.h" #include /** diff --git a/src/include/kdbio/uv.h b/src/include/kdbio/uv.h index e8e6d05af99..83ad2705a78 100644 --- a/src/include/kdbio/uv.h +++ b/src/include/kdbio/uv.h @@ -8,7 +8,7 @@ #ifndef KDB_IOWRAPPER_UV_H_ #define KDB_IOWRAPPER_UV_H_ -#include "kdbio.h" +#include "../kdbio.h" #include /** From d9d6d9d93039fe831c3eee28a8e2ba53a0da2f92 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 17 Jun 2018 21:48:56 +0200 Subject: [PATCH 31/46] notification: fix typo & wording --- doc/tutorials/notifications.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorials/notifications.md b/doc/tutorials/notifications.md index d7f1ab8ce25..28851280181 100644 --- a/doc/tutorials/notifications.md +++ b/doc/tutorials/notifications.md @@ -280,7 +280,7 @@ keyDel (elektraKey); Since our application needs to repeatedly initialize KDB on configuration changes we need to create a function which cleans -up and reinitializates KDB. +up and reinitializes KDB. ```C void initKdb (void) @@ -296,7 +296,7 @@ void initKdb (void) elektraIoSetBinding (kdb, binding); elektraNotificationOpen (kdb); - // registration code from snippet before + // Code for registration from snippet before Key * elektraKey = keyNew ("system/elektra", KEY_END); elektraNotificationRegisterCallbackSameOrBelow (kdb, elektraKey, elektraChangedCallback, NULL); keyDel (elektraKey); From 65ce08ea23a2269ab4107bcd5de4abfa123f2ea2 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 15 Jul 2018 19:18:36 +0200 Subject: [PATCH 32/46] notification: add "long long" and "unsigned long long" types --- src/include/kdbnotification.h | 2 ++ .../internalnotification/internalnotification.c | 13 +++++++++++++ .../testmod_internalnotification.c | 14 ++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/include/kdbnotification.h b/src/include/kdbnotification.h index 5f2a3f19732..a7723e5f09d 100644 --- a/src/include/kdbnotification.h +++ b/src/include/kdbnotification.h @@ -130,6 +130,8 @@ ELEKTRA_NOTIFICATION_TYPE_DECLARATION (int, Int) ELEKTRA_NOTIFICATION_TYPE_DECLARATION (unsigned int, UnsignedInt) ELEKTRA_NOTIFICATION_TYPE_DECLARATION (long, Long) ELEKTRA_NOTIFICATION_TYPE_DECLARATION (unsigned long, UnsignedLong) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (long long, LongLong) +ELEKTRA_NOTIFICATION_TYPE_DECLARATION (unsigned long long, UnsignedLongLong) ELEKTRA_NOTIFICATION_TYPE_DECLARATION (float, Float) ELEKTRA_NOTIFICATION_TYPE_DECLARATION (double, Double) diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index 83e553b77db..c25791ef6c1 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -352,6 +352,18 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" +#define TYPE long long +#define TYPE_NAME LongLong +#define TO_VALUE (strtoll (string, &end, 10)) +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION +#include "macros/add_type.h" + +#define TYPE unsigned long long +#define TYPE_NAME UnsignedLongLong +#define TO_VALUE (strtoull (string, &end, 10)) +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION +#include "macros/add_type.h" + #define TYPE float #define TYPE_NAME Float #define TO_VALUE (strtof (string, &end)) @@ -509,6 +521,7 @@ int elektraInternalnotificationGet (Plugin * handle, KeySet * returned, Key * pa // Export register* functions INTERNALNOTIFICATION_EXPORT_FUNCTION (Int), INTERNALNOTIFICATION_EXPORT_FUNCTION (UnsignedInt), INTERNALNOTIFICATION_EXPORT_FUNCTION (Long), INTERNALNOTIFICATION_EXPORT_FUNCTION (UnsignedLong), + INTERNALNOTIFICATION_EXPORT_FUNCTION (LongLong), INTERNALNOTIFICATION_EXPORT_FUNCTION (UnsignedLongLong), INTERNALNOTIFICATION_EXPORT_FUNCTION (Float), INTERNALNOTIFICATION_EXPORT_FUNCTION (Double), // Export register* functions for kdb_*_t types diff --git a/src/plugins/internalnotification/testmod_internalnotification.c b/src/plugins/internalnotification/testmod_internalnotification.c index a89fdec32b1..f8bd8255c87 100644 --- a/src/plugins/internalnotification/testmod_internalnotification.c +++ b/src/plugins/internalnotification/testmod_internalnotification.c @@ -591,6 +591,20 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define INVALID_VALUE "AA446744073709551615" #include "macros/create_type_tests.h" +#define TYPE long long +#define TYPE_NAME LongLong +#define FORMAT_STRING "%lld" +#define TEST_VALUE LLONG_MAX +#define INVALID_VALUE "322337abc6854775807" +#include "macros/create_type_tests.h" + +#define TYPE unsigned long long +#define TYPE_NAME UnsignedLongLong +#define FORMAT_STRING "%llu" +#define TEST_VALUE ULLONG_MAX +#define INVALID_VALUE "AA3223372036854775807" +#include "macros/create_type_tests.h" + #define TYPE float #define TYPE_NAME Float #define FORMAT_STRING "%f" From e46d092155c401c59bb235c9f9610778010d26e0 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 15 Jul 2018 19:20:21 +0200 Subject: [PATCH 33/46] notification: update tutorial & headers, add license --- doc/tutorials/notifications.md | 25 +++++++------ examples/CMakeLists.txt | 2 +- examples/notificationAsync.c | 20 +++++----- examples/notificationPolling.c | 20 +++++----- examples/notificationReload.c | 37 ++++++++++--------- src/include/kdbio/ev.h | 2 +- src/include/kdbio/glib.h | 2 +- src/include/kdbio/uv.h | 2 +- src/include/macros/type_create_to_value.h | 4 +- .../internalnotification/macros/add_type.h | 4 +- .../macros/create_type_tests.h | 4 +- 11 files changed, 66 insertions(+), 56 deletions(-) diff --git a/doc/tutorials/notifications.md b/doc/tutorials/notifications.md index 28851280181..0f6a857b32b 100644 --- a/doc/tutorials/notifications.md +++ b/doc/tutorials/notifications.md @@ -259,7 +259,7 @@ int main (void) ### How-To: Reload KDB when Elektra's configuration has changed -This section shows how the notification feature can be used to reload an +This section shows how the notification feature is used to reload an application's KDB instance when Elektra's configuration has changed. This enables applications to apply changes to mount points or globally mounted plugins without restarting. @@ -267,11 +267,11 @@ plugins without restarting. **Step 1: Register for changes to Elektra's configuration** To achieve reloading on Elektra configuration changes we register for changes -below the key `system/elektra` using +below the key `/elektra` using `elektraNotificationRegisterCallbackSameOrBelow()`. ```C -Key * elektraKey = keyNew ("system/elektra", KEY_END); +Key * elektraKey = keyNew ("/elektra", KEY_END); elektraNotificationRegisterCallbackSameOrBelow (kdb, elektraKey, elektraChangedCallback, NULL)) keyDel (elektraKey); ``` @@ -283,7 +283,7 @@ configuration changes we need to create a function which cleans up and reinitializes KDB. ```C -void initKdb (void) +void initKdb (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED) { if (kdb != NULL) { @@ -296,12 +296,12 @@ void initKdb (void) elektraIoSetBinding (kdb, binding); elektraNotificationOpen (kdb); - // Code for registration from snippet before + // Code for registration from snippet before Key * elektraKey = keyNew ("system/elektra", KEY_END); elektraNotificationRegisterCallbackSameOrBelow (kdb, elektraKey, elektraChangedCallback, NULL); keyDel (elektraKey); - // TODO: add application specific registrations + // TODO: add application specific registrations // Get configuration kdbGet (kdb, config, parentKey); @@ -326,6 +326,7 @@ First, we create the timer in the main loop setup of the application. ElektraIoTimerOperation * reload; // main loop setup (e.g. main()) +// the timer operation will reload KDB after 100 milliseconds reload = elektraIoNewTimerOperation (100, 0, initKdb, NULL); elektraIoBindingAddTimer (binding, reload); ``` @@ -346,14 +347,14 @@ Finally we disable the timer in the initialization function: ```C void initKdb (void) { - // Stop reload task - elektraIoTimerSetEnabled (reload, 0); - elektraIoBindingUpdateTimer (reload); + // Stop reload task + elektraIoTimerSetEnabled (reload, 0); + elektraIoBindingUpdateTimer (reload); if (kdb != NULL) { // Cleanup notifications and close KDB - elektraNotificationClose (kdb); + elektraNotificationClose (kdb); kdbClose (kdb, parentKey); } @@ -361,8 +362,8 @@ void initKdb (void) } ``` -By following these three steps any application can react to changes to Elektra's -configuration. +By correct application of these three steps any application can react to changes +to Elektra's configuration. The snippets above omit error handling for brevity. The complete code including error handling is available in the ["notification reload" example](https://www.libelektra.org/examples/notificationreload). diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ec687a009d9..4524ecd9250 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -52,7 +52,7 @@ if (FOUND_NAME GREATER -1) target_link_elektra (${EXAMPLE} elektra-kdb elektra-notification) - # TODO resolve https://github.com/ElektraInitiative/libelektra/issues/2007 + # TODO resolve issues.libelektra.org/2007 check_binding_was_added ("io_uv" IS_INCLUDED) if (IS_INCLUDED) diff --git a/examples/notificationAsync.c b/examples/notificationAsync.c index 99f588333ae..e2dc86f6592 100644 --- a/examples/notificationAsync.c +++ b/examples/notificationAsync.c @@ -8,9 +8,9 @@ * - io_uv binding * - Transport plugins (e.g. kdb global-mount dbus announce=once dbusrecv) * - * Ideas for this example: - * - /sw/tests/example_notification/#0/current/value: Set to any integer value - * - /sw/tests/example_notification/#0/current/color: Set the text color. Possible + * Relevant keys for this example: + * - /sw/elektra/example_notification/#0/current/value: Set to any integer value + * - /sw/elektra/example_notification/#0/current/color: Set the text color. Possible * values are "red", "green" and "blue". * * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) @@ -95,11 +95,11 @@ int main (void) KeySet * config = ksNew (20, KS_END); - Key * key = keyNew ("/sw/tests/example_notification/#0/current", KEY_END); + Key * key = keyNew ("/sw/elektra/example_notification/#0/current", KEY_END); KDB * kdb = kdbOpen (key); if (kdb == NULL) { - printf ("could not open KDB. aborting\n"); + printf ("could not open KDB, aborting\n"); return -1; } @@ -110,24 +110,24 @@ int main (void) int result = elektraNotificationOpen (kdb); if (!result) { - printf ("could not init notification. aborting\n"); + printf ("could not init notification, aborting\n"); return -1; } int value = 0; - Key * intKeyToWatch = keyNew ("/sw/tests/example_notification/#0/current/value", KEY_END); + Key * intKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/value", KEY_END); result = elektraNotificationRegisterInt (kdb, intKeyToWatch, &value); if (!result) { - printf ("could not register variable. aborting\n"); + printf ("could not register variable, aborting\n"); return -1; } - Key * callbackKeyToWatch = keyNew ("/sw/tests/example_notification/#0/current/color", KEY_END); + Key * callbackKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/color", KEY_END); result = elektraNotificationRegisterCallback (kdb, callbackKeyToWatch, &setTerminalColor, NULL); if (!result) { - printf ("could not register callback. aborting!"); + printf ("could not register callback, aborting!"); return -1; } diff --git a/examples/notificationPolling.c b/examples/notificationPolling.c index fc0c3e2b21d..b7f27534790 100644 --- a/examples/notificationPolling.c +++ b/examples/notificationPolling.c @@ -4,9 +4,9 @@ * @brief Example for notification library which repeatedly reads some keys and * reacts to them; see "example_notification_async" for an example without polling * - * Ideas for this example: - * - /sw/tests/example_notification/#0/current/value: Set to any integer value - * - /sw/tests/example_notification/#0/current/color: Set the text color. Possible + * Relevant keys for this example: + * - /sw/elektra/example_notification/#0/current/value: Set to any integer value + * - /sw/elektra/example_notification/#0/current/color: Set the text color. Possible * values are "red", "green" and "blue". * * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) @@ -89,35 +89,35 @@ int main (void) KeySet * config = ksNew (20, KS_END); - Key * key = keyNew ("/sw/tests/example_notification/#0/current", KEY_END); + Key * key = keyNew ("/sw/elektra/example_notification/#0/current", KEY_END); KDB * kdb = kdbOpen (key); if (kdb == NULL) { - printf ("could not open KDB. aborting\n"); + printf ("could not open KDB, aborting\n"); return -1; } int result = elektraNotificationOpen (kdb); if (!result) { - printf ("could not init notification. aborting\n"); + printf ("could not init notification, aborting\n"); return -1; } int value = 0; - Key * intKeyToWatch = keyNew ("/sw/tests/example_notification/#0/current/value", KEY_END); + Key * intKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/value", KEY_END); result = elektraNotificationRegisterInt (kdb, intKeyToWatch, &value); if (!result) { - printf ("could not register variable. aborting\n"); + printf ("could not register variable, aborting\n"); return -1; } - Key * callbackKeyToWatch = keyNew ("/sw/tests/example_notification/#0/current/color", KEY_END); + Key * callbackKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/color", KEY_END); result = elektraNotificationRegisterCallback (kdb, callbackKeyToWatch, &setTerminalColor, NULL); if (!result) { - printf ("could not register callback. aborting!"); + printf ("could not register callback, aborting!"); return -1; } diff --git a/examples/notificationReload.c b/examples/notificationReload.c index 22d4fc0a429..660db9d7a24 100644 --- a/examples/notificationReload.c +++ b/examples/notificationReload.c @@ -8,10 +8,10 @@ * - io_glib binding * - Transport plugins (e.g. kdb global-mount zeromqsend zeromqrecv && kdb run-hub-zeromq) * - * Ideas for this example: - * - /sw/tests/example_notification/#0/current/value: Set to any integer value - * - Try to add additional transport plugins and remove the original pair afterwards - * - Mount a file which sets the key above to a different value and unmount it + * Relevant keys for this example: + * - /sw/elektra/example_notification/#0/current/value: Set to any integer value + * Add additional transport plugins and remove the original pair afterwards or + * mount a file which sets the key above to a different value and unmount it again * * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) * @@ -30,6 +30,9 @@ #include // printf() & co #include // exit() +#define TWO_SECONDS 2000 +#define RELOAD_INTERVAL 100 + GMainLoop * loop; KDB * kdb; @@ -69,8 +72,8 @@ static void initKdb (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED) kdb = kdbOpen (parentKey); if (kdb == NULL) { - printf ("could not open KDB. aborting\n"); - exit (-1); + printf ("could not open KDB, aborting\n"); + exit (1); } elektraIoSetBinding (kdb, binding); @@ -78,22 +81,22 @@ static void initKdb (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED) int result = elektraNotificationOpen (kdb); if (!result) { - printf ("could not init notification. aborting\n"); - exit (-1); + printf ("could not init notification, aborting\n"); + exit (1); } result = elektraNotificationRegisterInt (kdb, intKeyToWatch, &valueToPrint); if (!result) { - printf ("could not register variable. aborting\n"); - exit (-1); + printf ("could not register variable, aborting\n"); + exit (1); } - Key * elektraKey = keyNew ("system/elektra", KEY_END); + Key * elektraKey = keyNew ("/elektra", KEY_END); if (!elektraNotificationRegisterCallbackSameOrBelow (kdb, elektraKey, elektraChangedCallback, NULL)) { - printf ("could not register for changes to Elektra's configuration. aborting\n"); - exit (-1); + printf ("could not register for changes to Elektra's configuration, aborting\n"); + exit (1); } keyDel (elektraKey); @@ -155,15 +158,15 @@ int main (void) g_unix_signal_add (SIGINT, onSIGNAL, NULL); config = ksNew (20, KS_END); - parentKey = keyNew ("/sw/tests/example_notification/#0/current", KEY_END); - intKeyToWatch = keyNew ("/sw/tests/example_notification/#0/current/value", KEY_END); + parentKey = keyNew ("/sw/elektra/example_notification/#0/current", KEY_END); + intKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/value", KEY_END); // Setup timer that repeatedly prints the variable - timer = elektraIoNewTimerOperation (2000, 1, printVariable, &valueToPrint); + timer = elektraIoNewTimerOperation (TWO_SECONDS, 1, printVariable, &valueToPrint); elektraIoBindingAddTimer (binding, timer); // Setup timer for reloading Elektra's configuration - reload = elektraIoNewTimerOperation (100, 0, initKdb, NULL); + reload = elektraIoNewTimerOperation (RELOAD_INTERVAL, 0, initKdb, NULL); elektraIoBindingAddTimer (binding, reload); printf ("Reloading Notification Example Application\n"); diff --git a/src/include/kdbio/ev.h b/src/include/kdbio/ev.h index 64361feb1a9..447cc7f933d 100644 --- a/src/include/kdbio/ev.h +++ b/src/include/kdbio/ev.h @@ -8,8 +8,8 @@ #ifndef KDB_IOWRAPPER_EV_H_ #define KDB_IOWRAPPER_EV_H_ -#include "../kdbio.h" #include +#include /** * Create and initialize a new I/O binding. diff --git a/src/include/kdbio/glib.h b/src/include/kdbio/glib.h index b62ef7d86cb..468bf7e56b9 100644 --- a/src/include/kdbio/glib.h +++ b/src/include/kdbio/glib.h @@ -8,8 +8,8 @@ #ifndef KDB_IOWRAPPER_GLIB_H_ #define KDB_IOWRAPPER_GLIB_H_ -#include "../kdbio.h" #include +#include /** * Create and initialize a new I/O binding. diff --git a/src/include/kdbio/uv.h b/src/include/kdbio/uv.h index 83ad2705a78..9a1fe4984c7 100644 --- a/src/include/kdbio/uv.h +++ b/src/include/kdbio/uv.h @@ -8,7 +8,7 @@ #ifndef KDB_IOWRAPPER_UV_H_ #define KDB_IOWRAPPER_UV_H_ -#include "../kdbio.h" +#include #include /** diff --git a/src/include/macros/type_create_to_value.h b/src/include/macros/type_create_to_value.h index 5181e2a624e..a07eadefa6d 100644 --- a/src/include/macros/type_create_to_value.h +++ b/src/include/macros/type_create_to_value.h @@ -1,5 +1,7 @@ /** - * Create key to type conversion function. + * @copyright BSD License (see doc/COPYING or http://www.libelektra.org) + * + * @brief Create key to type conversion function. * * This supermacro creates the following functions: * - int NAME_MACRO (TYPE_NAME) (Key * key, TYPE * variable) diff --git a/src/plugins/internalnotification/macros/add_type.h b/src/plugins/internalnotification/macros/add_type.h index 3391b9d2657..1945c1ec141 100644 --- a/src/plugins/internalnotification/macros/add_type.h +++ b/src/plugins/internalnotification/macros/add_type.h @@ -1,5 +1,7 @@ /** - * Add a type to the internalnotification plugin. + * @copyright BSD License (see doc/COPYING or http://www.libelektra.org) + * + * @brief Add a type to the internalnotification plugin. * * Additional required steps: * - Export the register function using INTERNALNOTIFICATION_EXPORT_FUNCTION in elektraInternalnotificationGet() diff --git a/src/plugins/internalnotification/macros/create_type_tests.h b/src/plugins/internalnotification/macros/create_type_tests.h index 8ca4233c386..de418063e4e 100644 --- a/src/plugins/internalnotification/macros/create_type_tests.h +++ b/src/plugins/internalnotification/macros/create_type_tests.h @@ -1,5 +1,7 @@ /** - * Create test cases for internalnotification type. + * @copyright BSD License (see doc/COPYING or http://www.libelektra.org) + * + * @brief Create test cases for internalnotification type. * * This supermacro creates the following functions: * - int internalnotificationRegisterTYPE_NAME (Plugin * plugin, Key * key, TYPE * variable) From 1bd7a2196c7a4471b5f04b2faf98d85ecfa7873a Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 15 Jul 2018 20:00:27 +0200 Subject: [PATCH 34/46] docker: add missing deps for I/O bindings --- scripts/docker/jenkinsnode/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/docker/jenkinsnode/Dockerfile b/scripts/docker/jenkinsnode/Dockerfile index f1ab5de6bb9..73edbe885a8 100644 --- a/scripts/docker/jenkinsnode/Dockerfile +++ b/scripts/docker/jenkinsnode/Dockerfile @@ -31,6 +31,7 @@ RUN apt-get -y install \ libgcrypt20-dev \ libbotan1.10-dev \ libev-dev \ + libuv1-dev \ libsystemd-dev \ libuv1-dev \ libzmq3-dev \ From 1ce62d307619e55847e2ab99d8d822ac127e4f9c Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 15 Jul 2018 20:17:13 +0200 Subject: [PATCH 35/46] notification: updated news --- doc/news/_preparation_next_release.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/news/_preparation_next_release.md b/doc/news/_preparation_next_release.md index a055d59620c..5130f91c68c 100644 --- a/doc/news/_preparation_next_release.md +++ b/doc/news/_preparation_next_release.md @@ -362,12 +362,6 @@ Thanks to Daniel Bugl. - replaced strdup with elektraStrDup (for C99 compatibility) *(Markus Raab)* - You can now remove the basename of a key via the C++ API by calling `key.delBaseName()`. *(René Schwaiger)* - The function `elektraArrayGetNextKey` now uses `NULL` instead of the empty string as init value for the returned key. *(René Schwaiger)* -- The - [notification API](https://doc.libelektra.org/api/current/html/group__kdbnotification.html) - was extended. - The API now supports contexts for callbacks, the types `int`, `unsigned int`, - `long`, `unsigned long`, `float` and `double`. - It also supports all of Elektra's `kdb_*_t` types defined in `kdbtypes.h`. *(Thomas Wahringer)* - <> - <> - <> @@ -395,6 +389,14 @@ Thanks to Daniel Bugl. It can be used to integrate the notification feature with applications based on [ev](http://libev.schmorp.de) main loops. *(Thomas Wahringer)* +## Notifications + +- The + [notification API](https://doc.libelektra.org/api/current/html/group__kdbnotification.html) + was extended. + The API now supports contexts for callbacks, the types `int`, `unsigned int`, + `long`, `unsigned long`, `long long`, `unsinged long long`, `float` and `double`. + It also supports all of Elektra's `kdb_*_t` types defined in `kdbtypes.h`. *(Thomas Wahringer)* ## Tools From 49ef7113b2a7051b67da10985218d219819dc511 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 22 Jul 2018 13:56:11 +0200 Subject: [PATCH 36/46] notification: add new tests --- src/plugins/internalnotification/testmod_internalnotification.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/internalnotification/testmod_internalnotification.c b/src/plugins/internalnotification/testmod_internalnotification.c index f8bd8255c87..127bac9bf75 100644 --- a/src/plugins/internalnotification/testmod_internalnotification.c +++ b/src/plugins/internalnotification/testmod_internalnotification.c @@ -741,6 +741,8 @@ int main (int argc, char ** argv) RUN_TYPE_TESTS (UnsignedInt) RUN_TYPE_TESTS (Long) RUN_TYPE_TESTS (UnsignedLong) + RUN_TYPE_TESTS (LongLong) + RUN_TYPE_TESTS (UnsignedLongLong) RUN_TYPE_TESTS (Float) RUN_TYPE_TESTS (Double) From dfc14fb2ec688f7459f229e9b9aa70c74edf40b9 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 22 Jul 2018 15:41:06 +0200 Subject: [PATCH 37/46] reformat-cmake --- examples/CMakeLists.txt | 11 +++++++---- src/plugins/internalnotification/CMakeLists.txt | 6 +++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4524ecd9250..495a98d4860 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -38,7 +38,9 @@ target_link_elektra (set_key elektra-kdb) # Notification examples # Cannot build examples without notification library which requires the internalnotification plugin -list (FIND ADDED_PLUGINS "internalnotification" FOUND_NAME) +list (FIND ADDED_PLUGINS + "internalnotification" + FOUND_NAME) if (FOUND_NAME GREATER -1) # Build notification polling example @@ -62,7 +64,8 @@ if (FOUND_NAME GREATER -1) set (SRC_FILES notificationAsync.c) set (SOURCES ${SRC_FILES} ${HDR_FILES}) if (BUILD_FULL OR BUILD_STATIC) - list (APPEND SOURCES $) # add sources for elektra-io-uv for static and full builds + list (APPEND SOURCES + $) # add sources for elektra-io-uv for static and full builds endif () add_executable (${EXAMPLE} ${SOURCES}) @@ -90,8 +93,8 @@ if (FOUND_NAME GREATER -1) set (SRC_FILES notificationReload.c) set (SOURCES ${SRC_FILES} ${HDR_FILES}) if (BUILD_FULL OR BUILD_STATIC) - list (APPEND SOURCES $) # add sources for elektra-io-uv for static and full - # builds + list (APPEND SOURCES + $) # add sources for elektra-io-uv for static and full builds endif () add_executable (${EXAMPLE} ${SOURCES}) diff --git a/src/plugins/internalnotification/CMakeLists.txt b/src/plugins/internalnotification/CMakeLists.txt index c512e38d078..ef7c56e245d 100644 --- a/src/plugins/internalnotification/CMakeLists.txt +++ b/src/plugins/internalnotification/CMakeLists.txt @@ -1,3 +1,7 @@ include (LibAddMacros) -add_plugin (internalnotification SOURCES internalnotification.h internalnotification.c ADD_TEST LINK_ELEKTRA elektra-kdb) +add_plugin (internalnotification + SOURCES internalnotification.h + internalnotification.c + ADD_TEST + LINK_ELEKTRA elektra-kdb) From 4cf4c17219aa56cfecea8fcccb5c62217cf3bd45 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 12 Aug 2018 23:17:25 +0200 Subject: [PATCH 38/46] notification: fix unsigned conversion --- .../internalnotification/internalnotification.c | 10 +++++----- .../testmod_internalnotification.c | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index c25791ef6c1..5e9c5f2f080 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -337,7 +337,7 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define VALUE_TYPE unsigned long int #define TYPE_NAME UnsignedInt #define TO_VALUE (strtoul (string, &end, 10)) -#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (value <= UINT_MAX) +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (string[0] != '-' && value <= UINT_MAX) #include "macros/add_type.h" #define TYPE long @@ -349,7 +349,7 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define TYPE unsigned long #define TYPE_NAME UnsignedLong #define TO_VALUE (strtoul (string, &end, 10)) -#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (string[0] != '-') #include "macros/add_type.h" #define TYPE long long @@ -361,7 +361,7 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define TYPE unsigned long long #define TYPE_NAME UnsignedLongLong #define TO_VALUE (strtoull (string, &end, 10)) -#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (string[0] != '-') #include "macros/add_type.h" #define TYPE float @@ -417,7 +417,7 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define TYPE kdb_unsigned_long_t #define TYPE_NAME KdbUnsignedLong #define TO_VALUE (strtoul (string, &end, 10)) -#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (string[0] != '-') #include "macros/add_type.h" #define TYPE kdb_long_long_t @@ -429,7 +429,7 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define TYPE kdb_unsigned_long_long_t #define TYPE_NAME KdbUnsignedLongLong #define TO_VALUE (ELEKTRA_UNSIGNED_LONG_LONG_S (string, &end, 10)) -#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (string[0] != '-') #include "macros/add_type.h" #define TYPE kdb_float_t diff --git a/src/plugins/internalnotification/testmod_internalnotification.c b/src/plugins/internalnotification/testmod_internalnotification.c index 127bac9bf75..b5f84bffe33 100644 --- a/src/plugins/internalnotification/testmod_internalnotification.c +++ b/src/plugins/internalnotification/testmod_internalnotification.c @@ -588,7 +588,7 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define TYPE_NAME UnsignedLong #define FORMAT_STRING "%lu" #define TEST_VALUE ULONG_MAX -#define INVALID_VALUE "AA446744073709551615" +#define INVALID_VALUE "-446744073715" #include "macros/create_type_tests.h" #define TYPE long long @@ -602,7 +602,7 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define TYPE_NAME UnsignedLongLong #define FORMAT_STRING "%llu" #define TEST_VALUE ULLONG_MAX -#define INVALID_VALUE "AA3223372036854775807" +#define INVALID_VALUE "-3223372036854775807" #include "macros/create_type_tests.h" #define TYPE float @@ -656,7 +656,7 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define TYPE_NAME KdbUnsignedShort #define FORMAT_STRING "%d" #define TEST_VALUE USHRT_MAX -#define INVALID_VALUE "55ABC" +#define INVALID_VALUE "-55" #include "macros/create_type_tests.h" #define TYPE kdb_long_t @@ -668,23 +668,23 @@ static void test_doUpdateShouldNotUpdateUnregisteredKey (void) #define TYPE kdb_unsigned_long_t #define TYPE_NAME KdbUnsignedLong -#define FORMAT_STRING "%d" +#define FORMAT_STRING "%u" #define TEST_VALUE UINT_MAX -#define INVALID_VALUE "B5C" +#define INVALID_VALUE "-523255" #include "macros/create_type_tests.h" #define TYPE kdb_long_long_t #define TYPE_NAME KdbLongLong #define FORMAT_STRING ELEKTRA_LONG_LONG_F -#define TEST_VALUE LONG_MIN +#define TEST_VALUE (kdb_long_long_t) LONG_MAX #define INVALID_VALUE "50000asasd" #include "macros/create_type_tests.h" #define TYPE kdb_unsigned_long_long_t #define TYPE_NAME KdbUnsignedLongLong #define FORMAT_STRING ELEKTRA_UNSIGNED_LONG_LONG_F -#define TEST_VALUE ULONG_MAX -#define INVALID_VALUE "-B5C" +#define TEST_VALUE (kdb_unsigned_long_long_t) ULONG_MAX +#define INVALID_VALUE "-5326523652" #include "macros/create_type_tests.h" #define TYPE kdb_float_t From ba653210387a3ca6cc61f6a9eef83cc5fb2ff2cf Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Mon, 13 Aug 2018 15:39:38 +0200 Subject: [PATCH 39/46] notification: improve unsigned conversion checks --- src/include/macros/type_create_to_value.h | 30 +++++++++++++++++-- .../internalnotification.c | 21 +++++++++---- .../internalnotification/macros/add_type.h | 2 ++ .../macros/create_type_tests.h | 11 +++---- 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/include/macros/type_create_to_value.h b/src/include/macros/type_create_to_value.h index a07eadefa6d..7ef10075ced 100644 --- a/src/include/macros/type_create_to_value.h +++ b/src/include/macros/type_create_to_value.h @@ -15,8 +15,12 @@ * conversion. Use ELEKTRA_TYPE_CHECK_CONVERSION to check if a conversion using * strto*()-functions was successful and ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (RANGE) * to check additionally for a specified range. - * @param DISABLE_UNDEF_PARAMETERS define to disable undefining of parameters after the macro. Use if parameters - * are used within another supermacro. + * @param PRE_CHECK_CONVERSION_BLOCK optional, defaults to empty. Allows to add additional code for pre-conversion checks + * (e.g. ELEKTRA_TYPE_NEGATIVE_PRE_CHECK_BLOCK) + * @param PRE_CHECK_CONVERSION optional, defaults to true. A boolean expression to check the contents of `string` before conversion + * (e.g. ELEKTRA_TYPE_NEGATIVE_PRE_CHECK). + * @param DISABLE_UNDEF_PARAMETERS define to disable undefining of parameters after the macro. Use if parameters + * are used within another supermacro. */ #ifndef TYPE #error "You have to #define TYPE, TYPE_NAME, TO_VALUE and NAME_MACRO before including the type_create_to_value supermacro" @@ -37,11 +41,25 @@ #ifndef CHECK_CONVERSION #define CHECK_CONVERSION 1 #endif +#ifndef PRE_CHECK_CONVERSION +#define PRE_CHECK_CONVERSION 1 +#endif +#ifndef PRE_CHECK_BLOCK +#define PRE_CHECK_BLOCK +#endif +// These macros get defined at first inclusion #ifndef ELEKTRA_TYPE_CONVERSION_MACROS #define ELEKTRA_TYPE_CONVERSION_MACROS #define ELEKTRA_TYPE_CHECK_CONVERSION (*end == 0 && errno == 0) #define ELEKTRA_TYPE_CHECK_CONVERSION_RANGE(CHECK_RANGE) (ELEKTRA_TYPE_CHECK_CONVERSION && CHECK_RANGE) +#define ELEKTRA_TYPE_NEGATIVE_PRE_CHECK_BLOCK \ + const char * test = string; \ + while (isspace (test[0]) || test[0] == 0) \ + { \ + test++; \ + } +#define ELEKTRA_TYPE_NEGATIVE_PRE_CHECK (test[0] != '-') #endif #include // errno @@ -72,6 +90,12 @@ TYPE_CONVERSION_SIGNATURE (TYPE, TYPE_NAME, NAME_MACRO) char * end ELEKTRA_UNUSED; const char * string = keyValue (key); errno = 0; + PRE_CHECK_BLOCK + if (!PRE_CHECK_CONVERSION) + { + ELEKTRA_LOG_WARNING ("pre-check for type conversion failed! string=%s", keyString (key)); + return 0; + } // convert string to target type VALUE_TYPE value = TO_VALUE; if (CHECK_CONVERSION) @@ -96,5 +120,7 @@ TYPE_CONVERSION_SIGNATURE (TYPE, TYPE_NAME, NAME_MACRO) #undef NAME_MACRO #undef TO_VALUE #undef CHECK_CONVERSION +#undef PRE_CHECK_BLOCK +#undef PRE_CHECK_CONVERSION #endif #undef DISABLE_UNDEF_PARAMETERS diff --git a/src/plugins/internalnotification/internalnotification.c b/src/plugins/internalnotification/internalnotification.c index 5e9c5f2f080..524f314ea4e 100644 --- a/src/plugins/internalnotification/internalnotification.c +++ b/src/plugins/internalnotification/internalnotification.c @@ -15,6 +15,7 @@ #include #include +#include // isspace() #include // errno #include // strto* functions @@ -337,7 +338,9 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define VALUE_TYPE unsigned long int #define TYPE_NAME UnsignedInt #define TO_VALUE (strtoul (string, &end, 10)) -#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (string[0] != '-' && value <= UINT_MAX) +#define PRE_CHECK_BLOCK ELEKTRA_TYPE_NEGATIVE_PRE_CHECK_BLOCK +#define PRE_CHECK_CONVERSION ELEKTRA_TYPE_NEGATIVE_PRE_CHECK +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (value <= UINT_MAX) #include "macros/add_type.h" #define TYPE long @@ -349,7 +352,9 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define TYPE unsigned long #define TYPE_NAME UnsignedLong #define TO_VALUE (strtoul (string, &end, 10)) -#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (string[0] != '-') +#define PRE_CHECK_BLOCK ELEKTRA_TYPE_NEGATIVE_PRE_CHECK_BLOCK +#define PRE_CHECK_CONVERSION ELEKTRA_TYPE_NEGATIVE_PRE_CHECK +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE long long @@ -361,7 +366,9 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define TYPE unsigned long long #define TYPE_NAME UnsignedLongLong #define TO_VALUE (strtoull (string, &end, 10)) -#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (string[0] != '-') +#define PRE_CHECK_BLOCK ELEKTRA_TYPE_NEGATIVE_PRE_CHECK_BLOCK +#define PRE_CHECK_CONVERSION ELEKTRA_TYPE_NEGATIVE_PRE_CHECK +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE float @@ -417,7 +424,9 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define TYPE kdb_unsigned_long_t #define TYPE_NAME KdbUnsignedLong #define TO_VALUE (strtoul (string, &end, 10)) -#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (string[0] != '-') +#define PRE_CHECK_BLOCK ELEKTRA_TYPE_NEGATIVE_PRE_CHECK_BLOCK +#define PRE_CHECK_CONVERSION ELEKTRA_TYPE_NEGATIVE_PRE_CHECK +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE kdb_long_long_t @@ -429,7 +438,9 @@ void elektraInternalnotificationUpdateRegisteredKeys (Plugin * plugin, KeySet * #define TYPE kdb_unsigned_long_long_t #define TYPE_NAME KdbUnsignedLongLong #define TO_VALUE (ELEKTRA_UNSIGNED_LONG_LONG_S (string, &end, 10)) -#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION_RANGE (string[0] != '-') +#define PRE_CHECK_BLOCK ELEKTRA_TYPE_NEGATIVE_PRE_CHECK_BLOCK +#define PRE_CHECK_CONVERSION ELEKTRA_TYPE_NEGATIVE_PRE_CHECK +#define CHECK_CONVERSION ELEKTRA_TYPE_CHECK_CONVERSION #include "macros/add_type.h" #define TYPE kdb_float_t diff --git a/src/plugins/internalnotification/macros/add_type.h b/src/plugins/internalnotification/macros/add_type.h index 1945c1ec141..5e9f5fa5aad 100644 --- a/src/plugins/internalnotification/macros/add_type.h +++ b/src/plugins/internalnotification/macros/add_type.h @@ -105,3 +105,5 @@ INTERNALNOTIFICATION_REGISTER_SIGNATURE (TYPE, TYPE_NAME) #undef TYPE_NAME #undef TO_VALUE #undef CHECK_CONVERSION +#undef PRE_CHECK_BLOCK +#undef PRE_CHECK_CONVERSION diff --git a/src/plugins/internalnotification/macros/create_type_tests.h b/src/plugins/internalnotification/macros/create_type_tests.h index de418063e4e..aa2fbb37fc2 100644 --- a/src/plugins/internalnotification/macros/create_type_tests.h +++ b/src/plugins/internalnotification/macros/create_type_tests.h @@ -13,20 +13,21 @@ * @param TEST_VALUE value of type TYPE. Used for the "update" test case * @param FORMAT_STRING format to convert TEST_VALUE to string (passed to elektraFormat()) * @param CHECK_VALUE optional, default is (value == TEST_VALUE). Boolean expression to check if `value` equals the test value - * @param INVALID_VALUE optional. Value of type string. Used for the no update test case. If not defined, "no update" test case is omitted + * @param INVALID_VALUE optional. Value of type string. Used for the no update test case. If not defined, "no update" test case is + * omitted * @param CHECK_INVALID optioal, defaults to (value == 0). Check if the variable `value` has not been updated. Value should be 0. */ #ifndef TYPE -#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the testCreateTypeRegister supermacro" +#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the create_type_tests supermacro" #endif #ifndef TYPE_NAME -#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the testCreateTypeRegister supermacro" +#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the create_type_tests supermacro" #endif #ifndef FORMAT_STRING -#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the testCreateTypeRegister supermacro" +#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the create_type_tests supermacro" #endif #ifndef TEST_VALUE -#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the testCreateTypeRegister supermacro" +#error "You have to #define TYPE, TYPE_NAME, FORMAT_STRING, TEST_VALUE and CHECK_VALUE before including the create_type_tests supermacro" #endif #ifndef CHECK_VALUE #define CHECK_VALUE (value == TEST_VALUE) From 6ad02a99b274282d58882fb78b9a9b4a63bebfd9 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sun, 17 Jun 2018 18:46:58 +0200 Subject: [PATCH 40/46] io-zeromq-adapter: move header to kdbio directory --- src/include/{kdbio_adapter_zeromq.h => kdbio/adapters/zeromq.h} | 0 src/libs/io/adapter/zeromq/zeromq.c | 2 +- src/plugins/zeromqrecv/testmod_zeromqrecv.c | 2 +- src/plugins/zeromqrecv/zeromqrecv.h | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/include/{kdbio_adapter_zeromq.h => kdbio/adapters/zeromq.h} (100%) diff --git a/src/include/kdbio_adapter_zeromq.h b/src/include/kdbio/adapters/zeromq.h similarity index 100% rename from src/include/kdbio_adapter_zeromq.h rename to src/include/kdbio/adapters/zeromq.h diff --git a/src/libs/io/adapter/zeromq/zeromq.c b/src/libs/io/adapter/zeromq/zeromq.c index fdcfc1006b4..00efa3124bb 100644 --- a/src/libs/io/adapter/zeromq/zeromq.c +++ b/src/libs/io/adapter/zeromq/zeromq.c @@ -7,7 +7,7 @@ */ #include #include -#include +#include #include #include diff --git a/src/plugins/zeromqrecv/testmod_zeromqrecv.c b/src/plugins/zeromqrecv/testmod_zeromqrecv.c index 23e691f282e..7e2ef92a55c 100644 --- a/src/plugins/zeromqrecv/testmod_zeromqrecv.c +++ b/src/plugins/zeromqrecv/testmod_zeromqrecv.c @@ -12,7 +12,7 @@ #include // time() #include // usleep() -#include // elektraIoUvNew() +#include // elektraIoUvNew() #include // ElektraIoPluginSetBinding #include diff --git a/src/plugins/zeromqrecv/zeromqrecv.h b/src/plugins/zeromqrecv/zeromqrecv.h index 66b7fa4c513..89462e9f306 100644 --- a/src/plugins/zeromqrecv/zeromqrecv.h +++ b/src/plugins/zeromqrecv/zeromqrecv.h @@ -17,7 +17,7 @@ #include -#include // elektraIoAdapterZeroMq*() +#include // elektraIoAdapterZeroMq*() #define ELEKTRA_ZEROMQ_DEFAULT_SUB_ENDPOINT "tcp://localhost:6001" From 5ecc4ce4e2a3eab2c80a7c9c1f12fa29cf6367dd Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Fri, 17 Aug 2018 16:37:37 +0200 Subject: [PATCH 41/46] notification: improve docs & examples --- doc/decisions/deferred_plugin_calls.md | 12 +- doc/tutorials/notifications.md | 138 +++++++++---------- examples/notificationAsync.c | 2 + examples/notificationReload.c | 138 +++++++++++-------- src/bindings/io/ev/io_ev.c | 2 +- src/bindings/io/glib/io_glib.c | 2 +- src/bindings/io/uv/io_uv.c | 2 +- src/include/kdbio/adapters/dbus.h | 4 +- src/include/kdbio/adapters/zeromq.h | 4 +- src/include/kdbnotification.h | 6 + src/libs/README.md | 9 +- src/libs/notification/example/CMakeLists.txt | 49 ------- src/plugins/internalnotification/README.md | 5 +- src/tools/hub-zeromq/README.md | 4 +- src/tools/hub-zeromq/hub-zeromq.c | 2 +- 15 files changed, 185 insertions(+), 194 deletions(-) delete mode 100644 src/libs/notification/example/CMakeLists.txt diff --git a/doc/decisions/deferred_plugin_calls.md b/doc/decisions/deferred_plugin_calls.md index 9fc323b79fd..b9c6e0e4406 100644 --- a/doc/decisions/deferred_plugin_calls.md +++ b/doc/decisions/deferred_plugin_calls.md @@ -15,8 +15,9 @@ required to be able to defer these calls until the plugins are loaded. For example when setting I/O bindings with `elektraIoSetBinding()` the exported function `setIoBinding` is called for all globally mounted plugins. -Since global mounting is implemented using the "list" plugin the exported -functions from the plugins are unavailable. +Since global mounting is implemented using the "list" plugin which uses +lazy-loading for its plugins the exported functions from the plugins are +unavailable. Other examples are the "dini" and "multifile" plugins which use multiple plugins to support different file formats. @@ -31,7 +32,8 @@ These plugins also "hide" functions exported by encapsulated plugins. ## Assumptions -1. The called functions do not return a value (e.g. `set`, `open`, `close`, ...) +1. The called functions do not return a value (e.g. `set`, `open`, `close`, ...). + Callbacks can be used as return channel (see "Implications") ## Considered Alternatives @@ -92,7 +94,7 @@ While called functions could return data using the `parameters` KeySet (or a separate KeySet) there is no defined moment when the data can be collected. Defining such a moment would break the lazy-loading constraint. It is recommended to use callbacks passed as `parameters`. -The callback function declaration is not affected by this decision. +Callback function declarations are not limited by this decision. ## Related decisions @@ -106,7 +108,7 @@ The callback function declaration is not affected by this decision. - Functions supporting deferred calls should allow for multiple calls (i.e. they should be idempotent). - This leaves state at affected plugins and does avoids duplicating state (e.g. + This leaves state at affected plugins and does avoid duplicating state (e.g. "was this function called for this plugin before?") in encapsulating plugins. diff --git a/doc/tutorials/notifications.md b/doc/tutorials/notifications.md index 0f6a857b32b..606d8125050 100644 --- a/doc/tutorials/notifications.md +++ b/doc/tutorials/notifications.md @@ -86,30 +86,30 @@ the initialization of an I/O binding. void main (void) { - KDB* repo; + KDB* repo; - // Open KDB - Key * key = keyNew ("/sw/myorg/myapp/#0/current", KEY_END); - KDB * kdb = kdbOpen (key); + // Open KDB + Key * key = keyNew ("/sw/myorg/myapp/#0/current", KEY_END); + KDB * kdb = kdbOpen (key); - // Create libuv event loop - uv_loop_t * loop = uv_default_loop (); + // Create libuv event loop + uv_loop_t * loop = uv_default_loop (); - // Initialize I/O binding tied to event loop - ElektraIoInterface * binding = elektraIoUvNew (loop); + // Initialize I/O binding tied to event loop + ElektraIoInterface * binding = elektraIoUvNew (loop); - // Use I/O binding for our kdb instance - elektraIoSetBinding (kdb, binding); + // Use I/O binding for our kdb instance + elektraIoSetBinding (kdb, binding); - // Normal application setup code ... + // Normal application setup code ... - // Start the event loop - uv_run (loop, UV_RUN_DEFAULT); + // Start the event loop + uv_run (loop, UV_RUN_DEFAULT); - // Cleanup - kdbClose (kdb, key); - elektraIoBindingCleanup (binding); - uv_loop_close (loop); + // Cleanup + kdbClose (kdb, key); + elektraIoBindingCleanup (binding); + uv_loop_close (loop); } ``` @@ -156,47 +156,47 @@ static void printVariable (ElektraIoTimerOperation * timerOp) void main (void) { - KDB* repo; - - // Open KDB - Key * key = keyNew ("/sw/myorg/myapp/#0/current", KEY_END); - KDB * kdb = kdbOpen (key); - - // Create libuv event loop - uv_loop_t * loop = uv_default_loop (); + KDB* repo; - // Initialize I/O binding tied to event loop - ElektraIoInterface * binding = elektraIoUvNew (loop); + // Open KDB + Key * key = keyNew ("/sw/myorg/myapp/#0/current", KEY_END); + KDB * kdb = kdbOpen (key); - // Use I/O binding for our kdb instance - elektraIoSetBinding (kdb, binding); + // Create libuv event loop + uv_loop_t * loop = uv_default_loop (); - // Initialize notification wrapper - elektraNotificationOpen (kdb); + // Initialize I/O binding tied to event loop + ElektraIoInterface * binding = elektraIoUvNew (loop); - // Register "value" for updates - Key * registeredKey = keyNew ("/sw/myorg/myapp/#0/current/value", KEY_END); - int value; - elektraNotificationRegisterInt (repo, registeredKey, &value); + // Use I/O binding for our kdb instance + elektraIoSetBinding (kdb, binding); - // Create a timer to repeatedly print "value" - ElektraIoTimerOperation * timer = elektraIoNewTimerOperation (2000, 1, printVariable, &value); - elektraIoBindingAddTimer (binding, timer); + // Initialize notification wrapper + elektraNotificationOpen (kdb); - // Get configuration - KeySet * config = ksNew(0, KS_END); - kdbGet (kdb, config, key); - printVariable (timer); // "value" was automatically updated + // Register "value" for updates + Key * registeredKey = keyNew ("/sw/myorg/myapp/#0/current/value", KEY_END); + int value; + elektraNotificationRegisterInt (repo, registeredKey, &value); - // Start the event loop - uv_run (loop, UV_RUN_DEFAULT); + // Create a timer to repeatedly print "value" + ElektraIoTimerOperation * timer = elektraIoNewTimerOperation (2000, 1, printVariable, &value); + elektraIoBindingAddTimer (binding, timer); - // Cleanup - elektraNotificationClose (kdb); - kdbClose (kdb, key); - elektraIoBindingRemoveTimer (timer); - elektraIoBindingCleanup (binding); - uv_loop_close (loop); + // Get configuration + KeySet * config = ksNew(0, KS_END); + kdbGet (kdb, config, key); + printVariable (timer); // "value" was automatically updated + + // Start the event loop + uv_run (loop, UV_RUN_DEFAULT); + + // Cleanup + elektraNotificationClose (kdb); + kdbClose (kdb, key); + elektraIoBindingRemoveTimer (timer); + elektraIoBindingCleanup (binding); + uv_loop_close (loop); } ``` @@ -228,32 +228,32 @@ changed key needs further processing. void setTerminalColor (Key * color, void * context ELEKTRA_UNUSED) { - // context contains whatever was passed as 4th parameter - // to elektraNotificationRegisterCallback() - char * value = keyString (color); - - if (strcmp (value, "red") == 0) - { - printf (ANSI_COLOR_RED); - } - if (strcmp (value, "green") == 0) - { - printf (ANSI_COLOR_GREEN); - } + // context contains whatever was passed as 4th parameter + // to elektraNotificationRegisterCallback() + char * value = keyString (color); + + if (strcmp (value, "red") == 0) + { + printf (ANSI_COLOR_RED); + } + if (strcmp (value, "green") == 0) + { + printf (ANSI_COLOR_GREEN); + } } int main (void) { - KDB * repo; + KDB * repo; - // ... initialization of KDB, I/O binding and notifications + // ... initialization of KDB, I/O binding and notifications - Key * color = keyNew ("/sw/myorg/myapp/#0/current/color", KEY_END); + Key * color = keyNew ("/sw/myorg/myapp/#0/current/color", KEY_END); - // Re-Initialize on key changes - elektraNotificationRegisterCallback(repo, color, &setTerminalColor, NULL); + // Re-Initialize on key changes + elektraNotificationRegisterCallback(repo, color, &setTerminalColor, NULL); - // ... start loop, etc. + // ... start loop, etc. } ``` @@ -358,7 +358,7 @@ void initKdb (void) kdbClose (kdb, parentKey); } - // ... + // ... } ``` diff --git a/examples/notificationAsync.c b/examples/notificationAsync.c index e2dc86f6592..bd60a781af7 100644 --- a/examples/notificationAsync.c +++ b/examples/notificationAsync.c @@ -136,6 +136,8 @@ int main (void) elektraIoBindingAddTimer (binding, timer); printf ("Asynchronous Notification Example Application\n"); + printf ("Please note that notification transport plugins are required see\n" + " https://www.libelektra.org/tutorials/notifications#notification-configuration!\n"); printf ("- Set \"%s\" to red, blue or green to change the text color\n", keyName (callbackKeyToWatch)); printf ("- Set \"%s\" to any integer value\n", keyName (intKeyToWatch)); printf ("Send SIGINT (Ctl+C) to exit.\n\n"); diff --git a/examples/notificationReload.c b/examples/notificationReload.c index 660db9d7a24..a7e77c96132 100644 --- a/examples/notificationReload.c +++ b/examples/notificationReload.c @@ -3,6 +3,7 @@ * * @brief Example for notification library which reloads KDB when Elektra's * configuration (e.g. mount points or global plugins) has changed. + * This example also shows how to pass user data using elektraIo*GetData(). * * Requires: * - io_glib binding @@ -33,19 +34,26 @@ #define TWO_SECONDS 2000 #define RELOAD_INTERVAL 100 -GMainLoop * loop; - -KDB * kdb; -Key * parentKey; -KeySet * config; -ElektraIoInterface * binding; -Key * intKeyToWatch; -int valueToPrint = 0; - -ElektraIoTimerOperation * timer; -ElektraIoTimerOperation * reload; - -static void elektraChangedCallback (Key * changedKey ELEKTRA_UNUSED, void * context ELEKTRA_UNUSED); +/** + * Data container for this example to demo + * usage of the elektraIo*GetData() functions. + * + * Members could also be globals. + */ +typedef struct ExampleUserData +{ + GMainLoop * loop; + KDB * kdb; + Key * parentKey; + KeySet * config; + ElektraIoInterface * binding; + Key * intKeyToWatch; + int valueToPrint; + ElektraIoTimerOperation * timer; + ElektraIoTimerOperation * reload; +} ExampleUserData; + +static void elektraChangedCallback (Key * changedKey ELEKTRA_UNUSED, void * context); /** * Initializes KDB on first call and performs cleanup before initialization on @@ -55,37 +63,39 @@ static void elektraChangedCallback (Key * changedKey ELEKTRA_UNUSED, void * cont */ static void initKdb (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED) { + ExampleUserData * data = (ExampleUserData *) elektraIoTimerGetData (timerOp); + int didReload = 0; // Stop reload task - elektraIoTimerSetEnabled (reload, 0); - elektraIoBindingUpdateTimer (reload); + elektraIoTimerSetEnabled (data->reload, 0); + elektraIoBindingUpdateTimer (data->reload); - if (kdb != NULL) + if (data->kdb != NULL) { // Cleanup notifications and close KDB - elektraNotificationClose (kdb); - kdbClose (kdb, parentKey); + elektraNotificationClose (data->kdb); + kdbClose (data->kdb, data->parentKey); didReload = 1; } - kdb = kdbOpen (parentKey); - if (kdb == NULL) + data->kdb = kdbOpen (data->parentKey); + if (data->kdb == NULL) { printf ("could not open KDB, aborting\n"); exit (1); } - elektraIoSetBinding (kdb, binding); + elektraIoSetBinding (data->kdb, data->binding); - int result = elektraNotificationOpen (kdb); + int result = elektraNotificationOpen (data->kdb); if (!result) { printf ("could not init notification, aborting\n"); exit (1); } - result = elektraNotificationRegisterInt (kdb, intKeyToWatch, &valueToPrint); + result = elektraNotificationRegisterInt (data->kdb, data->intKeyToWatch, &data->valueToPrint); if (!result) { printf ("could not register variable, aborting\n"); @@ -93,7 +103,7 @@ static void initKdb (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED) } Key * elektraKey = keyNew ("/elektra", KEY_END); - if (!elektraNotificationRegisterCallbackSameOrBelow (kdb, elektraKey, elektraChangedCallback, NULL)) + if (!elektraNotificationRegisterCallbackSameOrBelow (data->kdb, elektraKey, elektraChangedCallback, data)) { printf ("could not register for changes to Elektra's configuration, aborting\n"); exit (1); @@ -101,7 +111,7 @@ static void initKdb (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED) keyDel (elektraKey); // Get configuration - kdbGet (kdb, config, parentKey); + kdbGet (data->kdb, data->config, data->parentKey); if (didReload) { @@ -109,18 +119,19 @@ static void initKdb (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED) } } -static gboolean onSIGNAL (gpointer user_data ELEKTRA_UNUSED) +static gboolean onSIGNAL (gpointer user_data) { + ExampleUserData * data = (ExampleUserData *) user_data; // Cleanup - elektraIoBindingRemoveTimer (timer); - elektraFree (timer); - elektraIoBindingRemoveTimer (reload); - elektraFree (reload); - elektraNotificationClose (kdb); - kdbClose (kdb, parentKey); - elektraIoBindingCleanup (binding); - - g_main_loop_quit (loop); + elektraIoBindingRemoveTimer (data->timer); + elektraFree (data->timer); + elektraIoBindingRemoveTimer (data->reload); + elektraFree (data->reload); + elektraNotificationClose (data->kdb); + kdbClose (data->kdb, data->parentKey); + elektraIoBindingCleanup (data->binding); + + g_main_loop_quit (data->loop); return FALSE; } @@ -132,59 +143,70 @@ static gboolean onSIGNAL (gpointer user_data ELEKTRA_UNUSED) * @param changedKey unused * @param context unused */ -static void elektraChangedCallback (Key * changedKey ELEKTRA_UNUSED, void * context ELEKTRA_UNUSED) +static void elektraChangedCallback (Key * changedKey ELEKTRA_UNUSED, void * context) { printf ("\nElektra's configuration has changed.\n"); // Enable operation to reload KDB as soon as possible - elektraIoTimerSetEnabled (reload, 1); - elektraIoBindingUpdateTimer (reload); + ExampleUserData * data = (ExampleUserData *) context; + elektraIoTimerSetEnabled (data->reload, 1); + elektraIoBindingUpdateTimer (data->reload); } static void printVariable (ElektraIoTimerOperation * timerOp) { - int value = *(int *) elektraIoTimerGetData (timerOp); - printf ("\nMy integer value is %d\n", value); + // int value = *(int *) elektraIoTimerGetData (timerOp); + ExampleUserData * data = (ExampleUserData *) elektraIoTimerGetData (timerOp); + printf ("\nMy integer value is %d\n", data->valueToPrint); } int main (void) { + ExampleUserData * data = elektraCalloc (sizeof (*data)); + if (data == NULL) + { + printf ("elektraCalloc failed"); + return 1; + } + // Create glib main loop GMainContext * context = NULL; // use default context - loop = g_main_loop_new (context, 0); - binding = elektraIoGlibNew (context); + data->loop = g_main_loop_new (context, 0); + data->binding = elektraIoGlibNew (context); // Signal Handling - g_unix_signal_add (SIGINT, onSIGNAL, NULL); + g_unix_signal_add (SIGINT, onSIGNAL, data); - config = ksNew (20, KS_END); - parentKey = keyNew ("/sw/elektra/example_notification/#0/current", KEY_END); - intKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/value", KEY_END); + data->config = ksNew (20, KS_END); + data->parentKey = keyNew ("/sw/elektra/example_notification/#0/current", KEY_END); + data->intKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/value", KEY_END); // Setup timer that repeatedly prints the variable - timer = elektraIoNewTimerOperation (TWO_SECONDS, 1, printVariable, &valueToPrint); - elektraIoBindingAddTimer (binding, timer); + data->timer = elektraIoNewTimerOperation (TWO_SECONDS, 1, printVariable, data); + elektraIoBindingAddTimer (data->binding, data->timer); // Setup timer for reloading Elektra's configuration - reload = elektraIoNewTimerOperation (RELOAD_INTERVAL, 0, initKdb, NULL); - elektraIoBindingAddTimer (binding, reload); + data->reload = elektraIoNewTimerOperation (RELOAD_INTERVAL, 0, initKdb, data); + elektraIoBindingAddTimer (data->binding, data->reload); printf ("Reloading Notification Example Application\n"); - printf ("- Set \"%s\" to any integer value\n", keyName (intKeyToWatch)); + printf ("Please note that notification transport plugins are required see\n" + " https://www.libelektra.org/tutorials/notifications#notification-configuration!\n"); + printf ("- Set \"%s\" to any integer value\n", keyName (data->intKeyToWatch)); printf ("- Try to add additional transport plugins and remove the original pair afterwards\n"); printf ("- Mount a file which sets the key above to a different value and unmount it\n"); printf ("Send SIGINT (Ctl+C) to exit.\n\n"); // Initialize KDB - initKdb (reload); - printVariable (timer); // "value" was automatically updated + initKdb (data->reload); + printVariable (data->timer); // "value" was automatically updated - g_main_loop_run (loop); + g_main_loop_run (data->loop); - g_main_loop_unref (loop); + g_main_loop_unref (data->loop); - ksDel (config); - keyDel (intKeyToWatch); - keyDel (parentKey); + ksDel (data->config); + keyDel (data->intKeyToWatch); + keyDel (data->parentKey); printf ("cleanup done!\n"); } diff --git a/src/bindings/io/ev/io_ev.c b/src/bindings/io/ev/io_ev.c index 63bc229e141..188454dccca 100644 --- a/src/bindings/io/ev/io_ev.c +++ b/src/bindings/io/ev/io_ev.c @@ -80,7 +80,7 @@ static EvBindingData * newBindingData (void) EvBindingData * bindingData = elektraCalloc (sizeof (*bindingData)); if (bindingData == NULL) { - ELEKTRA_LOG_WARNING ("elektraMalloc failed"); + ELEKTRA_LOG_WARNING ("elektraCalloc failed"); return NULL; } diff --git a/src/bindings/io/glib/io_glib.c b/src/bindings/io/glib/io_glib.c index 2acf634ef2e..3db233468b0 100644 --- a/src/bindings/io/glib/io_glib.c +++ b/src/bindings/io/glib/io_glib.c @@ -102,7 +102,7 @@ static GlibBindingData * newBindingData (void) GlibBindingData * bindingData = elektraCalloc (sizeof (*bindingData)); if (bindingData == NULL) { - ELEKTRA_LOG_WARNING ("elektraMalloc failed"); + ELEKTRA_LOG_WARNING ("elektraCalloc failed"); return NULL; } diff --git a/src/bindings/io/uv/io_uv.c b/src/bindings/io/uv/io_uv.c index eb0f14b1343..24432600390 100644 --- a/src/bindings/io/uv/io_uv.c +++ b/src/bindings/io/uv/io_uv.c @@ -84,7 +84,7 @@ static UvBindingData * newBindingData (void) UvBindingData * bindingData = elektraCalloc (sizeof (*bindingData)); if (bindingData == NULL) { - ELEKTRA_LOG_WARNING ("elektraMalloc failed"); + ELEKTRA_LOG_WARNING ("elektraCalloc failed"); return NULL; } diff --git a/src/include/kdbio/adapters/dbus.h b/src/include/kdbio/adapters/dbus.h index 77f864316c6..726337d8335 100644 --- a/src/include/kdbio/adapters/dbus.h +++ b/src/include/kdbio/adapters/dbus.h @@ -5,8 +5,8 @@ * * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) */ -#ifndef __ELEKTAR_IO_ADAPTER_DBUS_H__ -#define __ELEKTAR_IO_ADAPTER_DBUS_H__ +#ifndef ELEKTRA_IO_ADAPTER_DBUS_H +#define ELEKTRA_IO_ADAPTER_DBUS_H #include #include diff --git a/src/include/kdbio/adapters/zeromq.h b/src/include/kdbio/adapters/zeromq.h index 3a991698a31..49b3c9a8b8e 100644 --- a/src/include/kdbio/adapters/zeromq.h +++ b/src/include/kdbio/adapters/zeromq.h @@ -5,8 +5,8 @@ * * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) */ -#ifndef __ELEKTAR_IO_ADAPTER_ZEROMQ_H__ -#define __ELEKTAR_IO_ADAPTER_ZEROMQ_H__ +#ifndef ELEKTRA_IO_ADAPTER_ZEROMQ_H +#define ELEKTRA_IO_ADAPTER_ZEROMQ_H #include #include diff --git a/src/include/kdbnotification.h b/src/include/kdbnotification.h index a7723e5f09d..c73dbe60fda 100644 --- a/src/include/kdbnotification.h +++ b/src/include/kdbnotification.h @@ -23,6 +23,12 @@ * For an introduction to notifications please see the * Notification Tutorial. * + * Examples: + * + * - [Basic notifications using polling](https://www.libelektra.org/examples/notificationpolling) + * - [Using asynchronous I/O bindings](https://www.libelektra.org/examples/notificationasync) + * - [Reload KDB when Elektra's configuration has changed](https://www.libelektra.org/examples/notificationreload) + * * @par Global Mounting * * elektraNotificationOpen() loads and mounts the diff --git a/src/libs/README.md b/src/libs/README.md index ce9c8aceb9e..976cdf66aea 100644 --- a/src/libs/README.md +++ b/src/libs/README.md @@ -87,10 +87,17 @@ data structures. libelektra-io.so -**[io](io/)** provides the common [I/O binding API](https://doc.libelektra.org/api/current/html/group__kdbio.html). +**[io](io/)** provides the +[common API](https://doc.libelektra.org/api/current/html/group__kdbio.html) for +using asynchronous I/O bindings. ### Notification libelektra-notification.so **[notification](notification/)** provides the [notification API](https://doc.libelektra.org/api/current/html/group__kdbnotification.html). +Usage examples: + +- [Basic notifications using polling](https://www.libelektra.org/examples/notificationpolling) +- [Using asynchronous I/O bindings](https://www.libelektra.org/examples/notificationasync) +- [Reload KDB when Elektra's configuration has changed](https://www.libelektra.org/examples/notificationreload) diff --git a/src/libs/notification/example/CMakeLists.txt b/src/libs/notification/example/CMakeLists.txt deleted file mode 100644 index 8f9baee5b68..00000000000 --- a/src/libs/notification/example/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -include (LibAddMacros) -include (LibAddBinding) - -# Build example_notification -set (EXAMPLE example_notification) - -set (SRC_FILES example_notification.c) -add_headers (ELEKTRA_HEADERS) -set (SOURCES ${SRC_FILES} ${ELEKTRA_HEADERS}) - -add_executable (${EXAMPLE} ${SOURCES}) -add_dependencies (${EXAMPLE} kdberrors_generated) - -# target_include_directories (${EXAMPLE} SYSTEM PUBLIC ${LIBUV_INCLUDE_DIRS}) - -target_link_elektra (${EXAMPLE} elektra-kdb elektra-notification) - -# - The asynchronous example requires io_uv -# - io_uv is disabled for ASAN builds, see https://github.com/ElektraInitiative/libelektra/issues/2007 -check_binding_included ("io_uv" IS_INCLUDED SUBDIRECTORY "io/uv" SILENT) -find_package (PkgConfig) -pkg_check_modules (LIBUV QUIET libuv) -if (IS_INCLUDED AND LIBUV_FOUND AND NOT ENABLE_ASAN) - - set (EXAMPLE example_notification_async) # Build example_notification_async - - set (SRC_FILES example_notification_async.c) - add_headers (ELEKTRA_HEADERS) - set (SOURCES ${SRC_FILES} ${ELEKTRA_HEADERS}) - if (BUILD_FULL OR BUILD_STATIC) - - list (APPEND SOURCES - $) # add sources for elektra-io-uv for static and full builds - endif () - - add_executable (${EXAMPLE} ${SOURCES}) - add_dependencies (${EXAMPLE} kdberrors_generated) - - target_link_elektra (${EXAMPLE} elektra-kdb elektra-notification elektra-io elektra-io-uv) - if (BUILD_FULL OR BUILD_STATIC) - target_link_libraries (${EXAMPLE} ${LIBUV_LDFLAGS}) - endif () - - if (LIBUV_VERSION VERSION_LESS "1.0") - target_compile_definitions (${EXAMPLE} PRIVATE "HAVE_LIBUV0") - else () - target_compile_definitions (${EXAMPLE} PRIVATE "HAVE_LIBUV1") - endif () -endif () diff --git a/src/plugins/internalnotification/README.md b/src/plugins/internalnotification/README.md index ddf432642d7..02ec3a26124 100644 --- a/src/plugins/internalnotification/README.md +++ b/src/plugins/internalnotification/README.md @@ -15,7 +15,7 @@ Allows applications to automatically update registered variables when the value of a specified key has changed. Application developers should use the -[Notification API](https://doc.libelektra.org/api/current/html/group__kdbnotification.html) +[notification API](https://doc.libelektra.org/api/current/html/group__kdbnotification.html) instead of the functions exported by this plugin. The API is easier to use and decouples applications from this plugin. @@ -24,4 +24,5 @@ The API is easier to use and decouples applications from this plugin. This plugin exports various functions starting with "register*" below `system/elektra/modules/internalnotification/exports/`. These functions should not be used directly. -Instead the notification API should be used. +Instead the [notification API](https://doc.libelektra.org/api/current/html/group__kdbnotification.html) +should be used. diff --git a/src/tools/hub-zeromq/README.md b/src/tools/hub-zeromq/README.md index 29fc63e8f5d..74922ca3aca 100644 --- a/src/tools/hub-zeromq/README.md +++ b/src/tools/hub-zeromq/README.md @@ -8,8 +8,8 @@ The hub does not feature authentication or encryption. ## Usage -The hub can be controlled using the CLI commands "kdb run-@tool@"" for -starting and "kdb stop-@tool@" for stopping. +The hub can be controlled using the CLI commands "kdb run-hub-zeromq" for +starting and "kdb stop-hub-zeromq" for stopping. ## Configuration diff --git a/src/tools/hub-zeromq/hub-zeromq.c b/src/tools/hub-zeromq/hub-zeromq.c index fec7e1f84e8..03011b6ff90 100644 --- a/src/tools/hub-zeromq/hub-zeromq.c +++ b/src/tools/hub-zeromq/hub-zeromq.c @@ -97,7 +97,7 @@ int main (void) } printf ("listening on %s (XSUB for zeromqsend)\n", xSubEndpoint); - printf ("listening on %s (XPUB for zeromqrecv)\n", xPubEndpoint); + printf ("listening on %s (XPUB for zeromqrecv)\n\n", xPubEndpoint); ksDel (config); // forward messages between sockets From 4cec993946361696e723426b9ed6f032990f1038 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sat, 18 Aug 2018 13:38:50 +0200 Subject: [PATCH 42/46] invoke/deferred calls: call direct and defer; fix return codes --- src/libs/invoke/invoke.c | 43 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/libs/invoke/invoke.c b/src/libs/invoke/invoke.c index b98be04427d..2b78f3ef85d 100644 --- a/src/libs/invoke/invoke.c +++ b/src/libs/invoke/invoke.c @@ -348,13 +348,13 @@ void elektraInvokeClose (ElektraInvokeHandle * handle, Key * errorKey) * @param elektraPluginFunctionName function name * @param parameters parameter key set * @retval 0 on success - * @retval 1 when function not exported and deferring is unsupported by plugin + * @retval -1 when the call failed (direct call and deferring not available) */ int elektraInvokeCallDeferable (ElektraInvokeHandle * handle, const char * elektraPluginFunctionName, KeySet * parameters) { if (!handle) { - return 1; + return -1; } return elektraDeferredCall (handle->plugin, elektraPluginFunctionName, parameters); } @@ -380,43 +380,48 @@ void elektraInvokeExecuteDeferredCalls (ElektraInvokeHandle * handle, ElektraDef * Call a deferable function on a plugin handle. * If the function is exported by the plugin it is directly invoked, * if the plugin supports deferring calls, the call is deferred. - * - * The parameters key set can be freed afterwards. + * If both is possible (function is exported and deferred calls are supported), + * the function is directly called and the call is deferred (i.e. for nested plugins). * * @param handle invoke handle * @param elektraPluginFunctionName function name - * @param parameters parameter key set + * @param parameters parameter key set. Can bee freed afterwards. * @retval 0 on success - * @retval 1 when function not exported and deferring is unsupported by plugin + * @retval -1 when the call failed (direct call and deferring not available) */ int elektraDeferredCall (Plugin * handle, const char * elektraPluginFunctionName, KeySet * parameters) { ELEKTRA_NOT_NULL (handle); ELEKTRA_NOT_NULL (elektraPluginFunctionName); + int result; size_t direct = elektraPluginGetFunction (handle, elektraPluginFunctionName); if (direct) { ElektraDeferredCallable directFn = (ElektraDeferredCallable) direct; directFn (handle, parameters); + result = 0; // success } else { - size_t deferredCall = elektraPluginGetFunction (handle, "deferredCall"); - if (deferredCall) - { - ElektraDeferredCall deferredCallFn = (ElektraDeferredCall) deferredCall; - deferredCallFn (handle, elektraPluginFunctionName, parameters); - } - else - { - // no direct call and deferring possible - return 1; - } + // no direct call possible + result = -1; + } + + size_t deferredCall = elektraPluginGetFunction (handle, "deferredCall"); + if (deferredCall) + { + ElektraDeferredCall deferredCallFn = (ElektraDeferredCall) deferredCall; + deferredCallFn (handle, elektraPluginFunctionName, parameters); + result = 0; // success + } + else + { + // deferred calls not possible + result = -1; } - // success - return 0; + return result; } /** From 9744189665ee8cd95ffee8e79143001496629ac7 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sat, 18 Aug 2018 13:39:45 +0200 Subject: [PATCH 43/46] zeromqsend plugin: fix timeout bug --- src/plugins/zeromqsend/publish.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/plugins/zeromqsend/publish.c b/src/plugins/zeromqsend/publish.c index f35a8afff9a..93e64f63a01 100644 --- a/src/plugins/zeromqsend/publish.c +++ b/src/plugins/zeromqsend/publish.c @@ -67,6 +67,22 @@ static int getMonitorEvent (void * monitor) return event; } +static struct timespec ts_diff (struct timespec now, struct timespec start) +{ + struct timespec diff; + if ((now.tv_nsec - start.tv_nsec) < 0) + { + diff.tv_sec = now.tv_sec - start.tv_sec - 1; + diff.tv_nsec = 1000000000 + now.tv_nsec - start.tv_nsec; + } + else + { + diff.tv_sec = now.tv_sec - start.tv_sec; + diff.tv_nsec = now.tv_nsec - start.tv_nsec; + } + return diff; +} + /** * Wait for connection message from ZeroMQ monitor socket. * @@ -134,9 +150,10 @@ static int waitForSubscription (void * socket, long subscribeTimeout) struct timespec start; struct timespec now; struct timespec wait; + struct timespec diff; time_t startFallback = -1; long timeoutSec = (subscribeTimeout / (1000)); - long timeoutNsec = (subscribeTimeout % (1000)) * (1000 * 1000 * 1000); + long timeoutNsec = (subscribeTimeout % (1000)) * (1000 * 1000); if (clock_gettime (CLOCK_MONOTONIC, &start) == -1) { ELEKTRA_LOG_WARNING ("Using slower fallback for timeout detection"); @@ -171,7 +188,8 @@ static int waitForSubscription (void * socket, long subscribeTimeout) if (startFallback == -1) { clock_gettime (CLOCK_MONOTONIC, &now); - timeout = now.tv_sec - start.tv_sec >= timeoutSec && now.tv_nsec - start.tv_nsec >= timeoutNsec; + diff = ts_diff (now, start); + timeout = diff.tv_sec >= timeoutSec && diff.tv_nsec >= timeoutNsec; } else { From d9f855e3a8f5a47636c736b81546c5be8136c34b Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sat, 18 Aug 2018 13:42:33 +0200 Subject: [PATCH 44/46] io-glib: fix for invalid access --- src/bindings/io/glib/io_glib.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/bindings/io/glib/io_glib.c b/src/bindings/io/glib/io_glib.c index 3db233468b0..7e87fd9cba8 100644 --- a/src/bindings/io/glib/io_glib.c +++ b/src/bindings/io/glib/io_glib.c @@ -178,6 +178,17 @@ static int ioGlibBindingFdDispatch (GSource * source, GSourceFunc callback, void return G_SOURCE_CONTINUE; } +/** + * Cleanup after file descriptor has been detached + * @param source glib source + */ +static void ioGlibBindingFdCleanup (GSource * source) +{ + FdSource * fdSource = (FdSource *) source; + elektraFree (fdSource->bindingData->fdFuncs); + elektraFree (fdSource->bindingData); +} + /** * Calls the associated callback. * Called by glib whenever a timer has elapsed. @@ -282,7 +293,7 @@ static int ioGlibBindingAddFd (ElektraIoInterface * binding, ElektraIoFdOperatio funcs->prepare = ioGlibBindingFdPrepare; funcs->check = ioGlibBindingFdCheck; funcs->dispatch = ioGlibBindingFdDispatch; - funcs->finalize = NULL; + funcs->finalize = ioGlibBindingFdCleanup; bindingData->fdFuncs = funcs; FdSource * fdSource = (FdSource *) g_source_new (funcs, sizeof *fdSource); GSource * gSource = &fdSource->gSource; @@ -320,8 +331,6 @@ static int ioGlibBindingRemoveFd (ElektraIoFdOperation * fdOp) } g_source_destroy (gSource); g_source_unref (gSource); - elektraFree (bindingData->fdFuncs); - elektraFree (bindingData); return 1; } From ffb88adfc913c1b93b0817c45d585e6c8e7bee81 Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sat, 18 Aug 2018 13:43:09 +0200 Subject: [PATCH 45/46] notification: add news highlight & improve docs --- doc/news/_preparation_next_release.md | 59 +++++++++++++++++++-------- doc/tutorials/notifications.md | 11 +++-- examples/notificationAsync.c | 12 +++--- examples/notificationPolling.c | 14 +++---- examples/notificationReload.c | 9 ++-- src/bindings/io/glib/README.md | 5 ++- src/bindings/io/uv/README.md | 5 ++- src/tools/hub-zeromq/hub-zeromq.c | 5 ++- 8 files changed, 75 insertions(+), 45 deletions(-) diff --git a/doc/news/_preparation_next_release.md b/doc/news/_preparation_next_release.md index 5130f91c68c..d87f8271b90 100644 --- a/doc/news/_preparation_next_release.md +++ b/doc/news/_preparation_next_release.md @@ -40,7 +40,7 @@ You can also read the news [on our website](https://www.libelektra.org/news/0.8. - Type system preview - Chef Cookbook - Elektra Web 1.6 - +- Notifications ### Type system preview @@ -133,6 +133,37 @@ Try it out now on: http://webdemo.libelektra.org/ Thanks to Daniel Bugl. +### Notifications + +Elektra's notification feature which allows applications to keep persistent +configuration settings in sync with the key database and other applications was +greatly improved with this release: + +- The [notification API](https://doc.libelektra.org/api/current/html/group__kdbnotification.html) + now supports more types and has improved support for callbacks. +- With the addition of the [zeromqsend](https://www.libelektra.org/plugins/zeromqsend) + and [zeromqrecv](https://www.libelektra.org/plugins/zeromqrecv) plugins + together with the [hub-zeromq](https://www.libelektra.org/tools/hub-zeromq) + tool we have an alternative to the D-Bus transport plugins + ([dbus](https://www.libelektra.org/plugins/dbus) and + [dbusrecv](https://www.libelektra.org/plugins/dbusrecv)). +- The new asynchronous I/O binding for [ev](https://www.libelektra.org/bindings/io_ev) + is the third I/O binding - so notifications can be used in applications using + [glib](https://www.libelektra.org/bindings/io_glib), [uv](https://www.libelektra.org/bindings/io_uv) + or [ev](https://www.libelektra.org/bindings/io_ev). + If your application uses a different library please check out the + ["How to create your own I/O binding" section](https://www.libelektra.org/tutorials/notifications#how-to-create-your-own-i-o-binding) in the [notification tutorial](https://www.libelektra.org/tutorials/notifications). +- Notifications can be used to reload KDB after Elektra's configuration (e.g. + mountpoints or globally mounted plugins) has changed. + We added a [how-to to the notification tutorial](https://www.libelektra.org/tutorials/notifications#howto-reload-kdb-when-elektras-configuration-has-changed) + that explains the required steps and the ["notificationReload"](https://www.libelektra.org/examples/notificationreload) example with the complete code. + +More details can be [found](#zeromq-transport-plugins) [in](#misc) [this](#bindings) [news](#notifications). +Check out the updated [notification tutorial](https://www.libelektra.org/tutorials/notifications) +and notification examples ([polling](https://www.libelektra.org/examples/notificationpolling), +[async](https://www.libelektra.org/examples/notificationasync) and +[reload](https://www.libelektra.org/examples/notificationreload). + ## Plugins ### CCode @@ -394,9 +425,12 @@ Thanks to Daniel Bugl. - The [notification API](https://doc.libelektra.org/api/current/html/group__kdbnotification.html) was extended. - The API now supports contexts for callbacks, the types `int`, `unsigned int`, + The API now supports more types: `int`, `unsigned int`, `long`, `unsigned long`, `long long`, `unsinged long long`, `float` and `double`. - It also supports all of Elektra's `kdb_*_t` types defined in `kdbtypes.h`. *(Thomas Wahringer)* + It also supports all of Elektra's `kdb_*_t` types defined in `kdbtypes.h`. + Also contexts for callbacks were added and + `elektraNotificationRegisterCallbackSameOrBelow()` allows for notifications + for the registered key or below. *(Thomas Wahringer)* ## Tools @@ -486,20 +520,6 @@ Thanks to Daniel Bugl. [Markdown Shell Recorder]: https://master.libelektra.org/tests/shell/shell_recorder/tutorial_wrapper [Shell Recorder]: (https://master.libelektra.org/tests/shell/shell_recorder) -## Compatibility - -As always, the ABI and API of kdb.h is fully compatible, i.e. programs -compiled against an older 0.8 version of Elektra will continue to work -(ABI) and you will be able to recompile programs without errors (API). - -- <> -- <> -- <> -- `kdbtypes.h` now comes with support for C99 types -- We added the private headerfiles `kdbnotificationinternal.h`, `kdbioplugin.h`. *(Thomas Wahringer)* -- The I/O binding header files have been moved a new directory called `kdbio`. - For example, instead of including `elektra/kdbio_ev.h` users of the binding now include `elektra/kdbio/ev.h`. *(Thomas Wahringer)* - ## Build ### CMake @@ -629,7 +649,10 @@ compiled against an older 0.8 version of Elektra will continue to work Following changes were made: - The C++ API was extended with delBaseName() -- <> +- `kdbtypes.h` now comes with support for C99 types +- We added the private headerfiles `kdbnotificationinternal.h`, `kdbioplugin.h`. *(Thomas Wahringer)* +- The I/O binding header files have been moved a new directory called `kdbio`. + For example, instead of including `elektra/kdbio_ev.h` users of the binding now include `elektra/kdbio/ev.h`. *(Thomas Wahringer)* - <> ## Website diff --git a/doc/tutorials/notifications.md b/doc/tutorials/notifications.md index 606d8125050..df66c209ec2 100644 --- a/doc/tutorials/notifications.md +++ b/doc/tutorials/notifications.md @@ -137,8 +137,8 @@ Values of registered variables are automatically updated when the value of the assigned key has changed. In the following example we will register an integer variable. -The following examples are shortened for tangibility. The complete example is available in -[src/libs/notification/example/example_notification_async.c](https://github.com/ElektraInitiative/libelektra/blob/master/src/libs/notification/example/example_notification_async.c). +The following examples are shortened for tangibility. The complete code is available in +["notificationAsync" example](https://www.libelektra.org/examples/notificationasync). ```C #include @@ -297,7 +297,7 @@ void initKdb (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED) elektraNotificationOpen (kdb); // Code for registration from snippet before - Key * elektraKey = keyNew ("system/elektra", KEY_END); + Key * elektraKey = keyNew ("/elektra", KEY_END); elektraNotificationRegisterCallbackSameOrBelow (kdb, elektraKey, elektraChangedCallback, NULL); keyDel (elektraKey); @@ -326,7 +326,8 @@ First, we create the timer in the main loop setup of the application. ElektraIoTimerOperation * reload; // main loop setup (e.g. main()) -// the timer operation will reload KDB after 100 milliseconds +// the timer operation is disabled for now and +// will reload KDB after 100 milliseconds reload = elektraIoNewTimerOperation (100, 0, initKdb, NULL); elektraIoBindingAddTimer (binding, reload); ``` @@ -367,6 +368,8 @@ to Elektra's configuration. The snippets above omit error handling for brevity. The complete code including error handling is available in the ["notification reload" example](https://www.libelektra.org/examples/notificationreload). +This example also omits globals by passing them as user data using the +`elektraIo*GetData()` functions. ## Emergent Behavior Guidelines diff --git a/examples/notificationAsync.c b/examples/notificationAsync.c index bd60a781af7..11583261535 100644 --- a/examples/notificationAsync.c +++ b/examples/notificationAsync.c @@ -9,8 +9,8 @@ * - Transport plugins (e.g. kdb global-mount dbus announce=once dbusrecv) * * Relevant keys for this example: - * - /sw/elektra/example_notification/#0/current/value: Set to any integer value - * - /sw/elektra/example_notification/#0/current/color: Set the text color. Possible + * - /sw/example/notification/#0/current/value: Set to any integer value + * - /sw/example/notification/#0/current/color: Set the text color. Possible * values are "red", "green" and "blue". * * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) @@ -95,7 +95,7 @@ int main (void) KeySet * config = ksNew (20, KS_END); - Key * key = keyNew ("/sw/elektra/example_notification/#0/current", KEY_END); + Key * key = keyNew ("/sw/example/notification/#0/current", KEY_END); KDB * kdb = kdbOpen (key); if (kdb == NULL) { @@ -115,7 +115,7 @@ int main (void) } int value = 0; - Key * intKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/value", KEY_END); + Key * intKeyToWatch = keyNew ("/sw/example/notification/#0/current/value", KEY_END); result = elektraNotificationRegisterInt (kdb, intKeyToWatch, &value); if (!result) { @@ -123,7 +123,7 @@ int main (void) return -1; } - Key * callbackKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/color", KEY_END); + Key * callbackKeyToWatch = keyNew ("/sw/example/notification/#0/current/color", KEY_END); result = elektraNotificationRegisterCallback (kdb, callbackKeyToWatch, &setTerminalColor, NULL); if (!result) { @@ -137,7 +137,7 @@ int main (void) printf ("Asynchronous Notification Example Application\n"); printf ("Please note that notification transport plugins are required see\n" - " https://www.libelektra.org/tutorials/notifications#notification-configuration!\n"); + " https://www.libelektra.org/tutorials/notifications#notification-configuration!\n"); printf ("- Set \"%s\" to red, blue or green to change the text color\n", keyName (callbackKeyToWatch)); printf ("- Set \"%s\" to any integer value\n", keyName (intKeyToWatch)); printf ("Send SIGINT (Ctl+C) to exit.\n\n"); diff --git a/examples/notificationPolling.c b/examples/notificationPolling.c index b7f27534790..9ae373264fc 100644 --- a/examples/notificationPolling.c +++ b/examples/notificationPolling.c @@ -2,11 +2,11 @@ * @file * * @brief Example for notification library which repeatedly reads some keys and - * reacts to them; see "example_notification_async" for an example without polling + * reacts to them; see "notificationAsync.c" for an example without polling * * Relevant keys for this example: - * - /sw/elektra/example_notification/#0/current/value: Set to any integer value - * - /sw/elektra/example_notification/#0/current/color: Set the text color. Possible + * - /sw/example/notification/#0/current/value: Set to any integer value + * - /sw/example/notification/#0/current/color: Set the text color. Possible * values are "red", "green" and "blue". * * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) @@ -89,7 +89,7 @@ int main (void) KeySet * config = ksNew (20, KS_END); - Key * key = keyNew ("/sw/elektra/example_notification/#0/current", KEY_END); + Key * key = keyNew ("/sw/example/notification/#0/current", KEY_END); KDB * kdb = kdbOpen (key); if (kdb == NULL) { @@ -105,7 +105,7 @@ int main (void) } int value = 0; - Key * intKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/value", KEY_END); + Key * intKeyToWatch = keyNew ("/sw/example/notification/#0/current/value", KEY_END); result = elektraNotificationRegisterInt (kdb, intKeyToWatch, &value); if (!result) { @@ -113,7 +113,7 @@ int main (void) return -1; } - Key * callbackKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/color", KEY_END); + Key * callbackKeyToWatch = keyNew ("/sw/example/notification/#0/current/color", KEY_END); result = elektraNotificationRegisterCallback (kdb, callbackKeyToWatch, &setTerminalColor, NULL); if (!result) { @@ -127,7 +127,7 @@ int main (void) while (!keepRunning) { // After this kdbGet the integer variable is updated and the callback was called. - // see "example_notification_async" for an example without polling + // see "notificationAsync" for an example without polling kdbGet (kdb, config, key); // Print values diff --git a/examples/notificationReload.c b/examples/notificationReload.c index a7e77c96132..5e102b6cac7 100644 --- a/examples/notificationReload.c +++ b/examples/notificationReload.c @@ -10,7 +10,7 @@ * - Transport plugins (e.g. kdb global-mount zeromqsend zeromqrecv && kdb run-hub-zeromq) * * Relevant keys for this example: - * - /sw/elektra/example_notification/#0/current/value: Set to any integer value + * - /sw/example/notification/#0/current/value: Set to any integer value * Add additional transport plugins and remove the original pair afterwards or * mount a file which sets the key above to a different value and unmount it again * @@ -178,8 +178,8 @@ int main (void) g_unix_signal_add (SIGINT, onSIGNAL, data); data->config = ksNew (20, KS_END); - data->parentKey = keyNew ("/sw/elektra/example_notification/#0/current", KEY_END); - data->intKeyToWatch = keyNew ("/sw/elektra/example_notification/#0/current/value", KEY_END); + data->parentKey = keyNew ("/sw/example/notification/#0/current", KEY_END); + data->intKeyToWatch = keyNew ("/sw/example/notification/#0/current/value", KEY_END); // Setup timer that repeatedly prints the variable data->timer = elektraIoNewTimerOperation (TWO_SECONDS, 1, printVariable, data); @@ -191,7 +191,7 @@ int main (void) printf ("Reloading Notification Example Application\n"); printf ("Please note that notification transport plugins are required see\n" - " https://www.libelektra.org/tutorials/notifications#notification-configuration!\n"); + " https://www.libelektra.org/tutorials/notifications#notification-configuration!\n"); printf ("- Set \"%s\" to any integer value\n", keyName (data->intKeyToWatch)); printf ("- Try to add additional transport plugins and remove the original pair afterwards\n"); printf ("- Mount a file which sets the key above to a different value and unmount it\n"); @@ -208,5 +208,6 @@ int main (void) ksDel (data->config); keyDel (data->intKeyToWatch); keyDel (data->parentKey); + elektraFree (data); printf ("cleanup done!\n"); } diff --git a/src/bindings/io/glib/README.md b/src/bindings/io/glib/README.md index 86a95fa2a75..ce507c493e0 100644 --- a/src/bindings/io/glib/README.md +++ b/src/bindings/io/glib/README.md @@ -36,7 +36,6 @@ Populated I/O interface ## Example ```C - #include #include #include @@ -65,5 +64,7 @@ void main (void) elektraIoBindingCleanup (binding); g_main_loop_unref (loop); } - ``` + +Please check out the ["notificationReload" example](https://www.libelektra.org/examples/notificationreload) +which uses this I/O binding. diff --git a/src/bindings/io/uv/README.md b/src/bindings/io/uv/README.md index a74a34601b3..1dfcd6c9fd5 100644 --- a/src/bindings/io/uv/README.md +++ b/src/bindings/io/uv/README.md @@ -35,7 +35,6 @@ Populated I/O interface ## Example ```C - #include #include #include @@ -63,5 +62,7 @@ void main (void) elektraIoBindingCleanup (binding); uv_loop_close (loop); } - ``` + +Please check out the ["notificationAsync" example](https://www.libelektra.org/examples/notificationasync) +which uses this I/O binding. diff --git a/src/tools/hub-zeromq/hub-zeromq.c b/src/tools/hub-zeromq/hub-zeromq.c index 03011b6ff90..b6280b8c7a5 100644 --- a/src/tools/hub-zeromq/hub-zeromq.c +++ b/src/tools/hub-zeromq/hub-zeromq.c @@ -35,7 +35,7 @@ static void onSignal (int signal) int main (void) { - printf ("lightweight zeromq message hub\n"); + printf ("\nlightweight zeromq message hub\n"); // exit on SIGINT signal (SIGINT, onSignal); @@ -97,7 +97,8 @@ int main (void) } printf ("listening on %s (XSUB for zeromqsend)\n", xSubEndpoint); - printf ("listening on %s (XPUB for zeromqrecv)\n\n", xPubEndpoint); + printf ("listening on %s (XPUB for zeromqrecv)\n", xPubEndpoint); + printf ("hub is running\n"); ksDel (config); // forward messages between sockets From a773bdf7fa52a2d20fcb4dc756bf4f42a82de2af Mon Sep 17 00:00:00 2001 From: Thomas Wahringer Date: Sat, 18 Aug 2018 14:45:43 +0200 Subject: [PATCH 46/46] zeromqsend plugin: improve connection timeout resolution --- src/plugins/zeromqsend/README.md | 2 +- src/plugins/zeromqsend/publish.c | 30 +++++++++++++++++++-- src/plugins/zeromqsend/testmod_zeromqsend.c | 2 +- src/plugins/zeromqsend/zeromqsend.h | 2 +- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/plugins/zeromqsend/README.md b/src/plugins/zeromqsend/README.md index ddcdda68c4d..88221820e0e 100644 --- a/src/plugins/zeromqsend/README.md +++ b/src/plugins/zeromqsend/README.md @@ -50,7 +50,7 @@ This plugin supports the following configuration options when mounting: [`ipc`](http://api.zeromq.org/4-2:zmq-ipc) and [`tcp`](http://api.zeromq.org/4-2:zmq-tcp) ZeroMQ transports are recommended. The default value is "tcp://localhost:6000". -- **connectTimeout**: Timeout for establishing connections in seconds. The default value is "2". +- **connectTimeout**: Timeout for establishing connections in miliseconds. The default value is "1000". - **subscribeTimeout**: Timeout for waiting for subscribers in miliseconds. The default value is "200". # Notification Format diff --git a/src/plugins/zeromqsend/publish.c b/src/plugins/zeromqsend/publish.c index 93e64f63a01..91f14e70b7d 100644 --- a/src/plugins/zeromqsend/publish.c +++ b/src/plugins/zeromqsend/publish.c @@ -93,8 +93,23 @@ static struct timespec ts_diff (struct timespec now, struct timespec start) */ static int waitForConnection (void * monitorSocket, long connectTimeout) { - time_t start = time (NULL); struct timespec wait; + struct timespec start; + struct timespec now; + struct timespec diff; + time_t startFallback = -1; + long timeoutSec = (connectTimeout / (1000)); + long timeoutNsec = (connectTimeout % (1000)) * (1000 * 1000); + if (clock_gettime (CLOCK_MONOTONIC, &start) == -1) + { + ELEKTRA_LOG_WARNING ("Using slower fallback for timeout detection"); + startFallback = time (NULL); + // minimum timeout is 1 second when using the fallback + if (timeoutSec == 0) + { + timeoutSec = 1; + } + } // wait for connection established event int connected = 0; @@ -107,7 +122,18 @@ static int waitForConnection (void * monitorSocket, long connectTimeout) int event = getMonitorEvent (monitorSocket); - if (time (NULL) - start > connectTimeout) + int timeout = 0; + if (startFallback == -1) + { + clock_gettime (CLOCK_MONOTONIC, &now); + diff = ts_diff (now, start); + timeout = diff.tv_sec >= timeoutSec && diff.tv_nsec >= timeoutNsec; + } + else + { + timeout = time (NULL) - startFallback >= timeoutSec; + } + if (timeout) { ELEKTRA_LOG_WARNING ("connection timed out. could not publish notification"); zmq_close (monitorSocket); diff --git a/src/plugins/zeromqsend/testmod_zeromqsend.c b/src/plugins/zeromqsend/testmod_zeromqsend.c index 39f5fbc18b2..090cc800be2 100644 --- a/src/plugins/zeromqsend/testmod_zeromqsend.c +++ b/src/plugins/zeromqsend/testmod_zeromqsend.c @@ -42,7 +42,7 @@ void * context; #define TEST_ENDPOINT "tcp://127.0.0.1:6002" /** extended timeouts for tests */ -#define TESTCONFIG_CONNECT_TIMEOUT "5" +#define TESTCONFIG_CONNECT_TIMEOUT "5000" #define TESTCONFIG_SUBSCRIBE_TIMEOUT "5000" /** diff --git a/src/plugins/zeromqsend/zeromqsend.h b/src/plugins/zeromqsend/zeromqsend.h index 94b8a31cef1..fa161baa8a7 100644 --- a/src/plugins/zeromqsend/zeromqsend.h +++ b/src/plugins/zeromqsend/zeromqsend.h @@ -21,7 +21,7 @@ #define ELEKTRA_ZEROMQ_DEFAULT_PUB_ENDPOINT "tcp://localhost:6000" /** default connection timeout for plugin */ -#define ELEKTRA_ZEROMQ_DEFAULT_CONNECT_TIMEOUT 2 +#define ELEKTRA_ZEROMQ_DEFAULT_CONNECT_TIMEOUT 1000 /** default subscription timeout for plugin */ #define ELEKTRA_ZEROMQ_DEFAULT_SUBSCRIBE_TIMEOUT 200