LCOV - code coverage report
Current view: top level - src/libs/elektra - plugin.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 160 192 83.3 %
Date: 2019-09-12 12:28:41 Functions: 9 12 75.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Interna of plugin functionality.
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #ifdef HAVE_KDBCONFIG_H
      10             : #include "kdbconfig.h"
      11             : #endif
      12             : 
      13             : #if DEBUG && defined(HAVE_STDIO_H)
      14             : #include <stdio.h>
      15             : #endif
      16             : 
      17             : #ifdef HAVE_LOCALE_H
      18             : #include <locale.h>
      19             : #endif
      20             : 
      21             : #ifdef HAVE_STDLIB_H
      22             : #include <stdlib.h>
      23             : #endif
      24             : 
      25             : #ifdef HAVE_STDARG_H
      26             : #include <stdarg.h>
      27             : #endif
      28             : 
      29             : #ifdef HAVE_CTYPE_H
      30             : #include <ctype.h>
      31             : #endif
      32             : 
      33             : #ifdef HAVE_STRING_H
      34             : #include <string.h>
      35             : #endif
      36             : 
      37             : #ifdef HAVE_STDIO_H
      38             : #include <stdio.h>
      39             : #endif
      40             : 
      41             : #include <kdbassert.h>
      42             : #include <kdberrors.h>
      43             : #include <kdbinternal.h>
      44             : #include <kdbversion.h>
      45             : 
      46             : /**
      47             :  * @retval 1 and an allocated string of the pluginName if a new plugins should be created.
      48             :  * @retval 2 and an allocated string of the referenceName if an old plugin should be used
      49             :  * @retval 3 and both if a new plugin should be created and made available for later
      50             :  *         back referencing.
      51             :  * @retval -1 on error
      52             :  */
      53       53785 : int elektraProcessPlugin (Key * cur, int * pluginNumber, char ** pluginName, char ** referenceName, Key * errorKey)
      54             : {
      55       53785 :         const char * fullname = keyBaseName (cur);
      56       53785 :         size_t fullsize = keyGetBaseNameSize (cur);
      57             : 
      58       53785 :         if (fullname[0] != '#')
      59             :         {
      60           2 :                 ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Names of Plugins must start with a #. Pluginname: %s", fullname);
      61           2 :                 return -1;
      62             :         }
      63       53783 :         if (fullname[1] < '0' || fullname[1] > '9')
      64             :         {
      65           2 :                 ELEKTRA_ADD_INSTALLATION_WARNINGF (
      66             :                         errorKey, "Names of Plugins must start with the position number as second char. Pluginname: %s", fullname);
      67           2 :                 return -1;
      68             :         }
      69       53781 :         *pluginNumber = fullname[1] - '0';
      70       53781 :         if (*pluginNumber > NR_OF_PLUGINS)
      71             :         {
      72           0 :                 ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Tried to set more plugins than %d (NR_OF_PLUGINS). Pluginname: %s",
      73             :                                                    NR_OF_PLUGINS, fullname);
      74           0 :                 return -1;
      75             :         }
      76             : 
      77       53781 :         if (fullname[2] == '#')
      78             :         {
      79       53753 :                 char prefixReferenceName[] = "system/elektra/plugins/";
      80             : 
      81             :                 /* We have a back reference here */
      82       53753 :                 if (fullname[fullsize - 2] == '#')
      83             :                 {
      84       23627 :                         const char * iter = &fullname[3];
      85       23627 :                         size_t pluginNameSize = 1; /* For null character */
      86       23627 :                         size_t referenceNameSize = 0;
      87             :                         /* We will introduce a new plugin */
      88      247088 :                         while (*iter != '#')
      89             :                         {
      90      199834 :                                 ++iter;
      91      199834 :                                 ++pluginNameSize;
      92             :                         }
      93             : 
      94       23627 :                         *pluginName = elektraMalloc (pluginNameSize);
      95       23627 :                         strncpy (*pluginName, &fullname[3], pluginNameSize);
      96       23627 :                         (*pluginName)[pluginNameSize - 1] = 0;
      97             : 
      98       23627 :                         referenceNameSize = fullsize - pluginNameSize - 4;
      99       23627 :                         ++iter; /* advance to one after hash */
     100       23627 :                         *referenceName = elektraMalloc (referenceNameSize + sizeof (prefixReferenceName));
     101       23627 :                         strcpy (*referenceName, prefixReferenceName);
     102       23627 :                         strncat (*referenceName, iter, referenceNameSize);
     103       23627 :                         (*referenceName)[referenceNameSize + sizeof (prefixReferenceName) - 2] = 0;
     104             : 
     105       23627 :                         return 3;
     106             :                 }
     107             :                 else
     108             :                 {
     109             :                         /* We reference back to a plugin */
     110             : 
     111       30126 :                         *referenceName = elektraMalloc (fullsize - 3 + sizeof (prefixReferenceName) - 1);
     112       30126 :                         strcpy (*referenceName, prefixReferenceName);
     113       30126 :                         strncat (*referenceName, &fullname[3], fullsize - 3);
     114             : 
     115       30126 :                         return 2;
     116             :                 }
     117             :         }
     118             :         else
     119             :         {
     120          28 :                 *pluginName = elektraMalloc (fullsize - 2); /* don't alloc for #n */
     121          28 :                 strncpy (*pluginName, &fullname[2], fullsize - 2);
     122             : 
     123          28 :                 return 1;
     124             :         }
     125             : 
     126             :         /* Should not be reached */
     127             :         return 0;
     128             : }
     129             : 
     130             : /**
     131             :  * Load a plugin.
     132             :  *
     133             :  * The array of plugins must be set to 0.
     134             :  * Its length is NR_OF_PLUGINS.
     135             :  *
     136             :  * systemConfig will only be used, not deleted.
     137             :  *
     138             :  * @param config the config with the information how the
     139             :  *        plugins should be put together
     140             :  * @param systemConfig the shared (system) config for the plugins.
     141             :  *        Every plugin additional get this config.
     142             :  * @param global the global keyset of the KDB instance
     143             :  *
     144             :  * @retval -1 on failure
     145             :  */
     146       21125 : int elektraProcessPlugins (Plugin ** plugins, KeySet * modules, KeySet * referencePlugins, KeySet * config, KeySet * systemConfig,
     147             :                            KeySet * global, Key * errorKey)
     148             : {
     149             :         Key * root;
     150             :         Key * cur;
     151             : 
     152       21125 :         ksRewind (config);
     153             : 
     154       21125 :         root = ksNext (config);
     155             : 
     156       96011 :         while ((cur = ksNext (config)) != 0)
     157             :         {
     158       53761 :                 if (keyRel (root, cur) == 1)
     159             :                 {
     160       53761 :                         char * pluginName = 0;
     161       53761 :                         char * referenceName = 0;
     162       53761 :                         int pluginNumber = 0;
     163             : 
     164       53761 :                         if (elektraProcessPlugin (cur, &pluginNumber, &pluginName, &referenceName, errorKey) == -1)
     165             :                         {
     166           0 :                                 elektraFree (pluginName);
     167           0 :                                 elektraFree (referenceName);
     168           0 :                                 ksDel (config);
     169           0 :                                 return -1;
     170             :                         }
     171             : 
     172       53761 :                         if (pluginName)
     173             :                         {
     174       23641 :                                 Key * key = keyDup (cur);
     175       23641 :                                 keyAddBaseName (key, "config");
     176       23641 :                                 KeySet * cutConfig = ksCut (config, key);
     177       23641 :                                 keyDel (key);
     178             : 
     179       23641 :                                 KeySet * pluginConfig = elektraRenameKeys (cutConfig, "user");
     180       23641 :                                 ksDel (cutConfig);
     181       23641 :                                 if (!pluginConfig) return -1;
     182       23641 :                                 ksAppend (pluginConfig, systemConfig);
     183       23641 :                                 ksRewind (pluginConfig); /* TODO: bug ksAppend invalidates cursor */
     184             : 
     185             :                                 /* case 1, we create a new plugin,
     186             :                                    note that errorKey is not passed here, because it would set error information
     187             :                                    but we only want a warning instead. */
     188       23641 :                                 plugins[pluginNumber] = elektraPluginOpen (pluginName, modules, pluginConfig, errorKey);
     189       23641 :                                 if (!plugins[pluginNumber])
     190             :                                 {
     191           0 :                                         ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Could not load plugin %s in process plugin",
     192             :                                                                            pluginName);
     193             :                                         /* Loading plugin did not work */
     194           0 :                                         elektraFree (pluginName);
     195           0 :                                         elektraFree (referenceName);
     196           0 :                                         ksDel (config);
     197           0 :                                         return -1;
     198             :                                 }
     199       23641 :                                 plugins[pluginNumber]->global = global;
     200             : 
     201             :                                 /* case 2, we label it for later use */
     202       23641 :                                 if (referenceName)
     203       23619 :                                         ksAppendKey (referencePlugins,
     204             :                                                      keyNew (referenceName, KEY_BINARY, KEY_SIZE, sizeof (plugins[pluginNumber]), KEY_VALUE,
     205             :                                                              &plugins[pluginNumber], KEY_END));
     206             :                         }
     207             :                         else
     208             :                         {
     209             :                                 /* case 3, we use an existing plugin */
     210       30120 :                                 Key * lookup = ksLookup (referencePlugins, keyNew (referenceName, KEY_END), KDB_O_DEL);
     211       30120 :                                 if (!lookup)
     212             :                                 {
     213           0 :                                         ELEKTRA_ADD_INTERNAL_WARNINGF (errorKey, "Could not reference back to plugin %s", referenceName);
     214             :                                         /* Getting a reference plugin at a previous stage did not work.
     215             :                                         Note that this check is necessary, because loading the plugin could
     216             :                                         fail for example at errorplugins and at a later point, for example
     217             :                                         at setplugins it is tried to refer to that.*/
     218           0 :                                         elektraFree (referenceName);
     219           0 :                                         ksDel (config);
     220           0 :                                         return -1;
     221             :                                 }
     222       30120 :                                 plugins[pluginNumber] = *(Plugin **) keyValue (lookup);
     223       30120 :                                 ++plugins[pluginNumber]->refcounter;
     224             :                         }
     225       53761 :                         elektraFree (pluginName);
     226       53761 :                         elektraFree (referenceName);
     227             :                 }
     228             :                 else
     229             :                 {
     230           0 :                         ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Unknown additional entries in plugin configuration: %s",
     231             :                                                            keyString (cur));
     232             :                 }
     233             :         }
     234             : 
     235       21125 :         ksDel (config);
     236       21125 :         return 0;
     237             : }
     238             : 
     239             : /**
     240             :  * Opens a plugin.
     241             :  *
     242             :  * The config will be used as is. So be sure to transfer ownership
     243             :  * of the config to it, with e.g. ksDup().
     244             :  * elektraPluginClose() will delete the config.
     245             :  *
     246             :  * @return a pointer to a new created plugin or 0 on error
     247             :  */
     248      190574 : Plugin * elektraPluginOpen (const char * name, KeySet * modules, KeySet * config, Key * errorKey)
     249             : {
     250      190574 :         Plugin * handle = 0;
     251             :         const char * n;
     252             : 
     253      190574 :         elektraPluginFactory pluginFactory = 0;
     254             : 
     255      190574 :         if (!name || name[0] == '\0')
     256             :         {
     257           4 :                 ELEKTRA_ADD_INSTALLATION_WARNING (errorKey, "Not a valid name supplied for a plugin: name is null or empty");
     258           4 :                 goto err_clup;
     259             :         }
     260             : 
     261             :         n = name;
     262      190602 :         while (*n != '\0')
     263             :         {
     264      190596 :                 if (*n == '/')
     265          32 :                         ++n;
     266             :                 else
     267             :                         break;
     268             :         }
     269             : 
     270      190570 :         if (*n == '\0')
     271             :         {
     272           6 :                 ELEKTRA_ADD_INSTALLATION_WARNING (errorKey, "Not a valid name supplied for a plugin: name contained slashes only");
     273           6 :                 goto err_clup;
     274             :         }
     275             : 
     276      190564 :         pluginFactory = elektraModulesLoad (modules, name, errorKey);
     277      190564 :         if (pluginFactory == 0)
     278             :         {
     279             :                 /* warning already set by elektraModulesLoad */
     280             :                 goto err_clup;
     281             :         }
     282             : 
     283      169593 :         handle = pluginFactory ();
     284      169593 :         if (handle == 0)
     285             :         {
     286           0 :                 ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Could not call function exported by ELEKTRA_PLUGIN_EXPORT: %s", name);
     287           0 :                 goto err_clup;
     288             :         }
     289             : 
     290             :         /* init reference counting */
     291      169593 :         handle->refcounter = 1;
     292      169593 :         handle->config = config;
     293      169593 :         config = 0; // for err_clup case
     294             : 
     295             :         /* let the plugin initialize itself */
     296      169593 :         if (handle->kdbOpen)
     297             :         {
     298      134257 :                 if ((handle->kdbOpen (handle, errorKey)) == -1)
     299             :                 {
     300          15 :                         ELEKTRA_ADD_PLUGIN_MISBEHAVIOR_WARNINGF (
     301             :                                 errorKey,
     302             :                                 "Open of plugin returned unsuccessfully: %s. Reason contains plugin, see other warnings for details", name);
     303          15 :                         elektraPluginClose (handle, errorKey);
     304          15 :                         goto err_clup;
     305             :                 }
     306             :         }
     307             : 
     308             :         ELEKTRA_LOG_DEBUG ("Finished loading plugin %s", name);
     309             :         return handle;
     310             : 
     311             : err_clup:
     312             :         ELEKTRA_LOG ("Failed to load plugin %s\n", name);
     313       20996 :         ksDel (config);
     314       20996 :         return 0;
     315             : }
     316             : 
     317     3704309 : int elektraPluginClose (Plugin * handle, Key * errorKey)
     318             : {
     319     3704309 :         int rc = 0;
     320             : 
     321     3704309 :         if (!handle) return 0;
     322             : 
     323      441871 :         --handle->refcounter;
     324             : 
     325             :         /* Check if we have the last reference on the plugin (unsigned!) */
     326      441871 :         if (handle->refcounter > 0) return 0;
     327             : 
     328      180115 :         if (handle->kdbClose)
     329             :         {
     330      131054 :                 rc = handle->kdbClose (handle, errorKey);
     331      131054 :                 if (rc == -1) ELEKTRA_ADD_RESOURCE_WARNING (errorKey, "Method 'kdbClose()' failed");
     332             :         }
     333             : 
     334      180115 :         ksDel (handle->config);
     335      180115 :         elektraFree (handle);
     336             : 
     337      180115 :         return rc;
     338             : }
     339             : 
     340             : 
     341             : /**
     342             :  * Retrieves a function exported by a plugin.
     343             :  *
     344             :  * @param  plugin Plugin handle
     345             :  * @param  name   Function name
     346             :  * @return        Pointer to function. NULL if function not found or not enough memory available
     347             :  */
     348        1474 : size_t elektraPluginGetFunction (Plugin * plugin, const char * name)
     349             : {
     350        1474 :         ELEKTRA_NOT_NULL (plugin);
     351        1474 :         ELEKTRA_NOT_NULL (name);
     352             : 
     353        1474 :         KeySet * exports = ksNew (0, KS_END);
     354        1474 :         Key * pk = keyNew ("system/elektra/modules", KEY_END);
     355        1474 :         keyAddBaseName (pk, plugin->name);
     356        1474 :         plugin->kdbGet (plugin, exports, pk);
     357        1474 :         ksRewind (exports);
     358        1474 :         keyAddBaseName (pk, "exports");
     359        1474 :         keyAddBaseName (pk, name);
     360        1474 :         Key * keyFunction = ksLookup (exports, pk, 0);
     361        1474 :         if (!keyFunction)
     362             :         {
     363             :                 ELEKTRA_LOG_DEBUG ("function \"%s\" from plugin \"%s\" not found", name, plugin->name);
     364        1080 :                 ksDel (exports);
     365        1080 :                 keyDel (pk);
     366        1080 :                 return 0;
     367             :         }
     368             : 
     369             :         size_t * buffer;
     370         394 :         size_t bufferSize = keyGetValueSize (keyFunction);
     371         394 :         buffer = elektraMalloc (bufferSize);
     372         394 :         if (buffer)
     373             :         {
     374         394 :                 int result = keyGetBinary (keyFunction, buffer, bufferSize);
     375         394 :                 if (result == -1 || buffer == NULL)
     376             :                 {
     377             :                         ELEKTRA_LOG_WARNING ("could not get function \"%s\" from plugin \"%s\"", name, plugin->name);
     378             :                         return 0;
     379             :                 }
     380             :         }
     381             : 
     382         394 :         size_t func = *buffer;
     383             : 
     384         394 :         elektraFree (buffer);
     385         394 :         ksDel (exports);
     386         394 :         keyDel (pk);
     387             : 
     388         394 :         return func;
     389             : }
     390             : 
     391           0 : static int elektraMissingGet (Plugin * plugin ELEKTRA_UNUSED, KeySet * ks ELEKTRA_UNUSED, Key * error)
     392             : {
     393           0 :         ELEKTRA_SET_INSTALLATION_ERRORF (error, "Tried to get a key from a missing backend: %s", keyName (error));
     394           0 :         return -1;
     395             : }
     396             : 
     397           0 : static int elektraMissingSet (Plugin * plugin ELEKTRA_UNUSED, KeySet * ks ELEKTRA_UNUSED, Key * error)
     398             : {
     399           0 :         ELEKTRA_SET_INSTALLATION_ERRORF (error, "Tried to set a key from a missing backend: %s", keyName (error));
     400           0 :         return -1;
     401             : }
     402             : 
     403             : 
     404           0 : Plugin * elektraPluginMissing (void)
     405             : {
     406             :         Plugin * returned;
     407             : 
     408           0 :         returned = elektraCalloc (sizeof (struct _Plugin));
     409           0 :         if (!returned) return 0;
     410             : 
     411           0 :         returned->name = "missing";
     412           0 :         returned->kdbGet = elektraMissingGet;
     413           0 :         returned->kdbSet = elektraMissingSet;
     414           0 :         return returned;
     415             : }
     416             : 
     417         196 : static int elektraVersionGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * error ELEKTRA_UNUSED)
     418             : {
     419         196 :         KeySet * info = elektraVersionKeySet ();
     420         196 :         keySetMeta (info->array[0], "restrict/write", "1");
     421         196 :         keySetMeta (info->array[0], "restrict/remove", "1");
     422        2352 :         for (size_t i = 1; i < info->size; i++)
     423             :         {
     424        2156 :                 keyCopyAllMeta (info->array[i], info->array[0]);
     425             :         }
     426         196 :         ksAppend (returned, info);
     427         196 :         ksDel (info);
     428         196 :         return 1;
     429             : }
     430             : 
     431           4 : static int elektraVersionSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * error)
     432             : {
     433           4 :         KeySet * info = elektraVersionKeySet ();
     434           4 :         ELEKTRA_SET_ERROR_READ_ONLY (info, returned, error);
     435           0 :         return 0;
     436             : }
     437             : 
     438       10523 : Plugin * elektraPluginVersion (void)
     439             : {
     440             :         Plugin * returned;
     441             : 
     442       10523 :         returned = elektraCalloc (sizeof (struct _Plugin));
     443       10523 :         if (!returned) return 0;
     444             : 
     445       10523 :         returned->name = "version";
     446       10523 :         returned->kdbGet = elektraVersionGet;
     447       10523 :         returned->kdbSet = elektraVersionSet;
     448       10523 :         return returned;
     449             : }
     450             : 
     451             : /**
     452             :  * Searches the global plugins for a given plugin name.
     453             :  *
     454             :  * NOTE: if the list plugin occupies the prerollback position,
     455             :  * this queries the list plugin first, and only if we don't find
     456             :  * anything there, we look directly in the global plugins array
     457             :  *
     458             :  * @param handle     The KDB handle to search
     459             :  * @param pluginName The plugin name to look for
     460             :  *
     461             :  * @return the plugin handle, if found or NULL otherwise
     462             :  */
     463          36 : Plugin * elektraPluginFindGlobal (KDB * handle, const char * pluginName)
     464             : {
     465          36 :         Plugin * listPlugin = handle->globalPlugins[PREROLLBACK][MAXONCE]; // take any position
     466          36 :         if (listPlugin != NULL && strcmp (listPlugin->name, "list") == 0)
     467             :         {
     468             :                 typedef Plugin * (*findPluginFun) (Plugin *, const char *);
     469          36 :                 findPluginFun listFindPlugin = (findPluginFun) elektraPluginGetFunction (listPlugin, "findplugin");
     470          36 :                 Plugin * plugin = listFindPlugin (listPlugin, pluginName);
     471          36 :                 if (plugin != NULL)
     472             :                 {
     473             :                         return plugin;
     474             :                 }
     475             :         }
     476             : 
     477         266 :         for (GlobalpluginPositions pos = 0; pos < NR_GLOBAL_POSITIONS; ++pos)
     478             :         {
     479        1008 :                 for (GlobalpluginSubPositions sub = 0; sub < NR_GLOBAL_SUBPOSITIONS; ++sub)
     480             :                 {
     481        1008 :                         Plugin * plugin = handle->globalPlugins[pos][sub];
     482        1008 :                         if (plugin != NULL && strcmp (plugin->name, pluginName) == 0)
     483             :                         {
     484             :                                 return plugin;
     485             :                         }
     486             :                 }
     487             :         }
     488             : 
     489             :         return NULL;
     490             : }

Generated by: LCOV version 1.13