Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Methods for metadata manipulation.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #include <kdb.h>
10 : #include <kdbconfig.h>
11 : #include <kdbease.h>
12 : #include <kdbmeta.h>
13 : #include <kdbprivate.h>
14 : #include <kdbproposal.h>
15 : #include <kdbtypes.h>
16 : #ifdef HAVE_STDIO_H
17 : #include <stdio.h>
18 : #endif
19 :
20 : #ifdef HAVE_STDARG_H
21 : #include <stdarg.h>
22 : #endif
23 :
24 : #ifdef HAVE_STRING_H
25 : #include <string.h>
26 : #endif
27 :
28 : #ifdef HAVE_STDLIB_H
29 : #include <stdlib.h>
30 : #endif
31 :
32 : #ifdef HAVE_ERRNO_H
33 : #include <errno.h>
34 : #endif
35 :
36 :
37 : /**
38 : * @defgroup meta Meta Data proposal+compatibility
39 : * @brief Meta data proposal+compatibility methods.
40 : * @ingroup proposal
41 : *
42 : * In versions before Elektra 0.8 only limited metadata was
43 : * available. Now any metadata can be added. These API methods are
44 : * implementations of the 0.7 API using 0.8 metadata.
45 : *
46 : * Additionally, new suggestions can be made here.
47 : *
48 : * It is planned that these methods will be generated from doc/METADATA.ini
49 : * and moved to a separate library.
50 : * Currently, you should better avoid the methods and directly use @link keymeta metainfo @endlink
51 : * instead.
52 : *
53 : * @{
54 : *
55 : */
56 :
57 :
58 : /*********************************************
59 : * General comment manipulation methods *
60 : *********************************************/
61 :
62 :
63 : /**
64 : * Return a pointer to the real internal @p key comment.
65 : *
66 : * This is a much more efficient version of keyGetComment() and you
67 : * should use it if you are responsible enough to not mess up things.
68 : * You are not allowed to change anything in the memory region the
69 : * returned pointer points to.
70 : *
71 : * keyComment() returns "" when there is no keyComment. The reason is
72 : * @code
73 : key=keyNew(0);
74 : keySetComment(key,"");
75 : keyComment(key); // you would expect "" here
76 : keyDel(key);
77 : * @endcode
78 : *
79 : * See keySetComment() for more information on comments.
80 : *
81 : * @note Note that the Key structure keeps its own size field that is calculated
82 : * by library internal calls, so to avoid inconsistencies, you
83 : * must never use the pointer returned by keyComment() method to set a new
84 : * value. Use keySetComment() instead.
85 : *
86 : * @param key the key object to work with
87 : * @return a pointer to the internal managed comment
88 : * @retval "" when there is no comment
89 : * @retval 0 on NULL pointer
90 : * @see keyGetCommentSize() for size and keyGetComment() as alternative
91 : */
92 1070 : const char * keyComment (const Key * key)
93 : {
94 : const char * comment;
95 :
96 1070 : if (!key) return 0;
97 1066 : comment = keyValue (keyGetMeta (key, "comment"));
98 :
99 1066 : if (!comment)
100 : {
101 : /*errno=KDB_ERR_NOKEY;*/
102 : return "";
103 : }
104 :
105 1052 : return comment;
106 : }
107 :
108 :
109 : /**
110 : * Calculates number of bytes needed to store a key comment, including
111 : * final NULL.
112 : *
113 : * Use this method to know to size for allocated memory to retrieve
114 : * a key comment.
115 : *
116 : * See keySetComment() for more information on comments.
117 : *
118 : * For an empty key name you need one byte to store the ending NULL.
119 : * For that reason 1 is returned.
120 : *
121 : * @code
122 : char *buffer;
123 : buffer = elektraMalloc (keyGetCommentSize (key));
124 : // use this buffer to store the comment
125 : // pass keyGetCommentSize (key) for maxSize
126 : * @endcode
127 : *
128 : * @param key the key object to work with
129 : * @return number of bytes needed
130 : * @retval 1 if there is no comment
131 : * @retval -1 on NULL pointer
132 : * @see keyGetComment(), keySetComment()
133 : */
134 1302 : ssize_t keyGetCommentSize (const Key * key)
135 : {
136 : ssize_t size;
137 1302 : if (!key) return -1;
138 :
139 1298 : size = keyGetValueSize (keyGetMeta (key, "comment"));
140 :
141 1298 : if (!size || size == -1)
142 : {
143 : /*errno=KDB_ERR_NODESC;*/
144 : return 1;
145 : }
146 :
147 1196 : return size;
148 : }
149 :
150 :
151 : /**
152 : * Get the key comment.
153 : *
154 : * @section comment Comments
155 : *
156 : * A Key comment is description for humans what this key is for. It may be a
157 : * textual explanation of valid values, when and why a user or administrator
158 : * changed the key or any other text that helps the user or administrator related
159 : * to that key.
160 : *
161 : * Don't depend on a comment in your program. A user is
162 : * always allowed to remove or change it in any way he wants to. But you are
163 : * allowed or even encouraged to always show the content of the comment
164 : * to the user and allow him to change it.
165 : *
166 : * @param key the key object to work with
167 : * @param returnedComment pre-allocated memory to copy the comments to
168 : * @param maxSize number of bytes that will fit returnedComment
169 : * @return the number of bytes actually copied to @p returnedString, including
170 : * final NULL
171 : * @retval 1 if the string is empty
172 : * @retval -1 on NULL pointer
173 : * @retval -1 if maxSize is 0, not enough to store the comment or when larger then SSIZE_MAX
174 : * @see keyGetCommentSize(), keySetComment()
175 : */
176 151 : ssize_t keyGetComment (const Key * key, char * returnedComment, size_t maxSize)
177 : {
178 : const char * comment;
179 : size_t commentSize;
180 151 : if (!key) return -1;
181 :
182 147 : if (!maxSize) return -1;
183 133 : if (!returnedComment) return -1;
184 129 : if (maxSize > SSIZE_MAX) return -1;
185 :
186 125 : comment = keyValue (keyGetMeta (key, "comment"));
187 125 : commentSize = keyGetValueSize (keyGetMeta (key, "comment"));
188 :
189 125 : if (!comment)
190 : {
191 : /*errno=KDB_ERR_NODESC;*/
192 16 : returnedComment[0] = 0;
193 16 : return 1;
194 : }
195 :
196 109 : strncpy (returnedComment, comment, maxSize);
197 109 : if (maxSize < commentSize)
198 : {
199 : /*errno=KDB_ERR_TRUNC;*/
200 : return -1;
201 : }
202 63 : return commentSize;
203 : }
204 :
205 :
206 : /**
207 : * Set a comment for a key.
208 : *
209 : * A key comment is like a configuration file comment.
210 : * See keySetComment() for more information.
211 : *
212 : * @param key the key object to work with
213 : * @param newComment the comment, that can be freed after this call.
214 : * @return the number of bytes actually saved including final NULL
215 : * @retval 0 when the comment was freed (newComment NULL or empty string)
216 : * @retval -1 on NULL pointer or memory problems
217 : * @see keyGetComment()
218 : */
219 1199 : ssize_t keySetComment (Key * key, const char * newComment)
220 : {
221 1199 : if (!key) return -1;
222 1195 : if (!newComment || *newComment == 0)
223 : {
224 22 : keySetMeta (key, "comment", 0);
225 22 : return 1;
226 : }
227 :
228 1173 : keySetMeta (key, "comment", newComment);
229 1173 : return keyGetCommentSize (key);
230 : }
231 :
232 :
233 : #ifndef _WIN32
234 :
235 : /*********************************************
236 : * UID, GID access methods *
237 : *********************************************/
238 :
239 :
240 : /**
241 : * Get the user ID of a key.
242 : *
243 : * @deprecated This API is obsolete.
244 : *
245 : * @section UID UID
246 : *
247 : * The user ID is a unique identification for every user present on a
248 : * system. Keys will belong to root (0) as long as you did not get their
249 : * real UID with kdbGet().
250 : *
251 : * Although usually the same, the UID of a key is not related to its owner.
252 : *
253 : * A fresh key will have no UID.
254 : *
255 : * @param key the key object to work with
256 : * @return the system's UID of the key
257 : * @retval (uid_t)-1 on NULL key
258 : * @see keyGetGID(), keySetUID(), keyGetOwner()
259 : */
260 36 : uid_t keyGetUID (const Key * key)
261 : {
262 : const char * uid;
263 : long int val;
264 : char * endptr;
265 36 : int errorval = errno;
266 :
267 36 : if (!key) return (uid_t) -1;
268 :
269 34 : uid = keyValue (keyGetMeta (key, "uid"));
270 34 : if (!uid) return (uid_t) -1;
271 30 : if (*uid == '\0') return (uid_t) -1;
272 :
273 : /*From now on we have to leave using cleanup*/
274 30 : errno = 0;
275 30 : val = strtol (uid, &endptr, 10);
276 :
277 : /*Check for errors*/
278 30 : if (errno) goto cleanup;
279 :
280 : /*Check if nothing was found*/
281 30 : if (endptr == uid) goto cleanup;
282 :
283 : /*Check if the whole string was processed*/
284 26 : if (*endptr != '\0') goto cleanup;
285 :
286 22 : return val;
287 : cleanup:
288 : /*First restore errno*/
289 8 : errno = errorval;
290 8 : return (uid_t) -1;
291 : }
292 :
293 :
294 : /**
295 : * Set the user ID of a key.
296 : *
297 : * @deprecated This API is obsolete.
298 : *
299 : * See @ref UID for more information about user IDs.
300 : *
301 : * @param key the key object to work with
302 : * @param uid the user ID to set
303 : * @retval 0 on success
304 : * @retval -1 on NULL key or conversion error
305 : * @see keySetGID(), keyGetUID(), keyGetOwner()
306 : */
307 20 : int keySetUID (Key * key, uid_t uid)
308 : {
309 : char str[MAX_LEN_INT];
310 20 : if (!key) return -1;
311 :
312 20 : if (snprintf (str, MAX_LEN_INT - 1, "%d", uid) < 0)
313 : {
314 : return -1;
315 : }
316 :
317 20 : keySetMeta (key, "uid", str);
318 :
319 20 : return 0;
320 : }
321 :
322 :
323 : /**
324 : * Get the group ID of a key.
325 : *
326 : * @deprecated This API is obsolete.
327 : *
328 : * @section GID GID
329 : *
330 : * The group ID is a unique identification for every group present on
331 : * a system. Keys will belong to root (0) as long as you did not get their
332 : * real GID with kdbGet().
333 : *
334 : * Unlike UID users might change their group. This makes it possible to
335 : * share configuration between some users.
336 : *
337 : * A fresh key will have (gid_t)-1 also known as the group nogroup.
338 : * It means that the key is not related to a group ID at the moment.
339 : *
340 : * @param key the key object to work with
341 : * @return the system's GID of the key
342 : * @retval (gid_t)-1 on NULL key or currently unknown ID
343 : * @see keySetGID(), keyGetUID()
344 : */
345 14 : gid_t keyGetGID (const Key * key)
346 : {
347 : const char * gid;
348 : long int val;
349 : char * endptr;
350 14 : int errorval = errno;
351 :
352 14 : if (!key) return (gid_t) -1;
353 :
354 12 : gid = keyValue (keyGetMeta (key, "gid"));
355 12 : if (!gid) return (gid_t) -1;
356 10 : if (*gid == '\0') return (gid_t) -1;
357 :
358 : /*From now on we have to leave using cleanup*/
359 10 : errno = 0;
360 10 : val = strtol (gid, &endptr, 10);
361 :
362 : /*Check for errors*/
363 10 : if (errno) goto cleanup;
364 :
365 : /*Check if nothing was found*/
366 10 : if (endptr == gid) goto cleanup;
367 :
368 : /*Check if the whole string was processed*/
369 10 : if (*endptr != '\0') goto cleanup;
370 :
371 10 : return val;
372 : cleanup:
373 : /*First restore errno*/
374 0 : errno = errorval;
375 0 : return (gid_t) -1;
376 : }
377 :
378 :
379 : /**
380 : * Set the group ID of a key.
381 : *
382 : * @deprecated This API is obsolete.
383 : *
384 : * See @ref GID for more information about group IDs.
385 : *
386 : * @param key the key object to work with
387 : * @param gid is the group ID
388 : * @retval 0 on success
389 : * @retval -1 on NULL key
390 : * @see keyGetGID(), keySetUID()
391 : */
392 12 : int keySetGID (Key * key, gid_t gid)
393 : {
394 : char str[MAX_LEN_INT];
395 12 : if (!key) return -1;
396 :
397 12 : if (snprintf (str, MAX_LEN_INT - 1, "%d", gid) < 0)
398 : {
399 : return -1;
400 : }
401 :
402 12 : keySetMeta (key, "gid", str);
403 :
404 12 : return 0;
405 : }
406 :
407 :
408 : /**
409 : * Set mode so that key will be recognized as directory.
410 : *
411 : * @deprecated This API is obsolete.
412 : *
413 : * The function will add all executable bits.
414 : *
415 : * - Mode 0200 will be translated to 0311
416 : * - Mode 0400 will be translated to 0711
417 : * - Mode 0664 will be translated to 0775
418 : *
419 : * The macro KDB_DIR_MODE (defined to 0111) will be used for that.
420 : *
421 : * The executable bits show that child keys are allowed and listable. There
422 : * is no way to have child keys which are not listable for anyone, but it is
423 : * possible to restrict listing the keys to the owner only.
424 : *
425 : * - Mode 0000 means that it is a key not read or writable to anyone.
426 : * - Mode 0111 means that it is a directory not read or writable to anyone.
427 : * But it is recognized as directory to anyone.
428 : *
429 : * For more about mode see keySetMode().
430 : *
431 : * It is not possible to access keys below a not executable key.
432 : * If a key is not writeable and executable kdbSet() will fail to access the
433 : * keys below.
434 : * If a key is not readable and executable kdbGet() will fail to access the
435 : * keys below.
436 : *
437 : * @param key the key to set permissions to be recognized as directory.
438 : * @retval 0 on success
439 : * @retval -1 on NULL pointer
440 : * @see keySetMode()
441 : */
442 1068 : int keySetDir (Key * key)
443 : {
444 : mode_t mode;
445 1068 : if (!key) return -1;
446 :
447 1068 : mode = keyGetMode (key);
448 1068 : mode |= KDB_DIR_MODE;
449 1068 : keySetMode (key, mode);
450 :
451 1068 : return 0;
452 : }
453 :
454 :
455 : /**
456 : * Return the key mode permissions.
457 : *
458 : * @deprecated This API is obsolete.
459 : *
460 : * Default is 0664 (octal) for keys and 0775 for directory keys
461 : * which used keySetDir().
462 : *
463 : * The defaults are defined with the macros KDB_FILE_MODE and KDB_DIR_MODE.
464 : *
465 : * For more information about the mode permissions see @ref mode.
466 : *
467 : * @param key the key object to work with
468 : * @return mode permissions of the key
469 : * @retval KDB_FILE_MODE as defaults
470 : * @retval (mode_t)-1 on NULL pointer
471 : * @see keySetMode()
472 : */
473 2144 : mode_t keyGetMode (const Key * key)
474 : {
475 : const char * mode;
476 : long int val;
477 : char * endptr;
478 2144 : int errorval = errno;
479 :
480 2144 : if (!key) return (mode_t) -1;
481 :
482 2142 : mode = keyValue (keyGetMeta (key, "mode"));
483 2142 : if (!mode) return KDB_FILE_MODE;
484 2098 : if (*mode == '\0') return KDB_FILE_MODE;
485 :
486 : /*From now on we have to leave using cleanup*/
487 2096 : errno = 0;
488 2096 : val = strtol (mode, &endptr, 8);
489 :
490 : /*Check for errors*/
491 2096 : if (errno) goto cleanup;
492 :
493 : /*Check if nothing was found*/
494 2096 : if (endptr == mode) goto cleanup;
495 :
496 : /*Check if the whole string was processed*/
497 2092 : if (*endptr != '\0') goto cleanup;
498 :
499 2088 : return val;
500 : cleanup:
501 : /*First restore errno*/
502 8 : errno = errorval;
503 8 : return KDB_FILE_MODE;
504 : }
505 :
506 :
507 : /**
508 : * Set the key mode permissions.
509 : *
510 : * @deprecated This API is obsolete.
511 : * It is only a mapping
512 : * to keySetMeta(key, "mode", str) which should be preferred.
513 : *
514 : * The mode consists of 9 individual bits for mode permissions.
515 : * In the following explanation the octal notation with leading
516 : * zero will be used.
517 : *
518 : * Default is 0664 (octal) for keys and 0775 for directory keys
519 : * which used keySetDir().
520 : *
521 : * The defaults are defined with the macros KDB_FILE_MODE and KDB_DIR_MODE.
522 : *
523 : * @note libelektra 0.7.0 only allows 0775 (directory keys) and
524 : * 0664 (other keys). More will be added later in a sense of the
525 : * description below.
526 : *
527 : * @section mode Modes
528 : *
529 : * 0000 is the most restrictive mode. No user might read, write
530 : * or execute the key.
531 : *
532 : * Reading the key means to get the value by kdbGet().
533 : *
534 : * Writing the key means to set the value by kdbSet().
535 : *
536 : * Execute the key means to make a step deeper in the hierarchy.
537 : * But you must be able to read the key to be able to list the
538 : * keys below. See also keySetDir() in that context.
539 : * But you must be able to write the key to be able to add or
540 : * remove keys below.
541 : *
542 : * 0777 is the most relaxing mode. Every user is allowed to
543 : * read, write and execute the key, if he is allowed to execute
544 : * and read all keys below.
545 : *
546 : * 0700 allows every action for the current user, identified by
547 : * the uid. See keyGetUID() and keySetUID().
548 : *
549 : * To be more specific for the user the single bits can elect
550 : * the mode for read, write and execute. 0100 only allows
551 : * executing which gives the information that it is a directory
552 : * for that user, but not accessible. 0200 only allows reading.
553 : * This information may be combined to 0300, which allows execute
554 : * and reading of the directory. Last 0400 decides about the
555 : * writing permissions.
556 : *
557 : * The same as above is also valid for the 2 other octal digits.
558 : * 0070 decides about the group permissions, in that case full
559 : * access. Groups are identified by the gid. See keyGetGID() and
560 : * keySetGID(). In that example everyone with a different uid,
561 : * but the gid of the the key, has full access.
562 : *
563 : * 0007 decides about the world permissions. This is taken into
564 : * account when neither the uid nor the gid matches. So that
565 : * example would allow everyone with a different uid and gid
566 : * of that key gains full access.
567 : *
568 : * @param key the key to set mode permissions
569 : * @param mode the mode permissions
570 : * @retval 0 on success
571 : * @retval -1 on NULL key
572 : * @see keyGetMode()
573 : */
574 2106 : int keySetMode (Key * key, mode_t mode)
575 : {
576 : char str[MAX_LEN_INT];
577 2106 : if (!key) return -1;
578 :
579 2106 : if (snprintf (str, MAX_LEN_INT - 1, "%o", mode) < 0)
580 : {
581 : return -1;
582 : }
583 :
584 2106 : keySetMeta (key, "mode", str);
585 :
586 2106 : return 0;
587 : }
588 :
589 :
590 : /*********************************************
591 : * Access times methods *
592 : *********************************************/
593 :
594 :
595 : /**
596 : * Get last time the key data was read from disk.
597 : *
598 : * @deprecated This API is obsolete.
599 : *
600 : * Every kdbGet() might update the access time
601 : * of a key. You get information when the key
602 : * was read the last time from the database.
603 : *
604 : * You will get 0 when the key was not read already.
605 : *
606 : * Beware that multiple copies of keys with keyDup() might have different
607 : * atimes because you kdbGet() one, but not the
608 : * other. You can use this information to decide which
609 : * key is the latest.
610 : *
611 : * @param key Key to get information from.
612 : * @return the time you got the key with kdbGet()
613 : * @retval 0 on key that was never kdbGet()
614 : * @retval (time_t)-1 on NULL pointer
615 : * @see keySetATime()
616 : * @see kdbGet()
617 : */
618 10 : time_t keyGetATime (const Key * key)
619 : {
620 : const char * atime;
621 : long int val;
622 : char * endptr;
623 10 : int errorval = errno;
624 :
625 10 : if (!key) return (time_t) -1;
626 :
627 8 : atime = keyValue (keyGetMeta (key, "atime"));
628 8 : if (!atime) return 0;
629 6 : if (*atime == '\0') return (time_t) -1;
630 :
631 : /*From now on we have to leave using cleanup*/
632 6 : errno = 0;
633 6 : val = strtol (atime, &endptr, 10);
634 :
635 : /*Check for errors*/
636 6 : if (errno) goto cleanup;
637 :
638 : /*Check if nothing was found*/
639 6 : if (endptr == atime) goto cleanup;
640 :
641 : /*Check if the whole string was processed*/
642 6 : if (*endptr != '\0') goto cleanup;
643 :
644 : return val;
645 : cleanup:
646 : /*First restore errno*/
647 0 : errno = errorval;
648 0 : return (time_t) -1;
649 : }
650 :
651 : /**
652 : * Update the atime information for a key.
653 : *
654 : * @deprecated This API is obsolete.
655 : *
656 : * When you do manual sync of keys you might also
657 : * update the atime to make them indistinguishable.
658 : *
659 : * It can also be useful if you work with
660 : * keys not using a keydatabase.
661 : *
662 : * @param key The Key object to work with
663 : * @param atime The new access time for the key
664 : * @retval 0 on success
665 : * @retval -1 on NULL pointer
666 : * @see keyGetATime()
667 : */
668 8 : int keySetATime (Key * key, time_t atime)
669 : {
670 : char str[MAX_LEN_INT];
671 8 : if (!key) return -1;
672 :
673 6 : if (snprintf (str, MAX_LEN_INT - 1, "%lu", atime) < 0)
674 : {
675 : return -1;
676 : }
677 :
678 6 : keySetMeta (key, "atime", str);
679 :
680 6 : return 0;
681 : }
682 :
683 :
684 : /**
685 : * Get last modification time of the key on disk.
686 : *
687 : * @deprecated This API is obsolete.
688 : *
689 : * You will get 0 when the key was not read already.
690 : *
691 : * Everytime you change value or comment and kdbSet()
692 : * the key the mtime will be updated. When you kdbGet()
693 : * the key, the atime is set appropriate.
694 : *
695 : * Not changed keys may not even passed to kdbSet_backend()
696 : * so it will not update this time, even after kdbSet().
697 : *
698 : * It is possible that other keys written to disc
699 : * influence this time if the backend is not grained
700 : * enough.
701 : *
702 : * If you add or remove a key the key thereunder
703 : * in the hierarchy will update the mtime
704 : * if written with kdbSet() to disc.
705 : *
706 : * @param key Key to get information from.
707 : * @see keySetMTime()
708 : * @return the last modification time
709 : * @retval (time_t)-1 on NULL pointer
710 : */
711 10 : time_t keyGetMTime (const Key * key)
712 : {
713 : const char * mtime;
714 : long int val;
715 : char * endptr;
716 10 : int errorval = errno;
717 :
718 10 : if (!key) return (time_t) -1;
719 :
720 8 : mtime = keyValue (keyGetMeta (key, "mtime"));
721 8 : if (!mtime) return 0;
722 6 : if (*mtime == '\0') return (time_t) -1;
723 :
724 : /*From now on we have to leave using cleanup*/
725 6 : errno = 0;
726 6 : val = strtol (mtime, &endptr, 10);
727 :
728 : /*Check for errors*/
729 6 : if (errno) goto cleanup;
730 :
731 : /*Check if nothing was found*/
732 6 : if (endptr == mtime) goto cleanup;
733 :
734 : /*Check if the whole string was processed*/
735 6 : if (*endptr != '\0') goto cleanup;
736 :
737 : return val;
738 : cleanup:
739 : /*First restore errno*/
740 0 : errno = errorval;
741 0 : return (time_t) -1;
742 : }
743 :
744 : /**
745 : * Update the mtime information for a key.
746 : *
747 : * @deprecated This API is obsolete.
748 : *
749 : * @param key The Key object to work with
750 : * @param mtime The new modification time for the key
751 : * @retval 0 on success
752 : * @see keyGetMTime()
753 : */
754 8 : int keySetMTime (Key * key, time_t mtime)
755 : {
756 : char str[MAX_LEN_INT];
757 8 : if (!key) return -1;
758 :
759 6 : if (snprintf (str, MAX_LEN_INT - 1, "%lu", mtime) < 0)
760 : {
761 : return -1;
762 : }
763 :
764 6 : keySetMeta (key, "mtime", str);
765 :
766 6 : return 0;
767 : }
768 :
769 :
770 : /**
771 : * Get last time the key metadata was changed from disk.
772 : *
773 : * @deprecated This API is obsolete.
774 : *
775 : * You will get 0 when the key was not read already.
776 : *
777 : * Any changed field in metadata will influence the
778 : * ctime of a key.
779 : *
780 : * This time is not updated if only value
781 : * or comment are changed.
782 : *
783 : * Not changed keys will not update this time,
784 : * even after kdbSet().
785 : *
786 : * It is possible that other keys written to disc
787 : * influence this time if the backend is not grained
788 : * enough.
789 : *
790 : * @param key Key to get information from.
791 : * @see keySetCTime()
792 : * @retval (time_t)-1 on NULL pointer
793 : * @return the metadata change time
794 : */
795 10 : time_t keyGetCTime (const Key * key)
796 : {
797 : const char * ctime;
798 : long int val;
799 : char * endptr;
800 10 : int errorval = errno;
801 :
802 10 : if (!key) return (time_t) -1;
803 :
804 8 : ctime = keyValue (keyGetMeta (key, "ctime"));
805 8 : if (!ctime) return 0;
806 6 : if (*ctime == '\0') return (time_t) -1;
807 :
808 : /*From now on we have to leave using cleanup*/
809 6 : errno = 0;
810 6 : val = strtol (ctime, &endptr, 10);
811 :
812 : /*Check for errors*/
813 6 : if (errno) goto cleanup;
814 :
815 : /*Check if nothing was found*/
816 6 : if (endptr == ctime) goto cleanup;
817 :
818 : /*Check if the whole string was processed*/
819 6 : if (*endptr != '\0') goto cleanup;
820 :
821 : return val;
822 : cleanup:
823 : /*First restore errno*/
824 0 : errno = errorval;
825 0 : return (time_t) -1;
826 : }
827 :
828 :
829 : /**
830 : * Update the ctime information for a key.
831 : *
832 : * @deprecated This API is obsolete.
833 : *
834 : * @param key The Key object to work with
835 : * @param ctime The new change metadata time for the key
836 : * @retval 0 on success
837 : * @retval -1 on NULL pointer
838 : * @see keyGetCTime()
839 : */
840 8 : int keySetCTime (Key * key, time_t ctime)
841 : {
842 : char str[MAX_LEN_INT];
843 8 : if (!key) return -1;
844 :
845 6 : if (snprintf (str, MAX_LEN_INT - 1, "%lu", ctime) < 0)
846 : {
847 : return -1;
848 : }
849 :
850 6 : keySetMeta (key, "ctime", str);
851 :
852 6 : return 0;
853 : }
854 :
855 : #endif
856 :
857 : /**
858 : * Compare the order metadata of two keys.
859 : *
860 : * @return a number less than, equal to or greater than zero if
861 : * the order of k1 is found, respectively, to be less than,
862 : * to match, or be greater than the order of k2. If one key is
863 : * NULL, but the other isn't, the key which is not NULL is considered
864 : * to be greater. If both keys are NULL, they are
865 : * considered to be equal. If one key does have an order
866 : * metadata but the other has not, the key with the metadata
867 : * is considered greater. If no key has metadata,
868 : * they are considered to be equal.
869 : *
870 : * @param ka key to compare with
871 : * @param kb other key to compare with
872 : */
873 520 : int elektraKeyCmpOrder (const Key * ka, const Key * kb)
874 : {
875 :
876 520 : if (!ka && !kb) return 0;
877 :
878 518 : if (ka && !kb) return 1;
879 :
880 516 : if (!ka && kb) return -1;
881 :
882 514 : int aorder = -1;
883 514 : int border = -1;
884 :
885 514 : const Key * kam = keyGetMeta (ka, "order");
886 514 : const Key * kbm = keyGetMeta (kb, "order");
887 :
888 952 : if (kam) aorder = atoi (keyString (kam));
889 971 : if (kbm) border = atoi (keyString (kbm));
890 :
891 514 : if (aorder > 0 && border > 0) return aorder - border;
892 :
893 100 : if (aorder < 0 && border < 0) return 0;
894 :
895 67 : if (aorder < 0 && border >= 0) return -1;
896 :
897 24 : if (aorder >= 0 && border < 0) return 1;
898 :
899 : /* cannot happen anyway */
900 0 : return 0;
901 : }
902 :
903 :
904 : /**
905 : * creates an metadata array or appends another element to an existing metadata array
906 : * e.g.
907 : * Key *key = keyNew("user/test", KEY_END);
908 : * elektraMetaArrayAdd(key, "test", "val0");
909 : * key now has "test/#0" with value "val0" as metadata
910 : * elektraMetaArrayAdd(key, "test", "val1");
911 : * appends "test/#1" with value "val1" to key
912 : *
913 : * @param key the key the metadata should be added to
914 : * @param metaName the name of the metakey array parent
915 : * @param value the value of the newly appended metakey
916 : */
917 :
918 1487 : void elektraMetaArrayAdd (Key * key, const char * metaName, const char * value)
919 : {
920 1487 : const Key * meta = keyGetMeta (key, metaName);
921 : Key * arrayKey;
922 1487 : if (!meta)
923 : {
924 762 : keySetMeta (key, metaName, "#0");
925 762 : arrayKey = keyDup (keyGetMeta (key, metaName));
926 762 : keySetString (arrayKey, 0);
927 762 : keyAddBaseName (arrayKey, "#");
928 : }
929 : else
930 : {
931 725 : arrayKey = keyDup (meta);
932 725 : keyAddBaseName (arrayKey, keyString (meta));
933 : }
934 1487 : elektraArrayIncName (arrayKey);
935 1487 : keySetMeta (key, keyName (arrayKey), value);
936 1487 : keySetMeta (key, metaName, keyBaseName (arrayKey));
937 1487 : keyDel (arrayKey);
938 1487 : }
939 :
940 : /**
941 : * Create a `KeySet` from a metakey array.
942 : *
943 : * For example, the following function call
944 : *
945 : * @code
946 : elektraMetaArrayToKS(
947 : keyNew ("/a", KEY_VALUE, "b, c",
948 : KEY_META, "dep", "#1",
949 : KEY_META, "dep/#0", "/b",
950 : KEY_META, "dep/#1", "/c", KEY_END),
951 : "dep");
952 : * @endcode
953 : *
954 : * returns a `KeySet` containing the keys `dep` with value `#1`, `"dep/#0"` with value `"/b"` and
955 : * `"dep/#1"` with value `"/c"`.
956 : *
957 : * If no meta key array is found, null is returned.
958 : * The returned `KeySet` must be freed with `ksDel`
959 : *
960 : * @returns a keyset containing all the metakeys of the metakey array
961 : * @param key the key containing the metakey array
962 : * @param metaName the name of the metakey array parent
963 : */
964 4832 : KeySet * elektraMetaArrayToKS (const Key * key, const char * metaName)
965 : {
966 4832 : const Key * meta = keyGetMeta (key, metaName);
967 4832 : if (!meta) return NULL;
968 :
969 862 : KeySet * result = ksNew (0, KS_END);
970 :
971 862 : if (keyString (meta)[0] != '#')
972 : {
973 0 : ksAppendKey (result, (Key *) meta);
974 0 : ksRewind (result);
975 0 : return result;
976 : }
977 862 : ksAppendKey (result, keyDup (meta));
978 862 : Key * currentKey = keyDup (meta);
979 862 : keyAddName (currentKey, "#");
980 862 : elektraArrayIncName (currentKey);
981 862 : Key * curMeta = NULL;
982 3367 : while ((curMeta = (Key *) keyGetMeta (key, keyName (currentKey))) != NULL)
983 : {
984 1643 : ksAppendKey (result, keyDup (curMeta));
985 1643 : elektraArrayIncName (currentKey);
986 : }
987 862 : keyDel (currentKey);
988 862 : ksRewind (result);
989 862 : return result;
990 : }
991 :
992 :
993 : /**
994 : * @internal
995 : *
996 : * elektraSortTopology helper
997 : * matrix struct
998 : */
999 : typedef struct
1000 : {
1001 : Key * key;
1002 : kdb_octet_t isResolved;
1003 : unsigned long * deps;
1004 : } _adjMatrix;
1005 :
1006 : /**
1007 : * @internal
1008 : *
1009 : * elektraSortTopology helper
1010 : * ordering function for qsort
1011 : */
1012 4672 : static int topCmpOrder (const void * a, const void * b)
1013 : {
1014 4672 : const Key * ka = (*(const Key **) a);
1015 4672 : const Key * kb = (*(const Key **) b);
1016 :
1017 4672 : if (!ka && !kb) return 0;
1018 4672 : if (ka && !kb) return 1;
1019 4672 : if (!ka && kb) return -1;
1020 :
1021 4672 : const Key * kam = keyGetMeta (ka, "order");
1022 4672 : const Key * kbm = keyGetMeta (kb, "order");
1023 :
1024 4672 : return strcmp (keyString (kam), keyString (kbm));
1025 : }
1026 :
1027 :
1028 : /**
1029 : * @internal
1030 : *
1031 : * elektraSortTopology helper
1032 : * returns the index of dependency depKey
1033 : */
1034 401 : static int getArrayIndex (Key * depKey, _adjMatrix * adjMatrix, size_t size)
1035 : {
1036 1083 : for (unsigned int i = 0; i < size; ++i)
1037 : {
1038 1079 : if (!strcmp (keyName (adjMatrix[i].key), keyString (depKey))) return i;
1039 : }
1040 : return -1;
1041 : }
1042 : /**
1043 : * @internal
1044 : *
1045 : * elektraSortTopology helper
1046 : * removes resolved dependency j from our matrix
1047 : */
1048 : static int resolveDep (unsigned int j, _adjMatrix * adjMatrix, size_t size)
1049 : {
1050 2824 : int removed = 0;
1051 11081 : for (unsigned int i = 0; i < size; ++i)
1052 : {
1053 8257 : if (adjMatrix[i].deps[j])
1054 : {
1055 125 : ++removed;
1056 125 : adjMatrix[i].deps[j] = 0;
1057 : }
1058 : }
1059 : return removed;
1060 : }
1061 :
1062 : /**
1063 : * @internal
1064 : *
1065 : * elektraSortTopology helper
1066 : * checks if the key with index j has unresolved dependencies
1067 : */
1068 : static int hasUnresolvedDependencies (unsigned int j, _adjMatrix * adjMatrix, size_t size)
1069 : {
1070 362 : for (unsigned int i = 0; i < size; ++i)
1071 : {
1072 328 : if (adjMatrix[j].deps[i]) return 1;
1073 : }
1074 : return 0;
1075 : }
1076 :
1077 : /**
1078 : * @internal
1079 : *
1080 : * elektraSortTopology helper
1081 : * resolve all dependencies of the key with the index j in our matrix.
1082 : */
1083 73 : static int resolveDeps (unsigned int j, _adjMatrix * adjMatrix, size_t size, KeySet * done, Key * orderCounter)
1084 73 : {
1085 73 : unsigned int loops = 0;
1086 73 : unsigned int frontier[size];
1087 73 : unsigned int todo = 0;
1088 340 : for (unsigned int i = 0; i < size; ++i)
1089 : {
1090 267 : if (adjMatrix[j].deps[i])
1091 : {
1092 2 : frontier[i] = 1;
1093 2 : ++todo;
1094 : }
1095 : else
1096 : {
1097 265 : frontier[i] = 0;
1098 : }
1099 : }
1100 : int found = 1;
1101 :
1102 : // loop until all dependencies are added to frontier
1103 146 : while (found)
1104 : {
1105 : found = 0;
1106 267 : for (unsigned int i = 0; i < size; ++i)
1107 : {
1108 267 : if (!frontier[i]) continue;
1109 2 : if (hasUnresolvedDependencies (i, adjMatrix, size))
1110 : {
1111 0 : for (unsigned int k = 0; k < size; ++k)
1112 : {
1113 0 : if (adjMatrix[i].deps[k])
1114 : {
1115 0 : if (!frontier[k])
1116 : {
1117 0 : found = 1;
1118 0 : ++todo;
1119 0 : frontier[k] = 1;
1120 : }
1121 : }
1122 : }
1123 : }
1124 : }
1125 : }
1126 73 : if (todo == 0)
1127 : {
1128 : // all dependencies are already resolved, give key an order number and add it to
1129 : // the our list of resolved keys (done)
1130 71 : adjMatrix[j].isResolved = 1;
1131 71 : resolveDep (j, adjMatrix, size);
1132 71 : keySetMeta (adjMatrix[j].key, "order", keyBaseName (orderCounter));
1133 71 : elektraArrayIncName (orderCounter);
1134 71 : ksAppendKey (done, keyDup (adjMatrix[j].key));
1135 71 : return 1;
1136 : }
1137 : unsigned int max_loops = todo;
1138 8 : for (unsigned int i = 0; todo; ++i)
1139 : {
1140 8 : if (i == size)
1141 : {
1142 0 : ++loops;
1143 0 : i = 0;
1144 : }
1145 8 : if (loops > max_loops) return -1; // more loops than we had unresolved keys -> cycle
1146 8 : if (!frontier[i]) continue;
1147 2 : if (!hasUnresolvedDependencies (i, adjMatrix, size))
1148 : {
1149 2 : resolveDep (i, adjMatrix, size);
1150 2 : frontier[i] = 0;
1151 2 : --todo;
1152 2 : adjMatrix[i].isResolved = 1;
1153 2 : resolveDep (i, adjMatrix, size);
1154 2 : keySetMeta (adjMatrix[i].key, "order", keyBaseName (orderCounter));
1155 2 : elektraArrayIncName (orderCounter);
1156 2 : ksAppendKey (done, keyDup (adjMatrix[i].key));
1157 : }
1158 : }
1159 : return 1;
1160 : }
1161 :
1162 : /**
1163 : * elektraSortTopology helper
1164 : * tests if name is a valid keyname
1165 : */
1166 401 : static int isValidKeyName (const char * testName)
1167 : {
1168 401 : int retVal = 0;
1169 401 : Key * testKey = keyNew (testName, KEY_CASCADING_NAME, KEY_END);
1170 401 : if (!strcmp (keyName (testKey), testName)) retVal = 1;
1171 401 : keyDel (testKey);
1172 401 : return retVal;
1173 : }
1174 :
1175 : /**
1176 : * @brief topological sorting
1177 : *
1178 : * @param array the array where the sorted keys will be stored in topological order.
1179 : * Nothing will be written into an array if
1180 : * @param ks is the keyset that should be sorted.
1181 : * Dependencies and order is defined by metakeys.
1182 : *
1183 : * - the "dep/#" metakeys
1184 : * e.g. the Key *k = keyNew ("/a", KEY_VALUE, "b, c",
1185 : * KEY_META, "dep", "#1", KEY_META, "dep/#0", "/b", KEY_META, "dep/#1", "/c", KEY_END), "dep");
1186 : * depends on Key "/b" and Key "/c".
1187 : * - if "order" metakeys are defined for the keys the algorithm tries to resolves them by that
1188 : * order using lexical comparison. You should prefer `#0` array syntax.
1189 : *
1190 : * Duplicated and reflexive dep entries are ignored.
1191 : *
1192 : * The algorithm used is a mixture of Kahn and BFS.
1193 : * Furthermore the algorithm does not use recursion.
1194 : *
1195 : * First a BFS with the keys sorted by "order" is used.
1196 : * Then all dependencies (recursively) of every key is collected.
1197 : *
1198 : * @retval 1 on success
1199 : * @retval 0 for cycles
1200 : * @retval -1 for invalid dependencies
1201 : */
1202 :
1203 1256 : int elektraSortTopology (KeySet * ks, Key ** array)
1204 1256 : {
1205 1256 : if (ks == NULL || array == NULL) return -1;
1206 1256 : KeySet * done = ksNew (0, KS_END);
1207 1256 : ksRewind (ks);
1208 : Key * cur;
1209 1256 : ssize_t size = ksGetSize (ks);
1210 1256 : Key * orderCounter = keyNew ("/#", KEY_CASCADING_NAME, KEY_END);
1211 1256 : elektraArrayIncName (orderCounter);
1212 1256 : _adjMatrix adjMatrix[size];
1213 1256 : int i = 0;
1214 1256 : int retVal = 1;
1215 1256 : int depCount = 0;
1216 1256 : Key ** localArray = elektraMalloc (size * sizeof (Key *));
1217 1256 : elektraKsToMemArray (ks, localArray);
1218 1256 : qsort (localArray, size, sizeof (Key *), topCmpOrder);
1219 4207 : for (long j = 0; j < size; ++j)
1220 : {
1221 2951 : adjMatrix[j].key = localArray[j];
1222 2951 : adjMatrix[j].isResolved = 0;
1223 2951 : adjMatrix[j].deps = elektraCalloc (sizeof (unsigned long) * size);
1224 : }
1225 1256 : kdb_octet_t hasOrder = 0;
1226 1256 : if (keyGetMeta (localArray[0], "order")) hasOrder = 1;
1227 1256 : unsigned int unresolved = 0;
1228 4203 : for (int j = 0; j < size; ++j)
1229 : {
1230 2951 : cur = localArray[j];
1231 2951 : KeySet * deps = elektraMetaArrayToKS (cur, "dep");
1232 2951 : keyDel (ksLookupByName (deps, "dep", KDB_O_POP));
1233 : Key * tmpDep;
1234 2951 : switch (ksGetSize (deps))
1235 : {
1236 : case -1:
1237 : {
1238 : // key has no dependencies, give it an order number and add it to list of resolved dependencies
1239 2711 : keySetMeta (cur, "order", keyBaseName (orderCounter));
1240 2711 : elektraArrayIncName (orderCounter);
1241 2711 : ksAppendKey (done, keyDup (cur));
1242 2711 : adjMatrix[j].isResolved = 1;
1243 2711 : ksDel (deps);
1244 2711 : break;
1245 : }
1246 : case 1:
1247 : {
1248 : // only 1 dependency:
1249 : // test if it's reflexive
1250 122 : tmpDep = ksHead (deps);
1251 122 : if (!strcmp (keyName (cur), keyString (tmpDep)))
1252 : {
1253 10 : keySetMeta (cur, "order", keyBaseName (orderCounter));
1254 10 : elektraArrayIncName (orderCounter);
1255 10 : ksAppendKey (done, keyDup (cur));
1256 10 : adjMatrix[j].isResolved = 1;
1257 10 : ksDel (deps);
1258 10 : break;
1259 : }
1260 : // if not, fallthrough to normal dependency handling
1261 : }
1262 : // FALLTHROUGH
1263 : default:
1264 : {
1265 : int gotUnresolved = 0;
1266 627 : while ((tmpDep = ksNext (deps)) != NULL)
1267 : {
1268 401 : if (!isValidKeyName (keyString (tmpDep)))
1269 : {
1270 : // invalid keyname -> ERROR
1271 : retVal = -1;
1272 : break;
1273 : }
1274 401 : i = getArrayIndex (tmpDep, adjMatrix, size);
1275 401 : if (i == -1)
1276 : {
1277 : // key doesn't exist yet but has valid name, ignore it.
1278 4 : continue;
1279 : }
1280 397 : else if (i == j)
1281 : {
1282 : // reflexiv dependency, do nothing
1283 : }
1284 : else
1285 : {
1286 387 : if (!adjMatrix[i].isResolved)
1287 : {
1288 : // unresolved dependency
1289 203 : adjMatrix[j].deps[i] = 1;
1290 203 : ++gotUnresolved;
1291 : // simple cycle detection
1292 203 : if (adjMatrix[i].deps[j])
1293 : {
1294 : retVal = 0;
1295 : break;
1296 : }
1297 : }
1298 : }
1299 : }
1300 230 : if (gotUnresolved)
1301 : {
1302 175 : adjMatrix[j].isResolved = 0;
1303 175 : ++unresolved;
1304 : // count unresolved dependencies
1305 175 : depCount += gotUnresolved;
1306 : }
1307 230 : ksDel (deps);
1308 230 : break;
1309 : }
1310 : }
1311 2951 : if (retVal <= 0) break;
1312 : }
1313 1256 : if (retVal <= 0)
1314 : {
1315 : // error or cycle: goto cleanup
1316 : goto TopSortCleanup;
1317 : }
1318 :
1319 : // resolve all dependencies that can be resolved immediately
1320 2939 : for (int j = 0; j < size; ++j)
1321 : {
1322 5658 : if (adjMatrix[j].isResolved) depCount -= resolveDep (j, adjMatrix, size);
1323 : }
1324 :
1325 1252 : ssize_t resolved = ksGetSize (done);
1326 1252 : if (((depCount + resolved) >= size) && (unresolved))
1327 : {
1328 : // more dependencies dependencies than keys:
1329 : // cycle found !
1330 : retVal = 0;
1331 : goto TopSortCleanup;
1332 : }
1333 :
1334 1240 : if (unresolved)
1335 : {
1336 : int found = 1;
1337 : // we have unresolved dependencies
1338 939 : for (int j = 0; j < size + 1; ++j)
1339 : {
1340 : // loop until no dependency can be resolved anymore
1341 1016 : if (j == size)
1342 : {
1343 168 : if (found)
1344 : {
1345 91 : found = 0;
1346 91 : j = -1;
1347 91 : unresolved = 0;
1348 91 : continue;
1349 : }
1350 : else
1351 : break;
1352 : }
1353 848 : if (adjMatrix[j].isResolved) continue;
1354 129 : ++unresolved;
1355 129 : if (hasOrder)
1356 : {
1357 : // resolve by order
1358 73 : int ret = resolveDeps (j, adjMatrix, size, done, orderCounter);
1359 73 : if (ret == -1) break;
1360 73 : j = -1;
1361 73 : found = 1;
1362 73 : continue;
1363 : }
1364 : else
1365 : {
1366 : // resolve next possible dependency in keyset
1367 112 : if (!hasUnresolvedDependencies (j, adjMatrix, size))
1368 : {
1369 34 : adjMatrix[j].isResolved = 1;
1370 68 : resolveDep (j, adjMatrix, size);
1371 34 : keySetMeta (localArray[j], "order", keyBaseName (orderCounter));
1372 34 : elektraArrayIncName (orderCounter);
1373 34 : ksAppendKey (done, keyDup (localArray[j]));
1374 34 : found = 1;
1375 : }
1376 : }
1377 : }
1378 : }
1379 1240 : if (unresolved == 0)
1380 : {
1381 : // everything resolved
1382 : // add dependencies in topological order to array
1383 1240 : elektraKsToMemArray (ks, array);
1384 1240 : qsort (array, size, sizeof (Key *), topCmpOrder);
1385 1240 : retVal = 1;
1386 : }
1387 : else
1388 : {
1389 : // still unresolved dependencies left:
1390 : // there must be a cycle somewhere
1391 : retVal = 0;
1392 : }
1393 : TopSortCleanup:
1394 1256 : ksDel (done);
1395 1256 : keyDel (orderCounter);
1396 1256 : elektraFree (localArray);
1397 4207 : for (ssize_t j = 0; j < size; ++j)
1398 : {
1399 2951 : elektraFree (adjMatrix[j].deps);
1400 : }
1401 : return retVal;
1402 : }
1403 :
1404 : /**
1405 : * returns the metakey array as a string separated by delim
1406 : *
1407 : * @param key the key containing the metakey array
1408 : * @param metaName the name of the metakey array parent
1409 : * @param delim delimiter for the records in the returned string
1410 : *
1411 : * @returns a string containing all metakey values separated by "delim"
1412 : */
1413 :
1414 164 : char * elektraMetaArrayToString (const Key * key, const char * metaName, const char * delim)
1415 : {
1416 164 : char * result = NULL;
1417 164 : Key * lookupElem = keyDup (keyGetMeta (key, metaName));
1418 164 : keyAddBaseName (lookupElem, "#0");
1419 164 : Key * elem = (Key *) keyGetMeta (key, keyName (lookupElem));
1420 164 : if (elem != NULL)
1421 : {
1422 164 : elektraRealloc ((void **) &result, keyGetValueSize (elem));
1423 164 : snprintf (result, keyGetValueSize (elem), "%s", keyString (elem));
1424 : }
1425 164 : elektraArrayIncName (lookupElem);
1426 164 : elem = (Key *) keyGetMeta (key, keyName (lookupElem));
1427 328 : while (elem != NULL)
1428 : {
1429 0 : elektraRealloc ((void **) &result,
1430 0 : elektraStrLen (result) + keyGetValueSize (elem) + 1); // String (incl. +2 times \0) + delimiter + whitespace
1431 0 : strcat (result, delim);
1432 0 : strcat (result, keyString (elem));
1433 0 : elektraArrayIncName (lookupElem);
1434 0 : elem = (Key *) keyGetMeta (key, keyName (lookupElem));
1435 : }
1436 164 : keyDel (lookupElem);
1437 164 : return result;
1438 : }
1439 :
1440 : /**
1441 : * @}
1442 : */
|