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