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