LCOV - code coverage report
Current view: top level - src/libs/invoke - invoke.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 103 136 75.7 %
Date: 2019-09-12 12:28:41 Functions: 9 17 52.9 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Library for invoking exported plugin functions
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : #include <kdbassert.h>
       9             : #include <kdbinvoke.h>
      10             : #include <kdbmodule.h>
      11             : #include <kdbprivate.h> // for elektraPluginOpen/Close
      12             : 
      13             : #include <stdio.h>
      14             : 
      15             : struct _ElektraInvokeHandle
      16             : {
      17             :         Plugin * plugin;
      18             :         KeySet * modules;
      19             :         KeySet * exports;
      20             : };
      21             : 
      22             : /**
      23             :  * Structure for deferred calls
      24             :  * @internal
      25             :  */
      26             : typedef struct _ElektraDeferredCall
      27             : {
      28             :         char * name;
      29             :         KeySet * parameters;
      30             :         struct _ElektraDeferredCall * next;
      31             : } _ElektraDeferredCall;
      32             : 
      33             : /**
      34             :  * Structure for internal plugin state
      35             :  * @internal
      36             :  */
      37             : struct _ElektraDeferredCallList
      38             : {
      39             :         _ElektraDeferredCall * head;
      40             :         _ElektraDeferredCall * last;
      41             : };
      42             : 
      43             : 
      44             : /**
      45             :  * @defgroup invoke
      46             :  * @brief Functionality to use plugins and invoke functions
      47             :  *
      48             :  * Allows invoking functions of plugins as needed within applications
      49             :  * and plugins inside and outside of the KDB.
      50             :  *
      51             :  * To use this library, you need to include:
      52             :  *
      53             :  * @code
      54             :  * #include <kdbinvoke.h>`
      55             :  * @endcode
      56             :  *
      57             :  * and link against `libelektra-invoke`.
      58             :  * Then you can use it:
      59             :  *
      60             :  * @code
      61             :  * ElektraInvokeHandle * handle = elektraInvokeOpen ("dini", 0);
      62             :  * elektraInvoke2Args (handle, "get", ks, k);
      63             :  * elektraInvokeClose (handle);
      64             :  * @endcode
      65             :  *
      66             :  * @{
      67             :  *
      68             :  */
      69             : 
      70             : /**
      71             :  * @deprecated Do not use.
      72             :  *
      73             :  * Use `elektraInvokeOpen (name, 0, 0)` instead.
      74             :  *
      75             :  * @see elektraInvokeOpen()
      76             :  */
      77           0 : ElektraInvokeHandle * elektraInvokeInitialize (const char * elektraPluginName)
      78             : {
      79           0 :         return elektraInvokeOpen (elektraPluginName, 0, 0);
      80             : }
      81             : 
      82             : /**
      83             :  * @brief Opens a new handle to invoke functions for a plugin.
      84             :  *
      85             :  * When opening the plugin, it calls the "open" function of the plugin.
      86             :  *
      87             :  * @param elektraPluginName the plugin on which we want to invoke functions.
      88             :  * @param config the config to be passed to the plugin.
      89             :  * @param errorKey a key where error messages will be stored
      90             :  *
      91             :  * @return the handle
      92             :  * @retval 0 on errors
      93             :  */
      94         380 : ElektraInvokeHandle * elektraInvokeOpen (const char * elektraPluginName, KeySet * config, Key * errorKey)
      95             : {
      96         380 :         if (!elektraPluginName)
      97             :         {
      98             :                 return NULL;
      99             :         }
     100         380 :         ElektraInvokeHandle * handle = elektraCalloc (sizeof (struct _ElektraInvokeHandle));
     101         380 :         if (!handle)
     102             :         {
     103             :                 return NULL;
     104             :         }
     105         380 :         KeySet * modules = ksNew (0, KS_END);
     106         380 :         handle->modules = modules;
     107         380 :         elektraModulesInit (modules, NULL);
     108             : 
     109         380 :         if (!config)
     110             :         {
     111         231 :                 config = ksNew (0, KS_END);
     112             :         }
     113             :         else
     114             :         {
     115         149 :                 config = ksDup (config);
     116             :         }
     117             : 
     118         380 :         int errorKeyMissing = !errorKey;
     119         380 :         if (errorKeyMissing)
     120             :         {
     121          16 :                 errorKey = keyNew (0, KEY_END);
     122             :         }
     123             : 
     124         380 :         Plugin * plugin = elektraPluginOpen (elektraPluginName, modules, config, errorKey);
     125         380 :         if (errorKeyMissing)
     126             :         {
     127          16 :                 keyDel (errorKey);
     128             :         }
     129         380 :         if (!plugin)
     130             :         {
     131           0 :                 elektraModulesClose (modules, NULL);
     132           0 :                 ksDel (modules);
     133           0 :                 elektraFree (handle);
     134           0 :                 return NULL;
     135             :         }
     136         380 :         handle->plugin = plugin;
     137         380 :         return handle;
     138             : }
     139             : 
     140             : /**
     141             :  * @brief Get a function pointer.
     142             :  *
     143             :  * @param handle the handle to use
     144             :  * @param elektraPluginFunctionName the name of the function to use, e.g., get/set
     145             :  *
     146             :  * @pre handle must be as returned from elektraInvokeOpen()
     147             :  *
     148             :  * Example:
     149             :  *
     150             :  * @code
     151             : typedef int (*elektra2Args) (KeySet*, Key *);
     152             : elektra2Args func = *(elektra2Args *)elektraInvokeGetFunction (handle, elektraPluginFunctionName);
     153             : if (!func) exit(1);   // no function found, handle error
     154             : func (ks, k);         // otherwise, call function
     155             :  * @endcode
     156             :  *
     157             :  * @see elektraInvoke2Args() a convenience function to be used for KeySet,Key arguments.
     158             :  *
     159             :  * @return a function pointer for the specified function.
     160             :  */
     161        1479 : const void * elektraInvokeGetFunction (ElektraInvokeHandle * handle, const char * elektraPluginFunctionName)
     162             : {
     163        1479 :         if (!handle || !elektraPluginFunctionName)
     164             :         {
     165             :                 return NULL;
     166             :         }
     167        1479 :         Plugin * plugin = handle->plugin;
     168        1479 :         KeySet * exports = NULL;
     169             : 
     170        1479 :         Key * exportParent = keyNew ("system/elektra/modules", KEY_END);
     171        1479 :         keyAddBaseName (exportParent, plugin->name);
     172             : 
     173        1479 :         if (handle->exports)
     174             :         {
     175             :                 exports = handle->exports;
     176             :         }
     177             :         else
     178             :         {
     179         364 :                 exports = ksNew (0, KS_END);
     180         364 :                 handle->exports = exports;
     181         364 :                 plugin->kdbGet (plugin, exports, exportParent);
     182             :         }
     183        1479 :         keyAddBaseName (exportParent, "exports");
     184        1479 :         keyAddBaseName (exportParent, elektraPluginFunctionName);
     185        1479 :         Key * functionKey = ksLookup (exports, exportParent, 0);
     186        1479 :         keyDel (exportParent);
     187        1479 :         if (!functionKey)
     188             :         {
     189             :                 return NULL;
     190             :         }
     191             :         else
     192             :         {
     193        1479 :                 return keyValue (functionKey);
     194             :         }
     195             : }
     196             : 
     197             : /**
     198             :  * @brief Get the configuration the plugin uses.
     199             :  *
     200             :  * @param handle the handle to use
     201             :  *
     202             :  * @pre handle must be as returned from elektraInvokeOpen()
     203             :  *
     204             :  * @return the config of the plugin.
     205             :  */
     206           0 : KeySet * elektraInvokeGetPluginConfig (ElektraInvokeHandle * handle)
     207             : {
     208           0 :         if (!handle) return NULL;
     209           0 :         return handle->plugin->config;
     210             : }
     211             : 
     212             : /**
     213             :  * @brief Get the name of the plugin.
     214             :  *
     215             :  * The name might differ from the name as passed with elektraInvokeOpen
     216             :  * when symlinks are used.
     217             :  *
     218             :  * @param handle the handle to work with.
     219             :  *
     220             :  * @pre handle must be as returned from elektraInvokeOpen()
     221             :  *
     222             :  * @return the name of the plugin
     223             :  */
     224           0 : const char * elektraInvokeGetPluginName (ElektraInvokeHandle * handle)
     225             : {
     226           0 :         if (!handle) return NULL;
     227           0 :         return handle->plugin->name;
     228             : }
     229             : 
     230             : /**
     231             :  * @brief Get the data of the plugin.
     232             :  *
     233             :  * @param handle the handle to work with.
     234             :  *
     235             :  * @pre handle must be as returned from elektraInvokeOpen()
     236             :  *
     237             :  * @return a pointer to the plugin's data.
     238             :  */
     239           0 : void * elektraInvokeGetPluginData (ElektraInvokeHandle * handle)
     240             : {
     241           0 :         if (!handle) return NULL;
     242           0 :         return handle->plugin->data;
     243             : }
     244             : 
     245             : /**
     246             :  * @brief Get the modules used for invoking.
     247             :  *
     248             :  * @warning The modules are closed within elektraInvokeClose().
     249             :  * It is *not* enough to ksDup() the keyset, you must not call
     250             :  * elektraInvokeClose() if you want to reuse the modules.
     251             :  *
     252             :  * @param handle the handle to work with.
     253             :  *
     254             :  * @pre handle must be as returned from elektraInvokeOpen()
     255             :  *
     256             :  * @return the modules used for invoking.
     257             :  */
     258           0 : KeySet * elektraInvokeGetModules (ElektraInvokeHandle * handle)
     259             : {
     260           0 :         if (!handle) return NULL;
     261           0 :         return handle->modules;
     262             : }
     263             : 
     264             : /**
     265             :  * @brief Get the exports from the plugin.
     266             :  *
     267             :  * @param handle the handle to work with.
     268             :  *
     269             :  * @pre handle must be as returned from elektraInvokeOpen()
     270             :  *
     271             :  * @return the exports of the plugin.
     272             :  */
     273           0 : KeySet * elektraInvokeGetExports (ElektraInvokeHandle * handle)
     274             : {
     275           0 :         if (!handle) return NULL;
     276           0 :         return handle->exports;
     277             : }
     278             : 
     279             : 
     280             : /**
     281             :  * @brief A convenience function to call a function with two arguments.
     282             :  *
     283             :  * @param handle the handle to work with
     284             :  * @param elektraPluginFunctionName the function to call, e.g. "get"
     285             :  * @param ks the keyset to be used as first parameter
     286             :  * @param k the key to be used as second parameter
     287             :  *
     288             :  * @pre handle must be as returned from elektraInvokeOpen()
     289             :  *
     290             :  * @return the return value of the invoked function (i.e. -1, 0, or 1)
     291             :  * @retval -2 if the function was not found.
     292             :  */
     293        1431 : int elektraInvoke2Args (ElektraInvokeHandle * handle, const char * elektraPluginFunctionName, KeySet * ks, Key * k)
     294             : {
     295        1431 :         if (!handle || !elektraPluginFunctionName) return -2;
     296             : 
     297             :         // If we cast this right away although the function wasn't found it will cause a deadlock
     298        1431 :         const void * rawFunc = elektraInvokeGetFunction (handle, elektraPluginFunctionName);
     299             : 
     300        1431 :         if (!rawFunc) return -2;
     301             : 
     302             :         typedef int (*elektra2Args) (Plugin *, KeySet *, Key *);
     303        1431 :         elektra2Args func = *(elektra2Args *) rawFunc;
     304             : 
     305        1431 :         return func (handle->plugin, ks, k);
     306             : }
     307             : 
     308             : /**
     309             :  * @brief Closes all affairs with the handle.
     310             :  *
     311             :  * The close function of the plugin will be called.
     312             :  *
     313             :  * @param handle the handle to work with
     314             :  * @param errorKey a key where error messages will be stored
     315             :  *
     316             :  * @pre handle must be as returned from elektraInvokeOpen()
     317             :  */
     318         380 : void elektraInvokeClose (ElektraInvokeHandle * handle, Key * errorKey)
     319             : {
     320         380 :         if (!handle)
     321             :         {
     322             :                 return;
     323             :         }
     324         380 :         int errorKeyMissing = !errorKey;
     325         380 :         if (errorKeyMissing)
     326             :         {
     327          71 :                 errorKey = keyNew (0, KEY_END);
     328             :         }
     329         380 :         elektraPluginClose (handle->plugin, errorKey);
     330         380 :         if (errorKeyMissing)
     331             :         {
     332          71 :                 keyDel (errorKey);
     333             :         }
     334         380 :         elektraModulesClose (handle->modules, NULL);
     335         380 :         ksDel (handle->modules);
     336         380 :         ksDel (handle->exports);
     337         380 :         elektraFree (handle);
     338             : }
     339             : 
     340             : /**
     341             :  * Invokes a deferable function on an invoke handle.
     342             :  * If the function is exported by the plugin it is directly invoked,
     343             :  * if the plugin supports deferring calls, the call is deferred.
     344             :  *
     345             :  * The parameters key set can be freed afterwards.
     346             :  *
     347             :  * @param  handle                    invoke handle
     348             :  * @param  elektraPluginFunctionName function name
     349             :  * @param  parameters                parameter key set
     350             :  * @retval 0 on success
     351             :  * @retval -1 when the call failed (direct call and deferring not available)
     352             :  */
     353           0 : int elektraInvokeCallDeferable (ElektraInvokeHandle * handle, const char * elektraPluginFunctionName, KeySet * parameters)
     354             : {
     355           0 :         if (!handle)
     356             :         {
     357             :                 return -1;
     358             :         }
     359           0 :         return elektraDeferredCall (handle->plugin, elektraPluginFunctionName, parameters);
     360             : }
     361             : 
     362             : /**
     363             :  * Execute deferred calls from list on given invoke handle.
     364             :  *
     365             :  * Used internally by plugins holding invoke handles.
     366             :  *
     367             :  * @param handle invoke handle
     368             :  * @param list   list
     369             :  */
     370           0 : void elektraInvokeExecuteDeferredCalls (ElektraInvokeHandle * handle, ElektraDeferredCallList * list)
     371             : {
     372           0 :         if (!handle)
     373             :         {
     374             :                 return;
     375             :         }
     376           0 :         elektraDeferredCallsExecute (handle->plugin, list);
     377             : }
     378             : 
     379             : /**
     380             :  * Call a deferable function on a plugin handle.
     381             :  * If the function is exported by the plugin it is directly invoked,
     382             :  * if the plugin supports deferring calls, the call is deferred.
     383             :  * If both is possible (function is exported and deferred calls are supported),
     384             :  * the function is directly called and the call is deferred (i.e. for nested plugins).
     385             :  *
     386             :  * @param  handle                    invoke handle
     387             :  * @param  elektraPluginFunctionName function name
     388             :  * @param  parameters                parameter key set. Can bee freed afterwards.
     389             :  * @retval 0 on success
     390             :  * @retval -1 when the call failed (direct call and deferring not available)
     391             :  */
     392         160 : int elektraDeferredCall (Plugin * handle, const char * elektraPluginFunctionName, KeySet * parameters)
     393             : {
     394         160 :         ELEKTRA_NOT_NULL (handle);
     395         160 :         ELEKTRA_NOT_NULL (elektraPluginFunctionName);
     396             : 
     397             :         int result;
     398         160 :         size_t direct = elektraPluginGetFunction (handle, elektraPluginFunctionName);
     399         160 :         if (direct)
     400             :         {
     401           0 :                 ElektraDeferredCallable directFn = (ElektraDeferredCallable) direct;
     402           0 :                 directFn (handle, parameters);
     403           0 :                 result = 0; // success
     404             :         }
     405             :         else
     406             :         {
     407             :                 // no direct call possible
     408             :                 result = -1;
     409             :         }
     410             : 
     411         160 :         size_t deferredCall = elektraPluginGetFunction (handle, "deferredCall");
     412         160 :         if (deferredCall)
     413             :         {
     414         160 :                 ElektraDeferredCall deferredCallFn = (ElektraDeferredCall) deferredCall;
     415         160 :                 deferredCallFn (handle, elektraPluginFunctionName, parameters);
     416         160 :                 result = 0; // success
     417             :         }
     418             :         else
     419             :         {
     420             :                 // deferred calls not possible
     421             :                 result = -1;
     422             :         }
     423             : 
     424         160 :         return result;
     425             : }
     426             : 
     427             : /**
     428             :  * Add a new deferred call to the deferred call list.
     429             :  *
     430             :  * Used internally by plugins.
     431             :  *
     432             :  * @param  list       deferred call list
     433             :  * @param  name       function name
     434             :  * @param  parameters function parameters
     435             :  * @retval 1 on success
     436             :  * @retval 0 when malloc failed
     437             :  */
     438         160 : int elektraDeferredCallAdd (ElektraDeferredCallList * list, const char * name, KeySet * parameters)
     439             : {
     440         160 :         ELEKTRA_NOT_NULL (list);
     441         160 :         ELEKTRA_NOT_NULL (name);
     442         160 :         _ElektraDeferredCall * item = elektraMalloc (sizeof *item);
     443         160 :         if (item == NULL)
     444             :         {
     445             :                 return 0;
     446             :         }
     447         160 :         item->name = elektraStrDup (name);
     448         160 :         item->parameters = ksDup (parameters);
     449         160 :         item->next = NULL;
     450             : 
     451         160 :         if (list->head == NULL)
     452             :         {
     453             :                 // Initialize list
     454           6 :                 list->head = list->last = item;
     455             :         }
     456             :         else
     457             :         {
     458             :                 // Make new item end of list
     459         154 :                 list->last->next = item;
     460         154 :                 list->last = item;
     461             :         }
     462             : 
     463             :         return 1;
     464             : }
     465             : 
     466             : /**
     467             :  * Create new deferred call list.
     468             :  *
     469             :  * The list needs to be deleted with elektraDeferredCallDeleteList().
     470             :  * Used internally by plugins.
     471             :  *
     472             :  * @return  new list
     473             :  */
     474       21064 : ElektraDeferredCallList * elektraDeferredCallCreateList (void)
     475             : {
     476       21064 :         ElektraDeferredCallList * list = elektraMalloc (sizeof *list);
     477       21064 :         if (list == NULL)
     478             :         {
     479             :                 return NULL;
     480             :         }
     481       21064 :         list->head = NULL;
     482       21064 :         list->last = NULL;
     483       21064 :         return list;
     484             : }
     485             : 
     486             : /**
     487             :  * Delete deferred call list.
     488             :  *
     489             :  * Used internally by plugins.
     490             :  *
     491             :  * @param list list
     492             :  */
     493       21064 : void elektraDeferredCallDeleteList (ElektraDeferredCallList * list)
     494             : {
     495       21064 :         ELEKTRA_NOT_NULL (list);
     496       21064 :         _ElektraDeferredCall * item = list->head;
     497       42288 :         while (item != NULL)
     498             :         {
     499         160 :                 elektraFree (item->name);
     500         160 :                 ksDel (item->parameters);
     501             : 
     502         160 :                 _ElektraDeferredCall * next = item->next;
     503         160 :                 elektraFree (item);
     504             : 
     505         160 :                 item = next;
     506             :         }
     507             : 
     508       21064 :         elektraFree (list);
     509       21064 : }
     510             : 
     511             : /**
     512             :  * Execute deferred calls on given plugin.
     513             :  *
     514             :  * Used internally by plugins.
     515             :  *
     516             :  * @param plugin plugin handle
     517             :  * @param list   list
     518             :  */
     519        8083 : void elektraDeferredCallsExecute (Plugin * plugin, ElektraDeferredCallList * list)
     520             : {
     521        8083 :         ELEKTRA_NOT_NULL (plugin);
     522        8083 :         ELEKTRA_NOT_NULL (list);
     523        8083 :         _ElektraDeferredCall * item = list->head;
     524       17086 :         while (item != NULL)
     525             :         {
     526         920 :                 size_t func = elektraPluginGetFunction (plugin, item->name);
     527         920 :                 if (!func)
     528             :                 {
     529         920 :                         item = item->next;
     530         920 :                         continue;
     531             :                 }
     532           0 :                 ElektraDeferredCallable callable = (ElektraDeferredCallable) func;
     533           0 :                 callable (plugin, item->parameters);
     534             : 
     535           0 :                 item = item->next;
     536             :         }
     537        8083 : }
     538             : 
     539             : /**
     540             :  * @}
     541             :  */

Generated by: LCOV version 1.13