LCOV - code coverage report
Current view: top level - src/plugins/list - list.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 444 494 89.9 %
Date: 2019-09-12 12:28:41 Functions: 18 21 85.7 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Source for list plugin
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : 
      11             : #ifndef HAVE_KDBCONFIG
      12             : #include "kdbconfig.h"
      13             : #endif
      14             : 
      15             : #include "list.h"
      16             : #include <kdbassert.h>
      17             : #include <kdbease.h>
      18             : #include <kdberrors.h>
      19             : #include <kdbinternal.h>
      20             : #include <kdbinvoke.h>
      21             : #include <kdbmodule.h>
      22             : #include <kdbos.h>
      23             : #include <stdio.h>
      24             : #include <stdlib.h>
      25             : #include <string.h>
      26             : 
      27             : 
      28             : typedef enum
      29             : {
      30             :         preGetStorage = 0,
      31             :         procGetStorage,
      32             :         postGetStorage,
      33             :         postGetCleanup,
      34             :         getEnd
      35             : } GetPlacements;
      36             : 
      37             : static const char * getStrings[] = { "pregetstorage", "procgetstorage", "postgetstorage", "postgetcleanup" };
      38             : 
      39             : typedef enum
      40             : {
      41             :         preSetStorage = 0,
      42             :         preSetCleanup,
      43             :         preCommit,
      44             :         postCommit,
      45             :         setEnd
      46             : } SetPlacements;
      47             : 
      48             : static const char * setStrings[] = { "presetstorage", "presetcleanup", "precommit", "postcommit" };
      49             : 
      50             : typedef enum
      51             : {
      52             :         preRollback = 0,
      53             :         postRollback,
      54             :         errEnd
      55             : } ErrPlacements;
      56             : 
      57             : static const char * errStrings[] = { "prerollback", "postrollback" };
      58             : 
      59             : typedef enum
      60             : {
      61             :         GET,
      62             :         SET,
      63             :         ERR
      64             : } OP;
      65             : 
      66             : typedef struct
      67             : {
      68             :         // keep track of placements
      69             :         ErrPlacements errCurrent;
      70             :         SetPlacements setCurrent;
      71             :         GetPlacements getCurrent;
      72             : 
      73             :         ErrPlacements errPlacements[2]; // prerollback and postrollback
      74             :         SetPlacements setPlacements[4]; // presetstorage, presetcleanup, precommit and postcommit
      75             :         GetPlacements getPlacements[4]; // pregetstorage, procgetstorage, postgetstorage, postgetclenaup
      76             : 
      77             :         // each keyset contains the list of plugin names for a given placement
      78             :         KeySet * setKS[4];
      79             :         KeySet * errKS[2];
      80             :         KeySet * getKS[4];
      81             :         KeySet * plugins;
      82             :         KeySet * modules;
      83             : 
      84             :         ElektraDeferredCallList * deferredCalls;
      85             : 
      86             : } Placements;
      87             : 
      88             : static char lastIndex[ELEKTRA_MAX_ARRAY_SIZE];
      89             : 
      90       21130 : static int listParseConfiguration (Placements * placements, KeySet * config)
      91             : {
      92             :         Key * cur;
      93       21130 :         Key * key = ksLookupByName (config, "/plugins", 0);
      94       21130 :         KeySet * cutKS = ksCut (config, key);
      95       21130 :         ksRewind (cutKS);
      96       21130 :         if (ksGetSize (cutKS) < 2)
      97             :         {
      98       10549 :                 ksDel (cutKS);
      99       10549 :                 return 0;
     100             :         }
     101             :         int rc = 0;
     102       42582 :         while ((cur = ksNext (cutKS)) != NULL)
     103             :         {
     104       32001 :                 if (keyRel (key, cur) != 1)
     105             :                 {
     106       21354 :                         continue;
     107             :                 }
     108       10647 :                 if (keyBaseName (cur)[0] == '#')
     109             :                 {
     110       10647 :                         if (strcmp (lastIndex, keyBaseName (cur)) < 0)
     111             :                         {
     112        3680 :                                 snprintf (lastIndex, ELEKTRA_MAX_ARRAY_SIZE, "%s", keyBaseName (cur));
     113             :                         }
     114             :                 }
     115             :                 Key * sub;
     116       10647 :                 Key * lookup = keyDup (cur);
     117       10647 :                 keyAddBaseName (lookup, "placements");
     118       10647 :                 keyAddBaseName (lookup, "set");
     119       10647 :                 sub = ksLookup (cutKS, lookup, 0);
     120       10647 :                 if (sub)
     121             :                 {
     122       10647 :                         const char * setString = keyString (sub);
     123       10647 :                         SetPlacements setPlacement = preSetStorage;
     124       63882 :                         while (setPlacement != setEnd)
     125             :                         {
     126       42588 :                                 if (strstr (setString, setStrings[setPlacement]))
     127             :                                 {
     128       10617 :                                         rc = 1;
     129       10617 :                                         ksAppendKey (placements->setKS[setPlacement], keyDup (cur));
     130             :                                 }
     131       42588 :                                 ++setPlacement;
     132             :                         }
     133             :                 }
     134       10647 :                 keySetBaseName (lookup, "get");
     135       10647 :                 sub = ksLookup (cutKS, lookup, 0);
     136       10647 :                 if (sub)
     137             :                 {
     138       10647 :                         const char * getString = keyString (sub);
     139       10647 :                         GetPlacements getPlacement = preGetStorage;
     140       63882 :                         while (getPlacement != getEnd)
     141             :                         {
     142       42588 :                                 if (strstr (getString, getStrings[getPlacement]))
     143             :                                 {
     144       10655 :                                         rc = 1;
     145       10655 :                                         ksAppendKey (placements->getKS[getPlacement], keyDup (cur));
     146             :                                 }
     147       42588 :                                 ++getPlacement;
     148             :                         }
     149             :                 }
     150       10647 :                 keySetBaseName (lookup, "error");
     151       10647 :                 sub = ksLookup (cutKS, lookup, 0);
     152       10647 :                 if (sub)
     153             :                 {
     154         126 :                         const char * errString = keyString (sub);
     155         126 :                         ErrPlacements errPlacement = preRollback;
     156         504 :                         while (errPlacement != errEnd)
     157             :                         {
     158         252 :                                 if (strstr (errString, errStrings[errPlacement]))
     159             :                                 {
     160          12 :                                         rc = 1;
     161          12 :                                         ksAppendKey (placements->errKS[errPlacement], keyDup (cur));
     162             :                                 }
     163         252 :                                 ++errPlacement;
     164             :                         }
     165             :                 }
     166       10647 :                 keyDel (lookup);
     167             :         }
     168       10581 :         ksDel (cutKS);
     169       10581 :         return rc;
     170             : }
     171             : 
     172         160 : void elektraListDeferredCall (Plugin * plugin, const char * name, KeySet * parameters)
     173             : {
     174         160 :         Placements * placements = elektraPluginGetData (plugin);
     175         160 :         ELEKTRA_NOT_NULL (placements);
     176         160 :         elektraDeferredCallAdd (placements->deferredCalls, name, parameters);
     177             : 
     178             :         // Execute call immediately on already loaded plugins
     179         160 :         ksRewind (placements->plugins);
     180             :         Key * current;
     181         400 :         while ((current = ksNext (placements->plugins)) != NULL)
     182             :         {
     183             :                 Plugin * slave;
     184          80 :                 slave = *(Plugin **) keyValue (current);
     185          80 :                 elektraDeferredCallsExecute (slave, placements->deferredCalls);
     186             :         }
     187         160 : }
     188             : 
     189       21130 : int elektraListOpen (Plugin * handle, Key * errorKey ELEKTRA_UNUSED)
     190             : {
     191             : 
     192       21130 :         Placements * placements = (Placements *) elektraPluginGetData (handle);
     193       21130 :         if (!placements)
     194             :         {
     195       21064 :                 placements = (Placements *) elektraMalloc (sizeof (Placements));
     196       21064 :                 memset (placements, 0, sizeof (Placements));
     197       21064 :                 placements->errCurrent = preRollback;
     198       21064 :                 placements->setCurrent = preSetStorage;
     199       21064 :                 placements->getCurrent = preGetStorage;
     200       21064 :                 placements->getKS[0] = ksNew (0, KS_END);
     201       21064 :                 placements->getKS[1] = ksNew (0, KS_END);
     202       21064 :                 placements->getKS[2] = ksNew (0, KS_END);
     203       21064 :                 placements->setKS[0] = ksNew (0, KS_END);
     204       21064 :                 placements->setKS[1] = ksNew (0, KS_END);
     205       21064 :                 placements->setKS[2] = ksNew (0, KS_END);
     206       21064 :                 placements->setKS[3] = ksNew (0, KS_END);
     207       21064 :                 placements->errKS[0] = ksNew (0, KS_END);
     208       21064 :                 placements->errKS[1] = ksNew (0, KS_END);
     209       21064 :                 placements->plugins = ksNew (0, KS_END);
     210       21064 :                 placements->modules = ksNew (0, KS_END);
     211       21064 :                 placements->deferredCalls = elektraDeferredCallCreateList ();
     212             :         }
     213       21130 :         elektraPluginSetData (handle, placements);
     214             : 
     215       21130 :         elektraModulesInit (placements->modules, NULL);
     216       21130 :         KeySet * config = ksDup (elektraPluginGetConfig (handle));
     217       21130 :         ksRewind (config);
     218       21130 :         Key * key = ksLookupByName (config, "/placements/set", 0);
     219       21130 :         if (key)
     220             :         {
     221       10591 :                 const char * setString = keyString (key);
     222       10591 :                 SetPlacements setPlacement = preSetStorage;
     223       63546 :                 while (setPlacement != setEnd)
     224             :                 {
     225       42364 :                         if (strstr (setString, setStrings[setPlacement]))
     226             :                         {
     227       31769 :                                 placements->setPlacements[setPlacement] = 1;
     228             :                         }
     229       42364 :                         ++setPlacement;
     230             :                 }
     231             :         }
     232       21130 :         key = ksLookupByName (config, "/placements/get", 0);
     233       21130 :         if (key)
     234             :         {
     235       10591 :                 const char * getString = keyString (key);
     236       10591 :                 GetPlacements getPlacement = preGetStorage;
     237       63546 :                 while (getPlacement != getEnd)
     238             :                 {
     239       42364 :                         if (strstr (getString, getStrings[getPlacement]))
     240             :                         {
     241       31731 :                                 placements->getPlacements[getPlacement] = 1;
     242             :                         }
     243       42364 :                         ++getPlacement;
     244             :                 }
     245             :         }
     246       21130 :         key = ksLookupByName (config, "/placements/error", 0);
     247       21130 :         if (key)
     248             :         {
     249       10589 :                 const char * errString = keyString (key);
     250       10589 :                 ErrPlacements errPlacement = preRollback;
     251       42356 :                 while (errPlacement != errEnd)
     252             :                 {
     253       21178 :                         if (strstr (errString, errStrings[errPlacement]))
     254             :                         {
     255       21178 :                                 placements->errPlacements[errPlacement] = 1;
     256             :                         }
     257       21178 :                         ++errPlacement;
     258             :                 }
     259             :         }
     260       21130 :         listParseConfiguration (placements, config);
     261       21130 :         ksDel (config);
     262       21130 :         return 1; /* success */
     263             : }
     264             : 
     265       21064 : int elektraListClose (Plugin * handle, Key * errorKey)
     266             : {
     267             :         /* free all plugin resources and shut it down */
     268       21064 :         Placements * placements = elektraPluginGetData (handle);
     269       21064 :         ksDel (placements->getKS[0]);
     270       21064 :         ksDel (placements->getKS[1]);
     271       21064 :         ksDel (placements->getKS[2]);
     272       21064 :         ksDel (placements->setKS[0]);
     273       21064 :         ksDel (placements->setKS[1]);
     274       21064 :         ksDel (placements->setKS[2]);
     275       21064 :         ksDel (placements->setKS[3]);
     276       21064 :         ksDel (placements->errKS[0]);
     277       21064 :         ksDel (placements->errKS[1]);
     278             :         Key * cur;
     279       21064 :         ksRewind (placements->plugins);
     280       47630 :         while ((cur = ksNext (placements->plugins)) != NULL)
     281             :         {
     282             :                 Plugin * slave;
     283        5502 :                 slave = *(Plugin **) keyValue (cur);
     284        5502 :                 elektraPluginClose (slave, errorKey);
     285             :         }
     286       21064 :         ksDel (placements->plugins);
     287       21064 :         elektraModulesClose (placements->modules, NULL);
     288       21064 :         ksDel (placements->modules);
     289       21064 :         elektraDeferredCallDeleteList (placements->deferredCalls);
     290       21064 :         elektraFree (placements);
     291       21064 :         elektraPluginSetData (handle, 0);
     292       21064 :         return 1; /* success */
     293             : }
     294             : 
     295       23663 : static int runPlugins (KeySet * pluginKS, KeySet * modules, KeySet * plugins, KeySet * configOrig, KeySet * returned, KeySet * global,
     296             :                        Key * parentKey, OP op, Key * (*traversalFunction) (KeySet *), ElektraDeferredCallList * deferredCalls)
     297             : {
     298             :         Key * current;
     299             : 
     300       23663 :         Plugin * slave = NULL;
     301             : 
     302             :         // for every plugin in our list: load it, run the expected function (set/get/error) and close it again
     303       23663 :         KeySet * realPluginConfig = NULL;
     304       55326 :         while ((current = traversalFunction (pluginKS)) != NULL)
     305             :         {
     306        8003 :                 const char * name = keyString (current);
     307             : 
     308        8003 :                 Key * handleKey = keyDup (current);
     309        8003 :                 keyAddName (handleKey, "handle");
     310        8003 :                 Key * handleLookup = ksLookup (configOrig, handleKey, 0);
     311        8003 :                 keyDel (handleKey);
     312        8003 :                 if (handleLookup)
     313             :                 {
     314           0 :                         slave = *(Plugin **) keyValue (handleLookup);
     315             :                 }
     316             :                 else
     317             :                 {
     318        8003 :                         Key * searchKey = keyNew ("/", KEY_END);
     319        8003 :                         keyAddBaseName (searchKey, name);
     320        8003 :                         Key * lookup = ksLookup (plugins, searchKey, 0);
     321        8003 :                         keyDel (searchKey);
     322        8003 :                         if (lookup)
     323             :                         {
     324        2511 :                                 slave = *(Plugin **) keyValue (lookup);
     325             :                         }
     326             :                         else
     327             :                         {
     328        5492 :                                 Key * userCutPoint = keyNew ("user", 0);
     329        5492 :                                 Key * globalConfCutPoint = keyNew ("/config", 0);
     330        5492 :                                 KeySet * config = ksDup (configOrig);
     331        5492 :                                 KeySet * globalConfigAll = ksCut (config, globalConfCutPoint);
     332        5492 :                                 KeySet * userConfigAll = ksCut (config, userCutPoint);
     333        5492 :                                 KeySet * pluginConfig = ksCut (userConfigAll, current);
     334             :                                 // replace "user/plugins/#X" with "user/"
     335        5492 :                                 KeySet * pluginConfigWithConfigPrefix = elektraRenameKeys (pluginConfig, "user");
     336        5492 :                                 ksDel (pluginConfig);
     337             :                                 // append config below "/config" to all plugins
     338        5492 :                                 KeySet * globalPluginConfig = elektraRenameKeys (globalConfigAll, "user/config");
     339        5492 :                                 ksAppend (pluginConfigWithConfigPrefix, globalPluginConfig);
     340        5492 :                                 ksDel (globalPluginConfig);
     341             :                                 // remove "placements" from plugin config
     342        5492 :                                 Key * toRemove = keyNew ("user/placements", 0);
     343        5492 :                                 ksDel (ksCut (pluginConfigWithConfigPrefix, toRemove));
     344        5492 :                                 ksRewind (pluginConfigWithConfigPrefix);
     345        5492 :                                 ksDel (globalConfigAll);
     346        5492 :                                 ksDel (userConfigAll);
     347        5492 :                                 ksDel (config);
     348        5492 :                                 keyDel (userCutPoint);
     349        5492 :                                 keyDel (globalConfCutPoint);
     350        5492 :                                 keyDel (toRemove);
     351             :                                 // replace "user/config/" with "user/"
     352        5492 :                                 realPluginConfig = elektraRenameKeys (pluginConfigWithConfigPrefix, "user");
     353        5492 :                                 ksDel (pluginConfigWithConfigPrefix);
     354        5492 :                                 slave = elektraPluginOpen (name, modules, ksDup (realPluginConfig), parentKey);
     355        5492 :                                 ksDel (realPluginConfig);
     356        5492 :                                 if (!slave)
     357             :                                 {
     358           0 :                                         ksDel (configOrig);
     359           0 :                                         return -1;
     360             :                                 }
     361        5492 :                                 slave->global = global;
     362        5492 :                                 Key * slaveKey = keyNew (name, KEY_BINARY, KEY_SIZE, sizeof (Plugin *), KEY_VALUE, &slave, KEY_END);
     363        5492 :                                 keySetName (slaveKey, "/");
     364        5492 :                                 keyAddBaseName (slaveKey, name);
     365        5492 :                                 ksAppendKey (plugins, keyDup (slaveKey));
     366        5492 :                                 keyDel (slaveKey);
     367             :                         }
     368             :                 }
     369        8003 :                 elektraDeferredCallsExecute (slave, deferredCalls);
     370             : 
     371        8003 :                 if ((op == GET && slave->kdbGet && (slave->kdbGet (slave, returned, parentKey)) == -1) ||
     372        2724 :                     (op == SET && slave->kdbSet && (slave->kdbSet (slave, returned, parentKey)) == -1) ||
     373           0 :                     (op == ERR && slave->kdbError && (slave->kdbError (slave, returned, parentKey)) == -1))
     374             :                 {
     375           3 :                         ksDel (configOrig);
     376           3 :                         return -1;
     377             :                 }
     378             :         }
     379       23660 :         ksDel (configOrig);
     380       23660 :         return 1;
     381             : }
     382             : 
     383       16275 : int elektraListGet (Plugin * handle, KeySet * returned, Key * parentKey)
     384             : {
     385       16275 :         if (!strcmp (keyName (parentKey), "system/elektra/modules/list"))
     386             :         {
     387         614 :                 KeySet * contract =
     388         614 :                         ksNew (30, keyNew ("system/elektra/modules/list", KEY_VALUE, "list plugin waits for your orders", KEY_END),
     389             :                                keyNew ("system/elektra/modules/list/exports", KEY_END),
     390             :                                keyNew ("system/elektra/modules/list/exports/open", KEY_FUNC, elektraListOpen, KEY_END),
     391             :                                keyNew ("system/elektra/modules/list/exports/close", KEY_FUNC, elektraListClose, KEY_END),
     392             :                                keyNew ("system/elektra/modules/list/exports/get", KEY_FUNC, elektraListGet, KEY_END),
     393             :                                keyNew ("system/elektra/modules/list/exports/set", KEY_FUNC, elektraListSet, KEY_END),
     394             :                                keyNew ("system/elektra/modules/list/exports/error", KEY_FUNC, elektraListError, KEY_END),
     395             :                                keyNew ("system/elektra/modules/list/exports/addPlugin", KEY_FUNC, elektraListAddPlugin, KEY_END),
     396             :                                keyNew ("system/elektra/modules/list/exports/editPlugin", KEY_FUNC, elektraListEditPlugin, KEY_END),
     397             :                                keyNew ("system/elektra/modules/list/exports/deferredCall", KEY_FUNC, elektraListDeferredCall, KEY_END),
     398             :                                keyNew ("system/elektra/modules/list/exports/mountplugin", KEY_FUNC, elektraListMountPlugin, KEY_END),
     399             :                                keyNew ("system/elektra/modules/list/exports/unmountplugin", KEY_FUNC, elektraListUnmountPlugin, KEY_END),
     400             :                                keyNew ("system/elektra/modules/list/exports/findplugin", KEY_FUNC, elektraListFindPlugin, KEY_END),
     401             : #include ELEKTRA_README
     402             :                                keyNew ("system/elektra/modules/list/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
     403         614 :                 ksAppend (returned, contract);
     404         614 :                 ksDel (contract);
     405             : 
     406         614 :                 return 1;
     407             :         }
     408       15661 :         Placements * placements = elektraPluginGetData (handle);
     409       15661 :         KeySet * config = elektraPluginGetConfig (handle);
     410       15661 :         GetPlacements currentPlacement = placements->getCurrent;
     411       15661 :         KeySet * pluginKS = ksDup ((placements)->getKS[currentPlacement]);
     412       15661 :         ksRewind (pluginKS);
     413       15661 :         int ret = runPlugins (pluginKS, placements->modules, placements->plugins, ksDup (config), returned,
     414             :                               elektraPluginGetGlobalKeySet (handle), parentKey, GET, ksNext, placements->deferredCalls);
     415       15661 :         placements->getCurrent = ((++currentPlacement) % getEnd);
     416       36582 :         while (currentPlacement < getEnd && !placements->getPlacements[currentPlacement])
     417             :         {
     418        5260 :                 placements->getCurrent = ((++currentPlacement) % getEnd);
     419             :         }
     420       15661 :         ksDel (pluginKS);
     421       15661 :         return ret;
     422             : }
     423             : 
     424        7810 : int elektraListSet (Plugin * handle, KeySet * returned, Key * parentKey)
     425             : {
     426        7810 :         Placements * placements = elektraPluginGetData (handle);
     427        7810 :         KeySet * config = elektraPluginGetConfig (handle);
     428        7810 :         SetPlacements currentPlacement = placements->setCurrent;
     429        7810 :         KeySet * pluginKS = ksDup ((placements)->setKS[currentPlacement]);
     430        7810 :         ksRewind (pluginKS);
     431        7810 :         int ret = 0;
     432        7810 :         ret = runPlugins (pluginKS, placements->modules, placements->plugins, ksDup (config), returned,
     433             :                           elektraPluginGetGlobalKeySet (handle), parentKey, SET, ksPop, placements->deferredCalls);
     434        7810 :         placements->setCurrent = ((++currentPlacement) % setEnd);
     435       18340 :         while (currentPlacement < setEnd && !placements->setPlacements[currentPlacement])
     436             :         {
     437        2720 :                 placements->setCurrent = ((++currentPlacement) % setEnd);
     438             :         }
     439        7810 :         elektraPluginSetData (handle, placements);
     440        7810 :         ksDel (pluginKS);
     441             : 
     442        7810 :         return ret;
     443             : }
     444             : 
     445         192 : int elektraListError (Plugin * handle, KeySet * returned, Key * parentKey)
     446             : {
     447         192 :         Placements * placements = elektraPluginGetData (handle);
     448         192 :         KeySet * config = elektraPluginGetConfig (handle);
     449         192 :         ErrPlacements currentPlacement = placements->errCurrent;
     450         192 :         KeySet * pluginKS = ksDup ((placements)->errKS[currentPlacement]);
     451         192 :         ksRewind (pluginKS);
     452         192 :         int ret = runPlugins (pluginKS, placements->modules, placements->plugins, ksDup (config), returned,
     453             :                               elektraPluginGetGlobalKeySet (handle), parentKey, ERR, ksPop, placements->deferredCalls);
     454         192 :         placements->errCurrent = ((++currentPlacement) % errEnd);
     455         384 :         while (currentPlacement < errEnd && !placements->errPlacements[currentPlacement])
     456             :         {
     457           0 :                 placements->errCurrent = ((++currentPlacement) % errEnd);
     458             :         }
     459         192 :         ksDel (pluginKS);
     460         192 :         return ret;
     461             : }
     462             : 
     463           0 : int elektraListAddPlugin (Plugin * handle, KeySet * pluginConfig)
     464             : {
     465           0 :         if (!pluginConfig)
     466             :         {
     467             :                 return 0;
     468             :         }
     469           0 :         ksRewind (pluginConfig);
     470           0 :         ksNext (pluginConfig);
     471           0 :         Key * lookup = ksNext (pluginConfig);
     472           0 :         if (keyBaseName (lookup)[0] != '#')
     473             :         {
     474             :                 return -1;
     475             :         }
     476             :         else
     477             :         {
     478           0 :                 if (strcmp (lastIndex, keyBaseName (lookup)) >= 0)
     479             :                 {
     480             :                         return -1;
     481             :                 }
     482             :         }
     483           0 :         Placements * placements = elektraPluginGetData (handle);
     484           0 :         KeySet * conf = ksDup (pluginConfig);
     485           0 :         ksRewind (conf);
     486           0 :         int rc = listParseConfiguration (placements, conf);
     487           0 :         ksDel (conf);
     488           0 :         return rc;
     489             : }
     490             : 
     491          40 : static char * getPluginPlacementList (Plugin * plugin)
     492             : {
     493          40 :         ELEKTRA_NOT_NULL (plugin);
     494             : 
     495             :         // Get placements from plugin
     496          40 :         Key * pluginInfo = keyNew ("system/elektra/modules/", KEY_END);
     497          40 :         keyAddBaseName (pluginInfo, plugin->name);
     498          40 :         KeySet * ksResult = ksNew (0, KS_END);
     499          40 :         plugin->kdbGet (plugin, ksResult, pluginInfo);
     500             : 
     501          40 :         Key * placementsKey = keyDup (pluginInfo);
     502          40 :         keyAddBaseName (placementsKey, "infos");
     503          40 :         keyAddBaseName (placementsKey, "placements");
     504          40 :         Key * placements = ksLookup (ksResult, placementsKey, 0);
     505          40 :         if (placements == NULL)
     506             :         {
     507             :                 ELEKTRA_LOG_WARNING ("could not read placements from plugin");
     508             :                 return 0;
     509             :         }
     510          40 :         char * placementList = elektraStrDup (keyString (placements));
     511             : 
     512          40 :         keyDel (pluginInfo);
     513          40 :         keyDel (placementsKey);
     514          40 :         ksDel (ksResult);
     515             : 
     516          40 :         return placementList;
     517             : }
     518             : 
     519          40 : static char * extractGetPlacements (const char * placementList)
     520             : {
     521          40 :         char * result = elektraMalloc (strlen (placementList) + 1);
     522          40 :         result[0] = '\0';
     523          40 :         char * resultPos = result;
     524          40 :         const char * last = placementList;
     525          40 :         const char * placement = strchr (last, ' ');
     526         118 :         while (placement != NULL)
     527             :         {
     528          38 :                 size_t len = placement - last;
     529          76 :                 if (strncasecmp (last, GlobalpluginPositionsStr[GETRESOLVER], len) == 0 ||
     530          74 :                     strncasecmp (last, GlobalpluginPositionsStr[PREGETSTORAGE], len) == 0 ||
     531          72 :                     strncasecmp (last, GlobalpluginPositionsStr[GETSTORAGE], len) == 0 ||
     532          70 :                     strncasecmp (last, GlobalpluginPositionsStr[PROCGETSTORAGE], len) == 0 ||
     533          42 :                     strncasecmp (last, GlobalpluginPositionsStr[POSTGETSTORAGE], len) == 0 ||
     534           8 :                     strncasecmp (last, GlobalpluginPositionsStr[POSTGETCLEANUP], len) == 0)
     535             :                 {
     536          30 :                         strncpy (resultPos, last, len);
     537          30 :                         resultPos[len] = ' ';
     538          30 :                         resultPos += len + 1;
     539             :                 }
     540             : 
     541          38 :                 last = placement + 1;
     542          38 :                 placement = strchr (last, ' ');
     543             :         }
     544             : 
     545          80 :         if (strcasecmp (last, GlobalpluginPositionsStr[GETRESOLVER]) == 0 ||
     546          80 :             strcasecmp (last, GlobalpluginPositionsStr[PREGETSTORAGE]) == 0 ||
     547          80 :             strcasecmp (last, GlobalpluginPositionsStr[GETSTORAGE]) == 0 ||
     548          66 :             strcasecmp (last, GlobalpluginPositionsStr[PROCGETSTORAGE]) == 0 ||
     549          52 :             strcasecmp (last, GlobalpluginPositionsStr[POSTGETSTORAGE]) == 0 ||
     550          26 :             strcasecmp (last, GlobalpluginPositionsStr[POSTGETCLEANUP]) == 0)
     551             :         {
     552          14 :                 strcpy (resultPos, last);
     553          14 :                 resultPos += strlen (last);
     554             :         }
     555             : 
     556          40 :         *resultPos = '\0';
     557          40 :         return result;
     558             : }
     559             : 
     560          40 : static char * extractSetPlacements (const char * placementList)
     561             : {
     562          40 :         char * result = elektraMalloc (strlen (placementList) + 1);
     563          40 :         result[0] = '\0';
     564          40 :         char * resultPos = result;
     565          40 :         const char * last = placementList;
     566          40 :         const char * placement = strchr (last, ' ');
     567         118 :         while (placement != NULL)
     568             :         {
     569          38 :                 size_t len = placement - last;
     570          76 :                 if (strncasecmp (last, GlobalpluginPositionsStr[SETRESOLVER], len) == 0 ||
     571          74 :                     strncasecmp (last, GlobalpluginPositionsStr[PRESETSTORAGE], len) == 0 ||
     572          72 :                     strncasecmp (last, GlobalpluginPositionsStr[SETSTORAGE], len) == 0 ||
     573          72 :                     strncasecmp (last, GlobalpluginPositionsStr[PRESETCLEANUP], len) == 0 ||
     574          70 :                     strncasecmp (last, GlobalpluginPositionsStr[PRECOMMIT], len) == 0 ||
     575          68 :                     strncasecmp (last, GlobalpluginPositionsStr[COMMIT], len) == 0 ||
     576          34 :                     strncasecmp (last, GlobalpluginPositionsStr[POSTCOMMIT], len) == 0)
     577             :                 {
     578           6 :                         strncpy (resultPos, last, len);
     579           6 :                         resultPos[len] = ' ';
     580           6 :                         resultPos += len + 1;
     581             :                 }
     582             : 
     583          38 :                 last = placement + 1;
     584          38 :                 placement = strchr (last, ' ');
     585             :         }
     586             : 
     587          80 :         if (strcasecmp (last, GlobalpluginPositionsStr[SETRESOLVER]) == 0 ||
     588          64 :             strcasecmp (last, GlobalpluginPositionsStr[PRESETSTORAGE]) == 0 ||
     589          48 :             strcasecmp (last, GlobalpluginPositionsStr[SETSTORAGE]) == 0 ||
     590          48 :             strcasecmp (last, GlobalpluginPositionsStr[PRESETCLEANUP]) == 0 ||
     591          72 :             strcasecmp (last, GlobalpluginPositionsStr[PRECOMMIT]) == 0 || strcasecmp (last, GlobalpluginPositionsStr[COMMIT]) == 0 ||
     592          24 :             strcasecmp (last, GlobalpluginPositionsStr[POSTCOMMIT]) == 0)
     593             :         {
     594          24 :                 strcpy (resultPos, last);
     595          24 :                 resultPos += strlen (last);
     596             :         }
     597             : 
     598          40 :         *resultPos = '\0';
     599          40 :         return result;
     600             : }
     601             : 
     602          40 : static char * extractErrorPlacements (const char * placementList)
     603             : {
     604          40 :         char * result = elektraMalloc (strlen (placementList) + 1);
     605          40 :         result[0] = '\0';
     606          40 :         char * resultPos = result;
     607          40 :         const char * last = placementList;
     608          40 :         const char * placement = strchr (last, ' ');
     609         118 :         while (placement != NULL)
     610             :         {
     611          38 :                 size_t len = placement - last;
     612          74 :                 if (strncasecmp (last, GlobalpluginPositionsStr[PREROLLBACK], len) == 0 ||
     613          72 :                     strncasecmp (last, GlobalpluginPositionsStr[ROLLBACK], len) == 0 ||
     614          36 :                     strncasecmp (last, GlobalpluginPositionsStr[POSTROLLBACK], len) == 0)
     615             :                 {
     616           2 :                         strncpy (resultPos, last, len);
     617           2 :                         resultPos[len] = ' ';
     618           2 :                         resultPos += len + 1;
     619             :                 }
     620             : 
     621          38 :                 last = placement + 1;
     622          38 :                 placement = strchr (last, ' ');
     623             :         }
     624             : 
     625          80 :         if (strcasecmp (last, GlobalpluginPositionsStr[PREROLLBACK]) == 0 || strcasecmp (last, GlobalpluginPositionsStr[ROLLBACK]) == 0 ||
     626          40 :             strcasecmp (last, GlobalpluginPositionsStr[POSTROLLBACK]) == 0)
     627             :         {
     628           2 :                 strcpy (resultPos, last);
     629           2 :                 resultPos += strlen (last);
     630             :         }
     631             : 
     632          40 :         *resultPos = '\0';
     633          40 :         return result;
     634             : }
     635             : 
     636          70 : static Key * findPluginInConfig (KeySet * config, const char * pluginName)
     637             : {
     638          70 :         Key * configBase = keyNew ("user/plugins", KEY_END);
     639          70 :         KeySet * array = elektraArrayGet (configBase, config);
     640             : 
     641          70 :         ksRewind (array);
     642          70 :         Key * cur = NULL;
     643          70 :         while ((cur = ksNext (array)) != NULL)
     644             :         {
     645          76 :                 if (strcmp (keyString (cur), pluginName) == 0)
     646             :                 {
     647             :                         // found plugin
     648          28 :                         Key * result = keyDup (cur);
     649          28 :                         keyDel (configBase);
     650          28 :                         ksDel (array);
     651          28 :                         return result;
     652             :                 }
     653             :         }
     654             : 
     655          42 :         keyDel (configBase);
     656          42 :         ksDel (array);
     657          42 :         return NULL;
     658             : }
     659             : 
     660          66 : static void resetPlugins (Plugin * handle, Key * errorKey)
     661             : {
     662          66 :         Placements * placements = elektraPluginGetData (handle);
     663          66 :         ksClear (placements->getKS[0]);
     664          66 :         ksClear (placements->getKS[1]);
     665          66 :         ksClear (placements->getKS[2]);
     666          66 :         ksClear (placements->setKS[0]);
     667          66 :         ksClear (placements->setKS[1]);
     668          66 :         ksClear (placements->setKS[2]);
     669          66 :         ksClear (placements->setKS[3]);
     670          66 :         ksClear (placements->errKS[0]);
     671          66 :         ksClear (placements->errKS[1]);
     672             :         Key * cur;
     673          66 :         ksRewind (placements->plugins);
     674         152 :         while ((cur = ksNext (placements->plugins)) != NULL)
     675             :         {
     676             :                 Plugin * slave;
     677          20 :                 slave = *(Plugin **) keyValue (cur);
     678          20 :                 elektraPluginClose (slave, errorKey);
     679             :         }
     680          66 :         ksClear (placements->plugins);
     681          66 : }
     682             : 
     683             : /**
     684             :  * Adds a plugin in all the intended positions (given in its infos/placements key).
     685             :  * If the plugin is already added, effectively equivalent to calling ksDel() on pluginConfig.
     686             :  *
     687             :  * @param handle       A handle of the list plugin
     688             :  * @param pluginName   The plugin to add
     689             :  * @param pluginConfig The config for the plugin, if it has to be mounted; the KeySet is consumed,
     690             :  *                     don't call ksDel() on it afterwards.
     691             :  * @param errorKey     Used for error reporting
     692             :  *
     693             :  * @retval #ELEKTRA_PLUGIN_STATUS_SUCCESS   if the plugin was added
     694             :  * @retval #ELEKTRA_PLUGIN_STATUS_NO_UPDATE if the plugin was added already
     695             :  * @retval #ELEKTRA_PLUGIN_STATUS_ERROR     on NULL pointers and other errors
     696             :  */
     697          42 : int elektraListMountPlugin (Plugin * handle, const char * pluginName, KeySet * pluginConfig, Key * errorKey)
     698             : {
     699          42 :         if (handle == NULL || pluginName == NULL || pluginConfig == NULL)
     700             :         {
     701             :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     702             :         }
     703             : 
     704          42 :         Placements * placements = elektraPluginGetData (handle);
     705          42 :         KeySet * config = elektraPluginGetConfig (handle);
     706             : 
     707             :         // check if plugin already added
     708          42 :         Key * pluginKey = findPluginInConfig (config, pluginName);
     709          42 :         if (pluginKey != NULL)
     710             :         {
     711           2 :                 keyDel (pluginKey);
     712           2 :                 ksDel (pluginConfig); // consume config
     713           2 :                 return ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
     714             :         }
     715             : 
     716             :         // Find name for next item in plugins array
     717          40 :         Key * configBase = keyNew ("user/plugins", KEY_END);
     718          40 :         KeySet * array = elektraArrayGet (configBase, config);
     719          40 :         Key * pluginItem = elektraArrayGetNextKey (array);
     720             : 
     721          40 :         if (pluginItem == NULL)
     722             :         {
     723           2 :                 pluginItem = keyNew ("user/plugins/#0", KEY_END);
     724             :         }
     725             : 
     726          40 :         keySetString (pluginItem, pluginName);
     727             : 
     728          40 :         keyDel (configBase);
     729          40 :         ksDel (array);
     730             : 
     731          40 :         Plugin * plugin = elektraPluginOpen (pluginName, placements->modules, pluginConfig, errorKey);
     732             : 
     733          40 :         if (plugin == NULL)
     734             :         {
     735           0 :                 keyDel (pluginItem);
     736           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     737             :         }
     738             : 
     739             :         // Store key with plugin handle
     740          40 :         Key * searchKey = keyNew ("/", KEY_END);
     741          40 :         keyAddBaseName (searchKey, pluginName);
     742          40 :         keySetBinary (searchKey, &plugin, sizeof (plugin));
     743             : 
     744             :         // Find plugin placements
     745          40 :         char * placementList = getPluginPlacementList (plugin);
     746             : 
     747          40 :         Key * pluginPlacements = keyDup (pluginItem);
     748          40 :         keyAddBaseName (pluginPlacements, "placements");
     749             : 
     750             :         // Append keys to list plugin configuration
     751          40 :         ksAppendKey (config, pluginItem);
     752          40 :         ksAppendKey (config, pluginPlacements);
     753             : 
     754             :         // Add get placements
     755          40 :         char * getPlacementsString = extractGetPlacements (placementList);
     756          40 :         if (getPlacementsString != NULL)
     757             :         {
     758          40 :                 Key * getPlacements = keyDup (pluginPlacements);
     759          40 :                 keyAddBaseName (getPlacements, "get");
     760          40 :                 keySetString (getPlacements, getPlacementsString);
     761          40 :                 ksAppendKey (config, getPlacements);
     762             :         }
     763          40 :         elektraFree (getPlacementsString);
     764             : 
     765             : 
     766             :         // Add set placements
     767          40 :         char * setPlacementsString = extractSetPlacements (placementList);
     768          40 :         if (setPlacementsString != NULL)
     769             :         {
     770          40 :                 Key * setPlacements = keyDup (pluginPlacements);
     771          40 :                 keyAddBaseName (setPlacements, "set");
     772          40 :                 keySetString (setPlacements, setPlacementsString);
     773          40 :                 ksAppendKey (config, setPlacements);
     774             :         }
     775          40 :         elektraFree (setPlacementsString);
     776             : 
     777             :         // Add error placements
     778          40 :         char * errorPlacementsString = extractErrorPlacements (placementList);
     779          40 :         if (errorPlacementsString != NULL)
     780             :         {
     781          40 :                 Key * errorPlacements = keyDup (pluginPlacements);
     782          40 :                 keyAddBaseName (errorPlacements, "error");
     783          40 :                 keySetString (errorPlacements, errorPlacementsString);
     784          40 :                 ksAppendKey (config, errorPlacements);
     785             :         }
     786          40 :         elektraFree (errorPlacementsString);
     787          40 :         elektraFree (placementList);
     788             : 
     789             :         // reload configuration
     790          40 :         resetPlugins (handle, errorKey);
     791             : 
     792             :         // store new handle
     793          40 :         ksAppendKey (placements->plugins, searchKey);
     794          40 :         return elektraListOpen (handle, errorKey);
     795             : }
     796             : 
     797             : /**
     798             :  * Removes a plugin from all the intended positions (given in its infos/placements key).
     799             :  * If the plugin isn't present, nothing happens.
     800             :  *
     801             :  * @param handle       A handle of the list plugin
     802             :  * @param pluginName   The plugin to remove
     803             :  * @param errorKey     Used for error reporting
     804             :  *
     805             :  * @retval #ELEKTRA_PLUGIN_STATUS_SUCCESS   if the plugin was added
     806             :  * @retval #ELEKTRA_PLUGIN_STATUS_NO_UPDATE if the plugin was added already
     807             :  * @retval #ELEKTRA_PLUGIN_STATUS_ERROR     on NULL pointers and other errors
     808             :  */
     809          28 : int elektraListUnmountPlugin (Plugin * handle, const char * pluginName, Key * errorKey)
     810             : {
     811          28 :         if (handle == NULL || pluginName == NULL)
     812             :         {
     813             :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     814             :         }
     815             : 
     816          28 :         Placements * placements = elektraPluginGetData (handle);
     817          28 :         KeySet * config = elektraPluginGetConfig (handle);
     818             : 
     819             :         // Find plugin
     820          28 :         Key * pluginItem = findPluginInConfig (config, pluginName);
     821          28 :         if (pluginItem == NULL)
     822             :         {
     823             :                 return ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
     824             :         }
     825             : 
     826             :         // Look for plugin via handle
     827          26 :         Key * pluginHandle = keyDup (pluginItem);
     828          26 :         keyAddName (pluginHandle, "handle");
     829          26 :         pluginHandle = ksLookup (config, pluginHandle, KDB_O_DEL);
     830             : 
     831             :         // unload plugin if loaded via handle
     832          26 :         if (pluginHandle != NULL)
     833             :         {
     834           0 :                 elektraPluginClose (*((Plugin **) keyValue (pluginHandle)), errorKey);
     835           0 :                 keyDel (pluginHandle);
     836             :         }
     837             : 
     838             :         // Look for plugin via plugins
     839          26 :         Key * searchKey = keyNew ("/", KEY_END);
     840          26 :         keyAddBaseName (searchKey, pluginName);
     841             : 
     842             :         // pop if found
     843          26 :         searchKey = ksLookup (placements->plugins, searchKey, KDB_O_DEL | KDB_O_POP);
     844             : 
     845             :         // unload plugin if loaded via plugins
     846          26 :         if (searchKey != NULL)
     847             :         {
     848          10 :                 elektraPluginClose (*((Plugin **) keyValue (searchKey)), errorKey);
     849          10 :                 keyDel (searchKey);
     850             :         }
     851             : 
     852             :         // Remove plugin data from config
     853          26 :         ksDel (ksCut (config, pluginItem));
     854          26 :         keyDel (pluginItem);
     855             : 
     856             :         // reload configuration
     857          26 :         resetPlugins (handle, errorKey);
     858          26 :         return elektraListOpen (handle, errorKey);
     859             : }
     860             : 
     861             : /**
     862             :  * Find the handle of plugin.
     863             :  *
     864             :  * If elektraListGet(), elektraListSet() and elektraListError()
     865             :  * haven't been called yet, only plugins added via elektraListMountPlugin()
     866             :  * will be found. Other plugins aren't opened (and therefore don't have a handle)
     867             :  * before get/set/error is called.
     868             :  *
     869             :  * @param handle     A handle of the list plugin
     870             :  * @param pluginName The name of the plugin to look for
     871             :  *
     872             :  * @return the handle for the given plugin, or NULL if not found
     873             :  * NULL is also returned if @p handle or @p pluginName are NULL
     874             :  */
     875          36 : Plugin * elektraListFindPlugin (Plugin * handle, const char * pluginName)
     876             : {
     877          36 :         if (handle == NULL || pluginName == NULL)
     878             :         {
     879             :                 return NULL;
     880             :         }
     881             : 
     882          36 :         Placements * placements = elektraPluginGetData (handle);
     883          36 :         KeySet * config = elektraPluginGetConfig (handle);
     884             : 
     885          36 :         Key * searchKey = keyNew ("/", KEY_END);
     886          36 :         keyAddBaseName (searchKey, pluginName);
     887          36 :         Key * lookup = ksLookup (placements->plugins, searchKey, KDB_O_DEL);
     888          36 :         if (lookup)
     889             :         {
     890          22 :                 return *(Plugin **) keyValue (lookup);
     891             :         }
     892             : 
     893             : 
     894             :         Key * current;
     895          56 :         for (int i = 0; i < getEnd; ++i)
     896             :         {
     897          56 :                 while ((current = ksNext (placements->getKS[i])) != NULL)
     898             :                 {
     899           0 :                         Key * handleKey = keyDup (current);
     900           0 :                         keyAddName (handleKey, "handle");
     901           0 :                         Key * handleLookup = ksLookup (config, handleKey, KDB_O_DEL);
     902           0 :                         if (handleLookup)
     903             :                         {
     904           0 :                                 return *(Plugin **) keyValue (handleLookup);
     905             :                         }
     906             :                 }
     907             :         }
     908             : 
     909          56 :         for (int i = 0; i < setEnd; ++i)
     910             :         {
     911          56 :                 while ((current = ksNext (placements->setKS[i])) != NULL)
     912             :                 {
     913           0 :                         Key * handleKey = keyDup (current);
     914           0 :                         keyAddName (handleKey, "handle");
     915           0 :                         Key * handleLookup = ksLookup (config, handleKey, KDB_O_DEL);
     916           0 :                         if (handleLookup)
     917             :                         {
     918           0 :                                 return *(Plugin **) keyValue (handleLookup);
     919             :                         }
     920             :                 }
     921             :         }
     922             : 
     923          28 :         for (int i = 0; i < errEnd; ++i)
     924             :         {
     925          28 :                 while ((current = ksNext (placements->errKS[i])) != NULL)
     926             :                 {
     927           0 :                         Key * handleKey = keyDup (current);
     928           0 :                         keyAddName (handleKey, "handle");
     929           0 :                         Key * handleLookup = ksLookup (config, handleKey, KDB_O_DEL);
     930           0 :                         if (handleLookup)
     931             :                         {
     932           0 :                                 return *(Plugin **) keyValue (handleLookup);
     933             :                         }
     934             :                 }
     935             :         }
     936             : 
     937             :         return NULL;
     938             : }
     939             : 
     940           0 : int elektraListEditPlugin (Plugin * handle, KeySet * pluginConfig)
     941             : {
     942           0 :         if (!pluginConfig)
     943             :         {
     944             :                 return 0;
     945             :         }
     946           0 :         ksRewind (pluginConfig);
     947           0 :         ksNext (pluginConfig);
     948           0 :         Key * lookup = ksNext (pluginConfig);
     949           0 :         if (keyBaseName (lookup)[0] != '#')
     950             :         {
     951             :                 return -1;
     952             :         }
     953             :         else
     954             :         {
     955           0 :                 if (strcmp (lastIndex, keyBaseName (lookup)) < 0)
     956             :                 {
     957             :                         return -1;
     958             :                 }
     959             :         }
     960           0 :         Placements * placements = elektraPluginGetData (handle);
     961           0 :         KeySet * conf = ksDup (pluginConfig);
     962           0 :         ksRewind (conf);
     963           0 :         int rc = listParseConfiguration (placements, conf);
     964           0 :         ksDel (conf);
     965           0 :         return rc;
     966             : }
     967             : 
     968       21064 : Plugin * ELEKTRA_PLUGIN_EXPORT
     969             : {
     970             :         // clang-format off
     971       21064 :         return elektraPluginExport("list",
     972             :                         ELEKTRA_PLUGIN_OPEN,    &elektraListOpen,
     973             :                         ELEKTRA_PLUGIN_CLOSE,   &elektraListClose,
     974             :                         ELEKTRA_PLUGIN_GET,     &elektraListGet,
     975             :                         ELEKTRA_PLUGIN_SET,     &elektraListSet,
     976             :                         ELEKTRA_PLUGIN_ERROR,   &elektraListError,
     977             :                         ELEKTRA_PLUGIN_END);
     978             : }

Generated by: LCOV version 1.13