Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Methods to do various operations on Key metadata.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : /***************************************************************************
10 : keymeta.c - Methods for Key manipulation
11 : -------------------
12 : begin : Fri Sep 26 2008
13 : copyright : (C) 2008 by Markus Raab
14 : email : elektra@markus-raab.org
15 : ***************************************************************************/
16 :
17 : /***************************************************************************
18 : * *
19 : * This program is free software; you can redistribute it and/or modify *
20 : * it under the terms of the BSD License (revised). *
21 : * *
22 : ***************************************************************************/
23 :
24 :
25 : /**
26 : * @defgroup keymeta Meta Info Manipulation Methods
27 : * @ingroup key
28 : * @brief Methods to do various operations on Key metadata.
29 : *
30 : * To use them:
31 : * @code
32 : #include <kdb.h>
33 : * @endcode
34 : *
35 : * Next to \link keyname Name (key and owner) \endlink and
36 : * \link keyvalue value (data and comment) \endlink there
37 : * is the so called meta information inside every key.
38 : *
39 : * Key meta information are an unlimited number of key/value
40 : * pairs strongly related to a key. It main purpose is to
41 : * give keys special semantics, so that plugins can treat
42 : * them differently.
43 : *
44 : * Metakey, as opposed to Key,
45 : * deliberately has following limitations:
46 : * - no null values
47 : * - no binary data
48 : * - no modification of references (COW)
49 : * - no guarantee of ordering
50 : *
51 : * ## Examples for metadata
52 : *
53 : * File system information (see stat(2) for more information):
54 : * - uid: the user id (positive number)
55 : * - gid: the group id (positive number)
56 : * - mode: filesystem-like mode permissions (positive octal number)
57 : * - atime: When was the key accessed the last time.
58 : * - mtime: When was the key modified the last time.
59 : * - ctime: When the uid, gid or mode of a key changes.
60 : * (times are represented through a positive number as unix timestamp)
61 : *
62 : * The comment can contain userdata which directly
63 : * belong to that key. The name of the meta information
64 : * is "comment" for a general purpose comment about
65 : * the key. Multi-Language comments are also supported
66 : * by appending [LANG] to the name.
67 : *
68 : * Validators are regular expressions which are tested
69 : * against the key value. The metakey "validator" can
70 : * hold a regular expression which will be matched
71 : * against.
72 : *
73 : * Types can be expressed with the meta information
74 : * "type".
75 : *
76 : * The relevance of the key can be tagged with a value
77 : * from -20 to 20. Negative numbers are the more important
78 : * and must be present in order to start the program.
79 : *
80 : * A version of a key may be stored with "version".
81 : * Its format is full.major.minor where all of these
82 : * are integers.
83 : *
84 : * The order inside a persistent storage can be described
85 : * with the tag "order" which contains a positive number.
86 : *
87 : * The metakey "app" describes to which application a
88 : * key belongs. It can be used to remove keys from an
89 : * application no longer installed.
90 : *
91 : * The metakey "path" describes where the key is physically
92 : * stored.
93 : *
94 : * The "owner" is the user that owns the key. It only
95 : * works for the user/ hierarchy. It rather says where
96 : * the key is stored and says nothing about the
97 : * filesystem properties.
98 : *
99 : */
100 :
101 :
102 : #include <kdb.h>
103 : #include <kdbconfig.h>
104 : #include <kdbprivate.h>
105 :
106 : #ifdef HAVE_STDIO_H
107 : #include <stdio.h>
108 : #endif
109 :
110 : #ifdef HAVE_STDARG_H
111 : #include <stdarg.h>
112 : #endif
113 :
114 : #ifdef HAVE_STRING_H
115 : #include <string.h>
116 : #endif
117 :
118 : #ifdef HAVE_STDLIB_H
119 : #include <stdlib.h>
120 : #endif
121 :
122 : #ifdef HAVE_ERRNO_H
123 : #include <errno.h>
124 : #endif
125 :
126 :
127 : /**Rewind the internal iterator to first metadata.
128 : *
129 : * Use it to set the cursor to the beginning of the Key Meta Infos.
130 : * keyCurrentMeta() will then always return NULL afterwards. So
131 : * you want to keyNextMeta() first.
132 : *
133 : * @code
134 : Key *key;
135 : const Key *meta;
136 :
137 : keyRewindMeta (key);
138 : while ((meta = keyNextMeta (key))!=0)
139 : {
140 : printf ("name: %s, value: %s", keyName(meta), keyString(meta));
141 : }
142 : * @endcode
143 : *
144 : * @param key the key object to work with
145 : * @retval 0 on success
146 : * @retval 0 if there is no meta information for that key
147 : * (keyNextMeta() will always return 0 in that case)
148 : * @retval -1 on NULL pointer
149 : * @see keyNextMeta(), keyCurrentMeta()
150 : * @see ksRewind() for pedant in iterator interface of KeySet
151 : * @ingroup keymeta
152 : **/
153 243856 : int keyRewindMeta (Key * key)
154 : {
155 243856 : if (!key) return -1;
156 243837 : if (!key->meta) return 0;
157 :
158 180534 : return ksRewind (key->meta);
159 : }
160 :
161 : /** Iterate to the next meta information.
162 : *
163 : * Keys have an internal cursor that can be reset with keyRewindMeta(). Every
164 : * time keyNextMeta() is called the cursor is incremented and the new current
165 : * Name of Meta Information is returned.
166 : *
167 : * You'll get a NULL pointer if the meta information after the end of the Key was reached.
168 : * On subsequent calls of keyNextMeta() it will still return the NULL pointer.
169 : *
170 : * The @p key internal cursor will be changed, so it is not const.
171 : *
172 : * @note That the resulting key is guaranteed to have a value, because
173 : * meta information has no binary or null pointer semantics.
174 : *
175 : * @note You must not delete or change the returned key,
176 : * use keySetMeta() if you want to delete or change it.
177 : *
178 : * @param key the key object to work with
179 : * @return a key representing meta information
180 : * @retval 0 when the end is reached
181 : * @retval 0 on NULL pointer
182 : *
183 : * @see ksNext() for pedant in iterator interface of KeySet
184 : * @ingroup keymeta
185 : **/
186 423917 : const Key * keyNextMeta (Key * key)
187 : {
188 : Key * ret;
189 423917 : if (!key) return 0;
190 423898 : if (!key->meta) return 0;
191 :
192 410168 : ret = ksNext (key->meta);
193 :
194 410168 : return ret;
195 : }
196 :
197 : /**Returns the value of a meta-information which is current.
198 : *
199 : * The pointer is NULL if you reached the end or after
200 : * ksRewind().
201 : *
202 : * @note You must not delete or change the returned key,
203 : * use keySetMeta() if you want to delete or change it.
204 : *
205 : * @param key the key object to work with
206 : * @return a buffer to the value pointed by @p key's cursor
207 : * @retval 0 on NULL pointer
208 : * @see keyNextMeta(), keyRewindMeta()
209 : *
210 : * @see ksCurrent() for pedant in iterator interface of KeySet
211 : * @ingroup keymeta
212 : **/
213 64688 : const Key * keyCurrentMeta (const Key * key)
214 : {
215 : Key * ret;
216 64688 : if (!key) return 0;
217 64688 : if (!key->meta) return 0;
218 :
219 64686 : ret = ksCurrent (key->meta);
220 :
221 64686 : return ret;
222 : }
223 :
224 : /**Do a shallow copy of metadata from source to dest.
225 : *
226 : * The key dest will have the same metadata referred with
227 : * metaName afterwards then source.
228 : *
229 : * For example the metadata type is copied into the
230 : * Key k.
231 : *
232 : * @code
233 : void l(Key *k)
234 : {
235 : // receive c
236 : keyCopyMeta(k, c, "type");
237 : // the caller will see the changed key k
238 : // with the metadata "type" from c
239 : }
240 : * @endcode
241 : *
242 : * The main purpose of this function is for plugins or
243 : * applications which want to add the same metadata to
244 : * n keys. When you do that with keySetMeta() it will
245 : * take n times the memory for the key. This can be
246 : * considerable amount of memory for many keys with
247 : * some metadata for each.
248 : *
249 : * To avoid that problem you can use keyCopyAllMeta()
250 : * or keyCopyMeta().
251 : *
252 : * @code
253 : void o(KeySet *ks)
254 : {
255 : Key *current;
256 : Key *shared = keyNew (0);
257 : keySetMeta(shared, "shared", "this metadata should be shared among many keys");
258 :
259 : ksRewind(ks);
260 : while ((current = ksNext(ks)) != 0)
261 : {
262 : if (needs_shared_data(current)) keyCopyMeta(current, shared, "shared");
263 : }
264 : }
265 : * @endcode
266 : *
267 : * @post keyGetMeta(source, metaName) == keyGetMeta(dest, metaName)
268 : *
269 : * @retval 1 if was successfully copied
270 : * @retval 0 if the metadata in dest was removed too
271 : * @retval -1 on null pointers (source or dest)
272 : * @retval -1 on memory problems
273 : * @param dest the destination where the metadata should be copied too
274 : * @param source the key where the metadata should be copied from
275 : * @param metaName the name of the metadata which should be copied
276 : * @ingroup keymeta
277 : */
278 1454 : int keyCopyMeta (Key * dest, const Key * source, const char * metaName)
279 : {
280 : Key * ret;
281 :
282 1454 : if (!source) return -1;
283 1452 : if (!dest) return -1;
284 1452 : if (dest->flags & KEY_FLAG_RO_META) return -1;
285 :
286 1448 : ret = (Key *) keyGetMeta (source, metaName);
287 :
288 1448 : if (!ret)
289 : {
290 : /*Make sure that dest also does not have metaName*/
291 2 : if (dest->meta)
292 : {
293 : Key * r;
294 0 : r = ksLookup (dest->meta, ret, KDB_O_POP);
295 0 : if (r)
296 : {
297 : /*It was already there, so lets drop that one*/
298 0 : keyDel (r);
299 : }
300 : }
301 : return 0;
302 : }
303 :
304 : /*Lets have a look if the key is already inserted.*/
305 1446 : if (dest->meta)
306 : {
307 : Key * r;
308 1040 : r = ksLookup (dest->meta, ret, KDB_O_POP);
309 1040 : if (r && r != ret)
310 : {
311 : /*It was already there, so lets drop that one*/
312 6 : keyDel (r);
313 : }
314 : }
315 : else
316 : {
317 : /*Create a new place for meta information.*/
318 406 : dest->meta = ksNew (0, KS_END);
319 406 : if (!dest->meta)
320 : {
321 : return -1;
322 : }
323 : }
324 :
325 : // now we can simply append that key
326 1446 : ksAppendKey (dest->meta, ret);
327 :
328 1446 : return 1;
329 : }
330 :
331 : /**Do a shallow copy of all metadata from source to dest.
332 : *
333 : * The key dest will additionally have all metadata
334 : * the source had.
335 : * Meta data not present in source will not be changed.
336 : * Meta data which was present in source and dest will
337 : * be overwritten.
338 : *
339 : * For example the metadata type is copied into the
340 : * Key k:
341 : *
342 : * @snippet keyMeta.c Basic Copy All
343 : *
344 : * The main purpose of this function is for plugins or
345 : * applications which want to add the same metadata to
346 : * n keys. When you do that with keySetMeta() it will
347 : * take n times the memory for the key. This can be
348 : * considerable amount of memory for many keys with
349 : * some metadata for each.
350 : *
351 : * To avoid that problem you can use keyCopyAllMeta()
352 : * or keyCopyMeta():
353 : *
354 : * @snippet keyMeta.c Shared Meta All
355 : *
356 : * @post for every metaName present in source: keyGetMeta(source, metaName) == keyGetMeta(dest, metaName)
357 : *
358 : * @retval 1 if was successfully copied
359 : * @retval 0 if source did not have any metadata
360 : * @retval -1 on null pointer of dest or source
361 : * @retval -1 on memory problems
362 : * @param dest the destination where the metadata should be copied too
363 : * @param source the key where the metadata should be copied from
364 : * @ingroup keymeta
365 : */
366 115801 : int keyCopyAllMeta (Key * dest, const Key * source)
367 : {
368 115801 : if (!source) return -1;
369 115778 : if (!dest) return -1;
370 115778 : if (dest->flags & KEY_FLAG_RO_META) return -1;
371 :
372 115774 : if (source->meta)
373 : {
374 : /*Make sure that dest also does not have metaName*/
375 113838 : if (dest->meta)
376 : {
377 111469 : ksAppend (dest->meta, source->meta);
378 : }
379 : else
380 : {
381 2369 : dest->meta = ksDup (source->meta);
382 : }
383 : return 1;
384 : }
385 :
386 : return 0;
387 : }
388 :
389 : /** Returns the value of a meta-information given by name.
390 : *
391 : * You are not allowed to modify the resulting key.
392 : *
393 : * @code
394 : int f(Key *k)
395 : {
396 : if (!strcmp(keyValue(keyGetMeta(k, "type")), "boolean"))
397 : {
398 : // the type of the key is boolean
399 : }
400 : }
401 : * @endcode
402 : *
403 : * @note You must not delete or change the returned key,
404 : * use keySetMeta() if you want to delete or change it.
405 : *
406 : * @param key the key object to work with
407 : * @param metaName the name of the meta information you want the value from
408 : * @retval 0 if the key or metaName is 0
409 : * @retval 0 if no such metaName is found
410 : * @return value of meta-information if meta-information is found
411 : * @see keySetMeta()
412 : * @ingroup keymeta
413 : **/
414 17072156 : const Key * keyGetMeta (const Key * key, const char * metaName)
415 : {
416 : Key * ret;
417 : Key * search;
418 :
419 17072156 : if (!key) return 0;
420 17068740 : if (!metaName) return 0;
421 17068740 : if (!key->meta) return 0;
422 :
423 3639527 : search = keyNew (0);
424 3639527 : elektraKeySetName (search, metaName, KEY_META_NAME | KEY_EMPTY_NAME);
425 :
426 3639527 : ret = ksLookup (key->meta, search, 0);
427 :
428 3639527 : keyDel (search);
429 :
430 3639527 : return ret;
431 : }
432 :
433 :
434 : /**Set a new meta-information.
435 : *
436 : * Will set a new meta-information pair consisting of
437 : * metaName and newMetaString.
438 : *
439 : * Will add a new Pair for meta-information if metaName was
440 : * not added up to now.
441 : *
442 : * It will modify an existing Pair of meta-information if the
443 : * the metaName was inserted already.
444 : *
445 : * It will remove a meta information if newMetaString is 0.
446 : *
447 : * @param key the key object to work with
448 : * @param metaName the name of the meta information where you
449 : * want to change the value
450 : * @param newMetaString the new value for the meta information
451 : * @retval -1 on error if key or metaName is 0, out of memory
452 : * or names are not valid
453 : * @retval 0 if the meta-information for metaName was removed
454 : * @return size (>0) of newMetaString if meta-information was
455 : * successfully added
456 : * @see keyGetMeta()
457 : * @ingroup keymeta
458 : **/
459 7717351 : ssize_t keySetMeta (Key * key, const char * metaName, const char * newMetaString)
460 : {
461 : Key * toSet;
462 : char * metaStringDup;
463 : ssize_t metaNameSize;
464 7717351 : ssize_t metaStringSize = 0;
465 :
466 7717351 : if (!key) return -1;
467 7717273 : if (key->flags & KEY_FLAG_RO_META) return -1;
468 7717255 : if (!metaName) return -1;
469 7717255 : metaNameSize = elektraStrLen (metaName);
470 7717255 : if (metaNameSize == -1) return -1;
471 7717255 : if (newMetaString) metaStringSize = elektraStrLen (newMetaString);
472 :
473 : // optimization: we have nothing and want to remove something:
474 7717255 : if (!key->meta && !newMetaString) return 0;
475 :
476 2538876 : toSet = keyNew (0);
477 2538876 : if (!toSet) return -1;
478 :
479 2538876 : elektraKeySetName (toSet, metaName, KEY_META_NAME | KEY_EMPTY_NAME);
480 :
481 : /*Lets have a look if the key is already inserted.*/
482 2538876 : if (key->meta)
483 : {
484 : Key * ret;
485 2070134 : ret = ksLookup (key->meta, toSet, KDB_O_POP);
486 2070134 : if (ret)
487 : {
488 : /*It was already there, so lets drop that one*/
489 277907 : keyDel (ret);
490 277907 : key->flags |= KEY_FLAG_SYNC;
491 : }
492 : }
493 :
494 2538876 : if (newMetaString)
495 : {
496 : /*Add the meta information to the key*/
497 1335773 : metaStringDup = elektraStrNDup (newMetaString, metaStringSize);
498 1335773 : if (!metaStringDup)
499 : {
500 : // TODO: actually we might already have changed
501 : // the key
502 0 : keyDel (toSet);
503 0 : return -1;
504 : }
505 :
506 1335773 : if (toSet->data.v && !test_bit (toSet->flags, KEY_FLAG_MMAP_DATA)) elektraFree (toSet->data.v);
507 1335773 : clear_bit (toSet->flags, (keyflag_t) KEY_FLAG_MMAP_DATA);
508 1335773 : toSet->data.c = metaStringDup;
509 1335773 : toSet->dataSize = metaStringSize;
510 : }
511 : else
512 : {
513 : /*The request is to remove the meta string.
514 : So simply drop it.*/
515 1203103 : keyDel (toSet);
516 1203103 : return 0;
517 : }
518 :
519 1335773 : if (!key->meta)
520 : {
521 : /*Create a new place for meta information.*/
522 468742 : key->meta = ksNew (0, KS_END);
523 468742 : if (!key->meta)
524 : {
525 0 : keyDel (toSet);
526 0 : return -1;
527 : }
528 : }
529 :
530 1335773 : set_bit (toSet->flags, KEY_FLAG_RO_NAME);
531 1335773 : set_bit (toSet->flags, KEY_FLAG_RO_VALUE);
532 1335773 : set_bit (toSet->flags, KEY_FLAG_RO_META);
533 :
534 1335773 : ksAppendKey (key->meta, toSet);
535 1335773 : key->flags |= KEY_FLAG_SYNC;
536 1335773 : return metaStringSize;
537 : }
|