LCOV - code coverage report
Current view: top level - src/libs/elektra - kdb.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 537 739 72.7 %
Date: 2019-09-12 12:28:41 Functions: 23 27 85.2 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Low level functions for access the Key Database.
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : 
      10             : #ifdef HAVE_KDBCONFIG_H
      11             : #include "kdbconfig.h"
      12             : #endif
      13             : 
      14             : #if DEBUG && defined(HAVE_STDIO_H)
      15             : #include <stdio.h>
      16             : #endif
      17             : 
      18             : #include <kdbassert.h>
      19             : 
      20             : #ifdef HAVE_LOCALE_H
      21             : #include <locale.h>
      22             : #endif
      23             : 
      24             : #ifdef HAVE_STDLIB_H
      25             : #include <stdlib.h>
      26             : #endif
      27             : 
      28             : #ifdef HAVE_STDARG_H
      29             : #include <stdarg.h>
      30             : #endif
      31             : 
      32             : #ifdef HAVE_CTYPE_H
      33             : #include <ctype.h>
      34             : #endif
      35             : 
      36             : #ifdef HAVE_STRING_H
      37             : #include <string.h>
      38             : #endif
      39             : 
      40             : #ifdef HAVE_STDIO_H
      41             : #include <stdio.h>
      42             : #endif
      43             : 
      44             : #ifdef HAVE_ERRNO_H
      45             : #include <errno.h>
      46             : #endif
      47             : 
      48             : #include <kdbinternal.h>
      49             : 
      50             : 
      51             : /**
      52             :  * @defgroup kdb KDB
      53             :  * @brief General methods to access the Key database.
      54             :  *
      55             :  * To use them:
      56             :  * @code
      57             :  * #include <kdb.h>
      58             :  * @endcode
      59             :  *
      60             :  * The kdb*() methods are used to access the storage, to get and set
      61             :  * @link keyset KeySets@endlink.
      62             :  *
      63             :  * Parameters common for all these functions are:
      64             :  *
      65             :  * - *handle*, as returned by kdbOpen(), need to be passed to every call
      66             :  * - *parentKey* is used for every call to add warnings and set an
      67             :  *   error. For kdbGet() / kdbSet() it is used to give an hint which keys
      68             :  *   should be retrieved/stored.
      69             :  *
      70             :  * @note The parentKey is an obligation for you, but only an hint for KDB.
      71             :  * KDB does not remember anything
      72             :  * about the configuration. You need to pass the same configuration
      73             :  * back to kdbSet(), otherwise parts of the configuration get
      74             :  * lost. Only keys below the parentKey are subject for change, the rest
      75             :  * must be left untouched.
      76             :  *
      77             :  * KDB uses different backend implementations that know the details
      78             :  * about how to access the storage.
      79             :  * One backend consists of multiple plugins.
      80             :  * See @link plugin writing a new plugin @endlink for information
      81             :  * about how to write a plugin.
      82             :  * Backends are state-less regarding the configuration (because of that
      83             :  * you must pass back the whole configuration for every backend), but
      84             :  * have a state for:
      85             :  *
      86             :  * - a two phase-commit
      87             :  * - a conflict detection (error 30) and
      88             :  * - optimizations that avoid redoing already done operations.
      89             :  *
      90             :  * @image html state.png "State"
      91             :  * @image latex state.png "State"
      92             :  *
      93             :  * As we see in the figure, kdbOpen() can be called arbitrarily often in any
      94             :  * number of threads.
      95             :  *
      96             :  * For every handle you got from kdbOpen(), for every parentKey with a
      97             :  * different name, *only* the shown state transitions
      98             :  * are valid. From a freshly opened KDB, only kdbGet() and kdbClose()
      99             :  * are allowed, because otherwise conflicts (error 30) would not be detected.
     100             :  *
     101             :  * Once kdbGet() was called (for a specific handle+parentKey),
     102             :  * any number of kdbGet() and kdbSet() can be
     103             :  * used with this handle respective parentKey, unless kdbSet() had
     104             :  * a conflict (error 30) with another application.
     105             :  * Every affair with KDB needs to be finished with kdbClose().
     106             :  *
     107             :  * The name of the parentKey in kdbOpen() and kdbClose() does not matter.
     108             :  *
     109             :  * In the usual case we just have one parentKey and one handle. In
     110             :  * these cases we just have to remember to use kdbGet() before
     111             :  * kdbSet():
     112             :  *
     113             :  * @include kdbintro.c
     114             :  *
     115             :  * To output warnings, you can use following code:
     116             :  *
     117             :  * @snippet tests.c warnings
     118             :  *
     119             :  * To output the error, you can use following code:
     120             :  *
     121             :  * @snippet tests.c error
     122             :  *
     123             :  * @{
     124             :  */
     125             : 
     126             : 
     127             : /**
     128             :  * @internal
     129             :  * Helper which iterates over MetaKeys from key
     130             :  * and removes all MetaKeys starting with
     131             :  * searchfor.
     132             :  */
     133       10523 : void elektraRemoveMetaData (Key * key, const char * searchfor)
     134             : {
     135             :         const Key * iter_key;
     136       10523 :         keyRewindMeta (key);
     137       31568 :         while ((iter_key = keyNextMeta (key)) != 0)
     138             :         {
     139             :                 /*startsWith*/
     140       10522 :                 if (strncmp (searchfor, keyName (iter_key), strlen (searchfor)) == 0)
     141             :                 {
     142           0 :                         keySetMeta (key, keyName (iter_key), 0);
     143             :                 }
     144             :         }
     145       10523 : }
     146             : 
     147             : /**
     148             :  * @brief Bootstrap, first phase with fallback
     149             :  * @internal
     150             :  *
     151             :  * @param handle already allocated, but without defaultBackend
     152             :  * @param [out] keys for bootstrapping
     153             :  * @param errorKey key to add errors too
     154             :  *
     155             :  * @retval -1 failure: cannot initialize defaultBackend
     156             :  * @retval 0 warning: could not get initial config
     157             :  * @retval 1 success
     158             :  * @retval 2 success in fallback mode
     159             :  */
     160       10523 : int elektraOpenBootstrap (KDB * handle, KeySet * keys, Key * errorKey)
     161             : {
     162       10523 :         handle->defaultBackend = backendOpenDefault (handle->modules, handle->global, KDB_DB_INIT, errorKey);
     163       10523 :         if (!handle->defaultBackend) return -1;
     164             : 
     165       10523 :         handle->split = splitNew ();
     166       10523 :         splitAppend (handle->split, handle->defaultBackend, keyNew (KDB_SYSTEM_ELEKTRA, KEY_END), 2);
     167             : 
     168       10523 :         keySetName (errorKey, KDB_SYSTEM_ELEKTRA);
     169       10523 :         keySetString (errorKey, "kdbOpen(): get");
     170             : 
     171       10523 :         int funret = 1;
     172       10523 :         int ret = kdbGet (handle, keys, errorKey);
     173       10523 :         int fallbackret = 0;
     174       10523 :         if (ret == 0 || ret == -1)
     175             :         {
     176             :                 // could not get KDB_DB_INIT, try KDB_DB_FILE
     177             :                 // first cleanup:
     178           1 :                 ksClear (keys);
     179           1 :                 backendClose (handle->defaultBackend, errorKey);
     180           1 :                 splitDel (handle->split);
     181             : 
     182             :                 // then create new setup:
     183           1 :                 handle->defaultBackend = backendOpenDefault (handle->modules, handle->global, KDB_DB_FILE, errorKey);
     184           1 :                 if (!handle->defaultBackend)
     185             :                 {
     186           0 :                         elektraRemoveMetaData (errorKey, "error"); // fix errors from kdbGet()
     187           0 :                         return -1;
     188             :                 }
     189           1 :                 handle->split = splitNew ();
     190           1 :                 splitAppend (handle->split, handle->defaultBackend, keyNew (KDB_SYSTEM_ELEKTRA, KEY_END), 2);
     191             : 
     192           1 :                 keySetName (errorKey, KDB_SYSTEM_ELEKTRA);
     193           1 :                 keySetString (errorKey, "kdbOpen(): get fallback");
     194           1 :                 fallbackret = kdbGet (handle, keys, errorKey);
     195           1 :                 keySetName (errorKey, "system/elektra/mountpoints");
     196             : 
     197           1 :                 KeySet * cutKeys = ksCut (keys, errorKey);
     198           1 :                 if (fallbackret == 1 && ksGetSize (cutKeys) != 0)
     199             :                 {
     200           0 :                         funret = 2;
     201             :                 }
     202           1 :                 ksAppend (keys, cutKeys);
     203           1 :                 ksDel (cutKeys);
     204             :         }
     205             : 
     206       10523 :         if (ret == -1 && fallbackret == -1)
     207             :         {
     208           0 :                 funret = 0;
     209             :         }
     210             : 
     211       10523 :         elektraRemoveMetaData (errorKey, "error"); // fix errors from kdbGet()
     212       10523 :         return funret;
     213             : }
     214             : 
     215             : 
     216             : /**
     217             :  * @brief Opens the session with the Key database.
     218             :  *
     219             :  * @pre errorKey must be a valid key, e.g. created with keyNew()
     220             :  *
     221             :  * The method will bootstrap itself the following way.
     222             :  * The first step is to open the default backend. With it
     223             :  * system/elektra/mountpoints will be loaded and all needed
     224             :  * libraries and mountpoints will be determined.
     225             :  * These libraries for backends will be loaded and with it the
     226             :  * @p KDB data structure will be initialized.
     227             :  *
     228             :  * You must always call this method before retrieving or committing any
     229             :  * keys to the database. In the end of the program,
     230             :  * after using the key database, you must not forget to kdbClose().
     231             :  *
     232             :  * The pointer to the @p KDB structure returned will be initialized
     233             :  * like described above, and it must be passed along on any kdb*()
     234             :  * method your application calls.
     235             :  *
     236             :  * Get a @p KDB handle for every thread using elektra. Don't share the
     237             :  * handle across threads, and also not the pointer accessing it:
     238             :  *
     239             :  * @snippet kdbopen.c open
     240             :  *
     241             :  * You don't need kdbOpen() if you only want to
     242             :  * manipulate plain in-memory Key or KeySet objects.
     243             :  *
     244             :  * @pre errorKey must be a valid key, e.g. created with keyNew()
     245             :  *
     246             :  * @param errorKey the key which holds errors and warnings which were issued
     247             :  * @see kdbGet(), kdbClose() to end all affairs to the key database.
     248             :  * @retval handle on success
     249             :  * @retval NULL on failure
     250             :  * @ingroup kdb
     251             :  */
     252       10523 : KDB * kdbOpen (Key * errorKey)
     253             : {
     254       10523 :         if (!errorKey)
     255             :         {
     256             :                 ELEKTRA_LOG ("no error key passed");
     257             :                 return 0;
     258             :         }
     259             : 
     260             :         ELEKTRA_LOG ("called with %s", keyName (errorKey));
     261             : 
     262       10523 :         int errnosave = errno;
     263       10523 :         KDB * handle = elektraCalloc (sizeof (struct _KDB));
     264       10523 :         Key * initialParent = keyDup (errorKey);
     265             : 
     266       10523 :         handle->global = ksNew (0, KS_END);
     267       10523 :         handle->modules = ksNew (0, KS_END);
     268       10523 :         if (elektraModulesInit (handle->modules, errorKey) == -1)
     269             :         {
     270           0 :                 ksDel (handle->global);
     271           0 :                 ksDel (handle->modules);
     272           0 :                 elektraFree (handle);
     273           0 :                 ELEKTRA_SET_INSTALLATION_ERROR (
     274             :                         errorKey, "Method 'elektraModulesInit' returned with -1. See other warning or error messages for concrete details");
     275             : 
     276           0 :                 keySetName (errorKey, keyName (initialParent));
     277           0 :                 keySetString (errorKey, keyString (initialParent));
     278           0 :                 keyDel (initialParent);
     279           0 :                 errno = errnosave;
     280           0 :                 return 0;
     281             :         }
     282             : 
     283       10523 :         KeySet * keys = ksNew (0, KS_END);
     284       10523 :         int inFallback = 0;
     285       10523 :         switch (elektraOpenBootstrap (handle, keys, errorKey))
     286             :         {
     287             :         case -1:
     288           0 :                 ksDel (handle->global);
     289           0 :                 ksDel (handle->modules);
     290           0 :                 elektraFree (handle);
     291           0 :                 ELEKTRA_SET_INSTALLATION_ERROR (errorKey,
     292             :                                                 "Could not open default backend. See other warning or error messages for concrete details");
     293             : 
     294           0 :                 keySetName (errorKey, keyName (initialParent));
     295           0 :                 keySetString (errorKey, keyString (initialParent));
     296           0 :                 keyDel (initialParent);
     297           0 :                 errno = errnosave;
     298           0 :                 return 0;
     299             :         case 0:
     300           0 :                 ELEKTRA_ADD_INSTALLATION_WARNING (errorKey, "Initial 'kdbGet()' failed, you should either fix " KDB_DB_INIT
     301             :                                                             " or the fallback " KDB_DB_FILE);
     302           0 :                 break;
     303             :         case 2:
     304             :                 ELEKTRA_LOG ("entered fallback code for bootstrapping");
     305           0 :                 inFallback = 1;
     306           0 :                 break;
     307             :         }
     308             : 
     309       10523 :         keySetString (errorKey, "kdbOpen(): mountGlobals");
     310             : 
     311       10523 :         if (mountGlobals (handle, ksDup (keys), handle->modules, errorKey) == -1)
     312             :         {
     313             :                 // mountGlobals also sets a warning containing the name of the plugin that failed to load
     314           0 :                 ELEKTRA_ADD_INSTALLATION_WARNING (errorKey, "Mounting global plugins failed. Please see warning of concrete plugin");
     315             :         }
     316             : 
     317       10523 :         keySetName (errorKey, keyName (initialParent));
     318       10523 :         keySetString (errorKey, "kdbOpen(): backendClose");
     319             : 
     320       10523 :         backendClose (handle->defaultBackend, errorKey);
     321       10523 :         splitDel (handle->split);
     322       10523 :         handle->defaultBackend = 0;
     323       10523 :         handle->trie = 0;
     324             : 
     325             : #ifdef HAVE_LOGGER
     326             :         if (inFallback) ELEKTRA_LOG_WARNING ("fallback for bootstrapping: you might want to run `kdb upgrade-bootstrap`");
     327             : 
     328             :         Key * key;
     329             : 
     330             :         ksRewind (keys);
     331             :         for (key = ksNext (keys); key; key = ksNext (keys))
     332             :         {
     333             :                 ELEKTRA_LOG_DEBUG ("config for createTrie name: %s value: %s", keyName (key), keyString (key));
     334             :         }
     335             : #endif
     336             : 
     337       10523 :         handle->split = splitNew ();
     338             : 
     339       10523 :         keySetString (errorKey, "kdbOpen(): mountOpen");
     340             :         // Open the trie, keys will be deleted within mountOpen
     341       10523 :         if (mountOpen (handle, keys, handle->modules, errorKey) == -1)
     342             :         {
     343           0 :                 ELEKTRA_ADD_INSTALLATION_WARNING (errorKey, "Initial loading of trie did not work");
     344             :         }
     345             : 
     346       10523 :         keySetString (errorKey, "kdbOpen(): mountDefault");
     347       10523 :         if (mountDefault (handle, handle->modules, inFallback, errorKey) == -1)
     348             :         {
     349           0 :                 ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Could not reopen and mount default backend");
     350           0 :                 keySetString (errorKey, "kdbOpen(): close");
     351           0 :                 kdbClose (handle, errorKey);
     352             : 
     353           0 :                 keySetName (errorKey, keyName (initialParent));
     354           0 :                 keySetString (errorKey, keyString (initialParent));
     355           0 :                 keyDel (initialParent);
     356           0 :                 errno = errnosave;
     357           0 :                 return 0;
     358             :         }
     359             : 
     360       10523 :         keySetString (errorKey, "kdbOpen(): mountVersion");
     361       10523 :         mountVersion (handle, errorKey);
     362             : 
     363       10523 :         keySetString (errorKey, "kdbOpen(): mountModules");
     364       10523 :         if (mountModules (handle, handle->modules, errorKey) == -1)
     365             :         {
     366           0 :                 ELEKTRA_ADD_INTERNAL_WARNING (errorKey, "Mounting modules did not work");
     367             :         }
     368             : 
     369       10523 :         keySetName (errorKey, keyName (initialParent));
     370       10523 :         keySetString (errorKey, keyString (initialParent));
     371       10523 :         keyDel (initialParent);
     372       10523 :         errno = errnosave;
     373       10523 :         return handle;
     374             : }
     375             : 
     376             : 
     377             : /**
     378             :  * Closes the session with the Key database.
     379             :  *
     380             :  * @pre The handle must be a valid handle as returned from kdbOpen()
     381             :  *
     382             :  * @pre errorKey must be a valid key, e.g. created with keyNew()
     383             :  *
     384             :  * This is the counterpart of kdbOpen().
     385             :  *
     386             :  * You must call this method when you finished your affairs with the key
     387             :  * database. You can manipulate Key and KeySet objects also after
     388             :  * kdbClose(), but you must not use any kdb*() call afterwards.
     389             :  *
     390             :  * The @p handle parameter will be finalized and all resources associated to it
     391             :  * will be freed. After a kdbClose(), the @p handle cannot be used anymore.
     392             :  *
     393             :  * @param handle contains internal information of
     394             :  *               @link kdbOpen() opened @endlink key database
     395             :  * @param errorKey the key which holds error/warning information
     396             :  * @retval 0 on success
     397             :  * @retval -1 on NULL pointer
     398             :  * @ingroup kdb
     399             :  */
     400       10577 : int kdbClose (KDB * handle, Key * errorKey)
     401             : {
     402       10577 :         if (!handle)
     403             :         {
     404             :                 return -1;
     405             :         }
     406             : 
     407       10561 :         Key * initialParent = keyDup (errorKey);
     408       10561 :         int errnosave = errno;
     409       10561 :         splitDel (handle->split);
     410             : 
     411       10561 :         trieClose (handle->trie, errorKey);
     412             : 
     413       10561 :         backendClose (handle->defaultBackend, errorKey);
     414       10561 :         handle->defaultBackend = 0;
     415             : 
     416             :         // not set in fallback mode, so lets check:
     417       10561 :         if (handle->initBackend)
     418             :         {
     419       10523 :                 backendClose (handle->initBackend, errorKey);
     420       10523 :                 handle->initBackend = 0;
     421             :         }
     422             : 
     423      190098 :         for (int i = 0; i < NR_GLOBAL_POSITIONS; ++i)
     424             :         {
     425      760392 :                 for (int j = 0; j < NR_GLOBAL_SUBPOSITIONS; ++j)
     426             :                 {
     427      760392 :                         elektraPluginClose (handle->globalPlugins[i][j], errorKey);
     428             :                 }
     429             :         }
     430             : 
     431       10561 :         if (handle->modules)
     432             :         {
     433       10547 :                 elektraModulesClose (handle->modules, errorKey);
     434       10547 :                 ksDel (handle->modules);
     435             :         }
     436             :         else
     437             :         {
     438          14 :                 ELEKTRA_ADD_RESOURCE_WARNING (errorKey, "Could not close modules: modules were not open");
     439             :         }
     440             : 
     441       10561 :         if (handle->global) ksDel (handle->global);
     442             : 
     443       10561 :         elektraFree (handle);
     444             : 
     445       10561 :         keySetName (errorKey, keyName (initialParent));
     446       10561 :         keySetString (errorKey, keyString (initialParent));
     447       10561 :         keyDel (initialParent);
     448       10561 :         errno = errnosave;
     449       10561 :         return 0;
     450             : }
     451             : 
     452             : /**
     453             :  * @internal
     454             :  *
     455             :  * @brief Check if an update is needed at all
     456             :  *
     457             :  * @retval -2 cache hit
     458             :  * @retval -1 an error occurred
     459             :  * @retval 0 no update needed
     460             :  * @retval number of plugins which need update
     461             :  */
     462       26178 : static int elektraGetCheckUpdateNeeded (Split * split, Key * parentKey)
     463             : {
     464       26178 :         int updateNeededOccurred = 0;
     465       26178 :         size_t cacheHits = 0;
     466       87912 :         for (size_t i = 0; i < split->size; i++)
     467             :         {
     468       61734 :                 int ret = -1;
     469       61734 :                 Backend * backend = split->handles[i];
     470       61734 :                 clear_bit (split->syncbits[i], (splitflag_t) SPLIT_FLAG_SYNC);
     471             : 
     472       61734 :                 Plugin * resolver = backend->getplugins[RESOLVER_PLUGIN];
     473       61734 :                 if (resolver && resolver->kdbGet)
     474             :                 {
     475       61734 :                         ksRewind (split->keysets[i]);
     476       61734 :                         keySetName (parentKey, keyName (split->parents[i]));
     477       61734 :                         keySetString (parentKey, "");
     478       61734 :                         ret = resolver->kdbGet (resolver, split->keysets[i], parentKey);
     479             :                         // store resolved filename
     480       61734 :                         keySetString (split->parents[i], keyString (parentKey));
     481             :                         // no keys in that backend
     482             :                         ELEKTRA_LOG_DEBUG ("backend: %s,%s ;; ret: %d", keyName (split->parents[i]), keyString (split->parents[i]), ret);
     483             : 
     484       61734 :                         backendUpdateSize (backend, split->parents[i], 0);
     485             :                 }
     486             :                 // TODO: set error in else case!
     487             : 
     488       61734 :                 switch (ret)
     489             :                 {
     490             :                 case ELEKTRA_PLUGIN_STATUS_CACHE_HIT:
     491             :                         // Keys in cache are up-to-date
     492           0 :                         ++cacheHits;
     493             :                         // Set sync flag, needed in case of cache miss
     494             :                         // FALLTHROUGH
     495             :                 case ELEKTRA_PLUGIN_STATUS_SUCCESS:
     496             :                         // Seems like we need to sync that
     497       18407 :                         set_bit (split->syncbits[i], SPLIT_FLAG_SYNC);
     498       18407 :                         ++updateNeededOccurred;
     499       18407 :                         break;
     500             :                 case ELEKTRA_PLUGIN_STATUS_NO_UPDATE:
     501             :                         // Nothing to do here
     502             :                         break;
     503             :                 default:
     504           0 :                         ELEKTRA_ASSERT (0, "resolver did not return 1 0 -1, but %d", ret);
     505             :                 case ELEKTRA_PLUGIN_STATUS_ERROR:
     506             :                         // Ohh, an error occurred, lets stop the
     507             :                         // process.
     508             :                         return -1;
     509             :                 }
     510             :         }
     511             : 
     512       26178 :         if (cacheHits == split->size)
     513             :         {
     514             :                 ELEKTRA_LOG_DEBUG ("all backends report cache is up-to-date");
     515             :                 return -2;
     516             :         }
     517             : 
     518       26178 :         return updateNeededOccurred;
     519             : }
     520             : 
     521             : typedef enum
     522             : {
     523             :         FIRST,
     524             :         LAST
     525             : } UpdatePass;
     526             : 
     527             : /**
     528             :  * @internal
     529             :  * @brief Do the real update.
     530             :  *
     531             :  * @retval -1 on error
     532             :  * @retval 0 on success
     533             :  */
     534       10551 : static int elektraGetDoUpdate (Split * split, Key * parentKey)
     535             : {
     536       10551 :         const int bypassedSplits = 1;
     537       21171 :         for (size_t i = 0; i < split->size - bypassedSplits; i++)
     538             :         {
     539       10620 :                 if (!test_bit (split->syncbits[i], SPLIT_FLAG_SYNC))
     540             :                 {
     541             :                         // skip it, update is not needed
     542          46 :                         continue;
     543             :                 }
     544       10574 :                 Backend * backend = split->handles[i];
     545       10574 :                 ksRewind (split->keysets[i]);
     546       10574 :                 keySetName (parentKey, keyName (split->parents[i]));
     547       10574 :                 keySetString (parentKey, keyString (split->parents[i]));
     548             : 
     549      105740 :                 for (size_t p = 1; p < NR_OF_PLUGINS; ++p)
     550             :                 {
     551       95166 :                         int ret = 0;
     552       95166 :                         if (backend->getplugins[p] && backend->getplugins[p]->kdbGet)
     553             :                         {
     554       10566 :                                 ret = backend->getplugins[p]->kdbGet (backend->getplugins[p], split->keysets[i], parentKey);
     555             :                         }
     556             : 
     557       95166 :                         if (ret == -1)
     558             :                         {
     559             :                                 // Ohh, an error occurred,
     560             :                                 // lets stop the process.
     561             :                                 return -1;
     562             :                         }
     563             :                 }
     564             :         }
     565             :         return 0;
     566             : }
     567             : 
     568         785 : static KeySet * prepareGlobalKS (KeySet * ks, Key * parentKey)
     569             : {
     570         785 :         ksRewind (ks);
     571         785 :         Key * cutKey = keyNew ("/", KEY_CASCADING_NAME, KEY_END);
     572         785 :         keyAddName (cutKey, strchr (keyName (parentKey), '/'));
     573         785 :         KeySet * cutKS = ksCut (ks, cutKey);
     574         785 :         Key * specCutKey = keyNew ("spec", KEY_END);
     575         785 :         KeySet * specCut = ksCut (cutKS, specCutKey);
     576         785 :         ksRewind (specCut);
     577             :         Key * cur;
     578        2234 :         while ((cur = ksNext (specCut)) != NULL)
     579             :         {
     580         664 :                 if (keyGetNamespace (cur) == KEY_NS_CASCADING)
     581             :                 {
     582           0 :                         ksAppendKey (cutKS, cur);
     583           0 :                         keyDel (ksLookup (specCut, cur, KDB_O_POP));
     584             :                 }
     585             :         }
     586         785 :         ksAppend (ks, specCut);
     587         785 :         ksDel (specCut);
     588         785 :         keyDel (specCutKey);
     589         785 :         keyDel (cutKey);
     590         785 :         ksRewind (cutKS);
     591         785 :         return cutKS;
     592             : }
     593             : 
     594       10405 : static int elektraGetDoUpdateWithGlobalHooks (KDB * handle, Split * split, KeySet * ks, Key * parentKey, Key * initialParent,
     595             :                                               UpdatePass run)
     596             : {
     597       10405 :         const int bypassedSplits = 1;
     598             : 
     599       10405 :         switch (run)
     600             :         {
     601             :         case FIRST:
     602        5209 :                 keySetName (parentKey, keyName (initialParent));
     603        5209 :                 elektraGlobalGet (handle, ks, parentKey, GETSTORAGE, INIT);
     604        5209 :                 elektraGlobalGet (handle, ks, parentKey, GETSTORAGE, MAXONCE);
     605        5209 :                 break;
     606             :         case LAST:
     607        5196 :                 keySetName (parentKey, keyName (initialParent));
     608        5196 :                 elektraGlobalGet (handle, ks, parentKey, PROCGETSTORAGE, INIT);
     609        5196 :                 elektraGlobalGet (handle, ks, parentKey, PROCGETSTORAGE, MAXONCE);
     610        5196 :                 elektraGlobalError (handle, ks, parentKey, PROCGETSTORAGE, DEINIT);
     611        5196 :                 break;
     612             :         default:
     613             :                 break;
     614             :         }
     615             : 
     616             :         // elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, INIT);
     617             : 
     618       25458 :         for (size_t i = 0; i < split->size - bypassedSplits; i++)
     619             :         {
     620       25471 :                 Backend * backend = split->handles[i];
     621       25471 :                 ksRewind (split->keysets[i]);
     622       25471 :                 keySetName (parentKey, keyName (split->parents[i]));
     623       25471 :                 keySetString (parentKey, keyString (split->parents[i]));
     624             :                 int start, end;
     625       25471 :                 if (run == FIRST)
     626             :                 {
     627             :                         start = 1;
     628             :                         end = STORAGE_PLUGIN + 1;
     629             :                 }
     630             :                 else
     631             :                 {
     632       12728 :                         start = STORAGE_PLUGIN + 1;
     633       12728 :                         end = NR_OF_PLUGINS;
     634             :                 }
     635      140085 :                 for (int p = start; p < end; ++p)
     636             :                 {
     637      114627 :                         int ret = 0;
     638             : 
     639      114627 :                         if (p == (STORAGE_PLUGIN + 1) && handle->globalPlugins[PROCGETSTORAGE][FOREACH])
     640             :                         {
     641           0 :                                 keySetName (parentKey, keyName (initialParent));
     642           0 :                                 ksRewind (ks);
     643           0 :                                 handle->globalPlugins[PROCGETSTORAGE][FOREACH]->kdbGet (handle->globalPlugins[PROCGETSTORAGE][FOREACH], ks,
     644             :                                                                                         parentKey);
     645           0 :                                 keySetName (parentKey, keyName (split->parents[i]));
     646             :                         }
     647      114627 :                         if (p == (STORAGE_PLUGIN + 2) && handle->globalPlugins[POSTGETSTORAGE][FOREACH])
     648             :                         {
     649           0 :                                 keySetName (parentKey, keyName (initialParent));
     650           0 :                                 ksRewind (ks);
     651           0 :                                 handle->globalPlugins[POSTGETSTORAGE][FOREACH]->kdbGet (handle->globalPlugins[POSTGETSTORAGE][FOREACH], ks,
     652             :                                                                                         parentKey);
     653           0 :                                 keySetName (parentKey, keyName (split->parents[i]));
     654             :                         }
     655      114627 :                         else if (p == (NR_OF_PLUGINS - 1) && handle->globalPlugins[POSTGETCLEANUP][FOREACH])
     656             :                         {
     657           0 :                                 keySetName (parentKey, keyName (initialParent));
     658           0 :                                 ksRewind (ks);
     659           0 :                                 handle->globalPlugins[POSTGETCLEANUP][FOREACH]->kdbGet (handle->globalPlugins[POSTGETCLEANUP][FOREACH], ks,
     660             :                                                                                         parentKey);
     661           0 :                                 keySetName (parentKey, keyName (split->parents[i]));
     662             :                         }
     663             : 
     664      114627 :                         if (backend->getplugins[p] && backend->getplugins[p]->kdbGet)
     665             :                         {
     666       12854 :                                 if (p <= STORAGE_PLUGIN)
     667             :                                 {
     668       12069 :                                         if (!test_bit (split->syncbits[i], SPLIT_FLAG_SYNC))
     669             :                                         {
     670             :                                                 // skip it, update is not needed
     671        4910 :                                                 continue;
     672             :                                         }
     673             : 
     674        7159 :                                         ret = backend->getplugins[p]->kdbGet (backend->getplugins[p], split->keysets[i], parentKey);
     675             :                                 }
     676             :                                 else
     677             :                                 {
     678         785 :                                         KeySet * cutKS = prepareGlobalKS (ks, parentKey);
     679         785 :                                         ret = backend->getplugins[p]->kdbGet (backend->getplugins[p], cutKS, parentKey);
     680         785 :                                         ksAppend (ks, cutKS);
     681         785 :                                         ksDel (cutKS);
     682             :                                 }
     683             :                         }
     684             : 
     685      109717 :                         if (ret == -1)
     686             :                         {
     687          13 :                                 keySetName (parentKey, keyName (initialParent));
     688             :                                 // Ohh, an error occurred,
     689             :                                 // lets stop the process.
     690          13 :                                 elektraGlobalError (handle, ks, parentKey, GETSTORAGE, DEINIT);
     691             :                                 // elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);
     692          13 :                                 return -1;
     693             :                         }
     694             :                 }
     695             :         }
     696             : 
     697       10392 :         if (run == FIRST)
     698             :         {
     699        5196 :                 keySetName (parentKey, keyName (initialParent));
     700        5196 :                 elektraGlobalGet (handle, ks, parentKey, GETSTORAGE, DEINIT);
     701             :                 // elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);
     702             :         }
     703             :         return 0;
     704             : }
     705             : 
     706       52398 : static int copyError (Key * dest, Key * src)
     707             : {
     708       52398 :         keyRewindMeta (src);
     709       52398 :         const Key * metaKey = keyGetMeta (src, "error");
     710       52398 :         if (!metaKey) return 0;
     711          82 :         keySetMeta (dest, keyName (metaKey), keyString (metaKey));
     712         820 :         while ((metaKey = keyNextMeta (src)) != NULL)
     713             :         {
     714         690 :                 if (strncmp (keyName (metaKey), "error/", 6)) break;
     715         656 :                 keySetMeta (dest, keyName (metaKey), keyString (metaKey));
     716             :         }
     717             :         return 1;
     718             : }
     719       23591 : static void clearError (Key * key)
     720             : {
     721       23591 :         keySetMeta (key, "error", 0);
     722       23591 :         keySetMeta (key, "error/number", 0);
     723       23591 :         keySetMeta (key, "error/description", 0);
     724       23591 :         keySetMeta (key, "error/reason", 0);
     725       23591 :         keySetMeta (key, "error/module", 0);
     726       23591 :         keySetMeta (key, "error/file", 0);
     727       23591 :         keySetMeta (key, "error/line", 0);
     728       23591 :         keySetMeta (key, "error/configfile", 0);
     729       23591 :         keySetMeta (key, "error/mountpoint", 0);
     730       23591 : }
     731             : 
     732           0 : static int elektraCacheCheckParent (KeySet * global, Key * cacheParent, Key * initialParent)
     733             : {
     734             :         // first check if parentkey matches
     735           0 :         Key * lastParentName = ksLookupByName (global, KDB_CACHE_PREFIX "/lastParentName", KDB_O_NONE);
     736             :         ELEKTRA_LOG_DEBUG ("LAST PARENT name: %s", keyString (lastParentName));
     737             :         ELEKTRA_LOG_DEBUG ("KDBG PARENT name: %s", keyName (cacheParent));
     738           0 :         if (!lastParentName || elektraStrCmp (keyString (lastParentName), keyName (cacheParent))) return -1;
     739             : 
     740           0 :         Key * lastParentValue = ksLookupByName (global, KDB_CACHE_PREFIX "/lastParentValue", KDB_O_NONE);
     741             :         ELEKTRA_LOG_DEBUG ("LAST PARENT value: %s", keyString (lastParentValue));
     742             :         ELEKTRA_LOG_DEBUG ("KDBG PARENT value: %s", keyString (cacheParent));
     743           0 :         if (!lastParentValue || elektraStrCmp (keyString (lastParentValue), keyString (cacheParent))) return -1;
     744             : 
     745           0 :         Key * lastInitalParentName = ksLookupByName (global, KDB_CACHE_PREFIX "/lastInitialParentName", KDB_O_NONE);
     746           0 :         Key * lastInitialParent = keyNew (keyString (lastInitalParentName), KEY_END);
     747             :         ELEKTRA_LOG_DEBUG ("LAST initial PARENT name: %s", keyName (lastInitialParent));
     748             :         ELEKTRA_LOG_DEBUG ("CURR initial PARENT name: %s", keyName (initialParent));
     749             : 
     750           0 :         if (!keyIsBelowOrSame (lastInitialParent, initialParent))
     751             :         {
     752             :                 ELEKTRA_LOG_DEBUG ("CACHE initial PARENT: key is not below or same");
     753           0 :                 keyDel (lastInitialParent);
     754           0 :                 return -1;
     755             :         }
     756             : 
     757           0 :         keyDel (lastInitialParent);
     758           0 :         return 0;
     759             : }
     760             : 
     761       15744 : static void elektraCacheCutMeta (KDB * handle)
     762             : {
     763       15744 :         Key * parentKey = keyNew (KDB_CACHE_PREFIX, KEY_END);
     764       15744 :         ksDel (ksCut (handle->global, parentKey));
     765       15744 :         keyDel (parentKey);
     766       15744 : }
     767             : 
     768           0 : KeySet * elektraCutProc (KeySet * ks)
     769             : {
     770           0 :         Key * parentKey = keyNew ("proc", KEY_END);
     771           0 :         KeySet * ret = ksCut (ks, parentKey);
     772           0 :         keyDel (parentKey);
     773           0 :         return ret;
     774             : }
     775             : 
     776             : static void elektraRestoreProc (KeySet * ks, KeySet * proc)
     777             : {
     778           0 :         ksAppend (ks, proc);
     779           0 :         ksDel (proc);
     780             : }
     781             : 
     782           0 : static void elektraCacheLoad (KDB * handle, KeySet * cache, Key * parentKey, Key * initialParent ELEKTRA_UNUSED, Key * cacheParent)
     783             : {
     784             :         // prune old cache info
     785           0 :         elektraCacheCutMeta (handle);
     786             : 
     787           0 :         if (elektraGlobalGet (handle, cache, cacheParent, PREGETCACHE, MAXONCE) != ELEKTRA_PLUGIN_STATUS_SUCCESS)
     788             :         {
     789             :                 ELEKTRA_LOG_DEBUG ("CACHE MISS: could not fetch cache");
     790           0 :                 elektraCacheCutMeta (handle);
     791           0 :                 return;
     792             :         }
     793           0 :         ELEKTRA_ASSERT (elektraStrCmp (keyName (initialParent), keyName (parentKey)) == 0, "parentKey name differs from initial");
     794           0 :         if (elektraCacheCheckParent (handle->global, cacheParent, parentKey) != 0)
     795             :         {
     796             :                 // parentKey in cache does not match, needs rebuild
     797             :                 ELEKTRA_LOG_DEBUG ("CACHE WRONG PARENTKEY");
     798           0 :                 elektraCacheCutMeta (handle);
     799           0 :                 return;
     800             :         }
     801             : }
     802             : 
     803           0 : static int elektraCacheLoadSplit (KDB * handle, Split * split, KeySet * ks, KeySet ** cache, Key ** cacheParent, Key * parentKey,
     804             :                                   Key * initialParent, int debugGlobalPositions)
     805             : {
     806             :         ELEKTRA_LOG_DEBUG ("CACHE parentKey: %s, %s", keyName (*cacheParent), keyString (*cacheParent));
     807             : 
     808           0 :         if (splitCacheCheckState (split, handle->global) == -1)
     809             :         {
     810             :                 ELEKTRA_LOG_DEBUG ("FAIL, have to discard cache because split state / SIZE FAIL, or file mismatch");
     811           0 :                 elektraCacheCutMeta (handle);
     812           0 :                 return -1;
     813             :         }
     814             : 
     815             :         ELEKTRA_LOG_DEBUG ("CACHE HIT");
     816           0 :         if (splitCacheLoadState (split, handle->global) != 0) return -1;
     817             : 
     818           0 :         if (debugGlobalPositions)
     819             :         {
     820           0 :                 keySetName (parentKey, keyName (initialParent));
     821           0 :                 elektraGlobalGet (handle, *cache, parentKey, PREGETSTORAGE, INIT);
     822           0 :                 elektraGlobalGet (handle, *cache, parentKey, PREGETSTORAGE, MAXONCE);
     823           0 :                 elektraGlobalGet (handle, *cache, parentKey, PREGETSTORAGE, DEINIT);
     824             :         }
     825             : 
     826           0 :         keySetName (parentKey, keyName (initialParent));
     827           0 :         elektraGlobalGet (handle, *cache, parentKey, PROCGETSTORAGE, INIT);
     828           0 :         elektraGlobalGet (handle, *cache, parentKey, PROCGETSTORAGE, MAXONCE);
     829           0 :         elektraGlobalGet (handle, *cache, parentKey, PROCGETSTORAGE, DEINIT);
     830             : 
     831             :         // replace ks with cached keyset
     832           0 :         ksRewind (*cache);
     833           0 :         if (ks->size == 0)
     834             :         {
     835             :                 ELEKTRA_LOG_DEBUG ("replacing keyset with cached keyset");
     836           0 :                 ksClose (ks);
     837           0 :                 ks->array = (*cache)->array;
     838           0 :                 ks->size = (*cache)->size;
     839           0 :                 ks->alloc = (*cache)->alloc;
     840           0 :                 ks->flags = (*cache)->flags;
     841           0 :                 elektraFree (*cache);
     842           0 :                 *cache = 0;
     843             :         }
     844             :         else
     845             :         {
     846             :                 ELEKTRA_LOG_DEBUG ("appending cached keyset (ks was not empty)");
     847           0 :                 ksAppend (ks, *cache);
     848           0 :                 ksDel (*cache);
     849           0 :                 *cache = 0;
     850             :         }
     851           0 :         keyDel (*cacheParent);
     852           0 :         *cacheParent = 0;
     853             : 
     854           0 :         if (debugGlobalPositions)
     855             :         {
     856           0 :                 keySetName (parentKey, keyName (initialParent));
     857           0 :                 elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, INIT);
     858           0 :                 elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE);
     859           0 :                 elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);
     860             :         }
     861             : 
     862             :         return 0;
     863             : }
     864             : 
     865             : 
     866             : /**
     867             :  * @brief Retrieve keys in an atomic and universal way.
     868             :  *
     869             :  * @pre The @p handle must be passed as returned from kdbOpen().
     870             :  *
     871             :  * @pre The @p returned KeySet must be a valid KeySet, e.g. constructed
     872             :  *     with ksNew().
     873             :  *
     874             :  * @pre The @p parentKey Key must be a valid Key, e.g. constructed with
     875             :  *     keyNew().
     876             :  *
     877             :  * If you pass NULL on any parameter kdbGet() will fail immediately without doing anything.
     878             :  *
     879             :  * The @p returned KeySet may already contain some keys, e.g. from previous
     880             :  * kdbGet() calls. The new retrieved keys will be appended using
     881             :  * ksAppendKey().
     882             :  *
     883             :  * If not done earlier kdbGet() will fully retrieve all keys under the @p parentKey
     884             :  * folder recursively (See Optimization below when it will not be done).
     885             :  *
     886             :  * @note kdbGet() might retrieve more keys than requested (that are not
     887             :  *     below parentKey). These keys must be passed to calls of kdbSet(),
     888             :  *     otherwise they will be lost. This stems from the fact that the
     889             :  *     user has the only copy of the whole configuration and backends
     890             :  *     only write configuration that was passed to them.
     891             :  *     For example, if you kdbGet() "system/mountpoint/interest"
     892             :  *     you will not only get all keys below system/mountpoint/interest,
     893             :  *     but also all keys below system/mountpoint (if system/mountpoint
     894             :  *     is a mountpoint as the name suggests, but
     895             :  *     system/mountpoint/interest is not a mountpoint).
     896             :  *     Make sure to not touch or remove keys outside the keys of interest,
     897             :  *     because others may need them!
     898             :  *
     899             :  * @par Example:
     900             :  * This example demonstrates the typical usecase within an application
     901             :  * (without error handling).
     902             :  *
     903             :  * @include kdbget.c
     904             :  *
     905             :  * When a backend fails kdbGet() will return -1 with all
     906             :  * error and warning information in the @p parentKey.
     907             :  * The parameter @p returned will not be changed.
     908             :  *
     909             :  * @par Optimization:
     910             :  * In the first run of kdbGet all requested (or more) keys are retrieved. On subsequent
     911             :  * calls only the keys are retrieved where something was changed
     912             :  * inside the key database. The other keys stay in the
     913             :  * KeySet returned as passed.
     914             :  *
     915             :  * It is your responsibility to save the original keyset if you
     916             :  * need it afterwards.
     917             :  *
     918             :  * If you want to be sure to get a fresh keyset again, you need to open a
     919             :  * second handle to the key database using kdbOpen().
     920             :  *
     921             :  * @param handle contains internal information of @link kdbOpen() opened @endlink key database
     922             :  * @param parentKey is used to add warnings and set an error
     923             :  *         information. Additionally, its name is a hint which keys
     924             :  *         should be retrieved (it is possible that more are retrieved, see Note above).
     925             :  *           - cascading keys (starting with /) will retrieve the same path in all namespaces
     926             :  *           - / will retrieve all keys
     927             :  * @param ks the (pre-initialized) KeySet returned with all keys found
     928             :  *      will not be changed on error or if no update is required
     929             :  * @see ksLookup(), ksLookupByName() for powerful
     930             :  *      lookups after the KeySet was retrieved
     931             :  * @see kdbOpen() which needs to be called before
     932             :  * @see kdbSet() to save the configuration afterwards and kdbClose() to
     933             :  *      finish affairs with the key database.
     934             :  * @retval 1 if the keys were retrieved successfully
     935             :  * @retval 0 if there was no update - no changes are made to the keyset then
     936             :  * @retval -1 on failure - no changes are made to the keyset then
     937             :  * @ingroup kdb
     938             :  */
     939       26180 : int kdbGet (KDB * handle, KeySet * ks, Key * parentKey)
     940             : {
     941       26180 :         elektraNamespace ns = keyGetNamespace (parentKey);
     942       26180 :         if (ns == KEY_NS_NONE)
     943             :         {
     944             :                 return -1;
     945             :         }
     946             : 
     947       26180 :         Key * oldError = keyNew (keyName (parentKey), KEY_END);
     948       26180 :         copyError (oldError, parentKey);
     949             : 
     950       26180 :         if (ns == KEY_NS_META)
     951             :         {
     952           0 :                 clearError (parentKey);
     953           0 :                 keyDel (oldError);
     954           0 :                 ELEKTRA_SET_INTERFACE_ERRORF (parentKey, "Metakey with name '%s' passed to kdbGet as parentkey", keyName (parentKey));
     955           0 :                 return -1;
     956             :         }
     957             : 
     958       26180 :         if (ns == KEY_NS_EMPTY)
     959             :         {
     960             :                 /*TODO: Solution ("Please use the cascading key / instead")*/
     961           0 :                 ELEKTRA_ADD_VALIDATION_SYNTACTIC_WARNING (parentKey, "Empty namespace passed to kdbGet");
     962             :         }
     963             : 
     964       26180 :         int errnosave = errno;
     965       26180 :         Key * initialParent = keyDup (parentKey);
     966             : 
     967             :         ELEKTRA_LOG ("now in new kdbGet (%s)", keyName (parentKey));
     968             : 
     969       26180 :         Split * split = splitNew ();
     970             : 
     971       26180 :         KeySet * cache = 0;
     972       26180 :         Key * cacheParent = 0;
     973       26180 :         int debugGlobalPositions = 0;
     974             : 
     975             : #ifdef DEBUG
     976       26180 :         if (keyGetMeta (parentKey, "debugGlobalPositions") != 0)
     977             :         {
     978           6 :                 debugGlobalPositions = 1;
     979             :         }
     980             : #endif
     981             : 
     982       26180 :         if (!handle || !ks)
     983             :         {
     984           2 :                 clearError (parentKey);
     985           2 :                 ELEKTRA_SET_INTERFACE_ERROR (parentKey, "Handle or KeySet null pointer passed");
     986           2 :                 goto error;
     987             :         }
     988             : 
     989       26178 :         if (splitBuildup (split, handle, parentKey) == -1)
     990             :         {
     991           0 :                 clearError (parentKey);
     992           0 :                 ELEKTRA_SET_INTERNAL_ERROR (parentKey, "Error in splitBuildup");
     993           0 :                 goto error;
     994             :         }
     995             : 
     996       26178 :         cache = ksNew (0, KS_END);
     997       26178 :         cacheParent = keyDup (mountGetMountpoint (handle, initialParent));
     998       26178 :         if (ns == KEY_NS_CASCADING) keySetMeta (cacheParent, "cascading", "");
     999       26178 :         if (handle->globalPlugins[PREGETCACHE][MAXONCE])
    1000             :         {
    1001           0 :                 elektraCacheLoad (handle, cache, parentKey, initialParent, cacheParent);
    1002             :         }
    1003             : 
    1004             :         // Check if a update is needed at all
    1005       26178 :         switch (elektraGetCheckUpdateNeeded (split, parentKey))
    1006             :         {
    1007             :         case -2: // We have a cache hit
    1008           0 :                 if (elektraCacheLoadSplit (handle, split, ks, &cache, &cacheParent, parentKey, initialParent, debugGlobalPositions) != 0)
    1009             :                 {
    1010             :                         goto cachemiss;
    1011             :                 }
    1012             : 
    1013           0 :                 keySetName (parentKey, keyName (initialParent));
    1014           0 :                 splitUpdateFileName (split, handle, parentKey);
    1015           0 :                 keyDel (initialParent);
    1016           0 :                 splitDel (split);
    1017           0 :                 errno = errnosave;
    1018           0 :                 keyDel (oldError);
    1019           0 :                 return 1;
    1020             :         case 0: // We don't need an update so let's do nothing
    1021             : 
    1022       10418 :                 if (debugGlobalPositions)
    1023             :                 {
    1024           4 :                         keySetName (parentKey, keyName (initialParent));
    1025           4 :                         if (elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, INIT) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1026             :                         {
    1027             :                                 goto error;
    1028             :                         }
    1029           4 :                         if (elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, MAXONCE) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1030             :                         {
    1031             :                                 goto error;
    1032             :                         }
    1033           4 :                         if (elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, DEINIT) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1034             :                         {
    1035             :                                 goto error;
    1036             :                         }
    1037             : 
    1038           4 :                         keySetName (parentKey, keyName (initialParent));
    1039           4 :                         if (elektraGlobalGet (handle, ks, parentKey, PROCGETSTORAGE, INIT) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1040             :                         {
    1041             :                                 goto error;
    1042             :                         }
    1043           4 :                         if (elektraGlobalGet (handle, ks, parentKey, PROCGETSTORAGE, MAXONCE) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1044             :                         {
    1045             :                                 goto error;
    1046             :                         }
    1047           4 :                         if (elektraGlobalGet (handle, ks, parentKey, PROCGETSTORAGE, DEINIT) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1048             :                         {
    1049             :                                 goto error;
    1050             :                         }
    1051             :                 }
    1052             : 
    1053       10418 :                 ksDel (cache);
    1054       10418 :                 cache = 0;
    1055       10418 :                 keyDel (cacheParent);
    1056       10418 :                 cacheParent = 0;
    1057             : 
    1058       10418 :                 if (debugGlobalPositions)
    1059             :                 {
    1060           4 :                         keySetName (parentKey, keyName (initialParent));
    1061           4 :                         if (elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, INIT) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1062             :                         {
    1063             :                                 goto error;
    1064             :                         }
    1065           4 :                         if (elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1066             :                         {
    1067             :                                 goto error;
    1068             :                         }
    1069           4 :                         if (elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, DEINIT) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1070             :                         {
    1071             :                                 goto error;
    1072             :                         }
    1073             :                 }
    1074             : 
    1075       10418 :                 keySetName (parentKey, keyName (initialParent));
    1076       10418 :                 splitUpdateFileName (split, handle, parentKey);
    1077       10418 :                 keyDel (initialParent);
    1078       10418 :                 splitDel (split);
    1079       10418 :                 errno = errnosave;
    1080       10418 :                 keyDel (oldError);
    1081       10418 :                 return 0;
    1082             :         case -1:
    1083             :                 goto error;
    1084             :                 // otherwise fall trough
    1085             :         }
    1086             : 
    1087             : cachemiss:
    1088       15760 :         ksDel (cache);
    1089       15760 :         cache = 0;
    1090             : 
    1091       15760 :         if (elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, INIT) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1092             :         {
    1093             :                 goto error;
    1094             :         }
    1095       15760 :         if (elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, MAXONCE) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1096             :         {
    1097             :                 goto error;
    1098             :         }
    1099       15760 :         if (elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, DEINIT) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1100             :         {
    1101             :                 goto error;
    1102             :         }
    1103             : 
    1104             :         // Appoint keys (some in the bypass)
    1105       15760 :         if (splitAppoint (split, handle, ks) == -1)
    1106             :         {
    1107           0 :                 clearError (parentKey);
    1108           0 :                 ELEKTRA_SET_INTERNAL_ERROR (parentKey, "Error in splitAppoint");
    1109           0 :                 goto error;
    1110             :         }
    1111             : 
    1112       31520 :         if (handle->globalPlugins[POSTGETSTORAGE][FOREACH] || handle->globalPlugins[POSTGETCLEANUP][FOREACH] ||
    1113       47280 :             handle->globalPlugins[PROCGETSTORAGE][FOREACH] || handle->globalPlugins[PROCGETSTORAGE][INIT] ||
    1114       26311 :             handle->globalPlugins[PROCGETSTORAGE][MAXONCE] || handle->globalPlugins[PROCGETSTORAGE][DEINIT])
    1115             :         {
    1116        5209 :                 clearError (parentKey);
    1117        5209 :                 if (elektraGetDoUpdateWithGlobalHooks (handle, split, ks, parentKey, initialParent, FIRST) == -1)
    1118             :                 {
    1119             :                         goto error;
    1120             :                 }
    1121             :                 else
    1122             :                 {
    1123        5196 :                         copyError (parentKey, oldError);
    1124             :                 }
    1125             : 
    1126        5196 :                 keySetName (parentKey, keyName (initialParent));
    1127             : 
    1128        5196 :                 if (splitGet (split, parentKey, handle) == -1)
    1129             :                 {
    1130           0 :                         ELEKTRA_ADD_PLUGIN_MISBEHAVIOR_WARNINGF (parentKey, "Wrong keys in postprocessing: %s", keyName (ksCurrent (ks)));
    1131             :                         // continue, because sizes are already updated
    1132             :                 }
    1133        5196 :                 ksClear (ks);
    1134        5196 :                 splitMergeBackends (split, ks);
    1135             : 
    1136        5196 :                 clearError (parentKey);
    1137       10392 :                 if (elektraGetDoUpdateWithGlobalHooks (handle, split, ks, parentKey, initialParent, LAST) == -1)
    1138             :                 {
    1139             :                         goto error;
    1140             :                 }
    1141             :                 else
    1142             :                 {
    1143        5196 :                         copyError (parentKey, oldError);
    1144             :                 }
    1145             :         }
    1146             :         else
    1147             :         {
    1148             : 
    1149             :                 /* Now do the real updating,
    1150             :                    but not for bypassed keys in split->size-1 */
    1151       10551 :                 clearError (parentKey);
    1152             :                 // do everything up to position get_storage
    1153       10551 :                 if (elektraGetDoUpdate (split, parentKey) == -1)
    1154             :                 {
    1155             :                         goto error;
    1156             :                 }
    1157             :                 else
    1158             :                 {
    1159       10551 :                         copyError (parentKey, oldError);
    1160             :                 }
    1161             : 
    1162             :                 /* Now post-process the updated keysets */
    1163       10551 :                 if (splitGet (split, parentKey, handle) == -1)
    1164             :                 {
    1165           0 :                         ELEKTRA_ADD_PLUGIN_MISBEHAVIOR_WARNINGF (parentKey, "Wrong keys in postprocessing: %s", keyName (ksCurrent (ks)));
    1166             :                         // continue, because sizes are already updated
    1167             :                 }
    1168             : 
    1169       10551 :                 ksClear (ks);
    1170       10551 :                 splitMergeBackends (split, ks);
    1171             :         }
    1172             : 
    1173       15747 :         keySetName (parentKey, keyName (initialParent));
    1174             : 
    1175       15747 :         if (elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, INIT) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1176             :         {
    1177             :                 goto error;
    1178             :         }
    1179       15747 :         if (elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1180             :         {
    1181             :                 goto error;
    1182             :         }
    1183       15744 :         if (elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, DEINIT) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1184             :         {
    1185             :                 goto error;
    1186             :         }
    1187             : 
    1188       15744 :         if (handle->globalPlugins[POSTGETCACHE][MAXONCE])
    1189             :         {
    1190           0 :                 splitCacheStoreState (handle, split, handle->global, cacheParent, initialParent);
    1191           0 :                 KeySet * proc = elektraCutProc (ks); // remove proc keys before caching
    1192           0 :                 if (elektraGlobalSet (handle, ks, cacheParent, POSTGETCACHE, MAXONCE) != ELEKTRA_PLUGIN_STATUS_SUCCESS)
    1193             :                 {
    1194             :                         ELEKTRA_LOG_DEBUG ("CACHE ERROR: could not store cache");
    1195             :                         // we must remove the stored split state from the global keyset
    1196             :                         // if there was an error, otherwise we get erroneous cache hits
    1197           0 :                         elektraCacheCutMeta (handle);
    1198             :                 }
    1199             :                 elektraRestoreProc (ks, proc);
    1200             :         }
    1201             :         else
    1202             :         {
    1203       15744 :                 elektraCacheCutMeta (handle);
    1204             :         }
    1205       15744 :         keyDel (cacheParent);
    1206       15744 :         cacheParent = 0;
    1207             : 
    1208             :         // the default split is not handled by POSTGETSTORAGE
    1209       15744 :         splitMergeDefault (split, ks);
    1210             : 
    1211       15744 :         ksRewind (ks);
    1212             : 
    1213       15744 :         keySetName (parentKey, keyName (initialParent));
    1214             : 
    1215       15744 :         splitUpdateFileName (split, handle, parentKey);
    1216       15744 :         keyDel (initialParent);
    1217       15744 :         keyDel (oldError);
    1218       15744 :         splitDel (split);
    1219       15744 :         errno = errnosave;
    1220       15744 :         return 1;
    1221             : 
    1222             : error:
    1223             :         ELEKTRA_LOG_DEBUG ("now in error state");
    1224          18 :         if (cacheParent) keyDel (cacheParent);
    1225          18 :         if (cache) ksDel (cache);
    1226          18 :         keySetName (parentKey, keyName (initialParent));
    1227          18 :         elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, INIT);
    1228          18 :         elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE);
    1229          18 :         elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);
    1230             : 
    1231          18 :         keySetName (parentKey, keyName (initialParent));
    1232          18 :         if (handle) splitUpdateFileName (split, handle, parentKey);
    1233          18 :         keyDel (initialParent);
    1234          18 :         keyDel (oldError);
    1235          18 :         splitDel (split);
    1236          18 :         errno = errnosave;
    1237          18 :         return -1;
    1238             : }
    1239             : 
    1240             : /**
    1241             :  * @internal
    1242             :  * @brief Does all set steps but not commit
    1243             :  *
    1244             :  * @param split all information for iteration
    1245             :  * @param parentKey to add warnings (also passed to plugins for the same reason)
    1246             :  * @param [out] errorKey may point to which key caused the error or 0 otherwise
    1247             :  *
    1248             :  * @retval -1 on error
    1249             :  * @retval 0 on success
    1250             :  */
    1251        2623 : static int elektraSetPrepare (Split * split, Key * parentKey, Key ** errorKey, Plugin * hooks[][NR_GLOBAL_SUBPOSITIONS])
    1252             : {
    1253        2623 :         int any_error = 0;
    1254        5297 :         for (size_t i = 0; i < split->size; i++)
    1255             :         {
    1256       17367 :                 for (size_t p = 0; p < COMMIT_PLUGIN; ++p)
    1257             :                 {
    1258       17560 :                         int ret = 0; // last return value
    1259             : 
    1260       17560 :                         Backend * backend = split->handles[i];
    1261       17560 :                         ksRewind (split->keysets[i]);
    1262       17560 :                         if (backend->setplugins[p] && backend->setplugins[p]->kdbSet)
    1263             :                         {
    1264        6773 :                                 if (p != 0)
    1265             :                                 {
    1266        4117 :                                         keySetString (parentKey, keyString (split->parents[i]));
    1267             :                                 }
    1268             :                                 else
    1269             :                                 {
    1270        2656 :                                         keySetString (parentKey, "");
    1271             :                                 }
    1272        6773 :                                 keySetName (parentKey, keyName (split->parents[i]));
    1273        6773 :                                 ret = backend->setplugins[p]->kdbSet (backend->setplugins[p], split->keysets[i], parentKey);
    1274             : 
    1275             : #if VERBOSE && DEBUG
    1276             :                                 printf ("Prepare %s with keys %zd in plugin: %zu, split: %zu, ret: %d\n", keyName (parentKey),
    1277             :                                         ksGetSize (split->keysets[i]), p, i, ret);
    1278             : #endif
    1279             : 
    1280        6773 :                                 if (p == 0)
    1281             :                                 {
    1282        2656 :                                         if (ret == 0)
    1283             :                                         {
    1284             :                                                 // resolver says that sync is
    1285             :                                                 // not needed, so we
    1286             :                                                 // skip other pre-commit
    1287             :                                                 // plugins
    1288             :                                                 break;
    1289             :                                         }
    1290        2463 :                                         keySetString (split->parents[i], keyString (parentKey));
    1291             :                                 }
    1292             :                         }
    1293             : 
    1294       17367 :                         if (p == 0)
    1295             :                         {
    1296        2481 :                                 if (hooks[PRESETSTORAGE][FOREACH])
    1297             :                                 {
    1298           0 :                                         ksRewind (split->keysets[i]);
    1299           0 :                                         hooks[PRESETSTORAGE][FOREACH]->kdbSet (hooks[PRESETSTORAGE][FOREACH], split->keysets[i], parentKey);
    1300             :                                 }
    1301             :                         }
    1302       14886 :                         else if (p == (STORAGE_PLUGIN - 1))
    1303             :                         {
    1304        2481 :                                 if (hooks[PRESETCLEANUP][FOREACH])
    1305             :                                 {
    1306           0 :                                         ksRewind (split->keysets[i]);
    1307           0 :                                         hooks[PRESETCLEANUP][FOREACH]->kdbSet (hooks[PRESETCLEANUP][FOREACH], split->keysets[i], parentKey);
    1308             :                                 }
    1309             :                         }
    1310             : 
    1311       17367 :                         if (ret == -1)
    1312             :                         {
    1313             :                                 // do not
    1314             :                                 // abort because it might
    1315             :                                 // corrupt the KeySet
    1316             :                                 // and leads to warnings
    1317             :                                 // because of .tmp files not
    1318             :                                 // found
    1319          88 :                                 *errorKey = ksCurrent (split->keysets[i]);
    1320             : 
    1321             :                                 // so better keep going, but of
    1322             :                                 // course we will not commit
    1323          88 :                                 any_error = -1;
    1324             :                         }
    1325             :                 }
    1326             :         }
    1327        2623 :         return any_error;
    1328             : }
    1329             : 
    1330             : /**
    1331             :  * @internal
    1332             :  * @brief Does the commit
    1333             :  *
    1334             :  * @param split all information for iteration
    1335             :  * @param parentKey to add warnings (also passed to plugins for the same reason)
    1336             :  */
    1337        2535 : static void elektraSetCommit (Split * split, Key * parentKey)
    1338             : {
    1339       10140 :         for (size_t p = COMMIT_PLUGIN; p < NR_OF_PLUGINS; ++p)
    1340             :         {
    1341        7707 :                 for (size_t i = 0; i < split->size; i++)
    1342             :                 {
    1343        7707 :                         int ret = 0;
    1344        7707 :                         Backend * backend = split->handles[i];
    1345             : 
    1346        7707 :                         if (backend->setplugins[p] && backend->setplugins[p]->kdbSet)
    1347             :                         {
    1348        2565 :                                 if (p != COMMIT_PLUGIN)
    1349             :                                 {
    1350           2 :                                         keySetString (parentKey, keyString (split->parents[i]));
    1351             :                                 }
    1352        2565 :                                 keySetName (parentKey, keyName (split->parents[i]));
    1353             : #if DEBUG && VERBOSE
    1354             :                                 printf ("elektraSetCommit: %p # %zu with %s - %s\n", backend, p, keyName (parentKey),
    1355             :                                         keyString (parentKey));
    1356             : #endif
    1357        2565 :                                 ksRewind (split->keysets[i]);
    1358        2565 :                                 if (p == COMMIT_PLUGIN)
    1359             :                                 {
    1360        2563 :                                         ret = backend->setplugins[p]->kdbCommit (backend->setplugins[p], split->keysets[i], parentKey);
    1361             :                                         // name of non-temp file
    1362        2563 :                                         keySetString (split->parents[i], keyString (parentKey));
    1363             :                                 }
    1364             :                                 else
    1365             :                                 {
    1366           2 :                                         ret = backend->setplugins[p]->kdbSet (backend->setplugins[p], split->keysets[i], parentKey);
    1367             :                                 }
    1368             :                         }
    1369             : 
    1370        7707 :                         if (ret == -1)
    1371             :                         {
    1372           0 :                                 ELEKTRA_ADD_INTERNAL_WARNINGF (parentKey, "Error during commit. This means backend is broken: %s",
    1373             :                                                                keyName (backend->mountpoint));
    1374             :                         }
    1375             :                 }
    1376             :         }
    1377        2535 : }
    1378             : 
    1379             : /**
    1380             :  * @internal
    1381             :  * @brief Does the rollback
    1382             :  *
    1383             :  * @param split all information for iteration
    1384             :  * @param parentKey to add warnings (also passed to plugins for the same reason)
    1385             :  */
    1386          88 : static void elektraSetRollback (Split * split, Key * parentKey)
    1387             : {
    1388         968 :         for (size_t p = 0; p < NR_OF_PLUGINS; ++p)
    1389             :         {
    1390        1050 :                 for (size_t i = 0; i < split->size; i++)
    1391             :                 {
    1392        1050 :                         int ret = 0;
    1393        1050 :                         Backend * backend = split->handles[i];
    1394             : 
    1395        1050 :                         ksRewind (split->keysets[i]);
    1396        1050 :                         if (backend->errorplugins[p])
    1397             :                         {
    1398          89 :                                 keySetName (parentKey, keyName (split->parents[i]));
    1399          89 :                                 ret = backend->errorplugins[p]->kdbError (backend->errorplugins[p], split->keysets[i], parentKey);
    1400             :                         }
    1401             : 
    1402        1050 :                         if (ret == -1)
    1403             :                         {
    1404           0 :                                 ELEKTRA_ADD_INTERNAL_WARNINGF (parentKey, "Error during rollback. This means backend is broken: %s",
    1405             :                                                                keyName (backend->mountpoint));
    1406             :                         }
    1407             :                 }
    1408             :         }
    1409          88 : }
    1410             : 
    1411             : 
    1412             : /** @brief Set keys in an atomic and universal way.
    1413             :  *
    1414             :  * @pre kdbGet() must be called before kdbSet():
    1415             :  *    - initially (after kdbOpen())
    1416             :  *    - after conflict errors in kdbSet().
    1417             :  *
    1418             :  * @pre The @p returned KeySet must be a valid KeySet, e.g. constructed
    1419             :  *     with ksNew().
    1420             :  *
    1421             :  * @pre The @p parentKey Key must be a valid Key, e.g. constructed with
    1422             :  *     keyNew().
    1423             :  *
    1424             :  * If you pass NULL on any parameter kdbSet() will fail immediately without doing anything.
    1425             :  *
    1426             :  * With @p parentKey you can give an hint which part of the given keyset
    1427             :  * is of interest for you. Then you promise to only modify or
    1428             :  * remove keys below this key. All others would be passed back
    1429             :  * as they were retrieved by kdbGet().
    1430             :  *
    1431             :  * @par Errors
    1432             :  * If some error occurs:
    1433             :  * - kdbSet() will leave the KeySet's * internal cursor on the key that generated the error.
    1434             :  * - Error information will be written into the metadata of
    1435             :  *   the parent key.
    1436             :  * - None of the keys are actually committed in this situation, i.e. no
    1437             :  *   configuration file will be modified.
    1438             :  *
    1439             :  * In case of errors you should present the error message to the user and let the user decide what
    1440             :  * to do. Possible solutions are:
    1441             :  * - remove the problematic key and use kdbSet() again (for validation or type errors)
    1442             :  * - change the value of the problematic key and use kdbSet() again (for validation errors)
    1443             :  * - do a kdbGet() (for conflicts, i.e. error 30) and then
    1444             :  *   - set the same keyset again (in favour of what was set by this user)
    1445             :  *   - drop the old keyset (in favour of what was set from another application)
    1446             :  *   - merge the original, your own and the other keyset
    1447             :  * - export the configuration into a file (for unresolvable errors)
    1448             :  * - repeat the same kdbSet might be of limited use if the user does
    1449             :  *   not explicitly request it, because temporary
    1450             :  *   errors are rare and its unlikely that they fix themselves
    1451             :  *   (e.g. disc full, permission problems)
    1452             :  *
    1453             :  * @par Optimization
    1454             :  * Each key is checked with keyNeedSync() before being actually committed.
    1455             :  * If no key of a backend needs to be synced
    1456             :  * any affairs to backends are omitted and 0 is returned.
    1457             :  *
    1458             :  * @snippet kdbset.c set
    1459             :  *
    1460             :  * showElektraErrorDialog() and doElektraMerge() need to be implemented
    1461             :  * by the user of Elektra. For doElektraMerge a 3-way merge algorithm exists in
    1462             :  * libelektra-tools.
    1463             :  *
    1464             :  * @param handle contains internal information of @link kdbOpen() opened @endlink key database
    1465             :  * @param ks a KeySet which should contain changed keys, otherwise nothing is done
    1466             :  * @param parentKey is used to add warnings and set an error
    1467             :  *         information. Additionally, its name is an hint which keys
    1468             :  *         should be committed (it is possible that more are changed).
    1469             :  *           - cascading keys (starting with /) will set the path in all namespaces
    1470             :  *           - / will commit all keys
    1471             :  *           - metanames will be rejected (error 104)
    1472             :  *           - empty/invalid (error 105)
    1473             :  * @retval 1 on success
    1474             :  * @retval 0 if nothing had to be done, no changes in KDB
    1475             :  * @retval -1 on failure, no changes in KDB
    1476             :  * @see keyNeedSync()
    1477             :  * @see ksCurrent() contains the error key
    1478             :  * @see kdbOpen() and kdbGet() that must be called first
    1479             :  * @see kdbClose() that must be called afterwards
    1480             :  * @ingroup kdb
    1481             :  */
    1482        2740 : int kdbSet (KDB * handle, KeySet * ks, Key * parentKey)
    1483             : {
    1484        2740 :         elektraNamespace ns = keyGetNamespace (parentKey);
    1485        2740 :         if (ns == KEY_NS_NONE)
    1486             :         {
    1487             :                 ELEKTRA_LOG ("ns == KEY_NS_NONE");
    1488             :                 return -1;
    1489             :         }
    1490        2740 :         Key * oldError = keyNew (keyName (parentKey), KEY_END);
    1491        2740 :         copyError (oldError, parentKey);
    1492             : 
    1493        2740 :         if (ns == KEY_NS_META)
    1494             :         {
    1495           2 :                 clearError (parentKey); // clear previous error to set new one
    1496           2 :                 ELEKTRA_SET_INTERFACE_ERRORF (parentKey, "Metakey with name '%s' passed to kdbSet as parentkey", keyName (parentKey));
    1497           2 :                 keyDel (oldError);
    1498             :                 ELEKTRA_LOG ("ns == KEY_NS_META");
    1499           2 :                 return -1;
    1500             :         }
    1501             : 
    1502        2738 :         if (ns == KEY_NS_EMPTY)
    1503             :         {
    1504           0 :                 ELEKTRA_ADD_INTERFACE_WARNING (parentKey, "Invalid key name passed to kdbSet");
    1505             :                 ELEKTRA_LOG ("ns == KEY_NS_EMPTY");
    1506             :         }
    1507             : 
    1508        2738 :         if (!handle || !ks)
    1509             :         {
    1510           0 :                 clearError (parentKey); // clear previous error to set new one
    1511           0 :                 ELEKTRA_SET_INTERFACE_ERROR (parentKey, "Handle or KeySet null pointer passed");
    1512           0 :                 keyDel (oldError);
    1513             :                 ELEKTRA_LOG ("!handle || !ks");
    1514           0 :                 return -1;
    1515             :         }
    1516             : 
    1517        2738 :         int errnosave = errno;
    1518        2738 :         Key * initialParent = keyDup (parentKey);
    1519             : 
    1520             :         ELEKTRA_LOG ("now in new kdbSet (%s) %p %zd", keyName (parentKey), (void *) handle, ksGetSize (ks));
    1521             : 
    1522        2738 :         elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, INIT);
    1523        2738 :         elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, MAXONCE);
    1524        2738 :         elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, DEINIT);
    1525             : 
    1526             :         ELEKTRA_LOG ("after presetstorage maxonce(%s) %p %zd", keyName (parentKey), (void *) handle, ksGetSize (ks));
    1527             : 
    1528        2738 :         Split * split = splitNew ();
    1529        2738 :         Key * errorKey = 0;
    1530             : 
    1531        2738 :         if (splitBuildup (split, handle, parentKey) == -1)
    1532             :         {
    1533           0 :                 clearError (parentKey); // clear previous error to set new one
    1534           0 :                 ELEKTRA_SET_INTERNAL_ERROR (parentKey, "Error in splitBuildup");
    1535           0 :                 goto error;
    1536             :         }
    1537             :         ELEKTRA_LOG ("after splitBuildup");
    1538             : 
    1539             :         // 1.) Search for syncbits
    1540        2738 :         int syncstate = splitDivide (split, handle, ks);
    1541        2738 :         if (syncstate == -1)
    1542             :         {
    1543           0 :                 clearError (parentKey); // clear previous error to set new one
    1544           0 :                 ELEKTRA_SET_INSTALLATION_ERRORF (parentKey, "No default backend found, but should be. Keyname: %s",
    1545             :                                                  keyName (ksCurrent (ks)));
    1546           0 :                 goto error;
    1547             :         }
    1548        2738 :         ELEKTRA_ASSERT (syncstate == 0 || syncstate == 1, "syncstate not 0 or 1, but %d", syncstate);
    1549             :         ELEKTRA_LOG ("after 1.) Search for syncbits");
    1550             : 
    1551             :         // 2.) Search for changed sizes
    1552        2738 :         syncstate |= splitSync (split);
    1553        2738 :         ELEKTRA_ASSERT (syncstate <= 1, "syncstate not equal or below 1, but %d", syncstate);
    1554        2738 :         if (syncstate != 1)
    1555             :         {
    1556             :                 /* No update is needed */
    1557             :                 ELEKTRA_LOG ("No update is needed");
    1558         115 :                 keySetName (parentKey, keyName (initialParent));
    1559         115 :                 if (syncstate < 0) clearError (parentKey); // clear previous error to set new one
    1560         115 :                 if (syncstate == -1)
    1561             :                 {
    1562           1 :                         ELEKTRA_SET_INTERNAL_ERROR (parentKey, "Assert failed: invalid namespace");
    1563             :                         ELEKTRA_LOG ("syncstate == -1");
    1564             :                 }
    1565         114 :                 else if (syncstate < -1)
    1566             :                 {
    1567             :                         /*TODO: Solution (Execute kdbGet before kdbSet)*/
    1568           7 :                         ELEKTRA_SET_CONFLICTING_STATE_ERRORF (
    1569             :                                 parentKey, "Sync state is wrong, maybe 'kdbSet()' is executed without prior 'kdbGet()' on %s",
    1570             :                                 keyName (split->parents[-syncstate - 2]));
    1571             :                         ELEKTRA_LOG ("syncstate < -1");
    1572             :                 }
    1573         115 :                 keyDel (initialParent);
    1574         115 :                 splitDel (split);
    1575         115 :                 errno = errnosave;
    1576         115 :                 keyDel (oldError);
    1577             :                 ELEKTRA_LOG ("return: %d", syncstate == 0 ? 0 : -1);
    1578         115 :                 return syncstate == 0 ? 0 : -1;
    1579             :         }
    1580             :         ELEKTRA_ASSERT (syncstate == 1, "syncstate not 1, but %d", syncstate);
    1581             :         ELEKTRA_LOG ("after 2.) Search for changed sizes");
    1582             : 
    1583        2623 :         splitPrepare (split);
    1584             : 
    1585        2623 :         clearError (parentKey); // clear previous error to set new one
    1586        2623 :         if (elektraSetPrepare (split, parentKey, &errorKey, handle->globalPlugins) == -1)
    1587             :         {
    1588             :                 goto error;
    1589             :         }
    1590             :         else
    1591             :         {
    1592             :                 // no error, restore old error
    1593        2535 :                 copyError (parentKey, oldError);
    1594             :         }
    1595        2535 :         keySetName (parentKey, keyName (initialParent));
    1596             : 
    1597        2535 :         elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, INIT);
    1598        2535 :         elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, MAXONCE);
    1599        2535 :         elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, DEINIT);
    1600             : 
    1601        2535 :         elektraSetCommit (split, parentKey);
    1602             : 
    1603        2535 :         elektraGlobalSet (handle, ks, parentKey, COMMIT, INIT);
    1604        2535 :         elektraGlobalSet (handle, ks, parentKey, COMMIT, MAXONCE);
    1605        2535 :         elektraGlobalSet (handle, ks, parentKey, COMMIT, DEINIT);
    1606             : 
    1607        2535 :         splitUpdateSize (split);
    1608             : 
    1609        2535 :         keySetName (parentKey, keyName (initialParent));
    1610             : 
    1611        2535 :         elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, INIT);
    1612        2535 :         elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, MAXONCE);
    1613        2535 :         elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, DEINIT);
    1614             : 
    1615      166685 :         for (size_t i = 0; i < ks->size; ++i)
    1616             :         {
    1617             :                 // remove all flags from all keys
    1618      164150 :                 clear_bit (ks->array[i]->flags, (keyflag_t) KEY_FLAG_SYNC);
    1619             :         }
    1620             : 
    1621        2535 :         keySetName (parentKey, keyName (initialParent));
    1622        2535 :         keyDel (initialParent);
    1623        2535 :         splitDel (split);
    1624             : 
    1625        2535 :         keyDel (oldError);
    1626        2535 :         errno = errnosave;
    1627             :         ELEKTRA_LOG ("before RETURN 1");
    1628        2535 :         return 1;
    1629             : 
    1630             : error:
    1631          88 :         keySetName (parentKey, keyName (initialParent));
    1632             : 
    1633          88 :         elektraGlobalError (handle, ks, parentKey, PREROLLBACK, INIT);
    1634          88 :         elektraGlobalError (handle, ks, parentKey, PREROLLBACK, MAXONCE);
    1635          88 :         elektraGlobalError (handle, ks, parentKey, PREROLLBACK, DEINIT);
    1636             : 
    1637          88 :         elektraSetRollback (split, parentKey);
    1638             : 
    1639          88 :         if (errorKey)
    1640             :         {
    1641          59 :                 Key * found = ksLookup (ks, errorKey, 0);
    1642          59 :                 if (!found)
    1643             :                 {
    1644           0 :                         ELEKTRA_ADD_INTERNAL_WARNINGF (parentKey, "Error key %s not found in keyset even though it was found before",
    1645             :                                                        keyName (errorKey));
    1646             :                 }
    1647             :         }
    1648             : 
    1649          88 :         keySetName (parentKey, keyName (initialParent));
    1650             : 
    1651          88 :         elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, INIT);
    1652          88 :         elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, MAXONCE);
    1653          88 :         elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, DEINIT);
    1654             : 
    1655          88 :         keySetName (parentKey, keyName (initialParent));
    1656          88 :         keyDel (initialParent);
    1657          88 :         splitDel (split);
    1658          88 :         errno = errnosave;
    1659          88 :         keyDel (oldError);
    1660          88 :         return -1;
    1661             : }
    1662             : 
    1663             : /**
    1664             :  * Checks whether the same instance of the list plugin is mounted in the global (maxonce) positions:
    1665             :  *
    1666             :  * pregetstorage, procgetstorage, postgetstorage, postgetcleanup,
    1667             :  * presetstorage, presetcleanup, precommit, postcommit,
    1668             :  * prerollback and postrollback
    1669             :  *
    1670             :  * @param handle the KDB to check
    1671             :  *
    1672             :  * @retval 1 if list is mounted everywhere
    1673             :  * @retval 0 otherwise
    1674             :  */
    1675          70 : static int ensureListPluginMountedEverywhere (KDB * handle)
    1676             : {
    1677          70 :         GlobalpluginPositions expectedPositions[] = { PREGETSTORAGE,
    1678             :                                                       PROCGETSTORAGE,
    1679             :                                                       POSTGETSTORAGE,
    1680             :                                                       POSTGETCLEANUP,
    1681             :                                                       PRESETSTORAGE,
    1682             :                                                       PRESETCLEANUP,
    1683             :                                                       PRECOMMIT,
    1684             :                                                       POSTCOMMIT,
    1685             :                                                       PREROLLBACK,
    1686             :                                                       POSTROLLBACK,
    1687             :                                                       -1 };
    1688             : 
    1689          70 :         Plugin * list = handle->globalPlugins[expectedPositions[0]][MAXONCE];
    1690          70 :         if (list == NULL || elektraStrCmp (list->name, "list") != 0)
    1691             :         {
    1692             :                 ELEKTRA_LOG_WARNING ("list plugin not mounted at position %s/maxonce", GlobalpluginPositionsStr[expectedPositions[0]]);
    1693             :                 return 0;
    1694             :         }
    1695             : 
    1696         490 :         for (int i = 1; expectedPositions[i] > 0; ++i)
    1697             :         {
    1698         490 :                 Plugin * plugin = handle->globalPlugins[expectedPositions[i]][MAXONCE];
    1699         490 :                 if (plugin != list)
    1700             :                 {
    1701             :                         // must always be the same instance
    1702             :                         ELEKTRA_LOG_WARNING ("list plugin not mounted at position %s/maxonce",
    1703             :                                              GlobalpluginPositionsStr[expectedPositions[i]]);
    1704             :                         return 0;
    1705             :                 }
    1706             :         }
    1707             : 
    1708             :         return 1;
    1709             : }
    1710             : 
    1711             : /**
    1712             :  * Finds the global placements in which a plugin should be mounted and mounts the plugin there, if it isn't already.
    1713             :  *
    1714             :  * @post The plugin is mounted globally in all placements defined in its infos/placements key.
    1715             :  *
    1716             :  * @param handle        the KDB handle to use
    1717             :  * @param pluginName    the name of the plugin to mount
    1718             :  * @param pluginConfig  the configuration to use, if the plugin has to be mounted
    1719             :  * @param errorKey      used for error reporting
    1720             :  *
    1721             :  * @retval  0 on success
    1722             :  * @retval -1 on error in list plugin
    1723             :  * @retval -2 on other errors, warnings will be logged
    1724             :  */
    1725          42 : static int ensureGlobalPluginMounted (KDB * handle, const char * pluginName, KeySet * pluginConfig, Key * errorKey)
    1726             : {
    1727          42 :         ELEKTRA_NOT_NULL (handle);
    1728          42 :         ELEKTRA_NOT_NULL (pluginName);
    1729             : 
    1730          42 :         if (!ensureListPluginMountedEverywhere (handle))
    1731             :         {
    1732             :                 ELEKTRA_LOG_WARNING ("the list plugin MUST be mounted in all global positions, or kdbEnsure will not work");
    1733             :                 return -2;
    1734             :         }
    1735             : 
    1736          42 :         Plugin * listPlugin = handle->globalPlugins[PREROLLBACK][MAXONCE]; // take any position
    1737             :         typedef int (*mountPluginFun) (Plugin *, const char *, KeySet *, Key *);
    1738          42 :         mountPluginFun listAddPlugin = (mountPluginFun) elektraPluginGetFunction (listPlugin, "mountplugin");
    1739             : 
    1740          42 :         int result = listAddPlugin (listPlugin, pluginName, pluginConfig, errorKey);
    1741          42 :         if (result == ELEKTRA_PLUGIN_STATUS_ERROR)
    1742             :         {
    1743             :                 ELEKTRA_LOG_WARNING ("could not add plugin %s to list plugin", pluginName);
    1744           0 :                 ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "The global plugin %s couldn't be mounted (via the list plugin)",
    1745             :                                                         pluginName);
    1746           0 :                 return -1;
    1747             :         }
    1748             : 
    1749             :         return 0;
    1750             : }
    1751             : 
    1752             : /**
    1753             :  * Finds the global placements in which a plugin should be mounted and removes the plugin from these, if it is present.
    1754             :  *
    1755             :  * @post The plugin is not mounted globally in any of the placements defined in its infos/placements key.
    1756             :  *
    1757             :  * @param handle        the KDB handle to use
    1758             :  * @param pluginName    the name of the plugin to mount
    1759             :  * @param errorKey      used for error reporting
    1760             :  *
    1761             :  * @retval  0 on success
    1762             :  * @retval -1 on error in list plugin
    1763             :  * @retval -2 on other errors, warnings will be logged
    1764             :  */
    1765          28 : static int ensureGlobalPluginUnmounted (KDB * handle, const char * pluginName, Key * errorKey)
    1766             : {
    1767          28 :         ELEKTRA_NOT_NULL (handle);
    1768          28 :         ELEKTRA_NOT_NULL (pluginName);
    1769             : 
    1770          28 :         if (!ensureListPluginMountedEverywhere (handle))
    1771             :         {
    1772             :                 ELEKTRA_LOG_WARNING ("the list plugin MUST be mounted in all global positions, or kdbEnsure will not work");
    1773             :                 return -2;
    1774             :         }
    1775             : 
    1776          28 :         Plugin * listPlugin = handle->globalPlugins[PREROLLBACK][MAXONCE]; // take any position
    1777             :         typedef int (*unmountPluginFun) (Plugin *, const char *, Key *);
    1778          28 :         unmountPluginFun listRemovePlugin = (unmountPluginFun) elektraPluginGetFunction (listPlugin, "unmountplugin");
    1779             : 
    1780          28 :         int result = listRemovePlugin (listPlugin, pluginName, errorKey);
    1781          28 :         if (result == ELEKTRA_PLUGIN_STATUS_ERROR)
    1782             :         {
    1783             :                 ELEKTRA_LOG_WARNING ("could not remove %s from list plugin", pluginName);
    1784           0 :                 ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "The global plugin %s couldn't be unmounted (via the list plugin)",
    1785             :                                                         pluginName);
    1786           0 :                 return -1;
    1787             :         }
    1788             : 
    1789             :         return 0;
    1790             : }
    1791             : 
    1792             : /**
    1793             :  * Finds the placements in which a plugin should be mounted and removes the plugin from these, if it is present.
    1794             :  * The functions only affects the mountpoint given in @p mountpoint.
    1795             :  *
    1796             :  * @post For mountpoint @p mountpoint, the plugin is not mounted in any of the placements defined in its infos/placements key.
    1797             :  *
    1798             :  * @param handle        the KDB handle to use
    1799             :  * @param mountpoint    the mountpoint to modify
    1800             :  * @param pluginName    the name of the plugin to mount
    1801             :  * @param errorKey      used for error reporting
    1802             :  *
    1803             :  * @retval 0 on error, warnings will be logged
    1804             :  * @retval 1 on success
    1805             :  */
    1806           4 : static int ensurePluginUnmounted (KDB * handle, const char * mountpoint, const char * pluginName, Key * errorKey)
    1807             : {
    1808           4 :         Key * mountpointKey = keyNew (mountpoint, KEY_END);
    1809           4 :         Backend * backend = mountGetBackend (handle, mountpointKey);
    1810             : 
    1811           4 :         int ret = 1;
    1812          44 :         for (int i = 0; i < NR_OF_PLUGINS; ++i)
    1813             :         {
    1814          40 :                 Plugin * getPlugin = backend->getplugins[i];
    1815          40 :                 Plugin * setPlugin = backend->setplugins[i];
    1816          40 :                 Plugin * errorPlugin = backend->errorplugins[i];
    1817             : 
    1818          40 :                 if (setPlugin != NULL && elektraStrCmp (setPlugin->name, pluginName) == 0)
    1819             :                 {
    1820           2 :                         if (elektraPluginClose (setPlugin, errorKey) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1821             :                         {
    1822           0 :                                 ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (
    1823             :                                         errorKey, "The plugin %s couldn't be closed (set, position: %d, mountpoint: %s)", pluginName, i,
    1824             :                                         mountpoint);
    1825           0 :                                 ret = 0;
    1826             :                         }
    1827           2 :                         backend->setplugins[i] = NULL;
    1828             :                 }
    1829             : 
    1830          40 :                 if (getPlugin != NULL && elektraStrCmp (getPlugin->name, pluginName) == 0)
    1831             :                 {
    1832           0 :                         if (elektraPluginClose (getPlugin, errorKey) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1833             :                         {
    1834           0 :                                 ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (
    1835             :                                         errorKey, "The plugin %s couldn't be closed (get, position: %d, mountpoint: %s)", pluginName, i,
    1836             :                                         mountpoint);
    1837           0 :                                 ret = 0;
    1838             :                         }
    1839           0 :                         backend->getplugins[i] = NULL;
    1840             :                 }
    1841             : 
    1842          40 :                 if (errorPlugin != NULL && elektraStrCmp (errorPlugin->name, pluginName) == 0)
    1843             :                 {
    1844           0 :                         if (elektraPluginClose (errorPlugin, errorKey) == ELEKTRA_PLUGIN_STATUS_ERROR)
    1845             :                         {
    1846           0 :                                 ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (
    1847             :                                         errorKey, "The plugin %s couldn't be closed (error, position: %d, mountpoint: %s)", pluginName, i,
    1848             :                                         mountpoint);
    1849           0 :                                 ret = 0;
    1850             :                         }
    1851           0 :                         backend->errorplugins[i] = NULL;
    1852             :                 }
    1853             :         }
    1854             : 
    1855           4 :         keyDel (mountpointKey);
    1856           4 :         return ret;
    1857             : }
    1858             : 
    1859             : enum PluginContractState
    1860             : {
    1861             :         PLUGIN_STATE_UNMOUNTED,
    1862             :         PLUGIN_STATE_MOUNTED,
    1863             :         PLUGIN_STATE_REMOUNT,
    1864             : };
    1865             : 
    1866             : /**
    1867             :  * Ensures a kdbEnsure() contract clause for a global plugin.
    1868             :  *
    1869             :  * @see kdbEnsure()
    1870             :  *
    1871             :  * @param handle       the KDB handle
    1872             :  * @param pluginName   the name of the plugin
    1873             :  * @param pluginState  the intended clause for the plugin
    1874             :  * @param pluginConfig the config KeySet for the plugin; is always consumed, i.e. you shouldn't ksDel() it after calling this
    1875             :  * @param errorKey     used for error reporting
    1876             :  *
    1877             :  * @retval  0 on success
    1878             :  * @retval -1 on error in list plugin
    1879             :  * @retval -2 on other errors
    1880             :  */
    1881          54 : static int ensureGlobalPluginState (KDB * handle, const char * pluginName, enum PluginContractState pluginState, KeySet * pluginConfig,
    1882             :                                     Key * errorKey)
    1883             : {
    1884          54 :         switch (pluginState)
    1885             :         {
    1886             :         case PLUGIN_STATE_UNMOUNTED:
    1887          12 :                 ksDel (pluginConfig);
    1888          12 :                 return ensureGlobalPluginUnmounted (handle, pluginName, errorKey);
    1889             :         case PLUGIN_STATE_MOUNTED:
    1890          26 :                 return ensureGlobalPluginMounted (handle, pluginName, pluginConfig, errorKey);
    1891             :         case PLUGIN_STATE_REMOUNT:
    1892             :         {
    1893          16 :                 int ret = ensureGlobalPluginUnmounted (handle, pluginName, errorKey);
    1894          16 :                 if (ret != 0)
    1895             :                 {
    1896             :                         return ret;
    1897             :                 }
    1898          16 :                 return ensureGlobalPluginMounted (handle, pluginName, pluginConfig, errorKey);
    1899             :         }
    1900             :         default:
    1901           0 :                 ELEKTRA_ASSERT (0, "missing switch case");
    1902             :                 return -2;
    1903             :         }
    1904             : }
    1905             : 
    1906             : /**
    1907             :  * Ensures a kdbEnsure() contract clause for a plugin under a certain mountpoint.
    1908             :  *
    1909             :  * @see kdbEnsure()
    1910             :  *
    1911             :  * @param handle       the KDB handle
    1912             :  * @param mountpoint   the mountpoint to use
    1913             :  * @param pluginName   the name of the plugin
    1914             :  * @param pluginState  the intended clause for the plugin
    1915             :  * @param pluginConfig the config KeySet for the plugin; is always consumed, i.e. you shouldn't ksDel() it after calling this
    1916             :  * @param errorKey     used for error reporting
    1917             :  *
    1918             :  * @retval 1 on success
    1919             :  * @retval 0 otherwise
    1920             :  */
    1921           4 : static int ensurePluginState (KDB * handle ELEKTRA_UNUSED, const char * mountpoint ELEKTRA_UNUSED, const char * pluginName ELEKTRA_UNUSED,
    1922             :                               enum PluginContractState pluginState, KeySet * pluginConfig ELEKTRA_UNUSED, Key * errorKey ELEKTRA_UNUSED)
    1923             : {
    1924           4 :         switch (pluginState)
    1925             :         {
    1926             :         case PLUGIN_STATE_UNMOUNTED:
    1927           4 :                 ksDel (pluginConfig);
    1928           4 :                 return ensurePluginUnmounted (handle, mountpoint, pluginName, errorKey);
    1929             :         case PLUGIN_STATE_MOUNTED:
    1930           0 :                 ELEKTRA_ASSERT (0, "not supported");
    1931             :                 return 0; // TODO: ensurePluginMounted (handle, mountpoint, pluginName, pluginConfig, errorKey);
    1932             :         case PLUGIN_STATE_REMOUNT:
    1933           0 :                 ELEKTRA_ASSERT (0, "not supported");
    1934             :                 return 0; // TODO: ensurePluginUnmounted (handle, mountpoint, pluginName, errorKey) && ensurePluginMounted (handle,
    1935             :                           // mountpoint, pluginName, pluginConfig, errorKey);
    1936             :         default:
    1937           0 :                 ELEKTRA_ASSERT (0, "missing switch case");
    1938             :                 return 0;
    1939             :         }
    1940             : }
    1941             : 
    1942             : /**
    1943             :  * This function can be used the given KDB @p handle meets certain clauses,
    1944             :  * specified in @p contract. Currently the following clauses are supported:
    1945             :  *
    1946             :  * - `system/elektra/ensure/plugins/<mountpoint>/<pluginname>` defines the state of the plugin
    1947             :  *   `<pluginname>` for the mountpoint `<mountpoint>`:
    1948             :  *      - The value `unmounted` ensures the plugin is not mounted, at this mountpoint.
    1949             :  *      - The value `mounted` ensures the plugin is mounted, at this mountpoint.
    1950             :  *        If the plugin is not mounted, we will try to mount it.
    1951             :  *      - The value `remount` always mounts the plugin, at this mountpoint.
    1952             :  *        If it was already mounted, it will me unmounted and mounted again.
    1953             :  *        This can be used to ensure the plugin is mounted with a certain configuration.
    1954             :  * - Keys below `system/elektra/ensure/plugins/<mountpoint>/<pluginname>/config` are extracted and used
    1955             :  *   as the plugins config KeySet during mounting. `system/elektra/ensure/plugins/<mountpoint>/<pluginname>`
    1956             :  *   will be repleced by `user` in the keynames. If no keys are given, an empty KeySet is used.
    1957             :  *
    1958             :  * There are a few special values for `<mountpoint>`:
    1959             :  * - `global` is used to indicate the plugin should (un)mounted as a global plugin.
    1960             :  *   Currently this only supports (un)mounting plugins from/to the subposition `maxonce`.
    1961             :  * - `parent` is used to indicate the keyname of @p parentKey shall be used as the mountpoint.
    1962             :  *
    1963             :  * If `<mountpoint>` is none of those values, it has to be valid keyname with the slashes escaped.
    1964             :  * That means it has to start with `/`, `user`, `system`, `dir` or `spec`.
    1965             :  *
    1966             :  * If `<mountpoint>` is NOT `global`, currently only `unmounted` is supported (not `mounted` and `remounted`).
    1967             :  *
    1968             :  * NOTE: This function only works properly, if the list plugin is mounted in all global positions.
    1969             :  * If this is not the case, 1 will be returned, because this is seen as an implicit clause in the contract.
    1970             :  * Additionally any contract that specifies clauses for the list plugin is rejected as malformed.
    1971             :  *
    1972             :  * @param handle    contains internal information of @link kdbOpen() opened @endlink key database
    1973             :  * @param contract  KeySet containing the contract described above.
    1974             :  *                  This will always be `ksDel()`ed. **Even in error cases.**
    1975             :  * @param parentKey The parentKey used if the `parent` special value is used,
    1976             :  *                  otherwise only used for error reporting.
    1977             :  *
    1978             :  * @retval  0 on success
    1979             :  * @retval  1 if clauses of the contract are unmet
    1980             :  * @retval -1 on NULL pointers, or malformed contract
    1981             :  */
    1982          44 : int kdbEnsure (KDB * handle, KeySet * contract, Key * parentKey)
    1983             : {
    1984          44 :         if (contract == NULL)
    1985             :         {
    1986             :                 return -1;
    1987             :         }
    1988             : 
    1989          44 :         if (handle == NULL || parentKey == NULL)
    1990             :         {
    1991           0 :                 ksDel (contract);
    1992           0 :                 return -1;
    1993             :         }
    1994             : 
    1995          44 :         Key * cutpoint = keyNew ("system/elektra/ensure/plugins", KEY_END);
    1996          44 :         KeySet * pluginsContract = ksCut (contract, cutpoint);
    1997             : 
    1998             :         // delete unused part of contract immediately
    1999          44 :         ksDel (contract);
    2000             : 
    2001          44 :         ksRewind (pluginsContract);
    2002          44 :         Key * clause = NULL;
    2003         146 :         while ((clause = ksNext (pluginsContract)) != NULL)
    2004             :         {
    2005             :                 // only handle 'system/elektra/ensure/plugins/<mountpoint>/<pluginname>' keys
    2006          58 :                 const char * condUNameBase = keyUnescapedName (clause);
    2007          58 :                 const char * condUName = condUNameBase;
    2008          58 :                 condUName += sizeof ("system\0elektra\0ensure\0plugins"); // skip known common part
    2009             : 
    2010          58 :                 size_t condUSize = keyGetUnescapedNameSize (clause);
    2011          58 :                 if (condUNameBase + condUSize <= condUName)
    2012             :                 {
    2013           0 :                         continue; // base key
    2014             :                 }
    2015             : 
    2016          58 :                 condUName += strlen (condUName) + 1; // skip mountpoint
    2017          58 :                 if (condUNameBase + condUSize <= condUName)
    2018             :                 {
    2019           0 :                         continue; // mountpoint key
    2020             :                 }
    2021             : 
    2022          58 :                 condUName += strlen (condUName) + 1; // skip pluginname
    2023          58 :                 if (condUNameBase + condUSize > condUName)
    2024             :                 {
    2025           0 :                         continue; // key below 'system/elektra/ensure/plugins/<mountpoint>/<pluginname>'
    2026             :                 }
    2027             : 
    2028          58 :                 const char * mountpoint = keyUnescapedName (clause);
    2029          58 :                 mountpoint += sizeof ("system\0elektra\0ensure\0plugins");
    2030          58 :                 const char * pluginName = keyBaseName (clause);
    2031          58 :                 const char * pluginStateString = keyString (clause);
    2032             : 
    2033          58 :                 if (elektraStrCmp (pluginName, "list") == 0)
    2034             :                 {
    2035           0 :                         ELEKTRA_SET_INTERFACE_ERROR (parentKey, "Cannot specify clauses for the list plugin");
    2036           0 :                         keyDel (cutpoint);
    2037           0 :                         ksDel (pluginsContract);
    2038           0 :                         return -1;
    2039             :                 }
    2040             : 
    2041             :                 enum PluginContractState pluginState;
    2042          58 :                 if (elektraStrCmp (pluginStateString, "unmounted") == 0)
    2043             :                 {
    2044             :                         pluginState = PLUGIN_STATE_UNMOUNTED;
    2045             :                 }
    2046          42 :                 else if (elektraStrCmp (pluginStateString, "mounted") == 0)
    2047             :                 {
    2048             :                         pluginState = PLUGIN_STATE_MOUNTED;
    2049             :                 }
    2050          16 :                 else if (elektraStrCmp (pluginStateString, "remount") == 0)
    2051             :                 {
    2052             :                         pluginState = PLUGIN_STATE_REMOUNT;
    2053             :                 }
    2054             :                 else
    2055             :                 {
    2056           0 :                         ELEKTRA_SET_INTERFACE_ERRORF (
    2057             :                                 parentKey,
    2058             :                                 "The key '%s' contained the value '%s', but only 'unmounted', 'mounted' or 'remounted' may be used",
    2059             :                                 keyName (clause), pluginStateString);
    2060           0 :                         keyDel (cutpoint);
    2061           0 :                         ksDel (pluginsContract);
    2062           0 :                         return -1;
    2063             :                 }
    2064             : 
    2065          58 :                 Key * pluginCutpoint = keyNew (keyName (clause), KEY_END);
    2066          58 :                 keyAddBaseName (pluginCutpoint, "config");
    2067          58 :                 KeySet * pluginConfig = ksCut (pluginsContract, pluginCutpoint);
    2068          58 :                 ksAppendKey (pluginConfig, pluginCutpoint);
    2069             :                 {
    2070          58 :                         KeySet * newPluginConfig = elektraRenameKeys (pluginConfig, "user");
    2071          58 :                         ksDel (pluginConfig);
    2072          58 :                         pluginConfig = newPluginConfig;
    2073             :                 }
    2074             : 
    2075          58 :                 if (elektraStrCmp (mountpoint, "global") == 0)
    2076             :                 {
    2077          54 :                         int ret = ensureGlobalPluginState (handle, pluginName, pluginState, pluginConfig, parentKey);
    2078          54 :                         if (ret != 0)
    2079             :                         {
    2080           0 :                                 keyDel (cutpoint);
    2081           0 :                                 ksDel (pluginsContract);
    2082             : 
    2083           0 :                                 if (ret != -1)
    2084             :                                 {
    2085           0 :                                         ksDel (pluginConfig);
    2086             :                                 }
    2087             : 
    2088             :                                 return 1;
    2089             :                         }
    2090             :                 }
    2091             :                 else
    2092             :                 {
    2093           4 :                         if (pluginState != PLUGIN_STATE_UNMOUNTED)
    2094             :                         {
    2095           0 :                                 ELEKTRA_SET_INTERFACE_ERRORF (
    2096             :                                         parentKey,
    2097             :                                         "The key '%s' contained the value '%s', but only 'unmounted' is supported for "
    2098             :                                         "non-global clauses at the moment",
    2099             :                                         keyName (clause), pluginStateString);
    2100           0 :                                 keyDel (cutpoint);
    2101           0 :                                 ksDel (pluginConfig);
    2102           0 :                                 ksDel (pluginsContract);
    2103           0 :                                 return -1;
    2104             :                         }
    2105             : 
    2106           4 :                         if (elektraStrCmp (mountpoint, "parent") == 0)
    2107             :                         {
    2108           4 :                                 mountpoint = keyName (parentKey);
    2109             :                         }
    2110             : 
    2111           4 :                         if (!ensurePluginState (handle, mountpoint, pluginName, pluginState, pluginConfig, parentKey))
    2112             :                         {
    2113           0 :                                 keyDel (cutpoint);
    2114           0 :                                 ksDel (pluginsContract);
    2115           0 :                                 return 1;
    2116             :                         }
    2117             :                 }
    2118             :         }
    2119          44 :         keyDel (cutpoint);
    2120          44 :         ksDel (pluginsContract);
    2121             : 
    2122          44 :         return 0;
    2123             : }
    2124             : 
    2125             : /**
    2126             :  * @}
    2127             :  */

Generated by: LCOV version 1.13