LCOV - code coverage report
Current view: top level - src/plugins/multifile - multifile.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 372 429 86.7 %
Date: 2019-09-12 12:28:41 Functions: 24 26 92.3 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Source for multifile plugin
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include "multifile.h"
      11             : 
      12             : #include <dirent.h>
      13             : #include <errno.h>
      14             : #include <fnmatch.h>
      15             : #include <fts.h>
      16             : #include <glob.h>
      17             : #include <kdbconfig.h>
      18             : #include <kdbhelper.h>
      19             : #include <kdbinternal.h>
      20             : #include <kdbmacros.h>
      21             : #include <kdbmodule.h>
      22             : #include <kdbplugin.h>
      23             : #include <kdbproposal.h>
      24             : #include <stdio.h>
      25             : #include <stdlib.h>
      26             : #include <string.h>
      27             : #include <sys/stat.h>
      28             : #include <sys/types.h>
      29             : #include <unistd.h>
      30             : 
      31             : #include "../resolver/shared.h"
      32             : #include <kdbinvoke.h>
      33             : 
      34             : #define DEFAULT_RESOLVER "resolver"
      35             : #define DEFAULT_PATTERN "*"
      36             : #define DEFAULT_STORAGE "ini"
      37             : 
      38             : typedef enum
      39             : {
      40             :         MULTI_SETRESOLVER = 0,
      41             :         MULTI_SETSTORAGE,
      42             :         MULTI_COMMIT,
      43             : } SetPhases;
      44             : 
      45             : typedef enum
      46             : {
      47             :         MULTI_GETRESOLVER = 0,
      48             :         MULTI_GETSTORAGE,
      49             : } GetPhases;
      50             : 
      51             : typedef enum
      52             : {
      53             :         EMPTY = -2,
      54             :         ERROR = -1,
      55             :         NOUPDATE = 0,
      56             :         SUCCESS = 1,
      57             :         CACHE_HIT = 2,
      58             : } Codes;
      59             : 
      60             : 
      61             : typedef struct
      62             : {
      63             :         char * directory;
      64             :         char * originalPath;
      65             :         char * pattern;
      66             :         SetPhases setPhase;
      67             :         GetPhases getPhase;
      68             :         KeySet * modules;
      69             :         KeySet * childBackends;
      70             :         KeySet * childConfig;
      71             :         char * resolver;
      72             :         char * storage;
      73             :         unsigned short stayAlive;
      74             :         unsigned short hasDeleted;
      75             :         unsigned short recursive;
      76             : } MultiConfig;
      77             : 
      78             : typedef struct
      79             : {
      80             :         char * filename;
      81             :         char * fullPath;
      82             :         char * parentString;
      83             :         char * tmpFilename;
      84             :         Plugin * resolver;
      85             :         Codes rcResolver;
      86             :         Plugin * storage;
      87             :         Codes rcStorage;
      88             :         KeySet * ks;
      89             : } SingleConfig;
      90             : 
      91             : static inline Codes rvToRc (int rc)
      92             : {
      93          63 :         switch (rc)
      94             :         {
      95             :         case -2:
      96             :                 return EMPTY;
      97             :                 break;
      98             :         case -1:
      99             :                 return ERROR;
     100             :                 break;
     101             :         case 0:
     102             :                 return NOUPDATE;
     103             :                 break;
     104             :         case 1:
     105             :                 return SUCCESS;
     106             :                 break;
     107             :         case 2:
     108             :                 return CACHE_HIT;
     109             :                 break;
     110             :         }
     111             :         return ERROR;
     112             : }
     113             : 
     114           9 : static int elektraResolveFilename (Key * parentKey, ElektraResolveTempfile tmpFile)
     115             : {
     116           9 :         int rc = 0;
     117           9 :         ElektraInvokeHandle * handle = elektraInvokeOpen ("resolver", 0, 0);
     118           9 :         if (!handle)
     119             :         {
     120             :                 rc = -1;
     121             :                 goto RESOLVE_FAILED;
     122             :         }
     123           9 :         ElektraResolved * resolved = NULL;
     124             :         typedef ElektraResolved * (*resolveFileFunc) (elektraNamespace, const char *, ElektraResolveTempfile, Key *);
     125           9 :         resolveFileFunc resolveFunc = *(resolveFileFunc *) elektraInvokeGetFunction (handle, "filename");
     126             : 
     127           9 :         if (!resolveFunc)
     128             :         {
     129             :                 rc = -1;
     130             :                 goto RESOLVE_FAILED;
     131             :         }
     132             : 
     133             :         typedef void (*freeHandleFunc) (ElektraResolved *);
     134           9 :         freeHandleFunc freeHandle = *(freeHandleFunc *) elektraInvokeGetFunction (handle, "freeHandle");
     135             : 
     136           9 :         if (!freeHandle)
     137             :         {
     138             :                 rc = -1;
     139             :                 goto RESOLVE_FAILED;
     140             :         }
     141             : 
     142           9 :         resolved = resolveFunc (keyGetNamespace (parentKey), keyString (parentKey), tmpFile, parentKey);
     143             : 
     144           9 :         if (!resolved)
     145             :         {
     146             :                 rc = -1;
     147             :                 goto RESOLVE_FAILED;
     148             :         }
     149             :         else
     150             :         {
     151           9 :                 keySetString (parentKey, resolved->fullPath);
     152           9 :                 freeHandle (resolved);
     153             :         }
     154             : 
     155           9 :         elektraInvokeClose (handle, 0);
     156           9 :         return rc;
     157             : 
     158             : RESOLVE_FAILED:
     159             :         ELEKTRA_LOG_DEBUG ("MULTIFILE: resolve failed!");
     160           0 :         elektraInvokeClose (handle, 0);
     161           0 :         return rc;
     162             : }
     163             : 
     164           2 : int elektraMultifileCheckFile (const char * filename)
     165             : {
     166           2 :         if (!filename) return -1;
     167           2 :         if (filename[0] == '/') return 0;
     168           2 :         return 1;
     169             : }
     170             : 
     171          88 : int elektraMultifileOpen (Plugin * handle ELEKTRA_UNUSED, Key * errorKey ELEKTRA_UNUSED)
     172             : {
     173             :         // plugin initialization logic
     174             :         // this function is optional
     175             : 
     176          88 :         return 1; // success
     177             : }
     178             : 
     179          30 : static void closeBackend (SingleConfig * s)
     180             : {
     181          30 :         if (!s) return;
     182          30 :         if (s->fullPath) elektraFree (s->fullPath);
     183          30 :         if (s->filename) elektraFree (s->filename);
     184          30 :         if (s->parentString) elektraFree (s->parentString);
     185          30 :         if (s->tmpFilename) elektraFree (s->tmpFilename);
     186          30 :         if (s->resolver) elektraPluginClose (s->resolver, NULL);
     187          30 :         if (s->storage) elektraPluginClose (s->storage, NULL);
     188          30 :         if (s->ks) ksDel (s->ks);
     189          30 :         elektraFree (s);
     190          30 :         s = NULL;
     191             : }
     192             : 
     193          18 : static void closeBackends (KeySet * b)
     194             : {
     195          18 :         ksRewind (b);
     196             :         Key * k;
     197          66 :         while ((k = ksNext (b)) != NULL)
     198             :         {
     199          30 :                 SingleConfig * s = *(SingleConfig **) keyValue (k);
     200             :                 // fprintf (stderr, "closing backend %s:(%s)\n", s->parentString, s->fullPath);
     201          30 :                 closeBackend (s);
     202             :         }
     203          18 : }
     204             : 
     205          88 : int elektraMultifileClose (Plugin * handle ELEKTRA_UNUSED, Key * errorKey ELEKTRA_UNUSED)
     206             : {
     207             :         // free all plugin resources and shut it down
     208             :         // this function is optional
     209          88 :         MultiConfig * mc = elektraPluginGetData (handle);
     210          88 :         if (!mc) return 1;
     211           9 :         closeBackends (mc->childBackends);
     212           9 :         if (mc->directory) elektraFree (mc->directory);
     213           9 :         if (mc->pattern) elektraFree (mc->pattern);
     214           9 :         if (mc->resolver) elektraFree (mc->resolver);
     215           9 :         if (mc->storage) elektraFree (mc->storage);
     216           9 :         if (mc->originalPath) elektraFree (mc->originalPath);
     217           9 :         elektraModulesClose (mc->modules, NULL);
     218           9 :         ksDel (mc->modules);
     219           9 :         ksDel (mc->childBackends);
     220           9 :         ksDel (mc->childConfig);
     221           9 :         elektraFree (mc);
     222           9 :         elektraPluginSetData (handle, NULL);
     223           9 :         return 1; // success
     224             : }
     225             : 
     226           9 : static MultiConfig * initialize (Plugin * handle, Key * parentKey)
     227             : {
     228             : 
     229           9 :         KeySet * config = elektraPluginGetConfig (handle);
     230           9 :         Key * origPath = ksLookupByName (config, "/path", 0);
     231           9 :         keySetString (parentKey, keyString (origPath));
     232           9 :         if (elektraResolveFilename (parentKey, ELEKTRA_RESOLVER_TEMPFILE_NONE) == -1)
     233             :         {
     234             :                 return NULL;
     235             :         }
     236           9 :         Key * patternKey = ksLookupByName (config, "/pattern", 0);
     237           9 :         Key * storageKey = ksLookupByName (config, "/storage", 0);
     238           9 :         Key * resolverKey = ksLookupByName (config, "/resolver", 0);
     239           9 :         Key * stayAliveKey = ksLookupByName (config, "/stayalive", 0);
     240           9 :         Key * recursiveKey = ksLookupByName (config, "/recursive", 0);
     241           9 :         MultiConfig * mc = elektraCalloc (sizeof (MultiConfig));
     242           9 :         mc->directory = elektraStrDup (keyString (parentKey));
     243           9 :         mc->originalPath = elektraStrDup (keyString (origPath));
     244           9 :         if (resolverKey)
     245             :         {
     246           9 :                 mc->resolver = elektraStrDup (keyString (resolverKey));
     247             :         }
     248             :         else
     249             :         {
     250           0 :                 mc->resolver = elektraStrDup (DEFAULT_RESOLVER);
     251             :         }
     252           9 :         if (storageKey)
     253             :         {
     254           9 :                 mc->storage = elektraStrDup (keyString (storageKey));
     255             :         }
     256             :         else
     257             :         {
     258           0 :                 mc->storage = elektraStrDup (DEFAULT_STORAGE);
     259             :         }
     260           9 :         if (patternKey)
     261             :         {
     262           9 :                 mc->pattern = elektraStrDup (keyString (patternKey));
     263             :         }
     264             :         else
     265             :         {
     266           0 :                 mc->pattern = elektraStrDup (DEFAULT_PATTERN);
     267             :         }
     268           9 :         if (stayAliveKey) mc->stayAlive = 1;
     269           9 :         if (recursiveKey) mc->recursive = 1;
     270           9 :         Key * cutKey = keyNew ("/child", KEY_END);
     271           9 :         KeySet * childConfig = ksCut (config, cutKey);
     272           9 :         keyDel (cutKey);
     273           9 :         mc->childConfig = elektraRenameKeys (childConfig, "system");
     274           9 :         ksAppend (config, childConfig);
     275           9 :         ksDel (childConfig);
     276           9 :         mc->childBackends = ksNew (0, KS_END);
     277           9 :         mc->modules = ksNew (0, KS_END);
     278           9 :         elektraModulesInit (mc->modules, NULL);
     279           9 :         elektraPluginSetData (handle, mc);
     280           9 :         return mc;
     281             : }
     282             : 
     283             : 
     284          30 : static Codes initBackend (Plugin * handle, MultiConfig * mc, SingleConfig * s, Key * parentKey)
     285             : {
     286          30 :         unsigned long fullPathLen = strlen (mc->originalPath) + strlen (s->filename) + 2;
     287          30 :         char * fullPath = elektraCalloc (fullPathLen);
     288          30 :         snprintf (fullPath, fullPathLen, "%s/%s", mc->originalPath, s->filename);
     289          30 :         s->fullPath = fullPath;
     290          30 :         unsigned long childParentStringLen = strlen (keyName (parentKey)) + strlen (s->filename) + 2;
     291          30 :         char * childParentString = elektraCalloc (childParentStringLen);
     292          30 :         snprintf (childParentString, childParentStringLen, "%s/%s", keyName (parentKey), s->filename);
     293          30 :         s->parentString = childParentString;
     294             :         // fprintf (stderr, "Added file %s:(%s)\n\tChildParentKey: %s\n", s->fullPath, s->filename, s->parentString);
     295          30 :         Plugin * resolver = NULL;
     296          30 :         KeySet * resolverChildConfig = ksDup (mc->childConfig);
     297          30 :         ksAppendKey (resolverChildConfig, keyNew ("/path", KEY_VALUE, s->fullPath, KEY_END));
     298          30 :         resolver = elektraPluginOpen (mc->resolver, mc->modules, resolverChildConfig, parentKey);
     299             :         // fprintf (stderr, "%s:(%s)\n", keyName (parentKey), keyString (parentKey));
     300          30 :         if (!resolver)
     301             :         {
     302             :                 // fprintf (stderr, "Failed to load resolver %s for %s\n", mc->resolver, s->parentString);
     303             :                 return ERROR;
     304             :         }
     305             :         else
     306             :         {
     307          30 :                 s->resolver = resolver;
     308          30 :                 resolver->global = elektraPluginGetGlobalKeySet (handle);
     309             :         }
     310          30 :         Plugin * storage = NULL;
     311          30 :         KeySet * storageChildConfig = ksDup (mc->childConfig);
     312          30 :         ksAppendKey (storageChildConfig, keyNew ("system/path", KEY_VALUE, s->fullPath, KEY_END));
     313          30 :         storage = elektraPluginOpen (mc->storage, mc->modules, storageChildConfig, parentKey);
     314          30 :         if (!storage)
     315             :         {
     316             :                 // fprintf (stderr, "Failed to load storage %s for %s\n", mc->storage, s->parentString);
     317             :                 return ERROR;
     318             :         }
     319             :         else
     320             :         {
     321          30 :                 s->storage = storage;
     322          30 :                 storage->global = elektraPluginGetGlobalKeySet (handle);
     323             :         }
     324          30 :         return SUCCESS;
     325             : }
     326             : 
     327          30 : static Codes resolverGet (SingleConfig * s, KeySet * returned, Key * parentKey)
     328             : {
     329          30 :         Plugin * resolver = s->resolver;
     330          30 :         int rc = resolver->kdbGet (resolver, returned, parentKey);
     331          30 :         s->rcResolver = rvToRc (rc);
     332             :         ;
     333          30 :         if (s->fullPath) elektraFree (s->fullPath);
     334          30 :         s->fullPath = elektraStrDup (keyString (parentKey));
     335          30 :         return s->rcResolver;
     336             : }
     337             : 
     338           2 : static Codes updateFilesRecursive (Plugin * handle, MultiConfig * mc, KeySet * found, Key * parentKey)
     339             : {
     340           2 :         Codes rc = NOUPDATE;
     341             :         char * dirs[2];
     342           2 :         dirs[0] = mc->directory;
     343           2 :         dirs[1] = (void *) NULL;
     344           2 :         FTS * fts = fts_open (dirs, FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOCHDIR, NULL);
     345           2 :         if (fts)
     346             :         {
     347             :                 FTSENT * ent = NULL;
     348          40 :                 while ((ent = fts_read (fts)) != NULL)
     349             :                 {
     350          38 :                         if (ent->fts_info == FTS_F)
     351             :                         {
     352           6 :                                 if (!fnmatch (mc->pattern, ent->fts_name, 0))
     353             :                                 {
     354           6 :                                         Key * lookup = keyNew ("/", KEY_CASCADING_NAME, KEY_END);
     355           6 :                                         keyAddBaseName (lookup, (ent->fts_path + strlen (mc->directory)));
     356             :                                         Key * k;
     357           6 :                                         if ((k = ksLookup (mc->childBackends, lookup, KDB_O_NONE)) != NULL)
     358             :                                         {
     359           0 :                                                 ksAppendKey (found, k);
     360             :                                         }
     361             :                                         else
     362             :                                         {
     363           6 :                                                 SingleConfig * s = elektraCalloc (sizeof (SingleConfig));
     364           6 :                                                 s->filename = elektraStrDup ((ent->fts_path) + strlen (mc->directory) + 1);
     365           6 :                                                 Codes r = initBackend (handle, mc, s, parentKey);
     366           6 :                                                 if (r == ERROR)
     367             :                                                 {
     368           0 :                                                         if (!mc->stayAlive)
     369             :                                                         {
     370           0 :                                                                 keyDel (lookup);
     371           0 :                                                                 fts_close (fts);
     372           0 :                                                                 return ERROR;
     373             :                                                         }
     374             :                                                         else
     375             :                                                         {
     376           0 :                                                                 closeBackend (s);
     377             :                                                         }
     378             :                                                 }
     379             :                                                 else
     380             :                                                 {
     381           6 :                                                         Key * childKey = keyNew (keyName (lookup), KEY_CASCADING_NAME, KEY_BINARY, KEY_SIZE,
     382             :                                                                                  sizeof (SingleConfig *), KEY_VALUE, &s, KEY_END);
     383           6 :                                                         ksAppendKey (mc->childBackends, childKey);
     384           6 :                                                         ksAppendKey (found, childKey);
     385           6 :                                                         rc = SUCCESS;
     386             :                                                 }
     387             :                                         }
     388           6 :                                         keyDel (lookup);
     389             :                                 }
     390             :                         }
     391             :                 }
     392           2 :                 fts_close (fts);
     393             :         }
     394             :         return rc;
     395             : }
     396             : 
     397           7 : static Codes updateFilesGlob (Plugin * handle, MultiConfig * mc, KeySet * found, Key * parentKey)
     398           7 : {
     399           7 :         Codes rc = NOUPDATE;
     400             :         glob_t results;
     401             :         int ret;
     402             : 
     403           7 :         char pattern[strlen (mc->directory) + strlen (mc->pattern) + 2];
     404           7 :         snprintf (pattern, sizeof (pattern), "%s/%s", mc->directory, mc->pattern);
     405             : 
     406           7 :         ret = glob (pattern, 0, NULL, &results);
     407           7 :         if (ret != 0)
     408             :         {
     409           0 :                 if (ret == GLOB_NOSPACE)
     410             :                 {
     411           0 :                         ELEKTRA_SET_OUT_OF_MEMORY_ERRORF (parentKey, "Glob(%s) ran out of memory", pattern);
     412             :                 }
     413           0 :                 else if (ret == GLOB_ABORTED)
     414             :                 {
     415           0 :                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Glob(%s) failed with a read error", pattern);
     416             :                 }
     417           0 :                 else if (ret == GLOB_NOMATCH)
     418             :                 {
     419           0 :                         ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Glob(%s) failed with no matches", pattern);
     420             :                 }
     421             :                 return ERROR;
     422             :         }
     423             :         struct stat sb;
     424          24 :         for (unsigned int i = 0; i < results.gl_pathc; ++i)
     425             :         {
     426          48 :                 ret = stat (results.gl_pathv[i], &sb);
     427          24 :                 if (S_ISREG (sb.st_mode))
     428             :                 {
     429          24 :                         Key * lookup = keyNew ("/", KEY_CASCADING_NAME, KEY_END);
     430          24 :                         keyAddBaseName (lookup, (results.gl_pathv[i]) + strlen (mc->directory));
     431             :                         Key * k;
     432          24 :                         if ((k = ksLookup (mc->childBackends, lookup, KDB_O_NONE)) != NULL)
     433             :                         {
     434           0 :                                 ksAppendKey (found, k);
     435             :                         }
     436             :                         else
     437             :                         {
     438          24 :                                 SingleConfig * s = elektraCalloc (sizeof (SingleConfig));
     439          24 :                                 s->filename = elektraStrDup ((results.gl_pathv[i]) + strlen (mc->directory) + 1);
     440          24 :                                 Codes r = initBackend (handle, mc, s, parentKey);
     441          24 :                                 if (r == ERROR)
     442             :                                 {
     443           0 :                                         if (!mc->stayAlive)
     444             :                                         {
     445           0 :                                                 keyDel (lookup);
     446           0 :                                                 globfree (&results);
     447           0 :                                                 return ERROR;
     448             :                                         }
     449             :                                         else
     450             :                                         {
     451           0 :                                                 closeBackend (s);
     452             :                                         }
     453             :                                 }
     454             :                                 else
     455             :                                 {
     456          24 :                                         Key * childKey = keyNew (keyName (lookup), KEY_CASCADING_NAME, KEY_BINARY, KEY_SIZE,
     457             :                                                                  sizeof (SingleConfig *), KEY_VALUE, &s, KEY_END);
     458          24 :                                         ksAppendKey (mc->childBackends, childKey);
     459          24 :                                         ksAppendKey (found, childKey);
     460          24 :                                         rc = SUCCESS;
     461             :                                 }
     462             :                         }
     463          24 :                         keyDel (lookup);
     464             :                 }
     465             :         }
     466           7 :         globfree (&results);
     467           7 :         return rc;
     468             : }
     469             : 
     470           9 : static Codes updateFiles (Plugin * handle, MultiConfig * mc, KeySet * returned, Key * parentKey)
     471             : {
     472           9 :         Codes rc = NOUPDATE;
     473           9 :         KeySet * found = ksNew (0, KS_END);
     474           9 :         Key * initialParent = keyDup (parentKey);
     475             : 
     476           9 :         if (!mc->recursive)
     477             :         {
     478           7 :                 rc = updateFilesGlob (handle, mc, found, parentKey);
     479             :         }
     480             :         else
     481             :         {
     482           2 :                 rc = updateFilesRecursive (handle, mc, found, parentKey);
     483             :         }
     484           9 :         if (rc == ERROR)
     485             :         {
     486           0 :                 ksDel (found);
     487           0 :                 keyDel (initialParent);
     488           0 :                 return ERROR;
     489             :         }
     490             : 
     491           9 :         ksRewind (mc->childBackends);
     492           9 :         ksRewind (found);
     493             :         Key * c;
     494           9 :         ssize_t cacheHits = 0;
     495           9 :         ssize_t numBackends = ksGetSize (found);
     496          48 :         while ((c = ksNext (found)) != NULL)
     497             :         {
     498          30 :                 if (ksLookup (mc->childBackends, c, KDB_O_POP))
     499             :                 {
     500          30 :                         SingleConfig * s = *(SingleConfig **) keyValue (c);
     501          30 :                         keySetName (parentKey, s->parentString);
     502          30 :                         keySetString (parentKey, s->fullPath);
     503          30 :                         int r = resolverGet (s, returned, parentKey);
     504          30 :                         elektraFree (s->fullPath);
     505          30 :                         s->fullPath = elektraStrDup (keyString (parentKey));
     506          30 :                         s->rcResolver = rvToRc (r);
     507          30 :                         if (s->rcResolver == ERROR)
     508             :                         {
     509           0 :                                 if (mc->stayAlive)
     510             :                                 {
     511           0 :                                         cursor_t savedCursor = ksGetCursor (found);
     512           0 :                                         ksAppendKey (found, c);
     513           0 :                                         ksSetCursor (found, savedCursor);
     514             :                                 }
     515             :                                 else
     516             :                                 {
     517             :                                         rc = ERROR;
     518             :                                         break;
     519             :                                 }
     520             :                         }
     521             : 
     522          30 :                         if (r == ELEKTRA_PLUGIN_STATUS_CACHE_HIT)
     523             :                         {
     524           0 :                                 ++cacheHits;
     525             :                         }
     526             : 
     527          30 :                         if (r > 0)
     528             :                         {
     529          30 :                                 rc = SUCCESS;
     530             :                         }
     531             :                 }
     532             :         }
     533           9 :         if (ksGetSize (mc->childBackends) > 0 && rc != -1)
     534             :         {
     535           0 :                 rc = SUCCESS;
     536           0 :                 mc->hasDeleted = 1;
     537             :         }
     538           9 :         closeBackends (mc->childBackends);
     539           9 :         ksDel (mc->childBackends);
     540           9 :         if (rc == ERROR)
     541             :         {
     542           0 :                 closeBackends (found);
     543           0 :                 ksDel (found);
     544             :         }
     545             :         else
     546             :         {
     547           9 :                 mc->childBackends = found;
     548             :         }
     549           9 :         if (cacheHits == numBackends)
     550             :         {
     551           0 :                 rc = CACHE_HIT;
     552             :         }
     553           9 :         keySetName (parentKey, keyName (initialParent));
     554           9 :         keySetString (parentKey, keyString (initialParent));
     555           9 :         keyDel (initialParent);
     556           9 :         return rc;
     557             : }
     558             : 
     559           9 : static Codes doGetStorage (MultiConfig * mc, Key * parentKey)
     560             : {
     561           9 :         ksRewind (mc->childBackends);
     562           9 :         Key * initialParent = keyDup (parentKey);
     563           9 :         Codes rc = NOUPDATE;
     564             :         Key * k;
     565          39 :         while ((k = ksNext (mc->childBackends)) != NULL)
     566             :         {
     567          30 :                 SingleConfig * s = *(SingleConfig **) keyValue (k);
     568             :                 // When we reach this stage, we will need to load
     569             :                 // any successfully resolved files (as it is done in the kdb core)
     570          30 :                 if (s->rcResolver < 0) continue;
     571          30 :                 keySetName (parentKey, s->parentString);
     572          30 :                 keySetString (parentKey, s->fullPath);
     573          30 :                 Plugin * storage = s->storage;
     574          30 :                 KeySet * readKS = ksNew (0, KS_END);
     575          30 :                 int r = storage->kdbGet (storage, readKS, parentKey);
     576          30 :                 if (r > 0)
     577             :                 {
     578          30 :                         if (s->ks) ksDel (s->ks);
     579          30 :                         s->ks = ksDup (readKS);
     580          30 :                         rc = SUCCESS;
     581             :                 }
     582             :                 else
     583             :                 {
     584             :                         rc = ERROR;
     585             :                 }
     586          30 :                 ksDel (readKS);
     587             :         }
     588           9 :         keySetName (parentKey, keyName (initialParent));
     589           9 :         keySetString (parentKey, keyString (initialParent));
     590           9 :         keyDel (initialParent);
     591           9 :         return rc;
     592             : }
     593             : 
     594           9 : static void fillReturned (MultiConfig * mc, KeySet * returned)
     595             : {
     596           9 :         ksRewind (mc->childBackends);
     597           9 :         ksClear (returned);
     598             :         Key * k;
     599          39 :         while ((k = ksNext (mc->childBackends)) != NULL)
     600             :         {
     601          30 :                 SingleConfig * s = *(SingleConfig **) keyValue (k);
     602          30 :                 ksAppend (returned, s->ks);
     603             :         }
     604           9 :         ksRewind (returned);
     605           9 : }
     606             : 
     607          62 : int elektraMultifileGet (Plugin * handle, KeySet * returned, Key * parentKey ELEKTRA_UNUSED)
     608             : {
     609          62 :         if (!elektraStrCmp (keyName (parentKey), "system/elektra/modules/multifile"))
     610             :         {
     611          44 :                 KeySet * contract = ksNew (
     612             :                         30, keyNew ("system/elektra/modules/multifile", KEY_VALUE, "multifile plugin waits for your orders", KEY_END),
     613             :                         keyNew ("system/elektra/modules/multifile/exports", KEY_END),
     614             :                         keyNew ("system/elektra/modules/multifile/exports/open", KEY_FUNC, elektraMultifileOpen, KEY_END),
     615             :                         keyNew ("system/elektra/modules/multifile/exports/close", KEY_FUNC, elektraMultifileClose, KEY_END),
     616             :                         keyNew ("system/elektra/modules/multifile/exports/get", KEY_FUNC, elektraMultifileGet, KEY_END),
     617             :                         keyNew ("system/elektra/modules/multifile/exports/set", KEY_FUNC, elektraMultifileSet, KEY_END),
     618             :                         keyNew ("system/elektra/modules/multifile/exports/commit", KEY_FUNC, elektraMultifileCommit, KEY_END),
     619             :                         keyNew ("system/elektra/modules/multifile/exports/error", KEY_FUNC, elektraMultifileError, KEY_END),
     620             :                         keyNew ("system/elektra/modules/multifile/exports/checkconf", KEY_FUNC, elektraMultifileCheckConfig, KEY_END),
     621             :                         keyNew ("system/elektra/modules/multifile/exports/checkfile", KEY_FUNC, elektraMultifileCheckFile, KEY_END),
     622             : 
     623             : #include ELEKTRA_README
     624             :                         keyNew ("system/elektra/modules/multifile/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
     625          44 :                 ksAppend (returned, contract);
     626          44 :                 ksDel (contract);
     627             : 
     628          44 :                 return 1; // success
     629             :         }
     630             :         // get all keys
     631          18 :         MultiConfig * mc = elektraPluginGetData (handle);
     632          18 :         if (!mc)
     633             :         {
     634           9 :                 mc = initialize (handle, parentKey);
     635             :         }
     636          18 :         if (!mc)
     637             :         {
     638             :                 return -1;
     639             :         }
     640          18 :         Codes rc = NOUPDATE;
     641          18 :         if (mc->getPhase == MULTI_GETRESOLVER)
     642             :         {
     643           9 :                 rc = updateFiles (handle, mc, returned, parentKey);
     644             :                 // if it is a only a partial cache hit, we still need to load everything
     645             :                 // in the next phase
     646           9 :                 if (rc >= SUCCESS)
     647             :                 {
     648           9 :                         mc->getPhase = MULTI_GETSTORAGE;
     649             :                 }
     650             :         }
     651           9 :         else if (mc->getPhase == MULTI_GETSTORAGE)
     652             :         {
     653           9 :                 rc = doGetStorage (mc, parentKey);
     654           9 :                 if (rc == SUCCESS || mc->hasDeleted)
     655             :                 {
     656           9 :                         fillReturned (mc, returned);
     657           9 :                         mc->hasDeleted = 0;
     658             :                 }
     659           9 :                 mc->getPhase = MULTI_GETRESOLVER;
     660             :         }
     661          18 :         elektraPluginSetData (handle, mc);
     662          18 :         if (rc == CACHE_HIT)
     663             :         {
     664             :                 return ELEKTRA_PLUGIN_STATUS_CACHE_HIT;
     665             :         }
     666          18 :         else if (rc == SUCCESS)
     667             :         {
     668             :                 return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     669             :         }
     670           0 :         else if (rc == NOUPDATE)
     671             :         {
     672             :                 return ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
     673             :         }
     674             :         else
     675             :         {
     676           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     677             :         }
     678             : }
     679             : 
     680           2 : static Codes resolverSet (MultiConfig * mc, Key * parentKey)
     681             : {
     682           2 :         ksRewind (mc->childBackends);
     683           2 :         Key * initialParent = keyDup (parentKey);
     684             :         Key * k;
     685           2 :         Codes rc = NOUPDATE;
     686           9 :         while ((k = ksNext (mc->childBackends)) != NULL)
     687             :         {
     688           7 :                 SingleConfig * s = *(SingleConfig **) keyValue (k);
     689           7 :                 if (s->rcResolver == NOUPDATE)
     690             :                 {
     691             :                         // fprintf (stderr, "SKIPPING %s:(%s)\n", s->parentString, s->fullPath);
     692           5 :                         continue;
     693             :                 }
     694           2 :                 else if (s->rcResolver == EMPTY)
     695             :                 {
     696             :                         // fprintf (stderr, "MARK FOR DELETE: %s:(%s)\n", s->parentString, s->fullPath);
     697             :                         // ++rc;
     698           1 :                         continue;
     699             :                 }
     700             :                 else
     701             :                 {
     702             :                         // fprintf (stderr, "UPDATE: %s:(%s)\n", s->parentString, s->fullPath);
     703             :                 }
     704             : 
     705           1 :                 Plugin * resolver = s->resolver;
     706           1 :                 keySetName (parentKey, s->parentString);
     707           1 :                 keySetString (parentKey, s->fullPath);
     708           1 :                 int r = resolver->kdbSet (resolver, s->ks, parentKey);
     709           1 :                 s->rcResolver = rc = rvToRc (r);
     710           1 :                 if (rc == ERROR) break;
     711           1 :                 if (s->tmpFilename) elektraFree (s->tmpFilename);
     712           1 :                 s->tmpFilename = elektraStrDup (keyString (parentKey));
     713             :                 // fprintf (stderr, "tmp filename for %s: %s\n", s->fullPath, s->tmpFilename);
     714             :         }
     715           2 :         keySetName (parentKey, keyName (initialParent));
     716           2 :         keySetString (parentKey, keyString (initialParent));
     717           2 :         keyDel (initialParent);
     718           2 :         return rc;
     719             : }
     720             : 
     721           2 : static Codes doSetStorage (MultiConfig * mc, Key * parentKey)
     722             : {
     723           2 :         ksRewind (mc->childBackends);
     724           2 :         Key * initialParent = keyDup (parentKey);
     725           2 :         Codes rc = NOUPDATE;
     726             :         Key * k;
     727           9 :         while ((k = ksNext (mc->childBackends)) != NULL)
     728             :         {
     729           7 :                 SingleConfig * s = *(SingleConfig **) keyValue (k);
     730           7 :                 if (s->rcResolver == EMPTY)
     731             :                 {
     732           1 :                         rc = SUCCESS;
     733           1 :                         s->rcStorage = EMPTY;
     734           1 :                         continue;
     735             :                 }
     736           6 :                 else if (s->rcResolver != SUCCESS)
     737             :                 {
     738           5 :                         continue;
     739             :                 }
     740             : 
     741           1 :                 keySetName (parentKey, s->parentString);
     742           1 :                 keySetString (parentKey, s->tmpFilename);
     743           1 :                 Plugin * storage = s->storage;
     744           1 :                 int r = storage->kdbSet (storage, s->ks, parentKey);
     745           1 :                 s->rcStorage = rc = rvToRc (r);
     746             : 
     747             :                 // fprintf (stderr, "%s ->kdbSet returned %d\n", mc->storage, r);
     748             : 
     749           1 :                 if (rc == ERROR) break;
     750             :         }
     751           2 :         keySetName (parentKey, keyName (initialParent));
     752           2 :         keySetString (parentKey, keyString (initialParent));
     753           2 :         keyDel (initialParent);
     754           2 :         return rc;
     755             : }
     756             : 
     757           1 : static Codes doCommit (MultiConfig * mc, Key * parentKey)
     758             : {
     759           1 :         ksRewind (mc->childBackends);
     760           1 :         Key * initialParent = keyDup (parentKey);
     761           1 :         Codes rc = NOUPDATE;
     762             :         Key * k;
     763           4 :         while ((k = ksNext (mc->childBackends)) != NULL)
     764             :         {
     765           3 :                 SingleConfig * s = *(SingleConfig **) keyValue (k);
     766           3 :                 if (s->rcStorage == EMPTY)
     767             :                 {
     768           0 :                         unlink (s->fullPath);
     769           0 :                         continue;
     770             :                 }
     771           3 :                 else if (s->rcStorage != 1)
     772             :                 {
     773           2 :                         continue;
     774             :                 }
     775             : 
     776           1 :                 keySetName (parentKey, s->parentString);
     777           1 :                 keySetString (parentKey, s->fullPath);
     778           1 :                 Plugin * resolver = s->resolver;
     779           1 :                 int r = resolver->kdbSet (resolver, s->ks, parentKey);
     780           1 :                 rc = rvToRc (r);
     781           1 :                 if (rc == ERROR) break;
     782             :                 // fprintf (stderr, "%s ->kdbSet returned %d\n", mc->resolver, r);
     783             :         }
     784           1 :         keySetName (parentKey, keyName (initialParent));
     785           1 :         keySetString (parentKey, keyString (initialParent));
     786           1 :         keyDel (initialParent);
     787           1 :         return rc;
     788             : }
     789             : 
     790           6 : static int diffOrNeedSync (KeySet * ks, KeySet * checkKS)
     791             : {
     792           6 :         if (ksGetSize (ks) != ksGetSize (checkKS)) return 1;
     793           6 :         ksRewind (ks);
     794           6 :         ksRewind (checkKS);
     795           6 :         Key * key = NULL;
     796           6 :         Key * check = NULL;
     797           6 :         int ret = -1;
     798          35 :         while (ret == -1)
     799             :         {
     800          23 :                 key = ksNext (ks);
     801          23 :                 check = ksNext (checkKS);
     802          23 :                 if (!key && !check)
     803             :                 {
     804             :                         ret = 0;
     805             :                 }
     806          18 :                 else if (!key || !check)
     807             :                 {
     808             :                         ret = 1;
     809             :                 }
     810          18 :                 else if (keyCmp (key, check))
     811             :                 {
     812             :                         ret = 1;
     813             :                 }
     814          18 :                 else if (keyNeedSync (check))
     815             :                 {
     816           1 :                         ret = 1;
     817             :                 }
     818             :         }
     819             :         return ret;
     820             : }
     821             : 
     822           2 : static void flagUpdateBackends (MultiConfig * mc, KeySet * returned)
     823             : {
     824           2 :         ksRewind (mc->childBackends);
     825             :         Key * k;
     826           9 :         while ((k = ksNext (mc->childBackends)) != NULL)
     827             :         {
     828           7 :                 SingleConfig * s = *(SingleConfig **) keyValue (k);
     829           7 :                 Key * cutKey = keyNew (s->parentString, KEY_END);
     830           7 :                 KeySet * cutKS = ksCut (returned, cutKey);
     831           7 :                 if (ksGetSize (cutKS) == 0)
     832             :                 {
     833           1 :                         s->rcResolver = EMPTY;
     834           1 :                         if (s->ks) ksDel (s->ks);
     835           1 :                         s->ks = NULL;
     836             :                 }
     837           6 :                 else if (diffOrNeedSync (s->ks, cutKS))
     838             :                 {
     839           1 :                         s->rcResolver = SUCCESS;
     840           1 :                         if (s->ks) ksDel (s->ks);
     841           1 :                         s->ks = ksDup (cutKS);
     842             :                 }
     843             :                 else
     844             :                 {
     845           5 :                         s->rcResolver = NOUPDATE;
     846             :                 }
     847           7 :                 keyDel (cutKey);
     848           7 :                 ksDel (cutKS);
     849             :         }
     850           2 : }
     851             : 
     852           5 : int elektraMultifileSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
     853             : {
     854           5 :         MultiConfig * mc = elektraPluginGetData (handle);
     855           5 :         if (!mc) return -1;
     856           5 :         Codes rc = NOUPDATE;
     857           5 :         if (mc->setPhase == MULTI_SETRESOLVER)
     858             :         {
     859           2 :                 flagUpdateBackends (mc, returned);
     860           2 :                 rc = resolverSet (mc, parentKey);
     861             :                 // fprintf (stderr, "resolverSet returned %d\n", rc);
     862           2 :                 if (rc == ERROR)
     863             :                 {
     864             :                         return -1;
     865             :                 }
     866           2 :                 mc->setPhase = MULTI_SETSTORAGE;
     867             :         }
     868           3 :         else if (mc->setPhase == MULTI_SETSTORAGE)
     869             :         {
     870           2 :                 rc = doSetStorage (mc, parentKey);
     871           2 :                 if (rc == ERROR)
     872             :                 {
     873             :                         return -1;
     874             :                 }
     875           2 :                 mc->setPhase = MULTI_COMMIT;
     876             :         }
     877           1 :         else if (mc->setPhase == MULTI_COMMIT)
     878             :         {
     879           1 :                 doCommit (mc, parentKey);
     880           1 :                 mc->setPhase = MULTI_SETRESOLVER;
     881             :         }
     882           5 :         if (rc == SUCCESS)
     883             :         {
     884             :                 return 1;
     885             :         }
     886           2 :         else if (rc == NOUPDATE)
     887             :         {
     888             :                 return 0;
     889             :         }
     890             :         else
     891             :         {
     892           0 :                 return -1;
     893             :         }
     894             : }
     895             : 
     896           0 : int elektraMultifileError (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
     897             : {
     898           0 :         MultiConfig * mc = elektraPluginGetData (handle);
     899           0 :         if (!mc) return 0;
     900           0 :         ksRewind (mc->childBackends);
     901             :         Key * key;
     902           0 :         Key * initialParent = keyDup (parentKey);
     903           0 :         while ((key = ksNext (mc->childBackends)) != NULL)
     904             :         {
     905           0 :                 SingleConfig * s = *(SingleConfig **) keyValue (key);
     906           0 :                 Plugin * resolver = s->resolver;
     907           0 :                 keySetName (parentKey, s->parentString);
     908           0 :                 keySetString (parentKey, s->fullPath);
     909           0 :                 if (resolver->kdbError)
     910             :                 {
     911           0 :                         resolver->kdbError (resolver, returned, parentKey);
     912             :                 }
     913             :         }
     914           0 :         keySetName (parentKey, keyName (initialParent));
     915           0 :         keySetString (parentKey, keyString (initialParent));
     916           0 :         keyDel (initialParent);
     917             : 
     918             : 
     919           0 :         return 1; // success
     920             : }
     921             : 
     922           2 : int elektraMultifileCommit (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
     923             : {
     924           2 :         return elektraMultifileSet (handle, returned, parentKey);
     925             : }
     926             : 
     927           2 : int elektraMultifileCheckConfig (Key * errorKey ELEKTRA_UNUSED, KeySet * conf ELEKTRA_UNUSED)
     928             : {
     929             :         // validate plugin configuration
     930             :         // this function is optional
     931             : 
     932             :         // the return codes have the following meaning:
     933             :         // 0: The configuration was OK and has not been changed
     934             :         // 1: The configuration has been changed and now it is OK
     935             :         // -1: The configuration was not OK and could not be fixed. An error has to be set to errorKey.
     936           2 :         return 0;
     937             : }
     938             : 
     939          88 : Plugin * ELEKTRA_PLUGIN_EXPORT
     940             : {
     941             :         // clang-format off
     942          88 :     return elektraPluginExport ("multifile",
     943             :             ELEKTRA_PLUGIN_OPEN,        &elektraMultifileOpen,
     944             :             ELEKTRA_PLUGIN_CLOSE,       &elektraMultifileClose,
     945             :             ELEKTRA_PLUGIN_GET, &elektraMultifileGet,
     946             :             ELEKTRA_PLUGIN_SET, &elektraMultifileSet,
     947             :             ELEKTRA_PLUGIN_ERROR,       &elektraMultifileError,
     948             :             ELEKTRA_PLUGIN_COMMIT,      &elektraMultifileCommit,
     949             :             ELEKTRA_PLUGIN_END);
     950             : }
     951             : 

Generated by: LCOV version 1.13