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 :
|