Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief A plugin that makes use of libaugeas to read and write configuration files
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #ifndef HAVE_KDBCONFIG
11 : #include "kdbconfig.h"
12 : #endif
13 :
14 : /* used for asprintf */
15 : #define _GNU_SOURCE
16 :
17 : #include <ctype.h>
18 : #include <glob.h>
19 : #include <libgen.h>
20 :
21 : #include "aug.h"
22 :
23 : struct KeyConversion
24 : {
25 : KeySet * ks;
26 : Key * parentKey;
27 : int currentOrder;
28 : };
29 :
30 : struct OrphanSearch
31 : {
32 : KeySet * ks;
33 : Key * parentKey;
34 : };
35 :
36 : typedef int (*ForeachAugNodeClb) (augeas *, const char *, void *);
37 :
38 938 : int keySetOrderMeta (Key * key, int order)
39 : {
40 : char * buffer;
41 : int result;
42 938 : result = asprintf (&buffer, "%d", order);
43 :
44 938 : if (result < 0) return result;
45 :
46 938 : result = keySetMeta (key, "order", buffer);
47 938 : elektraFree (buffer);
48 938 : return result;
49 : }
50 :
51 278 : static int keyCmpOrderWrapper (const void * a, const void * b)
52 : {
53 278 : return elektraKeyCmpOrder (*((const Key **) a), *((const Key **) b));
54 : }
55 :
56 18 : static const char * getLensPath (Plugin * handle)
57 : {
58 18 : KeySet * config = elektraPluginGetConfig (handle);
59 18 : Key * lensPathKey = ksLookupByName (config, "/lens", 0);
60 18 : return keyString (lensPathKey);
61 : }
62 :
63 2 : int elektraAugeasGenConf (KeySet * ks, Key * errorKey ELEKTRA_UNUSED)
64 : {
65 : glob_t pglob;
66 2 : int retval = 1;
67 2 : const char * f = LIBAUGEAS_PREFIX "/share/augeas/lenses/dist/*.aug";
68 2 : if (glob (f, GLOB_NOSORT, NULL, &pglob) == 0)
69 : {
70 : ELEKTRA_LOG ("has glob %zd", pglob.gl_pathc);
71 422 : for (size_t i = 0; i < pglob.gl_pathc; ++i)
72 : {
73 422 : char * p = elektraStrDup (basename (pglob.gl_pathv[i]));
74 422 : size_t l = strlen (p);
75 422 : if (l > 4)
76 : {
77 422 : p[l - 4] = '\0';
78 422 : Key * k = keyNew ("system/", KEY_END);
79 422 : keyAddBaseName (k, p);
80 422 : ksAppendKey (ks, keyDup (k));
81 :
82 422 : Key * b = keyDup (k);
83 422 : keyAddBaseName (b, "infos");
84 422 : ksAppendKey (ks, keyDup (b));
85 422 : keyAddBaseName (b, "provides");
86 422 : char * s = elektraFormat ("storage/%s", p);
87 422 : keySetString (b, s);
88 422 : elektraFree (s);
89 422 : ksAppendKey (ks, b);
90 :
91 422 : keyAddBaseName (k, "config");
92 422 : ksAppendKey (ks, keyDup (k));
93 422 : keyAddBaseName (k, "lens");
94 422 : p[0] = toupper (p[0]);
95 422 : p[l - 1] = 's';
96 422 : p[l - 2] = 'n';
97 422 : p[l - 3] = 'l';
98 422 : p[l - 4] = '.';
99 422 : keySetString (k, p);
100 422 : ksAppendKey (ks, k);
101 : }
102 422 : elektraFree (p);
103 : }
104 2 : globfree (&pglob);
105 : }
106 : else
107 : {
108 0 : ELEKTRA_SET_INSTALLATION_ERRORF (errorKey, "Could not glob %s because of augeas", f);
109 0 : retval = -1;
110 : }
111 2 : return retval;
112 : }
113 :
114 0 : static const char * getAugeasError (augeas * augeasHandle)
115 : {
116 0 : const char * reason = 0;
117 0 : if (aug_error (augeasHandle) != 0)
118 : {
119 0 : reason = aug_error_message (augeasHandle);
120 : }
121 : else
122 : {
123 : const char * augeasError;
124 0 : aug_get (augeasHandle, "/augeas/text" AUGEAS_TREE_ROOT "/error", &augeasError);
125 :
126 0 : if (augeasError)
127 : {
128 : const char * lens;
129 : const char * line;
130 : const char * character;
131 : const char * message;
132 :
133 0 : aug_get (augeasHandle, "/augeas/text" AUGEAS_TREE_ROOT "/error/lens", &lens);
134 0 : aug_get (augeasHandle, "/augeas/text" AUGEAS_TREE_ROOT "/error/line", &line);
135 0 : aug_get (augeasHandle, "/augeas/text" AUGEAS_TREE_ROOT "/error/char", &character);
136 0 : aug_get (augeasHandle, "/augeas/text" AUGEAS_TREE_ROOT "/error/message", &message);
137 :
138 0 : const char * format = "%s\n\tposition: %s:%s\n\tmessage: %s\n\tlens: %s";
139 0 : size_t messageSize = (augeasError ? strlen (augeasError) : 0) + (line ? strlen (line) : 0) +
140 0 : (character ? strlen (character) : 0) + (message ? strlen (message) : 0) +
141 0 : (lens ? strlen (lens) : 0) + strlen (format);
142 0 : char * buffer = elektraMalloc (messageSize);
143 0 : snprintf (buffer, messageSize, format, augeasError ? augeasError : "", line ? line : "", character ? character : "",
144 0 : message ? message : "", lens ? lens : "");
145 0 : reason = buffer;
146 : }
147 : else
148 : {
149 : reason = "No specific reason was reported";
150 : }
151 : }
152 :
153 : /* should not happen, but avoid 0 return */
154 0 : if (!reason)
155 : {
156 0 : reason = "";
157 : }
158 0 : return reason;
159 : }
160 :
161 1080 : static Key * createKeyFromPath (Key * parentKey, const char * treePath)
162 : {
163 1080 : Key * key = keyDup (parentKey);
164 1080 : const char * baseName = (treePath + strlen (AUGEAS_TREE_ROOT) + 1);
165 :
166 1080 : size_t baseSize = keyGetNameSize (key);
167 1080 : size_t keyNameSize = strlen (baseName) + baseSize + 1;
168 1080 : char * newName = elektraMalloc (keyNameSize);
169 :
170 1080 : if (!newName) return 0;
171 :
172 1080 : strcpy (newName, keyName (key));
173 1080 : newName[baseSize - 1] = KDB_PATH_SEPARATOR;
174 1080 : newName[baseSize] = 0;
175 1080 : strcat (newName, baseName);
176 :
177 1080 : keySetName (key, newName);
178 1080 : elektraFree (newName);
179 :
180 1080 : return key;
181 : }
182 :
183 : /**
184 : * Creates a new Elektra key from the specified Augeas key.
185 : * If any step during key conversion fails, an error is returned
186 : * in order to prevent inconsistent keys.
187 : */
188 938 : static int convertToKey (augeas * handle, const char * treePath, void * data)
189 : {
190 938 : struct KeyConversion * conversionData = (struct KeyConversion *) data;
191 938 : int result = 0;
192 938 : const char * value = 0;
193 938 : result = aug_get (handle, treePath, &value);
194 :
195 : /* we were unable to retrieve the augeas value */
196 938 : if (result < 0) return result;
197 :
198 938 : Key * key = createKeyFromPath (conversionData->parentKey, treePath);
199 :
200 : /* fill key values */
201 938 : keySetString (key, value);
202 938 : conversionData->currentOrder++;
203 938 : result = keySetOrderMeta (key, conversionData->currentOrder);
204 :
205 : /* setting the correct key order failed */
206 938 : if (result < 0) return result;
207 :
208 938 : result = ksAppendKey (conversionData->ks, key);
209 :
210 938 : return result;
211 : }
212 :
213 : /**
214 : * Checks whether a given path must be pruned from the Augeas tree by comparing
215 : * it with the supplied keyset. If any operation during pruning fails, an error
216 : * is returned in order to prevent invalid keys.
217 : */
218 120 : static int removeOrphan (augeas * handle, const char * treePath, void * data)
219 : {
220 : int result;
221 120 : struct OrphanSearch * orphanData = (struct OrphanSearch *) data;
222 :
223 120 : Key * key = createKeyFromPath (orphanData->parentKey, treePath);
224 :
225 120 : if (!ksLookup (orphanData->ks, key, KDB_O_NONE))
226 : {
227 : char * nodeMatch;
228 : char ** matches;
229 12 : result = asprintf (&nodeMatch, "%s/*", treePath);
230 :
231 12 : if (result < 0) return -1;
232 :
233 12 : int numChildNodes = aug_match (handle, nodeMatch, &matches);
234 12 : elektraFree (nodeMatch);
235 :
236 : /* if the node is a leaf node we can safely delete it */
237 12 : if (numChildNodes == 0)
238 : {
239 6 : aug_rm (handle, treePath);
240 : }
241 : else
242 : {
243 : short pruneTree = 1;
244 22 : for (int i = 0; i < numChildNodes; i++)
245 : {
246 22 : Key * childKey = createKeyFromPath (orphanData->parentKey, matches[i]);
247 22 : if (ksLookup (orphanData->ks, childKey, KDB_O_NONE))
248 : {
249 16 : pruneTree = 0;
250 : }
251 22 : keyDel (childKey);
252 22 : elektraFree (matches[i]);
253 : }
254 6 : elektraFree (matches);
255 :
256 6 : if (pruneTree)
257 : {
258 2 : aug_rm (handle, treePath);
259 : }
260 : }
261 : }
262 :
263 120 : keyDel (key);
264 :
265 120 : return 0;
266 : }
267 :
268 18 : static int foreachAugeasNode (augeas * handle, const char * treePath, ForeachAugNodeClb callback, void * callbackData)
269 : {
270 : char * matchPath;
271 18 : int result = 0;
272 18 : result = asprintf (&matchPath, "%s//*", treePath);
273 :
274 18 : if (result < 0) return -1;
275 :
276 : /* must be non NULL for aug_match to return matches */
277 18 : char ** matches = (char **) 1;
278 18 : int numMatches = aug_match (handle, matchPath, &matches);
279 18 : elektraFree (matchPath);
280 :
281 18 : if (numMatches < 0) return numMatches;
282 :
283 : int i;
284 1058 : for (i = 0; i < numMatches; i++)
285 : {
286 : /* retrieve the value from augeas */
287 1058 : char * curPath = matches[i];
288 :
289 1058 : result = (*callback) (handle, curPath, callbackData);
290 :
291 : /* if handling the key failed, abort with an error as
292 : * the failed key could be a crucial one
293 : */
294 1058 : if (result < 0) break;
295 :
296 1058 : elektraFree (curPath);
297 : }
298 :
299 0 : for (; i < numMatches; i++)
300 : {
301 0 : elektraFree (matches[i]);
302 : }
303 :
304 18 : elektraFree (matches);
305 :
306 18 : return result;
307 : }
308 :
309 12 : static char * loadFile (FILE * fh)
310 : {
311 : /* open the file */
312 12 : char * content = 0;
313 :
314 12 : if (fseek (fh, 0, SEEK_END) != 0) return 0;
315 :
316 12 : long fileSize = ftell (fh);
317 12 : rewind (fh);
318 :
319 12 : if (fileSize > 0)
320 : {
321 10 : content = elektraMalloc (fileSize * sizeof (char) + 1);
322 10 : if (content == 0) return 0;
323 10 : int readBytes = fread (content, sizeof (char), fileSize, fh);
324 :
325 10 : if (feof (fh) || ferror (fh) || readBytes != fileSize) return 0;
326 :
327 : /* null terminate the string, as fread doesn't do it */
328 10 : content[fileSize] = 0;
329 : }
330 2 : else if (fileSize == 0)
331 : {
332 2 : content = elektraMalloc (1);
333 2 : if (content == 0) return 0;
334 2 : *content = (char) 0;
335 : }
336 :
337 : return content;
338 : }
339 :
340 12 : static int loadTree (augeas * augeasHandle, const char * lensPath, char * content)
341 : {
342 12 : aug_set (augeasHandle, AUGEAS_CONTENT_ROOT, content);
343 :
344 12 : return aug_text_store (augeasHandle, lensPath, AUGEAS_CONTENT_ROOT, AUGEAS_TREE_ROOT);
345 : }
346 :
347 8 : static int saveFile (augeas * augeasHandle, FILE * fh)
348 : {
349 : /* retrieve the file content */
350 8 : int ret = 0;
351 8 : const char * value = 0;
352 8 : aug_get (augeasHandle, AUGEAS_OUTPUT_ROOT, &value);
353 :
354 : /* write the file */
355 8 : if (value)
356 : {
357 8 : ret = fwrite (value, sizeof (char), strlen (value), fh);
358 :
359 8 : if (feof (fh) || ferror (fh)) return -1;
360 : }
361 :
362 : return ret;
363 : }
364 :
365 8 : static int saveTree (augeas * augeasHandle, KeySet * ks, const char * lensPath, Key * parentKey)
366 : {
367 8 : int ret = 0;
368 :
369 8 : size_t prefixSize = keyGetNameSize (parentKey) - 1;
370 8 : size_t arraySize = ksGetSize (ks);
371 8 : Key ** keyArray = calloc (ksGetSize (ks), sizeof (Key *));
372 8 : ret = elektraKsToMemArray (ks, keyArray);
373 :
374 8 : if (ret < 0) goto memoryerror;
375 :
376 8 : qsort (keyArray, arraySize, sizeof (Key *), keyCmpOrderWrapper);
377 :
378 : /* convert the Elektra KeySet to an Augeas tree */
379 124 : for (size_t i = 0; i < arraySize; i++)
380 : {
381 116 : Key * key = keyArray[i];
382 : char * nodeName;
383 116 : ret = asprintf (&nodeName, AUGEAS_TREE_ROOT "%s", (keyName (key) + prefixSize));
384 :
385 116 : if (ret < 0) goto memoryerror;
386 :
387 116 : aug_set (augeasHandle, nodeName, keyString (key));
388 116 : elektraFree (nodeName);
389 : }
390 :
391 8 : elektraFree (keyArray);
392 :
393 : /* remove keys not present in the KeySet */
394 8 : struct OrphanSearch * data = elektraMalloc (sizeof (struct OrphanSearch));
395 :
396 8 : if (!data) return -1;
397 :
398 8 : data->ks = ks;
399 8 : data->parentKey = parentKey;
400 :
401 8 : ret = foreachAugeasNode (augeasHandle, AUGEAS_TREE_ROOT, &removeOrphan, data);
402 :
403 8 : elektraFree (data);
404 :
405 : /* build the tree */
406 8 : ret = aug_text_retrieve (augeasHandle, lensPath, AUGEAS_CONTENT_ROOT, AUGEAS_TREE_ROOT, AUGEAS_OUTPUT_ROOT);
407 :
408 8 : if (ret < 0)
409 : {
410 : /* report the augeas specific error */
411 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (parentKey, getAugeasError (augeasHandle));
412 : }
413 :
414 : return ret;
415 :
416 : memoryerror:
417 0 : elektraFree (keyArray);
418 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey, "Unable to allocate memory while saving the augeas tree");
419 0 : return -1;
420 : }
421 :
422 310 : int elektraAugeasOpen (Plugin * handle, Key * parentKey)
423 : {
424 : augeas * augeasHandle;
425 310 : augeasHandle = aug_init (NULL, NULL, AUG_NO_MODL_AUTOLOAD | AUG_NO_ERR_CLOSE);
426 : int ret;
427 :
428 310 : if (aug_error (augeasHandle) != AUG_NOERROR)
429 : {
430 : char * errormessage;
431 0 : ret = asprintf (&errormessage, "Unable to initialize augeas: %s", aug_error_message (augeasHandle));
432 :
433 0 : if (ret >= 0)
434 : {
435 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey, "Unable to allocate memory for a detailed augeas error message");
436 0 : return -1;
437 : }
438 :
439 0 : ELEKTRA_SET_INSTALLATION_ERROR (parentKey, errormessage);
440 0 : elektraFree (errormessage);
441 0 : return -1;
442 : }
443 :
444 310 : elektraPluginSetData (handle, augeasHandle);
445 310 : return 0;
446 : }
447 :
448 310 : int elektraAugeasClose (Plugin * handle, Key * parentKey ELEKTRA_UNUSED)
449 : {
450 310 : augeas * augeasHandle = elektraPluginGetData (handle);
451 :
452 310 : if (augeasHandle)
453 : {
454 310 : aug_close (augeasHandle);
455 310 : elektraPluginSetData (handle, 0);
456 : }
457 :
458 310 : return 0;
459 : }
460 :
461 308 : int elektraAugeasGet (Plugin * handle, KeySet * returned, Key * parentKey)
462 : {
463 308 : int errnosave = errno;
464 308 : int ret = 0;
465 :
466 308 : if (!strcmp (keyName (parentKey), "system/elektra/modules/augeas"))
467 : {
468 298 : KeySet * info =
469 : #include "contract.h"
470 :
471 298 : ksAppend (returned, info);
472 298 : ksDel (info);
473 298 : return 1;
474 : }
475 :
476 10 : augeas * augeasHandle = elektraPluginGetData (handle);
477 :
478 : /* retrieve the lens to use */
479 10 : const char * lensPath = getLensPath (handle);
480 10 : if (!lensPath) ELEKTRA_SET_INSTALLATION_ERRORF (parentKey, "No Augeas lens was configured: %s", keyName (parentKey));
481 :
482 10 : FILE * fh = fopen (keyString (parentKey), "r");
483 :
484 10 : if (fh == 0)
485 : {
486 0 : ELEKTRA_SET_ERROR_GET (parentKey);
487 0 : errno = errnosave;
488 0 : return -1;
489 : }
490 :
491 : /* load its contents into a string */
492 10 : char * content = loadFile (fh);
493 :
494 10 : if (content == 0)
495 : {
496 0 : fclose (fh);
497 0 : ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Error while reading file. Reason: %s", strerror (errno));
498 : }
499 :
500 : /* convert the string into an augeas tree */
501 10 : ret = loadTree (augeasHandle, lensPath, content);
502 10 : elektraFree (content);
503 :
504 10 : if (ret < 0)
505 : {
506 0 : fclose (fh);
507 0 : ELEKTRA_SET_INSTALLATION_ERROR (parentKey, getAugeasError (augeasHandle));
508 : }
509 :
510 : /* convert the augeas tree to an Elektra KeySet */
511 10 : ksClear (returned);
512 10 : KeySet * append = ksNew (ksGetSize (returned) * 2, KS_END);
513 :
514 10 : Key * key = keyDup (parentKey);
515 10 : ksAppendKey (append, key);
516 :
517 10 : struct KeyConversion * conversionData = elektraMalloc (sizeof (struct KeyConversion));
518 :
519 10 : if (!conversionData)
520 : {
521 0 : fclose (fh);
522 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERRORF (parentKey, "Out of memory. Errno: %s", strerror (errno));
523 : }
524 :
525 10 : conversionData->currentOrder = 0;
526 10 : conversionData->parentKey = key;
527 10 : conversionData->ks = append;
528 :
529 10 : ret = foreachAugeasNode (augeasHandle, AUGEAS_TREE_ROOT, &convertToKey, conversionData);
530 :
531 10 : elektraFree (conversionData);
532 :
533 10 : if (ret < 0)
534 : {
535 0 : fclose (fh);
536 0 : ksDel (append);
537 0 : ELEKTRA_SET_INSTALLATION_ERROR (parentKey, getAugeasError (augeasHandle));
538 : }
539 :
540 10 : fclose (fh);
541 :
542 10 : ksAppend (returned, append);
543 10 : ksDel (append);
544 10 : errno = errnosave;
545 10 : return 1;
546 : }
547 :
548 8 : int elektraAugeasSet (Plugin * handle, KeySet * returned, Key * parentKey)
549 : {
550 8 : int errnosave = errno;
551 8 : augeas * augeasHandle = elektraPluginGetData (handle);
552 :
553 8 : const char * lensPath = getLensPath (handle);
554 :
555 8 : if (!lensPath)
556 : {
557 0 : ELEKTRA_SET_INSTALLATION_ERRORF (parentKey, "No Augeas lens was configured: %s", keyName (parentKey));
558 : }
559 :
560 8 : FILE * fh = fopen (keyValue (parentKey), "w+");
561 :
562 8 : if (fh == 0)
563 : {
564 0 : ELEKTRA_SET_ERROR_SET (parentKey);
565 0 : errno = errnosave;
566 0 : return -1;
567 : }
568 :
569 8 : int ret = 0;
570 :
571 8 : if (aug_match (augeasHandle, AUGEAS_TREE_ROOT, NULL) == 0)
572 : {
573 : /* load a fresh copy of the file into the tree */
574 2 : char * content = loadFile (fh);
575 :
576 2 : if (content == 0)
577 : {
578 0 : fclose (fh);
579 0 : ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Error while reading file. Reason: %s", strerror (errno));
580 : }
581 :
582 : /* convert the string into an augeas tree */
583 2 : ret = loadTree (augeasHandle, lensPath, content);
584 2 : elektraFree (content);
585 :
586 2 : if (ret < 0)
587 : {
588 0 : fclose (fh);
589 0 : ELEKTRA_SET_INSTALLATION_ERROR (parentKey, getAugeasError (augeasHandle));
590 : }
591 : }
592 :
593 8 : ret = saveTree (augeasHandle, returned, lensPath, parentKey);
594 :
595 8 : if (ret < 0)
596 : {
597 0 : fclose (fh);
598 0 : errno = errnosave;
599 0 : return -1;
600 : }
601 :
602 : /* write the Augeas tree to the file */
603 8 : ret = saveFile (augeasHandle, fh);
604 8 : fclose (fh);
605 :
606 8 : if (ret < 0) ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Could not open file for writing. Reason: %s", strerror (errno));
607 :
608 8 : errno = errnosave;
609 8 : return 1;
610 : }
611 :
612 310 : Plugin * ELEKTRA_PLUGIN_EXPORT
613 : {
614 : // clang-format off
615 310 : return elektraPluginExport ("augeas",
616 : ELEKTRA_PLUGIN_GET, &elektraAugeasGet,
617 : ELEKTRA_PLUGIN_SET, &elektraAugeasSet,
618 : ELEKTRA_PLUGIN_OPEN, &elektraAugeasOpen,
619 : ELEKTRA_PLUGIN_CLOSE, &elektraAugeasClose,
620 : ELEKTRA_PLUGIN_END);
621 : }
622 :
|