Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Methods for making tests
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 :
10 : #ifdef HAVE_KDBCONFIG_H
11 : #include "kdbconfig.h"
12 : #endif
13 :
14 : #ifdef HAVE_STDARG_H
15 : #include <stdarg.h>
16 : #endif
17 :
18 : #ifdef HAVE_STRING_H
19 : #include <string.h>
20 : #endif
21 :
22 : #ifdef HAVE_STDLIB_H
23 : #include <stdlib.h>
24 : #endif
25 :
26 : #include "kdb.h"
27 : #include "kdbinternal.h"
28 : #include "kdbprivate.h"
29 :
30 :
31 : /**
32 : * @defgroup keytest Methods for Making Tests
33 : * @ingroup key
34 : * @brief Methods to do various tests on Keys
35 : *
36 : * To use them:
37 : * @code
38 : #include <kdb.h>
39 : * @endcode
40 : *
41 : *
42 : */
43 :
44 : /**
45 : * @internal
46 : *
47 : * Clear sync flag of a key.
48 : *
49 : * @param key the key object to work with
50 : * @retval -1 on null key
51 : * @return new flags for that key otherwise
52 : * @ingroup keytest
53 : *
54 : */
55 317375 : int keyClearSync (Key * key)
56 : {
57 317375 : if (!key) return -1;
58 :
59 317375 : keyflag_t semiflag = KEY_FLAG_SYNC;
60 :
61 317375 : semiflag = ~semiflag;
62 317375 : key->flags &= semiflag;
63 :
64 317375 : return key->flags;
65 : }
66 :
67 :
68 : /**
69 : * Test if a key needs to be synced to backend storage.
70 : *
71 : * If any key modification took place the key will be flagged
72 : * so that kdbSet() knows which keys were modified
73 : * and which not.
74 : *
75 : * After keyNew() the flag will normally be set, but after kdbGet()
76 : * and kdbSet() the flag will be removed. When you modify the key
77 : * the flag will be set again.
78 : *
79 : * In your application you can make use of that flag to know
80 : * if you changed something in a key after a kdbGet() or kdbSet().
81 : *
82 : * @note Note that the sync status will be updated on any change,
83 : * including metadata.
84 : *
85 : * @deprecated The handling of synchronization is done internally and
86 : * does not need to be checked by neither application nor plugins.
87 : *
88 : * @see after keyNew(), keyDup() keys need sync
89 : *
90 : * @param key the key object to work with
91 : * @retval 1 if @p key was changed in memory, 0 otherwise
92 : * @retval -1 on NULL pointer
93 : * @ingroup keytest
94 : */
95 161456 : int keyNeedSync (const Key * key)
96 : {
97 161456 : if (!key) return -1;
98 :
99 161446 : return (key->flags & KEY_FLAG_SYNC) == KEY_FLAG_SYNC;
100 : }
101 :
102 :
103 711 : int keyIsSpec (const Key * key)
104 : {
105 711 : if (!key) return -1;
106 :
107 711 : if (key->key)
108 711 : return keyNameIsSpec (key->key);
109 : else
110 : return 0;
111 : }
112 :
113 0 : int keyIsProc (const Key * key)
114 : {
115 0 : if (!key) return -1;
116 :
117 0 : if (key->key)
118 0 : return keyNameIsProc (key->key);
119 : else
120 : return 0;
121 : }
122 :
123 :
124 334 : int keyIsDir (const Key * key)
125 : {
126 334 : if (!key) return -1;
127 :
128 334 : if (key->key)
129 334 : return keyNameIsDir (key->key);
130 : else
131 : return 0;
132 : }
133 :
134 :
135 : /**
136 : * @internal
137 : *
138 : * Check whether a key is under the @p system namespace or not
139 : *
140 : * @param key the key object to work with
141 : * @retval 1 if key name begins with @p system, 0 otherwise
142 : * @retval -1 on NULL pointer
143 : * @see keyIsUser(), keySetName(), keyName()
144 : * @ingroup keytest
145 : *
146 : */
147 1228631 : int keyIsSystem (const Key * key)
148 : {
149 1228631 : if (!key) return -1;
150 :
151 1228629 : if (key->key)
152 1228627 : return keyNameIsSystem (key->key);
153 : else
154 : return 0;
155 : }
156 :
157 :
158 : /**
159 : * @internal
160 : *
161 : * Check whether a key is under the @p user namespace or not.
162 : *
163 : * @param key the key object to work with
164 : * @retval 1 if key name begins with @p user, 0 otherwise
165 : * @retval -1 on NULL pointer
166 : * @see keyIsSystem(), keySetName(), keyName()
167 : * @ingroup keytest
168 : *
169 : */
170 1128608 : int keyIsUser (const Key * key)
171 : {
172 1128608 : if (!key) return -1;
173 :
174 1128606 : if (key->key)
175 1128604 : return keyNameIsUser (key->key);
176 : else
177 : return 0;
178 : }
179 :
180 : /**
181 : * Check if the key check is below the key key or not.
182 : *
183 : * Example:
184 : @verbatim
185 : key user/sw/app
186 : check user/sw/app/key
187 : @endverbatim
188 : *
189 : * returns true because check is below key
190 : *
191 : * Example:
192 : @verbatim
193 : key user/sw/app
194 : check user/sw/app/folder/key
195 : @endverbatim
196 : *
197 : * returns also true because check is indirect below key
198 : *
199 : * Obviously, there is no key above a namespace (e.g. user, system, /):
200 : *
201 : @verbatim
202 : key *
203 : check user
204 : @endverbatim
205 : *
206 : * @param key the key object to work with
207 : * @param check the key to find the relative position of
208 : * @retval 1 if check is below key
209 : * @retval 0 if it is not below or if it is the same key
210 : * @retval -1 if key or check is null
211 : * @see keySetName(), keyGetName(), keyIsDirectBelow()
212 : * @ingroup keytest
213 : *
214 : */
215 :
216 1143842 : int keyIsBelow (const Key * key, const Key * check)
217 : {
218 1143842 : if (key == NULL || check == NULL)
219 : {
220 : return -1;
221 : }
222 :
223 : // same key, only if namespace and size are equal
224 : // size alone could be equal with cascading keys
225 1486394 : return keyIsBelowOrSame (key, check) &&
226 684848 : (keyGetNamespace (key) != keyGetNamespace (check) || keyGetUnescapedNameSize (key) != keyGetUnescapedNameSize (check));
227 : }
228 :
229 :
230 : /**
231 : * Check if a key is below or same or not.
232 : *
233 : * @param key the key object to work with
234 : * @see keyIsBelow()
235 : */
236 2536442 : int keyIsBelowOrSame (const Key * key, const Key * check)
237 : {
238 2536442 : if (key == NULL || check == NULL)
239 : {
240 : return -1;
241 : }
242 :
243 2536436 : const char * above = keyUnescapedName (key);
244 2536436 : const char * below = keyUnescapedName (check);
245 :
246 2536436 : size_t sizeAbove = keyGetUnescapedNameSize (key);
247 2536436 : size_t sizeBelow = keyGetUnescapedNameSize (check);
248 :
249 2536436 : if (sizeAbove == 1 && above[0] == '\0' && below[0] != '\0' && sizeBelow == strlen (below) + 1)
250 : {
251 : // cascading root compared against other root
252 : return 0;
253 : }
254 :
255 2536432 : if (sizeBelow == 1 && below[0] == '\0' && above[0] != '\0' && sizeAbove == strlen (above) + 1)
256 : {
257 : // cascading root compared against other root
258 : return 0;
259 : }
260 :
261 2536428 : if (above[0] != '\0' && below[0] == '\0')
262 : {
263 : // cascading
264 642 : size_t len = strlen (above);
265 642 : above += len;
266 642 : sizeAbove -= len;
267 : }
268 :
269 2536428 : if (below[0] != '\0' && above[0] == '\0')
270 : {
271 : // cascading
272 6658 : size_t len = strlen (below);
273 6658 : below += len;
274 6658 : sizeBelow -= len;
275 : }
276 :
277 2536428 : if (sizeAbove > sizeBelow)
278 : {
279 : return 0;
280 : }
281 :
282 2003858 : return memcmp (above, below, sizeAbove) == 0;
283 : }
284 :
285 :
286 : /**
287 : * Check if the key check is direct below the key key or not.
288 : *
289 : @verbatim
290 : Example:
291 : key user/sw/app
292 : check user/sw/app/key
293 :
294 : returns true because check is below key
295 :
296 : Example:
297 : key user/sw/app
298 : check user/sw/app/folder/key
299 :
300 : does not return true, because there is only an indirect relation
301 : @endverbatim
302 : *
303 : * @param key the key object to work with
304 : * @param check the key to find the relative position of
305 : * @retval 1 if check is below key
306 : * @retval 0 if it is not below or if it is the same key
307 : * @retval -1 on null pointer
308 : * @see keyIsBelow(), keySetName(), keyGetName()
309 : * @ingroup keytest
310 : *
311 : */
312 1114163 : int keyIsDirectBelow (const Key * key, const Key * check)
313 : {
314 1114163 : if (key == NULL || check == NULL)
315 : {
316 : return -1;
317 : }
318 :
319 1114157 : const char * above = keyUnescapedName (key);
320 1114157 : const char * below = keyUnescapedName (check);
321 :
322 1114157 : size_t sizeAbove = keyGetUnescapedNameSize (key);
323 1114157 : size_t sizeBelow = keyGetUnescapedNameSize (check);
324 :
325 1114157 : if (above[0] != '\0' && below[0] == '\0')
326 : {
327 : // cascading
328 634 : size_t len = strlen (above);
329 634 : above += len;
330 634 : sizeAbove -= len;
331 : }
332 :
333 1114157 : if (below[0] != '\0' && above[0] == '\0')
334 : {
335 : // cascading
336 1223 : size_t len = strlen (below);
337 1223 : below += len;
338 1223 : sizeBelow -= len;
339 : }
340 :
341 1114157 : if (sizeAbove >= sizeBelow)
342 : {
343 : return 0;
344 : }
345 :
346 689283 : size_t nextPartSize = strlen (below + sizeAbove);
347 689283 : return memcmp (above, below, sizeAbove) == 0 && sizeAbove + nextPartSize + 1 == sizeBelow;
348 : }
349 :
350 :
351 : /**
352 : * Information about the relation in the hierarchy between
353 : * two keys.
354 : *
355 : * Unlike keyCmp() the number gives information
356 : * about hierarchical information.
357 : *
358 : *
359 : * - If the keys are the same 0 is returned.
360 : * So it is the key itself.
361 : @verbatim
362 : user/key
363 : user/key
364 : @endverbatim
365 : *
366 : *@code
367 : keySetName (key, "user/key/folder");
368 : keySetName (check, "user/key/folder");
369 : succeed_if (keyRel (key, check) == 0, "should be same");
370 : *@endcode
371 : *
372 : * @note this relation can be checked with keyCmp() too.
373 : *
374 : *
375 : * - If the key is direct below the other one 1 is returned.
376 : * That means that, in terms of hierarchy, no other key is
377 : * between them - it is a direct child.
378 : @verbatim
379 : user/key/folder
380 : user/key/folder/child
381 : @endverbatim
382 : *
383 : *@code
384 : keySetName (key, "user/key/folder");
385 : keySetName (check, "user/key/folder/child");
386 : succeed_if (keyRel (key, check) == 1, "should be direct below");
387 : *@endcode
388 : *
389 : *
390 : * - If the key is below the other one, but not directly 2 is returned.
391 : * This is also called grand-child.
392 : @verbatim
393 : user/key/folder
394 : user/key/folder/any/depth/deeper/grand-child
395 : @endverbatim
396 : *
397 : *
398 : *@code
399 : keySetName (key, "user/key/folder");
400 : keySetName (check, "user/key/folder/any/depth/deeper/grand-child");
401 : succeed_if (keyRel (key, check) >= 2, "should be below (but not direct)");
402 : succeed_if (keyRel (key, check) > 0, "should be below");
403 : succeed_if (keyRel (key, check) >= 0, "should be the same or below");
404 : *@endcode
405 : *
406 : *
407 : * - If an invalid or null ptr key is passed, -1 is returned
408 : *
409 : *
410 : * - If the keys have no relations, but are not invalid, -2 is returned.
411 : *
412 : *
413 : * - If the keys are in the same hierarchy, a value smaller then -2 is returned.
414 : * It means that the key is not below.
415 : @verbatim
416 : user/key/myself
417 : user/key/sibling
418 : @endverbatim
419 : *
420 : * @code
421 : keySetName (key, "user/key/folder");
422 : keySetName (check, "user/notsame/folder");
423 : succeed_if (keyRel (key, check) < -2, "key is not below, but same namespace");
424 : * @endcode
425 : *
426 : * @code
427 : * @endcode
428 : *
429 : *
430 : * TODO Below is an idea how it could be extended:
431 : * It could continue the search into the other direction
432 : * if any (grand-)parents are equal.
433 : *
434 : * - If the keys are direct below a key which is next to the key, -2 is returned.
435 : * This is also called nephew. (TODO not implemented)
436 : * @verbatim
437 : user/key/myself
438 : user/key/sibling
439 : @endverbatim
440 : *
441 : * - If the keys are direct below a key which is next to the key, -2 is returned.
442 : * This is also called nephew. (TODO not implemented)
443 : * @verbatim
444 : user/key/myself
445 : user/key/sibling/nephew
446 : @endverbatim
447 : *
448 : * - If the keys are below a key which is next to the key, -3 is returned.
449 : * This is also called grand-nephew. (TODO not implemented)
450 : @verbatim
451 : user/key/myself
452 : user/key/sibling/any/depth/deeper/grand-nephew
453 : @endverbatim
454 : *
455 : * The same holds true for the other direction, but with negative values.
456 : * For no relation INT_MIN is returned.
457 : *
458 : * @note to check if the keys are the same, you must use
459 : * keyCmp() == 0!
460 : * keyRel() does not give you the information if it did not
461 : * find a relation or if it is the same key.
462 : *
463 : * @return depending on the relation
464 : * @retval 2 if below
465 : * @retval 1 if direct below
466 : * @retval 0 if the same
467 : * @retval -1 on null or invalid keys
468 : * @retval -2 if none of any other relation
469 : * @retval -3 if same hierarchy (none of those below)
470 : * @retval -4 if sibling (in same hierarchy)
471 : * @retval -5 if nephew (in same hierarchy)
472 : *
473 : * @param key the key object to work with
474 : * @param check the second key object to check the relation with
475 : * @ingroup keytest
476 : */
477 1225811 : int keyRel (const Key * key, const Key * check)
478 : {
479 1225811 : if (!key || !check) return -1;
480 1225807 : if (!key->key || !check->key) return -1;
481 :
482 1225803 : if (!keyCmp (key, check)) return 0;
483 1077266 : if (keyIsDirectBelow (key, check)) return 1;
484 839381 : if (keyIsBelow (key, check)) return 2;
485 782887 : if (keyIsUser (key) && keyIsUser (check)) return -3;
486 774792 : if (keyIsSystem (key) && keyIsSystem (check)) return -3;
487 : // if (keyIsSibling(key, check)) return -4;
488 : // if (keyIsNephew(key, check)) return -5;
489 :
490 : return -2;
491 : }
492 :
493 :
494 : /**
495 : * Check whether a key is inactive.
496 : *
497 : * In Elektra terminology a hierarchy of keys is inactive if
498 : * the rootkey's basename starts with '.'. So a key is
499 : * also inactive if it is below an inactive key.
500 : * For example, user/key/.hidden is inactive and so
501 : * is user/.hidden/below.
502 : *
503 : * Inactive keys should not have any meaning to applications,
504 : * they are only a convention reserved for users and
505 : * administrators. To automatically remove all inactive keys
506 : * for an application, consider to use the hidden plugin.
507 : *
508 : * @param key the key object to work with
509 : * @retval 1 if the key is inactive
510 : * @retval 0 if the key is active
511 : * @retval -1 on NULL pointer or when key has no name
512 : * @ingroup keytest
513 : *
514 : */
515 48 : int keyIsInactive (const Key * key)
516 : {
517 48 : if (!key) return -1;
518 :
519 46 : const char * p = keyName (key);
520 46 : if (!p) return -1;
521 46 : if (p[0] == '\0') return -1;
522 :
523 44 : size_t size = 0;
524 :
525 173 : while (*(p = keyNameGetOneLevel (p + size, &size)))
526 : {
527 116 : if (size > 0)
528 : {
529 116 : if (p[0] == '.')
530 : {
531 : return 1;
532 : }
533 : }
534 : }
535 :
536 : return 0;
537 : }
538 :
539 :
540 : /**
541 : * Check if a key is binary type.
542 : *
543 : * The function checks if the key is a binary. Opposed to string values binary
544 : * values can have '\\0' inside the value and may not be terminated by a null
545 : * character. Their disadvantage is that you need to pass their size.
546 : *
547 : * Make sure to use this function and don't test the binary type another way to
548 : * ensure compatibility and to write less error prone programs.
549 : *
550 : * @retval 1 if it is binary
551 : * @retval 0 if it is not
552 : * @retval -1 on NULL pointer
553 : * @see keyGetBinary(), keySetBinary()
554 : * @param key the key to check
555 : * @ingroup keytest
556 : */
557 2755749 : int keyIsBinary (const Key * key)
558 : {
559 2755749 : if (!key) return -1;
560 :
561 2755749 : return keyGetMeta (key, "binary") != 0;
562 : }
563 :
564 :
565 : /**
566 : * Check if a key is string type.
567 : *
568 : * String values are null terminated and are not allowed to have any '\\0' characters
569 : * inside the string.
570 : *
571 : * Make sure to use this function and don't test the string type another way to
572 : * ensure compatibility and to write less error prone programs.
573 : *
574 : * @retval 1 if it is string
575 : * @retval 0 if it is not
576 : * @retval -1 on NULL pointer
577 : * @see keyGetString(), keySetString()
578 : * @param key the key to check
579 : * @ingroup keytest
580 : */
581 252970 : int keyIsString (const Key * key)
582 : {
583 252970 : if (!key) return -1;
584 :
585 252970 : return keyGetMeta (key, "binary") == 0;
586 : }
587 :
588 :
589 : /**
590 : * @internal
591 : *
592 : * Compare 2 keys.
593 : *
594 : * The returned flags bit array has 1s (differ) or 0s (equal) for each key
595 : * meta info compared, that can be logically ORed using @c #keyswitch_t flags.
596 : * @link keyswitch_t::KEY_NAME KEY_NAME @endlink,
597 : * @link keyswitch_t::KEY_VALUE KEY_VALUE @endlink,
598 : * @link keyswitch_t::KEY_OWNER KEY_OWNER @endlink,
599 : * @link keyswitch_t::KEY_COMMENT KEY_COMMENT @endlink,
600 : * @link keyswitch_t::KEY_META KEY_META @endlink (will be set in addition to owner and comment),
601 : *
602 : * @par A very simple example would be
603 : * @code
604 : Key *key1, *key;
605 : uint32_t changes;
606 :
607 : // omited key1 and key2 initialization and manipulation
608 :
609 : changes=keyCompare(key1,key2);
610 :
611 : if (changes == 0) printf("key1 and key2 are identicall\n");
612 :
613 : if (changes & KEY_VALUE)
614 : printf("key1 and key2 have different values\n");
615 :
616 : if (changes & KEY_UID)
617 : printf("key1 and key2 have different UID\n");
618 :
619 : *
620 : * @endcode
621 : *
622 : *
623 : * @par Example of very powerful specific Key lookup in a KeySet:
624 : * @code
625 : Key *base = keyNew ("/sw/MyApp/something", KEY_END);
626 : KDB *handle = kdbOpen(base);
627 : KeySet *ks=ksNew(0, KS_END);
628 : Key *current;
629 : uint32_t match;
630 : uint32_t interests;
631 :
632 :
633 : kdbGet(handle, ks, base);
634 :
635 : // we are interested only in key type and access permissions
636 : interests=(KEY_TYPE | KEY_MODE);
637 :
638 : ksRewind(ks); // put cursor in the beginning
639 : while ((curren=ksNext(ks))) {
640 : match=keyCompare(current,base);
641 :
642 : if ((~match & interests) == interests)
643 : printf("Key %s has same type and permissions of base key",keyName(current));
644 :
645 : // continue walking in the KeySet....
646 : }
647 :
648 : // now we want same name and/or value
649 : interests=(KEY_NAME | KEY_VALUE);
650 :
651 : // we don't really need ksRewind(), since previous loop achieved end of KeySet
652 : ksRewind(ks);
653 : while ((current=ksNext(ks))) {
654 : match=keyCompare(current,base);
655 :
656 : if ((~match & interests) == interests) {
657 : printf("Key %s has same name, value, and sync status
658 : of base key",keyName(current));
659 : }
660 : // continue walking in the KeySet....
661 : }
662 :
663 : ksDel(ks);
664 : kdbClose (handle, base);
665 : keyDel(base);
666 : * @endcode
667 : *
668 : * @return a bit array pointing the differences
669 : * @param key1 first key
670 : * @param key2 second key
671 : * @see #keyswitch_t
672 : * @ingroup keytest
673 : */
674 86 : keyswitch_t keyCompare (const Key * key1, const Key * key2)
675 : {
676 86 : if (!key1 && !key2) return 0;
677 86 : if (!key1 || !key2) return KEY_NULL;
678 :
679 66 : keyswitch_t ret = 0;
680 66 : ssize_t nsize1 = keyGetNameSize (key1);
681 66 : ssize_t nsize2 = keyGetNameSize (key2);
682 66 : const char * name1 = keyName (key1);
683 66 : const char * name2 = keyName (key2);
684 66 : const Key * comment1 = keyGetMeta (key1, "comment");
685 66 : const Key * comment2 = keyGetMeta (key2, "comment");
686 66 : const char * owner1 = keyOwner (key1);
687 66 : const char * owner2 = keyOwner (key2);
688 66 : const void * value1 = keyValue (key1);
689 66 : const void * value2 = keyValue (key2);
690 66 : ssize_t size1 = keyGetValueSize (key1);
691 66 : ssize_t size2 = keyGetValueSize (key2);
692 :
693 : // TODO: might be (binary) by chance
694 66 : if (strcmp (keyString (comment1), keyString (comment2))) ret |= KEY_COMMENT;
695 :
696 66 : if (strcmp (owner1, owner2)) ret |= KEY_OWNER;
697 :
698 66 : if (keyCompareMeta (key1, key2)) ret |= KEY_META;
699 :
700 66 : if (nsize1 != nsize2)
701 2 : ret |= KEY_NAME;
702 64 : else if (!name1 || !name2)
703 0 : ret |= KEY_NAME;
704 64 : else if (strcmp (name1, name2))
705 0 : ret |= KEY_NAME;
706 :
707 :
708 66 : if (size1 != size2)
709 6 : ret |= KEY_VALUE;
710 60 : else if (!value1 || !value2)
711 0 : ret |= KEY_VALUE;
712 60 : else if (memcmp (value1, value2, size1))
713 4 : ret |= KEY_VALUE;
714 :
715 : // TODO: rewind metadata to previous position
716 : return ret;
717 : }
718 :
719 : /**
720 : * @brief Compares metadata of two keys
721 : *
722 : * @retval KEY_META if there is a difference
723 : * @retval 0 if metadata is identical
724 : */
725 66 : int keyCompareMeta (const Key * k1, const Key * k2)
726 : {
727 : const Key * meta1;
728 :
729 66 : Key * key1 = (Key *) k1;
730 66 : Key * key2 = (Key *) k2;
731 :
732 66 : keyRewindMeta (key1);
733 66 : keyRewindMeta (key2);
734 196 : while ((meta1 = keyNextMeta (key1)) != 0)
735 : {
736 98 : const Key * meta2 = keyNextMeta (key2);
737 98 : if (!meta2)
738 : {
739 : return KEY_META;
740 : }
741 :
742 90 : if (strcmp (keyName (meta1), keyName (meta2))) return KEY_META;
743 64 : if (strcmp (keyString (meta1), keyString (meta2))) return KEY_META;
744 : }
745 :
746 : // TODO: rewind metadata to previous position
747 : return 0;
748 : }
|