Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #include "xmltool.h"
10 :
11 : #ifdef HAVE_KDBCONFIG_H
12 : #include "kdbconfig.h"
13 : #endif
14 :
15 : #include <stdio.h>
16 : #include <stdlib.h>
17 : #include <string.h>
18 : #include <time.h>
19 : #include <unistd.h>
20 :
21 : #include "kdbtools.h"
22 : #include <kdbinternal.h>
23 :
24 :
25 : /**
26 : * @brief Methods to output, generate and toXML Keys and Keysets.
27 : *
28 : * Here are some functions that are in a separate library because they
29 : * depend on non-basic libraries as libxml. Link against kdbtools library if your
30 : * program won't be installed in /bin, or is not essential in early boot
31 : * stages.
32 : *
33 : * It is also possible to have a dynamic linkage, see the
34 : * sourcecode from kdb-tool or testing framework how to achieve
35 : * that.
36 : *
37 : * Output prints keys line per line, meant to be read by humans.
38 : * - keyOutput()
39 : * - ksOutput()
40 : *
41 : * toXML prints keys and keysets as XML, meant to be used
42 : * as exchange format.
43 : * - keyToStream()
44 : * - ksToStream()
45 : *
46 : * To use them:
47 : * @code
48 : #include <kdbtools.h>
49 : * @endcode
50 : *
51 : *
52 : */
53 :
54 :
55 : /*********************************************
56 : * Textual XML methods *
57 : *********************************************/
58 :
59 :
60 : /**
61 : * Prints an XML representation of the key.
62 : *
63 : * String generated is of the form:
64 : * @verbatim
65 : <key name="system/sw/xorg/Monitor/Monitor0/Name"
66 : type="string" uid="root" gid="root" mode="0600">
67 :
68 : <value>Samsung TFT panel</value>
69 : <comment>My monitor</comment>
70 : </key>@endverbatim
71 :
72 :
73 :
74 : * @verbatim
75 : <key parent="system/sw/xorg/Monitor/Monitor0" basename="Name"
76 : type="string" uid="root" gid="root" mode="0600">
77 :
78 : <value>Samsung TFT panel</value>
79 : <comment>My monitor</comment>
80 : </key>@endverbatim
81 : *
82 : * @param key the key object to work with
83 : * @param stream where to write output: a file or stdout
84 : * @param options Some #option_t ORed:
85 : * - @p option_t::KDB_O_NUMBERS \n
86 : * Do not convert UID and GID into user and group names
87 : * - @p option_t::KDB_O_CONDENSED \n
88 : * Less human readable, more condensed output
89 : * - @p option_t::KDB_O_FULLNAME \n
90 : * The @p user keys are exported with their full names (including
91 : * user domains)
92 : *
93 : * @see ksToStream()
94 : * @return number of bytes written to output
95 : */
96 0 : ssize_t keyToStream (const Key * key, FILE * stream, option_t options)
97 : {
98 0 : return keyToStreamBasename (key, stream, 0, 0, options);
99 : }
100 :
101 :
102 : /**
103 : * Same as keyToStream() but tries to strip @p parentSize bytes from
104 : * @p key name if it matches @p parent .
105 : *
106 : * Taking the example from keyToStream(), if @p parent is
107 : * @c "system/sw/xorg", the generated string is of the form:
108 : * @verbatim
109 : <key basename="Monitor/Monitor0/Name"
110 : type="string" uid="root" gid="root" mode="0600">
111 :
112 : <value>Samsung TFT panel</value>
113 : <comment>My monitor</comment>
114 : </key>@endverbatim
115 : *
116 : * It useful to produce more human readable XML output of a key when
117 : * it is being represented in a context that defines the parent key name.
118 : * For example:
119 : *
120 : * @verbatim
121 : <keyset parent="user/sw">
122 : <key basename="kdbedit"..../>
123 : <key basename="phototools"..../>
124 : <key basename="myapp"..../>
125 : </keyset>@endverbatim
126 : *
127 : * In the above example, each @p @<key@> entry was generated by a call to
128 : * keyToStreamBasename() having @c "user/sw" as @p parent .
129 : *
130 : * This method is used when ksToStream() is called with
131 : * KDBOption::KDB_O_HIER option.
132 : *
133 : * @param key the key object to work with
134 : * @param stream the FILE where to send the stream
135 : * @param parentSize the maximum size of @p parent that will be used.
136 : * If 0, the entire @p parent will be used.
137 : * @param parent the string (or part of it, defined by @p parentSize ) that
138 : * will be used to strip from the key name.
139 : * @param options Some #option_t ORed:
140 : * - @p option_t::KDB_O_NUMBERS \n
141 : * Do not convert UID and GID into user and group names
142 : * - @p option_t::KDB_O_CONDENSED \n
143 : * Less human readable, more condensed output
144 : * - @p option_t::KDB_O_FULLNAME \n
145 : * The @p user keys are exported with their full names (including
146 : * user domains)
147 : *
148 : * @return number of bytes written to output
149 : */
150 0 : ssize_t keyToStreamBasename (const Key * key, FILE * stream, const char * parent, const size_t parentSize, option_t options)
151 : {
152 0 : ssize_t written = 0;
153 :
154 : /* Write key name */
155 0 : if (parent)
156 : {
157 : /* some logic to see if we should print only the relative basename */
158 : int found;
159 0 : size_t skip = parentSize ? parentSize : elektraStrLen (parent) - 1;
160 :
161 0 : found = memcmp (parent, key->key, skip);
162 0 : if (found == 0)
163 : {
164 0 : while (*(key->key + skip) == KDB_PATH_SEPARATOR)
165 0 : ++skip;
166 :
167 0 : if (*(key->key + skip) != 0) /* we don't want a null basename */
168 0 : written += fprintf (stream, "<key basename=\"%s\"", key->key + skip);
169 : }
170 : }
171 :
172 0 : if (written == 0)
173 : { /* no "<key basename=..." was written so far */
174 0 : if (options & KDB_O_FULLNAME)
175 : {
176 : char buffer[KDB_MAX_PATH_LENGTH];
177 0 : keyGetFullName (key, buffer, sizeof (buffer));
178 0 : written += fprintf (stream, "<key name=\"%s\"", buffer);
179 : }
180 : else
181 0 : written += fprintf (stream, "<key name=\"%s\"", key->key);
182 : }
183 :
184 :
185 : /* Key type
186 : TODO: xml schema does not output type
187 : if (options & KDB_O_NUMBERS) {
188 : written+=fprintf(stream," type=\"%d\"", key->type);
189 : } else {
190 : buffer[0]=0;
191 :
192 : if (key->type & KEY_TYPE_DIR) written+=fprintf(stream, " isdir=\"yes\"");
193 : if (key->type & KEY_TYPE_REMOVE) written+=fprintf(stream, " isremove=\"yes\"");
194 : if (key->type & KEY_TYPE_BINARY) written+=fprintf(stream, " isbinary=\"yes\"");
195 : }
196 : */
197 :
198 0 : if (keyGetUID (key) != (uid_t) -1) written += fprintf (stream, " uid=\"%d\"", (int) keyGetUID (key));
199 0 : if (keyGetGID (key) != (gid_t) -1) written += fprintf (stream, " gid=\"%d\"", (int) keyGetGID (key));
200 :
201 0 : if (keyGetMode (key) != KDB_FILE_MODE)
202 : {
203 0 : written += fprintf (stream, " mode=\"0%o\"", keyGetMode (key));
204 : }
205 :
206 :
207 0 : if (!key->data.v && !keyComment (key))
208 : { /* no data AND no comment */
209 0 : written += fprintf (stream, "/>");
210 0 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n\n");
211 :
212 : return written; /* end of <key/> */
213 : }
214 : else
215 : {
216 0 : if (key->data.v)
217 : {
218 0 : if ((key->dataSize <= 16) && keyIsString (key) && /*TODO: is this for string?*/
219 0 : !strchr (key->data.c, '\n'))
220 : {
221 :
222 : /* we'll use a "value" attribute instead of a <value> node,
223 : for readability, so the cut size will be 16, which is
224 : the maximum size of an IPv4 address */
225 :
226 0 : if (options & KDB_O_CONDENSED)
227 0 : written += fprintf (stream, " ");
228 : else
229 0 : written += fprintf (stream, "\n\t");
230 :
231 0 : written += fprintf (stream, "value=\"%s\"", key->data.c);
232 :
233 0 : if (keyComment (key))
234 0 : written += fprintf (stream, ">\n");
235 : else
236 : {
237 0 : written += fprintf (stream, "/>");
238 0 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n");
239 :
240 : return written;
241 : }
242 : }
243 : else
244 : { /* value is bigger than 16 bytes: deserves own <value> */
245 0 : written += fprintf (stream, ">");
246 0 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n\n ");
247 :
248 0 : written += fprintf (stream, "<value>");
249 0 : if (keyIsString (key))
250 : { /*TODO: is this for string?*/
251 0 : written += fprintf (stream, "<![CDATA[");
252 0 : fflush (stream);
253 : /* must chop ending \\0 */
254 0 : written += fwrite (key->data.v, sizeof (char), key->dataSize - 1, stream);
255 0 : written += fprintf (stream, "]]>");
256 : }
257 : else
258 : {
259 : /* TODO Binary values
260 : char *encoded=elektraMalloc(3*key->dataSize);
261 : size_t encodedSize;
262 :
263 : written+=fprintf(stream,"\n");
264 : encodedSize=kdbbEncode(key->data.c,key->dataSize,encoded);
265 : fflush(stream);
266 : written+=fwrite(encoded,sizeof(char),encodedSize,stream);
267 : elektraFree (encoded);
268 : written+=fprintf(stream,"\n");
269 : */
270 : }
271 : /* fflush(stream); */
272 0 : written += fprintf (stream, "</value>");
273 : }
274 : }
275 : else
276 : { /* we have no data */
277 0 : if (keyComment (key))
278 : {
279 0 : written += fprintf (stream, ">");
280 0 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n");
281 : }
282 : else
283 : {
284 0 : written += fprintf (stream, "/>");
285 0 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n\n");
286 :
287 : return written;
288 : }
289 : }
290 : }
291 :
292 0 : if (!(options & KDB_O_CONDENSED))
293 : {
294 0 : written += fprintf (stream, "\n");
295 0 : if (keyComment (key)) written += fprintf (stream, " ");
296 : }
297 :
298 0 : if (keyComment (key))
299 : {
300 0 : written += fprintf (stream, "<comment><![CDATA[%s]]></comment>", keyComment (key));
301 0 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n");
302 : }
303 :
304 0 : written += fprintf (stream, "</key>");
305 :
306 0 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n\n");
307 :
308 : return written;
309 : }
310 :
311 : /**
312 : * Writes to @p stream an XML version of the @p ks object.
313 : *
314 : * String generated is of the form:
315 : * @verbatim
316 : <keyset>
317 : <key name=...>...</key>
318 : <key name=...>...</key>
319 : <key name=...>...</key>
320 :
321 : </keyset>
322 : * @endverbatim
323 : *
324 : * or if KDB_O_HIER is used, the form will be:
325 : * @verbatim
326 : <keyset parent="user/smallest/parent/name">
327 :
328 : <key basename=...>...</key>
329 : <key name=...>...</key> <!-- a key thats not under this keyset's parent -->
330 : <key basename=...>...</key>
331 :
332 : </keyset>
333 : * @endverbatim
334 : *
335 : * KDB_O_HEADER will additionally generate a header like:
336 : * @verbatim
337 : <?xml version="1.0" encoding="UTF-8"?>
338 : <!-- Generated by Elektra API. Total of n keys. -->
339 : <keyset xmlns="https://www.libelektra.org"
340 : xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
341 : xsi:schemaLocation="https://www.libelektra.org elektra.xsd">
342 : * @endverbatim
343 : *
344 : * @param ks the KeySet to serialise
345 : * @param stream where to write output: a file or stdout
346 : * @param options accepted #option_t ORed:
347 : * - @p option_t::KDB_O_NUMBERS \n
348 : * Do not convert UID and GID into user and group names.
349 : * - @p option_t::KDB_O_FULLNAME \n
350 : * The @c user keys are exported with their full names (including
351 : * user domains)
352 : * - @p option_t::KDB_O_CONDENSED \n
353 : * Less human readable, more condensed output.
354 : * - @p option_t::KDB_O_XMLHEADERS \n
355 : * Exclude the correct XML headers in the output. If not used, the
356 : * <?xml?> and schema info inside the <keyset> object will not be generated.
357 : * - @p option_t::KDB_O_HIER \n
358 : * Will generate a <keyset> node containing a @c parent attribute, and
359 : * <key> nodes with a @c basename relative to that @c parent. The @c parent
360 : * is calculated by taking the smallest key name in the keyset, so it is a
361 : * good idea to have only related keys on the keyset. Otherwise, a valid
362 : * consistent XML document still will be generated with regular absolute
363 : * @c name attribute for the <key> nodes, due to a
364 : * clever keyToStreamBasename() implementation.
365 : *
366 : * @see keyToStream()
367 : * @see commandList() for usage example
368 : * @return number of bytes written to output, or -1 if some error occurs
369 : * @param ks The keyset to output
370 : * @param stream the file pointer where to send the stream
371 : * @param options see above text
372 : */
373 0 : ssize_t ksToStream (const KeySet * ks, FILE * stream, option_t options)
374 : {
375 0 : size_t written = 0;
376 0 : Key * key = 0;
377 0 : KeySet * cks = ksDup (ks);
378 :
379 0 : ksRewind (cks);
380 :
381 0 : if (options & KDB_O_HEADER)
382 : {
383 0 : written += fprintf (stream, "<?xml version=\"1.0\" encoding=\"%s\"?>", "UTF-8");
384 0 : if (~options & KDB_O_CONDENSED)
385 0 : written += fprintf (stream, "\n<!-- Generated by Elektra API. Total of %d keys. -->\n", (int) cks->size);
386 0 : if (~options & KDB_O_CONDENSED)
387 0 : written += fprintf (stream,
388 : "<keyset xmlns=\"https://www.libelektra.org\"\n"
389 : "\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
390 : "\txsi:schemaLocation=\"https://www.libelektra.org elektra.xsd\"\n");
391 : else
392 0 : written += fprintf (stream,
393 : "<keyset xmlns=\"https://www.libelektra.org\""
394 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
395 : " xsi:schemaLocation=\"https://www.libelektra.org elektra.xsd\"");
396 : }
397 : else
398 0 : written += fprintf (stream, "<keyset");
399 :
400 0 : if (options & KDB_O_HIER)
401 : {
402 : char commonParent[KDB_MAX_PATH_LENGTH];
403 :
404 0 : ksGetCommonParentName (cks, commonParent, sizeof (commonParent));
405 :
406 0 : if (commonParent[0])
407 : {
408 0 : written += fprintf (stream, " parent=\"%s\">\n", commonParent);
409 0 : ksRewind (cks);
410 0 : while ((key = ksNext (cks)) != 0)
411 0 : written += keyToStreamBasename (key, stream, commonParent, 0, options);
412 : }
413 : else
414 : {
415 0 : written += fprintf (stream, ">\n");
416 0 : ksRewind (cks);
417 0 : while ((key = ksNext (cks)) != 0)
418 0 : written += keyToStream (key, stream, options);
419 : }
420 : }
421 : else
422 : { /* No KDB_O_HIER*/
423 0 : written += fprintf (stream, ">\n");
424 0 : ksRewind (cks);
425 0 : while ((key = ksNext (cks)) != 0)
426 0 : written += keyToStream (key, stream, options);
427 : }
428 :
429 0 : written += fprintf (stream, "</keyset>\n");
430 0 : ksDel (cks);
431 0 : return written;
432 : }
433 :
434 : /**
435 : * Output every information of a single key depending on options.
436 : *
437 : * The format is not very strict and only intend to be read
438 : * by human eyes for debugging purposes. Don't rely on the
439 : * format in your applications.
440 : *
441 : * @param k the key object to work with
442 : * @param stream the file pointer where to send the stream
443 : * @param options see text above
444 : * @see ksOutput()
445 : * @retval 1 on success
446 : * @retval -1 on allocation errors
447 : * @ingroup stream
448 : */
449 0 : int keyOutput (const Key * k, FILE * stream, option_t options)
450 : {
451 : time_t t;
452 : size_t s;
453 : char * tmc;
454 :
455 : size_t c;
456 :
457 : size_t n;
458 :
459 0 : n = keyGetNameSize (k);
460 0 : if (n > 1)
461 : {
462 0 : char * nam = (char *) elektraMalloc (n);
463 0 : if (nam == NULL) return -1;
464 0 : keyGetName (k, nam, n);
465 :
466 0 : fprintf (stream, "Name[%d]: %s : ", (int) n, nam);
467 :
468 0 : elektraFree (nam);
469 : }
470 :
471 0 : s = keyGetValueSize (k);
472 0 : if (options & KEY_VALUE && s > 1)
473 : {
474 0 : char * str = (char *) elektraMalloc (s);
475 0 : if (str == NULL) return -1;
476 0 : if (keyIsBinary (k))
477 : {
478 : /*
479 : char * bin;
480 : bin = (char*) elektraMalloc (s*3+1);
481 : keyGetBinary(k, str, s);
482 : kdbbEncode (str, s, bin);
483 : elektraFree (bin);
484 : */
485 0 : keyGetBinary (k, str, s);
486 0 : fprintf (stream, "Binary[%d]: %s : ", (int) s, str);
487 : }
488 : else
489 : {
490 0 : keyGetString (k, str, s);
491 0 : fprintf (stream, "String[%d]: %s : ", (int) s, str);
492 : }
493 :
494 0 : elektraFree (str);
495 : }
496 :
497 0 : c = keyGetCommentSize (k);
498 0 : if (options & KEY_COMMENT && c > 1)
499 : {
500 0 : char * com = (char *) elektraMalloc (c);
501 0 : if (com == NULL) return -1;
502 0 : keyGetComment (k, com, c);
503 :
504 0 : fprintf (stream, "Comment[%d]: %s : ", (int) c, com);
505 :
506 0 : elektraFree (com);
507 : }
508 :
509 :
510 0 : if (options & KDB_O_SHOWMETA) fprintf (stream, " : ");
511 0 : if (options & KEY_UID) fprintf (stream, "UID: %d : ", (int) keyGetUID (k));
512 0 : if (options & KEY_GID) fprintf (stream, "GID: %d : ", (int) keyGetGID (k));
513 0 : if (options & KEY_MODE) fprintf (stream, "Mode: %o : ", (int) keyGetMode (k));
514 :
515 0 : if (options & KEY_ATIME)
516 : {
517 0 : t = keyGetATime (k);
518 0 : tmc = ctime (&t);
519 0 : tmc[24] = '\0';
520 0 : fprintf (stream, "ATime: %s : ", tmc);
521 : }
522 :
523 0 : if (options & KEY_MTIME)
524 : {
525 0 : t = keyGetMTime (k);
526 0 : tmc = ctime (&t);
527 0 : tmc[24] = '\0';
528 0 : fprintf (stream, "MTime: %s : ", tmc);
529 : }
530 :
531 0 : if (options & KEY_CTIME)
532 : {
533 0 : t = keyGetCTime (k);
534 0 : tmc = ctime (&t);
535 0 : tmc[24] = '\0';
536 0 : fprintf (stream, "CTime: %s : ", tmc);
537 : }
538 :
539 0 : if (options & KDB_O_SHOWFLAGS)
540 : {
541 0 : if (!(options & KDB_O_SHOWMETA)) fprintf (stream, " ");
542 0 : fprintf (stream, "Flags: ");
543 0 : if (keyIsBinary (k)) fprintf (stream, "b");
544 0 : if (keyIsString (k)) fprintf (stream, "s");
545 0 : if (keyIsInactive (k)) fprintf (stream, "i");
546 0 : if (keyNeedSync (k)) fprintf (stream, "s");
547 : }
548 :
549 0 : fprintf (stream, "\n");
550 0 : return 1;
551 : }
552 :
553 :
554 : /**
555 : * Output all information of a keyset.
556 : *
557 : * The format is not very strict and only intend to be read
558 : * by human eyes for debugging purposes. Don't rely on the
559 : * format in your applications.
560 : *
561 : * Keys are printed line per line with keyOutput().
562 : *
563 : * The same options as keyOutput() are taken and passed
564 : * to them.
565 : *
566 : * Additional KDB_O_HEADER will print the number of keys
567 : * as first line.
568 : *
569 : * @param ks the keyset to work with
570 : * @param stream the file pointer where to send the stream
571 : * @param options
572 : * @see keyOutput()
573 : * @retval 1 on success
574 : * @retval -1 on allocation errors
575 : * @ingroup stream
576 : */
577 0 : int ksOutput (const KeySet * ks, FILE * stream, option_t options)
578 : {
579 : Key * key;
580 0 : KeySet * cks = ksDup (ks);
581 0 : size_t size = 0;
582 :
583 0 : ksRewind (cks);
584 :
585 0 : if (KDB_O_HEADER & options)
586 : {
587 0 : fprintf (stream, "Output keyset of size %d\n", (int) ksGetSize (cks));
588 : }
589 0 : while ((key = ksNext (cks)) != NULL)
590 : {
591 0 : if (options & KDB_O_SHOWINDICES) fprintf (stream, "[%d] ", (int) size);
592 0 : keyOutput (key, stream, options);
593 0 : size++;
594 : }
595 :
596 0 : ksDel (cks);
597 0 : return 1;
598 : }
|