Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Everything related to a backend.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #ifdef HAVE_KDBCONFIG_H
10 : #include "kdbconfig.h"
11 : #endif
12 :
13 : #include <kdbassert.h>
14 :
15 : #ifdef HAVE_LOCALE_H
16 : #include <locale.h>
17 : #endif
18 :
19 : #ifdef HAVE_STDLIB_H
20 : #include <stdlib.h>
21 : #endif
22 :
23 : #ifdef HAVE_STDARG_H
24 : #include <stdarg.h>
25 : #endif
26 :
27 : #ifdef HAVE_CTYPE_H
28 : #include <ctype.h>
29 : #endif
30 :
31 : #ifdef HAVE_STRING_H
32 : #include <string.h>
33 : #endif
34 :
35 : #include <kdbinternal.h>
36 :
37 : /**
38 : * @brief Allocate a backend
39 : *
40 : * Initialize everything with zero, except: sizes with -1
41 : * and refcounter with 1
42 : *
43 : * @return
44 : */
45 : static Backend * elektraBackendAllocate (void)
46 : {
47 97143 : Backend * backend = elektraCalloc (sizeof (struct _Backend));
48 :
49 97143 : backend->refcounter = 1;
50 :
51 97143 : backend->specsize = -1;
52 97143 : backend->dirsize = -1;
53 97143 : backend->usersize = -1;
54 97143 : backend->systemsize = -1;
55 : return backend;
56 : }
57 :
58 :
59 : /**
60 : * @brief sets mountpoint
61 : *
62 : * @param backend where the mountpoint should be set
63 : * @param elektraConfig the config where the mountpoint can be found
64 : * @param [out] errorKey the name also has the mountpoint set
65 : *
66 : * @pre ksCurrent() is root key
67 : * @post ksCurrent() is root key
68 : *
69 : * @retval -1 if no mountpoint is found or memory allocation problem
70 : * @retval 0 on success
71 : */
72 7163 : int elektraBackendSetMountpoint (Backend * backend, KeySet * elektraConfig, Key * errorKey)
73 : {
74 7163 : Key * root = ksCurrent (elektraConfig);
75 7163 : Key * searchMountpoint = keyDup (root);
76 7163 : keyAddBaseName (searchMountpoint, "mountpoint");
77 7163 : Key * foundMountpoint = ksLookup (elektraConfig, searchMountpoint, 0);
78 7163 : keyDel (searchMountpoint);
79 7163 : ksLookup (elektraConfig, root, 0); // reset ksCurrent()
80 :
81 7163 : if (!foundMountpoint)
82 : {
83 0 : ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Could not find mountpoint within root %s", keyName (root));
84 0 : return -1;
85 : }
86 :
87 7163 : backend->mountpoint = keyNew ("", KEY_VALUE, keyBaseName (root), KEY_END);
88 7163 : elektraKeySetName (backend->mountpoint, keyString (foundMountpoint), KEY_CASCADING_NAME | KEY_EMPTY_NAME);
89 :
90 7163 : keySetName (errorKey, keyName (backend->mountpoint));
91 :
92 7163 : if (!backend->mountpoint)
93 : {
94 0 : ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Could not create mountpoint with name '%s' and value %s",
95 : keyString (foundMountpoint), keyBaseName (root));
96 0 : return -1;
97 : }
98 :
99 7163 : keyIncRef (backend->mountpoint);
100 7163 : return 0;
101 : }
102 :
103 : /**
104 : * Opens the internal backend that indicates that a backend
105 : * is missing at that place.
106 : *
107 : * @param global the global keyset of the KDB instance
108 : *
109 : * @return the fresh allocated backend or 0 if no memory
110 : */
111 0 : static Backend * backendOpenMissing (KeySet * global, Key * mp)
112 : {
113 0 : Backend * backend = elektraBackendAllocate ();
114 :
115 0 : Plugin * plugin = elektraPluginMissing ();
116 0 : if (!plugin)
117 : {
118 : /* Could not allocate plugin */
119 0 : elektraFree (backend);
120 0 : return 0;
121 : }
122 0 : plugin->global = global;
123 :
124 0 : backend->getplugins[0] = plugin;
125 0 : backend->setplugins[0] = plugin;
126 0 : plugin->refcounter = 2;
127 :
128 0 : keySetString (mp, "missing");
129 0 : backend->mountpoint = mp;
130 0 : keyIncRef (backend->mountpoint);
131 :
132 0 : return backend;
133 : }
134 :
135 : /**Builds a backend out of the configuration supplied
136 : * from:
137 : *
138 : @verbatim
139 : system/elektra/mountpoints/<name>
140 : @endverbatim
141 : *
142 : * The root key must be like the above example. You do
143 : * not need to rewind the keyset. But every key must be
144 : * below the root key.
145 : *
146 : * The internal consistency will be checked in this
147 : * function. If necessary parts are missing, like
148 : * no plugins, they cant be loaded or similar 0
149 : * will be returned.
150 : *
151 : * ksCut() is perfectly suitable for cutting out the
152 : * configuration like needed.
153 : *
154 : * @note The given KeySet will be deleted within the function,
155 : * don't use it afterwards.
156 : *
157 : * @param elektraConfig the configuration to work with.
158 : * It is used to build up this backend.
159 : * @param modules used to load new modules or get references
160 : * to existing one
161 : * @param global the global keyset of the KDB instance
162 : * @param errorKey the key where an error and warnings are added
163 : *
164 : * @return a pointer to a freshly allocated backend
165 : * this could be the requested backend or a so called
166 : * "missing backend".
167 : * @retval 0 if out of memory
168 : * @ingroup backend
169 : */
170 7163 : Backend * backendOpen (KeySet * elektraConfig, KeySet * modules, KeySet * global, Key * errorKey)
171 : {
172 : Key * cur;
173 7163 : KeySet * referencePlugins = 0;
174 7163 : KeySet * systemConfig = 0;
175 7163 : int failure = 0;
176 :
177 7163 : referencePlugins = ksNew (0, KS_END);
178 7163 : ksRewind (elektraConfig);
179 :
180 7163 : Key * root = ksNext (elektraConfig);
181 :
182 7163 : Backend * backend = elektraBackendAllocate ();
183 7163 : if (elektraBackendSetMountpoint (backend, elektraConfig, errorKey) == -1)
184 : { // warning already set
185 0 : failure = 1;
186 : }
187 :
188 42494 : while ((cur = ksNext (elektraConfig)) != 0)
189 : {
190 35331 : if (keyRel (root, cur) == 1)
191 : {
192 : // direct below root key
193 35331 : KeySet * cut = ksCut (elektraConfig, cur);
194 35331 : if (!strcmp (keyBaseName (cur), "config"))
195 : {
196 7043 : systemConfig = elektraRenameKeys (cut, "system");
197 7043 : ksDel (cut);
198 : }
199 28288 : else if (!strcmp (keyBaseName (cur), "errorplugins"))
200 : {
201 7039 : if (elektraProcessPlugins (backend->errorplugins, modules, referencePlugins, cut, systemConfig, global,
202 : errorKey) == -1)
203 : {
204 0 : if (!failure)
205 0 : ELEKTRA_ADD_INSTALLATION_WARNING (errorKey,
206 : "Method 'elektraProcessPlugins' for error failed");
207 : failure = 1;
208 : }
209 : }
210 21249 : else if (!strcmp (keyBaseName (cur), "getplugins"))
211 : {
212 7043 : if (elektraProcessPlugins (backend->getplugins, modules, referencePlugins, cut, systemConfig, global,
213 : errorKey) == -1)
214 : {
215 0 : if (!failure)
216 0 : ELEKTRA_ADD_INSTALLATION_WARNING (errorKey,
217 : "Method 'elektraProcessPlugins' for get failed");
218 : failure = 1;
219 : }
220 : }
221 14206 : else if (!strcmp (keyBaseName (cur), "mountpoint"))
222 : {
223 7163 : ksDel (cut); // already handled by elektraBackendSetMountpoint
224 7163 : continue;
225 : }
226 7043 : else if (!strcmp (keyBaseName (cur), "setplugins"))
227 : {
228 7043 : if (elektraProcessPlugins (backend->setplugins, modules, referencePlugins, cut, systemConfig, global,
229 : errorKey) == -1)
230 : {
231 0 : if (!failure)
232 0 : ELEKTRA_ADD_INSTALLATION_WARNING (errorKey,
233 : "Method 'elektraProcessPlugins' for set failed");
234 : failure = 1;
235 : }
236 : }
237 : else
238 : {
239 : // no one cares about that config
240 0 : if (!failure)
241 0 : ELEKTRA_ADD_VALIDATION_SYNTACTIC_WARNINGF (
242 : errorKey,
243 : "Found garbage within the backend configuration. found: %s but expected config, "
244 : "setplugins, getplugins, errorplugins or mountpoint",
245 : keyBaseName (cur));
246 0 : ksDel (cut);
247 : }
248 : }
249 : }
250 :
251 7163 : if (failure)
252 : {
253 0 : Backend * tmpBackend = backendOpenMissing (global, backend->mountpoint);
254 0 : backendClose (backend, errorKey);
255 0 : backend = tmpBackend;
256 : }
257 :
258 7163 : ksDel (systemConfig);
259 7163 : ksDel (elektraConfig);
260 7163 : ksDel (referencePlugins);
261 :
262 7163 : return backend;
263 : }
264 :
265 : /**
266 : * Opens a default backend using the plugin named KDB_RESOLVER
267 : * and KDB_STORAGE.
268 : *
269 : * @param modules the modules to work with
270 : * @param global the global keyset of the KDB instance
271 : * @param errorKey the key to issue warnings and errors to
272 : * @return the fresh allocated default backend or 0 if it failed
273 : */
274 31630 : Backend * backendOpenDefault (KeySet * modules, KeySet * global, const char * file, Key * errorKey)
275 : {
276 31630 : Backend * backend = elektraBackendAllocate ();
277 :
278 31630 : KeySet * resolverConfig = ksNew (5, keyNew ("system/path", KEY_VALUE, file, KEY_END), KS_END);
279 :
280 31630 : elektraKeySetName (errorKey, "", KEY_CASCADING_NAME | KEY_EMPTY_NAME);
281 :
282 31630 : Plugin * resolver = elektraPluginOpen (KDB_RESOLVER, modules, resolverConfig, errorKey);
283 31630 : if (!resolver)
284 : {
285 0 : elektraFree (backend);
286 : /* error already set in elektraPluginOpen */
287 0 : return 0;
288 : }
289 31630 : resolver->global = global;
290 :
291 : #ifdef ENABLE_TRACER
292 : KeySet * tracerConfig = ksNew (5,
293 : // does not matter because it is mounted differently in system/elektra/modules:
294 : // keyNew("system/logmodule", KEY_VALUE, "1", KEY_END),
295 : KS_END);
296 : Plugin * tracer = elektraPluginOpen ("tracer", modules, tracerConfig, errorKey);
297 : if (tracer)
298 : {
299 : backend->getplugins[RESOLVER_PLUGIN + 1] = tracer;
300 : backend->setplugins[RESOLVER_PLUGIN + 1] = tracer;
301 : backend->errorplugins[RESOLVER_PLUGIN + 1] = tracer;
302 : tracer->refcounter = 3;
303 : tracer->global = global;
304 : }
305 : #endif
306 :
307 31630 : backend->getplugins[RESOLVER_PLUGIN] = resolver;
308 31630 : backend->setplugins[RESOLVER_PLUGIN] = resolver;
309 31630 : backend->setplugins[COMMIT_PLUGIN] = resolver;
310 31630 : backend->errorplugins[STORAGE_PLUGIN] = resolver;
311 31630 : resolver->refcounter = 4;
312 :
313 31630 : KeySet * storageConfig = ksNew (5, KS_END);
314 :
315 31630 : Plugin * storage = elektraPluginOpen (KDB_STORAGE, modules, storageConfig, errorKey);
316 31630 : if (!storage)
317 : {
318 0 : elektraPluginClose (resolver, errorKey);
319 0 : elektraFree (backend);
320 : /* error already set in elektraPluginOpen */
321 0 : return 0;
322 : }
323 31630 : storage->global = global;
324 :
325 31630 : backend->getplugins[STORAGE_PLUGIN] = storage;
326 31630 : backend->setplugins[STORAGE_PLUGIN] = storage;
327 31630 : storage->refcounter = 2;
328 :
329 31630 : Key * mp = keyNew ("", KEY_VALUE, "default", KEY_END);
330 31630 : backend->mountpoint = mp;
331 31630 : keyIncRef (backend->mountpoint);
332 :
333 31630 : return backend;
334 : }
335 :
336 : /**@return a backend which gives plugin configuration of the module
337 : * which is currently point to.
338 : *
339 : * @param modules the modules to work with
340 : * @param global the global keyset of the KDB instance
341 : * @param errorKey the key to issue warnings and errors to
342 : */
343 47827 : Backend * backendOpenModules (KeySet * modules, KeySet * global, Key * errorKey)
344 : {
345 47827 : Backend * backend = elektraBackendAllocate ();
346 :
347 47827 : cursor_t save = ksGetCursor (modules);
348 47827 : KeySet * defaultConfig =
349 47827 : ksNew (5, keyNew ("system/module", KEY_VALUE, "1", KEY_END), keyNew ("user/module", KEY_VALUE, "1", KEY_END), KS_END);
350 47827 : Key * cur = ksCurrent (modules);
351 :
352 47827 : elektraKeySetName (errorKey, keyName (cur), KEY_CASCADING_NAME | KEY_EMPTY_NAME);
353 :
354 47827 : Plugin * plugin = elektraPluginOpen (keyBaseName (cur), modules, defaultConfig, errorKey);
355 47827 : if (!plugin)
356 : {
357 : /* Error already set in plugin */
358 0 : elektraFree (backend);
359 0 : return 0;
360 : }
361 47827 : plugin->global = global;
362 :
363 :
364 47827 : Key * mp = keyNew ("system/elektra/modules", KEY_VALUE, "modules", KEY_END);
365 :
366 : // for "virtual" plugins the keyBaseName (cur) would be "resolver" or "storage"
367 : // thus we use plugin->name here, which must be handled by kdbGet() of every
368 : // plugin properly by convention.
369 47827 : keyAddBaseName (mp, plugin->name);
370 :
371 47827 : backend->getplugins[0] = plugin;
372 47827 : plugin->refcounter = 1;
373 :
374 47827 : backend->mountpoint = mp;
375 47827 : keyIncRef (backend->mountpoint);
376 :
377 47827 : ksSetCursor (modules, save);
378 :
379 47827 : return backend;
380 : }
381 :
382 : /**
383 : * Opens the internal version backend.
384 : *
385 : * @param global the global keyset of the KDB instance
386 : * @param errorKey the key to issue warnings and errors to
387 : * @return the fresh allocated default backend or 0 if it failed
388 : */
389 10523 : Backend * backendOpenVersion (KeySet * global, Key * errorKey ELEKTRA_UNUSED)
390 : {
391 10523 : Backend * backend = elektraBackendAllocate ();
392 :
393 10523 : Plugin * plugin = elektraPluginVersion ();
394 10523 : if (!plugin)
395 : {
396 : /* Could not allocate plugin */
397 0 : elektraFree (backend);
398 0 : return 0;
399 : }
400 10523 : plugin->global = global;
401 :
402 10523 : Key * mp = keyNew ("system/elektra/version", KEY_VALUE, "version", KEY_END);
403 :
404 10523 : backend->getplugins[0] = plugin;
405 10523 : backend->setplugins[0] = plugin;
406 10523 : plugin->refcounter = 2;
407 :
408 10523 : backend->mountpoint = mp;
409 10523 : keyIncRef (backend->mountpoint);
410 :
411 10523 : return backend;
412 : }
413 :
414 :
415 : /**
416 : * @brief Update internal size in backend
417 : *
418 : * @param backend the backend to update
419 : * @param parent for parent
420 : * @param size to update (-1 default, 0 empty, >0 otherwise)
421 : *
422 : * @pre parent must be serializable namespace
423 : *
424 : * @retval -1 if invalid parent (assert)
425 : * @retval 0 on success
426 : */
427 85142 : int backendUpdateSize (Backend * backend, Key * parent, int size)
428 : {
429 85142 : switch (keyGetNamespace (parent))
430 : {
431 : case KEY_NS_SPEC:
432 14194 : backend->specsize = size;
433 14194 : break;
434 : case KEY_NS_DIR:
435 14116 : backend->dirsize = size;
436 14116 : break;
437 : case KEY_NS_USER:
438 16011 : backend->usersize = size;
439 16011 : break;
440 : case KEY_NS_SYSTEM:
441 40821 : backend->systemsize = size;
442 40821 : break;
443 : case KEY_NS_PROC:
444 : case KEY_NS_EMPTY:
445 : case KEY_NS_META:
446 : case KEY_NS_CASCADING:
447 : case KEY_NS_NONE:
448 0 : ELEKTRA_ASSERT (0, "invalid namespace %d", keyGetNamespace (parent));
449 : return -1;
450 : }
451 :
452 : ELEKTRA_LOG_DEBUG ("spec: %zd", backend->specsize);
453 : ELEKTRA_LOG_DEBUG ("dir: %zd", backend->dirsize);
454 : ELEKTRA_LOG_DEBUG ("user: %zd", backend->usersize);
455 : ELEKTRA_LOG_DEBUG ("system: %zd", backend->systemsize);
456 :
457 : return 0;
458 : }
459 :
460 113952 : int backendClose (Backend * backend, Key * errorKey)
461 : {
462 113952 : int errorOccurred = 0;
463 :
464 113952 : if (!backend) return -1;
465 :
466 113926 : --backend->refcounter;
467 :
468 : /* Check if we have the last reference on the backend (unsigned!) */
469 113926 : if (backend->refcounter > 0) return 0;
470 :
471 97319 : keyDecRef (backend->mountpoint);
472 97319 : keySetName (errorKey, keyName (backend->mountpoint));
473 97319 : keyDel (backend->mountpoint);
474 :
475 1070509 : for (int i = 0; i < NR_OF_PLUGINS; ++i)
476 : {
477 973190 : int ret = elektraPluginClose (backend->setplugins[i], errorKey);
478 973190 : if (ret == -1) ++errorOccurred;
479 :
480 973190 : ret = elektraPluginClose (backend->getplugins[i], errorKey);
481 973190 : if (ret == -1) ++errorOccurred;
482 :
483 973190 : ret = elektraPluginClose (backend->errorplugins[i], errorKey);
484 973190 : if (ret == -1) ++errorOccurred;
485 : }
486 97319 : elektraFree (backend);
487 :
488 97319 : if (errorOccurred)
489 : return -1;
490 : else
491 97319 : return 0;
492 : }
|