LCOV - code coverage report
Current view: top level - src/libs/highlevel - elektra.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 75 149 50.3 %
Date: 2019-09-12 12:28:41 Functions: 9 13 69.2 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Elektra High Level API.
       5             :  *
       6             :  * @copyright BSD License (see doc/LICENSE.md or http://www.libelektra.org)
       7             :  */
       8             : 
       9             : #include "elektra.h"
      10             : #include "elektra/conversion.h"
      11             : #include "elektra/errors.h"
      12             : #include "elektra/types.h"
      13             : #include "kdberrors.h"
      14             : #include "kdbhelper.h"
      15             : #include "kdblogger.h"
      16             : #include "kdbprivate.h"
      17             : #include <stdlib.h>
      18             : 
      19             : #ifdef __cplusplus
      20             : extern "C" {
      21             : #endif
      22             : 
      23           0 : static void defaultFatalErrorHandler (ElektraError * error)
      24             : {
      25             :         ELEKTRA_LOG_DEBUG ("FATAL ERROR [%s]: %s", error->code, error->description);
      26           0 :         elektraErrorReset (&error);
      27           0 :         exit (EXIT_FAILURE);
      28             : }
      29             : 
      30             : static void insertDefaults (KeySet * config, const Key * parentKey, KeySet * defaults);
      31             : static bool checkHighlevelContract (const char * application, KeySet * contract, ElektraError ** error);
      32             : 
      33             : /**
      34             :  * \defgroup highlevel High-level API
      35             :  * @{
      36             :  */
      37             : 
      38             : /**
      39             :  * Initializes a new Elektra instance.
      40             :  *
      41             :  * To free the memory allocated by this function call elektraClose(),
      42             :  * once you are done using this instance.
      43             :  *
      44             :  * @param application   Your application's base name. The the simplest version for this string is
      45             :  *                      "/sw/org/<appname>/#0/current", where '<appname>' is a unique name for
      46             :  *                      your application. For more information see the man-page elektra-key-names(7).
      47             :  * @param defaults      A KeySet containing default values. If you pass NULL, trying to read
      48             :  *                      a non-existent value will cause a fatal error. It is recommended, to
      49             :  *                      only pass NULL, if you are using a specification, which provides
      50             :  *                      default values inside of the KDB.
      51             :  *                      If a key in this KeySet doesn't have a value, we will use the value of the "default"
      52             :  *                      metakey of this key.
      53             :  * @param contract      Will be passed to kdbEnsure() as the contract. If it is NULL, kdbEnsure() won't be called.
      54             :  *                      Unlike @p defaults, this KeySet is consumed and must not be used afterwards.
      55             :  * @param error         If an error occurs during initialization of the Elektra instance, this pointer
      56             :  *                      will be used to report the error.
      57             :  *
      58             :  * @return An Elektra instance initialized for the application (free with elektraClose()).
      59             :  *
      60             :  * @see elektraClose
      61             :  * @see kdbEnsure
      62             :  */
      63          36 : Elektra * elektraOpen (const char * application, KeySet * defaults, KeySet * contract, ElektraError ** error)
      64             : {
      65          36 :         Key * const parentKey = keyNew (application, KEY_END);
      66          36 :         KDB * const kdb = kdbOpen (parentKey);
      67             : 
      68          36 :         if (kdb == NULL)
      69             :         {
      70           0 :                 *error = elektraErrorFromKey (parentKey);
      71           0 :                 return NULL;
      72             :         }
      73             : 
      74          36 :         if (contract != NULL)
      75             :         {
      76          14 :                 Key * contractCut = keyNew ("system/elektra/highlevel", KEY_END);
      77          14 :                 KeySet * highlevelContract = ksCut (contract, contractCut);
      78             : 
      79          14 :                 if (ksGetSize (highlevelContract) > 0)
      80             :                 {
      81           0 :                         if (!checkHighlevelContract (application, highlevelContract, error))
      82             :                         {
      83           0 :                                 keyDel (contractCut);
      84           0 :                                 ksDel (highlevelContract);
      85           0 :                                 ksDel (contract); // consume contract, like kdbEnsure would
      86             : 
      87           0 :                                 kdbClose (kdb, parentKey);
      88           0 :                                 keyDel (parentKey);
      89             : 
      90           0 :                                 return NULL;
      91             :                         }
      92             :                 }
      93             : 
      94          14 :                 keyDel (contractCut);
      95          14 :                 ksDel (highlevelContract);
      96             : 
      97          14 :                 ksAppendKey (contract, keyNew ("system/elektra/ensure/plugins/global/spec", KEY_VALUE, "remount", KEY_END));
      98          14 :                 ksAppendKey (contract,
      99             :                              keyNew ("system/elektra/ensure/plugins/global/spec/config/conflict/get", KEY_VALUE, "ERROR", KEY_END));
     100          14 :                 ksAppendKey (contract,
     101             :                              keyNew ("system/elektra/ensure/plugins/global/spec/config/conflict/set", KEY_VALUE, "ERROR", KEY_END));
     102             : 
     103          14 :                 const int kdbEnsureResult = kdbEnsure (kdb, contract, parentKey);
     104             : 
     105          14 :                 if (kdbEnsureResult == 1)
     106             :                 {
     107           0 :                         const char * reason = keyString (keyGetMeta (parentKey, "error/reason"));
     108           0 :                         *error = elektraErrorEnsureFailed (reason);
     109             : 
     110           0 :                         kdbClose (kdb, parentKey);
     111           0 :                         keyDel (parentKey);
     112           0 :                         return NULL;
     113             :                 }
     114          14 :                 else if (kdbEnsureResult != 0)
     115             :                 {
     116           0 :                         *error = elektraErrorFromKey (parentKey);
     117             : 
     118           0 :                         kdbClose (kdb, parentKey);
     119           0 :                         keyDel (parentKey);
     120           0 :                         return NULL;
     121             :                 }
     122             :         }
     123             : 
     124          36 :         KeySet * const config = ksNew (0, KS_END);
     125          36 :         if (defaults != NULL)
     126             :         {
     127          16 :                 insertDefaults (config, parentKey, defaults);
     128             :         }
     129             : 
     130          36 :         const int kdbGetResult = kdbGet (kdb, config, parentKey);
     131             : 
     132          36 :         if (kdbGetResult == -1)
     133             :         {
     134           0 :                 *error = elektraErrorFromKey (parentKey);
     135           0 :                 return NULL;
     136             :         }
     137             : 
     138          36 :         Elektra * const elektra = elektraCalloc (sizeof (struct _Elektra));
     139          36 :         elektra->kdb = kdb;
     140          36 :         elektra->parentKey = parentKey;
     141          36 :         elektra->parentKeyLength = keyGetNameSize (parentKey) - 1;
     142          36 :         elektra->config = config;
     143          36 :         elektra->lookupKey = keyNew (NULL, KEY_END);
     144          36 :         elektra->fatalErrorHandler = &defaultFatalErrorHandler;
     145          36 :         elektra->defaults = ksDup (defaults);
     146             : 
     147          36 :         return elektra;
     148             : }
     149             : 
     150           0 : Elektra * ELEKTRA_SYMVER (elektraOpen, v1) (const char * application, KeySet * defaults, ElektraError ** error)
     151             : {
     152           0 :         return elektraOpen (application, defaults, NULL, error);
     153             : }
     154             : 
     155             : ELEKTRA_SYMVER_DECLARE ("libelektra_0.8", elektraOpen, v1)
     156             : 
     157             : /**
     158             :  * Promote an ElektraError to fatal and call the fatal error handler.
     159             :  *
     160             :  * @param elektra    Elektra instance whose fatal error handler shall be used.
     161             :  * @param fatalError The error that will be raised.
     162             :  */
     163          30 : void elektraFatalError (Elektra * elektra, ElektraError * fatalError)
     164             : {
     165          30 :         elektra->fatalErrorHandler (fatalError);
     166           0 : }
     167             : 
     168             : /**
     169             :  * This function is only intended for use with code-generation.
     170             :  *
     171             :  * It looks for the key proc/elektra/gopts/help (absolute name) created by gopts,
     172             :  * and returns it if found.
     173             :  *
     174             :  * @param elektra The Elektra instance to check
     175             :  *
     176             :  * @return the help key if found, NULL otherwise
     177             :  *   The pointer returned may become invalid, when any `elektraSet*()` function or
     178             :  *   any other function that modifies the state of @p elektra is called.
     179             :  *   It will always become invalid, when elektraClose() is called on @p elektra.
     180             :  */
     181          14 : Key * elektraHelpKey (Elektra * elektra)
     182             : {
     183          14 :         return ksLookupByName (elektra->config, "proc/elektra/gopts/help", 0);
     184             : }
     185             : 
     186             : /**
     187             :  * Sets the fatal error handler that will be called, whenever a fatal error occurs.
     188             :  *
     189             :  * Errors occurring in a function, which does not take a pointer to ElektraError,
     190             :  * are always considered fatal.
     191             :  *
     192             :  * If this function returns, i.e. it does not call exit() or interrupt the thread of
     193             :  * execution in some other way, the behaviour of the function from which the error
     194             :  * originated is generally undefined.
     195             :  *
     196             :  * @param elektra           An Elektra instance.
     197             :  * @param fatalErrorHandler The error handler that will be used henceforth.
     198             :  */
     199          36 : void elektraFatalErrorHandler (Elektra * elektra, ElektraErrorHandler fatalErrorHandler)
     200             : {
     201          36 :         elektra->fatalErrorHandler = fatalErrorHandler;
     202          36 : }
     203             : 
     204             : /**
     205             :  * Releases all resources used by the given elektra instance. The elektra instance must not be used anymore after calling this.
     206             :  * @param elektra An Elektra instance.
     207             :  */
     208          36 : void elektraClose (Elektra * elektra)
     209             : {
     210          36 :         kdbClose (elektra->kdb, elektra->parentKey);
     211          36 :         keyDel (elektra->parentKey);
     212          36 :         ksDel (elektra->config);
     213          36 :         keyDel (elektra->lookupKey);
     214             : 
     215          36 :         if (elektra->resolvedReference != NULL)
     216             :         {
     217           0 :                 elektraFree (elektra->resolvedReference);
     218             :         }
     219             : 
     220          36 :         if (elektra->defaults != NULL)
     221             :         {
     222          16 :                 ksDel (elektra->defaults);
     223             :         }
     224             : 
     225          36 :         elektraFree (elektra);
     226          36 : }
     227             : 
     228             : /**
     229             :  * @}
     230             :  */
     231             : 
     232             : // Private definitions
     233             : 
     234             : KDBType KDB_TYPE_STRING = "string";
     235             : KDBType KDB_TYPE_BOOLEAN = "boolean";
     236             : KDBType KDB_TYPE_CHAR = "char";
     237             : KDBType KDB_TYPE_OCTET = "octet";
     238             : KDBType KDB_TYPE_SHORT = "short";
     239             : KDBType KDB_TYPE_UNSIGNED_SHORT = "unsigned_short";
     240             : KDBType KDB_TYPE_LONG = "long";
     241             : KDBType KDB_TYPE_UNSIGNED_LONG = "unsigned_long";
     242             : KDBType KDB_TYPE_LONG_LONG = "long_long";
     243             : KDBType KDB_TYPE_UNSIGNED_LONG_LONG = "unsigned_long_long";
     244             : KDBType KDB_TYPE_FLOAT = "float";
     245             : KDBType KDB_TYPE_LONG_DOUBLE = "long_double";
     246             : KDBType KDB_TYPE_DOUBLE = "double";
     247             : KDBType KDB_TYPE_ENUM = "enum";
     248             : 
     249        1710 : void elektraSetLookupKey (Elektra * elektra, const char * name)
     250             : {
     251        1710 :         keySetName (elektra->lookupKey, keyName (elektra->parentKey));
     252        1710 :         keyAddName (elektra->lookupKey, name);
     253        1710 : }
     254             : 
     255         204 : void elektraSetArrayLookupKey (Elektra * elektra, const char * name, kdb_long_long_t index)
     256             : {
     257         204 :         elektraSetLookupKey (elektra, name);
     258             :         char arrayPart[ELEKTRA_MAX_ARRAY_SIZE];
     259         204 :         elektraWriteArrayNumber (arrayPart, index);
     260         204 :         keyAddName (elektra->lookupKey, arrayPart);
     261         204 : }
     262             : 
     263         796 : void elektraSaveKey (Elektra * elektra, Key * key, ElektraError ** error)
     264             : {
     265         796 :         int ret = 0;
     266             :         do
     267             :         {
     268         796 :                 ksAppendKey (elektra->config, key);
     269             : 
     270         796 :                 ret = kdbSet (elektra->kdb, elektra->config, elektra->parentKey);
     271         796 :                 if (ret == -1)
     272             :                 {
     273           0 :                         ElektraError * kdbSetError = elektraErrorFromKey (elektra->parentKey);
     274           0 :                         if (strcmp (elektraErrorCode (kdbSetError), ELEKTRA_ERROR_CONFLICTING_STATE) != 0)
     275             :                         {
     276           0 :                                 *error = kdbSetError;
     277           0 :                                 return;
     278             :                         }
     279             : 
     280           0 :                         elektraErrorReset (&kdbSetError);
     281             : 
     282           0 :                         Key * problemKey = ksCurrent (elektra->config);
     283             :                         if (problemKey != NULL)
     284             :                         {
     285             :                                 ELEKTRA_LOG_DEBUG ("problemKey: %s\n", keyName (problemKey));
     286             :                         }
     287             : 
     288           0 :                         key = keyDup (key);
     289           0 :                         kdbGet (elektra->kdb, elektra->config, elektra->parentKey);
     290             :                 }
     291         796 :         } while (ret == -1);
     292             : }
     293             : 
     294          16 : void insertDefaults (KeySet * config, const Key * parentKey, KeySet * defaults)
     295             : {
     296          16 :         ksRewind (defaults);
     297          98 :         for (Key * key = ksNext (defaults); key != NULL; key = ksNext (defaults))
     298             :         {
     299          82 :                 Key * const dup = keyDup (key);
     300          82 :                 const char * name = keyName (key);
     301          82 :                 keySetName (dup, keyName (parentKey));
     302          82 :                 keyAddName (dup, name);
     303             : 
     304          82 :                 if (strlen (keyString (dup)) == 0)
     305             :                 {
     306          78 :                         const Key * defaultMeta = keyGetMeta (dup, "default");
     307          78 :                         if (defaultMeta != NULL)
     308             :                         {
     309          68 :                                 keySetString (dup, keyString (defaultMeta));
     310             :                         }
     311             :                 }
     312             : 
     313          82 :                 ksAppendKey (config, dup);
     314             :         }
     315          16 : }
     316             : 
     317           0 : static bool minimalValidation (const char * application)
     318             : {
     319           0 :         Key * parent = keyNew ("system/elektra/mountpoints", KEY_END);
     320           0 :         KDB * kdb = kdbOpen (parent);
     321           0 :         KeySet * mountpoints = ksNew (0, KS_END);
     322           0 :         if (kdbGet (kdb, mountpoints, parent) < 0)
     323             :         {
     324           0 :                 ksDel (mountpoints);
     325           0 :                 kdbClose (kdb, parent);
     326           0 :                 keyDel (parent);
     327           0 :                 return false;
     328             :         }
     329             : 
     330           0 :         char * specName = elektraFormat ("spec%s", application);
     331           0 :         Key * lookup = keyNew ("system/elektra/mountpoints", KEY_END);
     332           0 :         keyAddBaseName (lookup, specName);
     333           0 :         elektraFree (specName);
     334             : 
     335           0 :         if (ksLookup (mountpoints, lookup, 0) == NULL)
     336             :         {
     337           0 :                 keyDel (lookup);
     338             : 
     339           0 :                 ksDel (mountpoints);
     340           0 :                 kdbClose (kdb, parent);
     341           0 :                 keyDel (parent);
     342           0 :                 return false;
     343             :         }
     344             : 
     345           0 :         keyDel (lookup);
     346             : 
     347           0 :         lookup = keyNew ("system/elektra/mountpoints", KEY_END);
     348           0 :         keyAddBaseName (lookup, application);
     349             : 
     350           0 :         if (ksLookup (mountpoints, lookup, 0) == NULL)
     351             :         {
     352           0 :                 keyDel (lookup);
     353             : 
     354           0 :                 ksDel (mountpoints);
     355           0 :                 kdbClose (kdb, parent);
     356           0 :                 keyDel (parent);
     357           0 :                 return false;
     358             :         }
     359           0 :         keyDel (lookup);
     360             : 
     361           0 :         ksDel (mountpoints);
     362           0 :         kdbClose (kdb, parent);
     363           0 :         keyDel (parent);
     364             : 
     365           0 :         return true;
     366             : }
     367             : 
     368           0 : bool checkHighlevelContract (const char * application, KeySet * contract, ElektraError ** error)
     369             : {
     370           0 :         Key * validationKey = ksLookupByName (contract, "system/elektra/highlevel/validation", 0);
     371           0 :         if (validationKey != NULL)
     372             :         {
     373           0 :                 if (strcmp (keyString (validationKey), "minimal") == 0 && !minimalValidation (application))
     374             :                 {
     375           0 :                         *error = elektraErrorMinimalValidationFailed (application);
     376           0 :                         return false;
     377             :                 }
     378             :         }
     379             : 
     380             :         return true;
     381             : }
     382             : 
     383             : 
     384             : #ifdef __cplusplus
     385             : };
     386             : #endif

Generated by: LCOV version 1.13