settings: Add functions to add sections and key/value pairs to a section
[strongswan.git] / src / libstrongswan / settings / settings.c
1 /*
2 * Copyright (C) 2010-2014 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include <string.h>
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include <limits.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26
27 #include "settings.h"
28 #include "settings_types.h"
29
30 #include "collections/array.h"
31 #include "collections/hashtable.h"
32 #include "collections/linked_list.h"
33 #include "threading/rwlock.h"
34 #include "utils/debug.h"
35
36 typedef struct private_settings_t private_settings_t;
37
38 /**
39 * Parse function provided by the generated parser.
40 */
41 bool settings_parser_parse_file(section_t *root, char *name);
42
43 /**
44 * Private data of settings
45 */
46 struct private_settings_t {
47
48 /**
49 * Public interface
50 */
51 settings_t public;
52
53 /**
54 * Top level section
55 */
56 section_t *top;
57
58 /**
59 * Contents of replaced settings (char*)
60 *
61 * FIXME: This is required because the pointer returned by get_str()
62 * is not refcounted. Might cause ever increasing usage stats.
63 */
64 array_t *contents;
65
66 /**
67 * Lock to safely access the settings
68 */
69 rwlock_t *lock;
70 };
71
72 static void kv_destroy(kv_t *kv, int idx, array_t *contents)
73 {
74 settings_kv_destroy(kv, contents);
75 }
76
77 /**
78 * Purge contents of a section, returns if section can be safely removed.
79 */
80 static bool section_purge(section_t *this, array_t *contents)
81 {
82 section_t *current;
83 int i;
84
85 array_destroy_function(this->kv, (void*)kv_destroy, contents);
86 this->kv = NULL;
87 /* we ensure sections used as fallback, or configured with fallbacks (or
88 * having any such subsections) are not removed */
89 for (i = array_count(this->sections) - 1; i >= 0; i--)
90 {
91 array_get(this->sections, i, &current);
92 if (section_purge(current, contents))
93 {
94 array_remove(this->sections, i, NULL);
95 settings_section_destroy(current, contents);
96 }
97 }
98 return !this->fallbacks && !array_count(this->sections);
99 }
100
101 /**
102 * Print a format key, but consume already processed arguments
103 */
104 static bool print_key(char *buf, int len, char *start, char *key, va_list args)
105 {
106 va_list copy;
107 char *pos = start;
108 bool res;
109
110 va_copy(copy, args);
111 while (TRUE)
112 {
113 pos = memchr(pos, '%', key - pos);
114 if (!pos)
115 {
116 break;
117 }
118 pos++;
119 switch (*pos)
120 {
121 case 'd':
122 va_arg(copy, int);
123 break;
124 case 's':
125 va_arg(copy, char*);
126 break;
127 case 'N':
128 va_arg(copy, enum_name_t*);
129 va_arg(copy, int);
130 break;
131 case '%':
132 break;
133 default:
134 DBG1(DBG_CFG, "settings with %%%c not supported!", *pos);
135 break;
136 }
137 pos++;
138 }
139 res = vsnprintf(buf, len, key, copy) < len;
140 va_end(copy);
141 return res;
142 }
143
144 /**
145 * Find a section by a given key, using buffered key, reusable buffer.
146 * If "ensure" is TRUE, the sections are created if they don't exist.
147 */
148 static section_t *find_section_buffered(section_t *section,
149 char *start, char *key, va_list args, char *buf, int len,
150 bool ensure)
151 {
152 char *pos;
153 section_t *found = NULL;
154
155 if (section == NULL)
156 {
157 return NULL;
158 }
159 pos = strchr(key, '.');
160 if (pos)
161 {
162 *pos = '\0';
163 pos++;
164 }
165 if (!print_key(buf, len, start, key, args))
166 {
167 return NULL;
168 }
169 if (!strlen(buf))
170 {
171 found = section;
172 }
173 else if (array_bsearch(section->sections, buf, settings_section_find,
174 &found) == -1)
175 {
176 if (ensure)
177 {
178 found = settings_section_create(strdup(buf));
179 settings_section_add(section, found, NULL);
180 }
181 }
182 if (found && pos)
183 {
184 return find_section_buffered(found, start, pos, args, buf, len, ensure);
185 }
186 return found;
187 }
188
189 /**
190 * Find all sections via a given key considering fallbacks, using buffered key,
191 * reusable buffer.
192 */
193 static void find_sections_buffered(section_t *section, char *start, char *key,
194 va_list args, char *buf, int len, array_t **sections)
195 {
196 section_t *found = NULL, *fallback;
197 char *pos;
198 int i;
199
200 if (!section)
201 {
202 return;
203 }
204 pos = strchr(key, '.');
205 if (pos)
206 {
207 *pos = '\0';
208 }
209 if (!print_key(buf, len, start, key, args))
210 {
211 return;
212 }
213 if (pos)
214 { /* restore so we can follow fallbacks */
215 *pos = '.';
216 }
217 if (!strlen(buf))
218 {
219 found = section;
220 }
221 else
222 {
223 array_bsearch(section->sections, buf, settings_section_find, &found);
224 }
225 if (found)
226 {
227 if (pos)
228 {
229 find_sections_buffered(found, start, pos+1, args, buf, len,
230 sections);
231 }
232 else
233 {
234 array_insert_create(sections, ARRAY_TAIL, found);
235 for (i = 0; i < array_count(found->fallbacks); i++)
236 {
237 array_get(found->fallbacks, i, &fallback);
238 array_insert_create(sections, ARRAY_TAIL, fallback);
239 }
240 }
241 }
242 if (section->fallbacks)
243 {
244 for (i = 0; i < array_count(section->fallbacks); i++)
245 {
246 array_get(section->fallbacks, i, &fallback);
247 find_sections_buffered(fallback, start, key, args, buf, len,
248 sections);
249 }
250 }
251 }
252
253 /**
254 * Ensure that the section with the given key exists (thread-safe).
255 */
256 static section_t *ensure_section(private_settings_t *this, section_t *section,
257 const char *key, va_list args)
258 {
259 char buf[128], keybuf[512];
260 section_t *found;
261
262 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
263 {
264 return NULL;
265 }
266 /* we might have to change the tree */
267 this->lock->write_lock(this->lock);
268 found = find_section_buffered(section, keybuf, keybuf, args, buf,
269 sizeof(buf), TRUE);
270 this->lock->unlock(this->lock);
271 return found;
272 }
273
274 /**
275 * Find a section by a given key with its fallbacks (not thread-safe!).
276 * Sections are returned in depth-first order (array is allocated). NULL is
277 * returned if no sections are found.
278 */
279 static array_t *find_sections(private_settings_t *this, section_t *section,
280 char *key, va_list args)
281 {
282 char buf[128], keybuf[512];
283 array_t *sections = NULL;
284
285 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
286 {
287 return NULL;
288 }
289 find_sections_buffered(section, keybuf, keybuf, args, buf,
290 sizeof(buf), &sections);
291 return sections;
292 }
293
294 /**
295 * Check if the given fallback section already exists
296 */
297 static bool fallback_exists(section_t *section, section_t *fallback)
298 {
299 if (section == fallback)
300 {
301 return TRUE;
302 }
303 else if (section->fallbacks)
304 {
305 section_t *existing;
306 int i;
307
308 for (i = 0; i < array_count(section->fallbacks); i++)
309 {
310 array_get(section->fallbacks, i, &existing);
311 if (existing == fallback)
312 {
313 return TRUE;
314 }
315 }
316 }
317 return FALSE;
318 }
319
320 /**
321 * Ensure that the section with the given key exists and add the given fallback
322 * section (thread-safe).
323 */
324 static void add_fallback_to_section(private_settings_t *this,
325 section_t *section, const char *key, va_list args,
326 section_t *fallback)
327 {
328 char buf[128], keybuf[512];
329 section_t *found;
330
331 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
332 {
333 return;
334 }
335 this->lock->write_lock(this->lock);
336 found = find_section_buffered(section, keybuf, keybuf, args, buf,
337 sizeof(buf), TRUE);
338 if (!fallback_exists(found, fallback))
339 {
340 /* to ensure sections referred to as fallback are not purged, we create
341 * the array there too */
342 if (!fallback->fallbacks)
343 {
344 fallback->fallbacks = array_create(0, 0);
345 }
346 array_insert_create(&found->fallbacks, ARRAY_TAIL, fallback);
347 }
348 this->lock->unlock(this->lock);
349 }
350
351 /**
352 * Find the key/value pair for a key, using buffered key, reusable buffer
353 * If "ensure" is TRUE, the sections (and key/value pair) are created if they
354 * don't exist.
355 * Fallbacks are only considered if "ensure" is FALSE.
356 */
357 static kv_t *find_value_buffered(section_t *section, char *start, char *key,
358 va_list args, char *buf, int len, bool ensure)
359 {
360 int i;
361 char *pos;
362 kv_t *kv = NULL;
363 section_t *found = NULL;
364
365 if (section == NULL)
366 {
367 return NULL;
368 }
369
370 pos = strchr(key, '.');
371 if (pos)
372 {
373 *pos = '\0';
374 if (!print_key(buf, len, start, key, args))
375 {
376 return NULL;
377 }
378 /* restore so we can retry for fallbacks */
379 *pos = '.';
380 if (!strlen(buf))
381 {
382 found = section;
383 }
384 else if (array_bsearch(section->sections, buf, settings_section_find,
385 &found) == -1)
386 {
387 if (ensure)
388 {
389 found = settings_section_create(strdup(buf));
390 settings_section_add(section, found, NULL);
391 }
392 }
393 if (found)
394 {
395 kv = find_value_buffered(found, start, pos+1, args, buf, len,
396 ensure);
397 }
398 if (!kv && !ensure && section->fallbacks)
399 {
400 for (i = 0; !kv && i < array_count(section->fallbacks); i++)
401 {
402 array_get(section->fallbacks, i, &found);
403 kv = find_value_buffered(found, start, key, args, buf, len,
404 ensure);
405 }
406 }
407 }
408 else
409 {
410 if (!print_key(buf, len, start, key, args))
411 {
412 return NULL;
413 }
414 if (array_bsearch(section->kv, buf, settings_kv_find, &kv) == -1)
415 {
416 if (ensure)
417 {
418 kv = settings_kv_create(strdup(buf), NULL);
419 settings_kv_add(section, kv, NULL);
420 }
421 else if (section->fallbacks)
422 {
423 for (i = 0; !kv && i < array_count(section->fallbacks); i++)
424 {
425 array_get(section->fallbacks, i, &found);
426 kv = find_value_buffered(found, start, key, args, buf, len,
427 ensure);
428 }
429 }
430 }
431 }
432 return kv;
433 }
434
435 /**
436 * Find the string value for a key (thread-safe).
437 */
438 static char *find_value(private_settings_t *this, section_t *section,
439 char *key, va_list args)
440 {
441 char buf[128], keybuf[512], *value = NULL;
442 kv_t *kv;
443
444 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
445 {
446 return NULL;
447 }
448 this->lock->read_lock(this->lock);
449 kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
450 FALSE);
451 if (kv)
452 {
453 value = kv->value;
454 }
455 this->lock->unlock(this->lock);
456 return value;
457 }
458
459 /**
460 * Set a value to a copy of the given string (thread-safe).
461 */
462 static void set_value(private_settings_t *this, section_t *section,
463 char *key, va_list args, char *value)
464 {
465 char buf[128], keybuf[512];
466 kv_t *kv;
467
468 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
469 {
470 return;
471 }
472 this->lock->write_lock(this->lock);
473 kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
474 TRUE);
475 if (kv)
476 {
477 if (!value)
478 {
479 if (kv->value)
480 {
481 array_insert(this->contents, ARRAY_TAIL, kv->value);
482 }
483 kv->value = NULL;
484 }
485 else if (kv->value && (strlen(value) <= strlen(kv->value)))
486 { /* overwrite in-place, if possible */
487 strcpy(kv->value, value);
488 }
489 else
490 { /* otherwise clone the string and cache the replaced one */
491 if (kv->value)
492 {
493 array_insert(this->contents, ARRAY_TAIL, kv->value);
494 }
495 kv->value = strdup(value);
496 }
497 }
498 this->lock->unlock(this->lock);
499 }
500
501 METHOD(settings_t, get_str, char*,
502 private_settings_t *this, char *key, char *def, ...)
503 {
504 char *value;
505 va_list args;
506
507 va_start(args, def);
508 value = find_value(this, this->top, key, args);
509 va_end(args);
510 if (value)
511 {
512 return value;
513 }
514 return def;
515 }
516
517 /**
518 * Described in header
519 */
520 inline bool settings_value_as_bool(char *value, bool def)
521 {
522 if (value)
523 {
524 if (strcaseeq(value, "1") ||
525 strcaseeq(value, "yes") ||
526 strcaseeq(value, "true") ||
527 strcaseeq(value, "enabled"))
528 {
529 return TRUE;
530 }
531 else if (strcaseeq(value, "0") ||
532 strcaseeq(value, "no") ||
533 strcaseeq(value, "false") ||
534 strcaseeq(value, "disabled"))
535 {
536 return FALSE;
537 }
538 }
539 return def;
540 }
541
542 METHOD(settings_t, get_bool, bool,
543 private_settings_t *this, char *key, bool def, ...)
544 {
545 char *value;
546 va_list args;
547
548 va_start(args, def);
549 value = find_value(this, this->top, key, args);
550 va_end(args);
551 return settings_value_as_bool(value, def);
552 }
553
554 /**
555 * Described in header
556 */
557 inline int settings_value_as_int(char *value, int def)
558 {
559 int intval;
560 if (value)
561 {
562 errno = 0;
563 intval = strtol(value, NULL, 10);
564 if (errno == 0)
565 {
566 return intval;
567 }
568 }
569 return def;
570 }
571
572 METHOD(settings_t, get_int, int,
573 private_settings_t *this, char *key, int def, ...)
574 {
575 char *value;
576 va_list args;
577
578 va_start(args, def);
579 value = find_value(this, this->top, key, args);
580 va_end(args);
581 return settings_value_as_int(value, def);
582 }
583
584 /**
585 * Described in header
586 */
587 inline double settings_value_as_double(char *value, double def)
588 {
589 double dval;
590 if (value)
591 {
592 errno = 0;
593 dval = strtod(value, NULL);
594 if (errno == 0)
595 {
596 return dval;
597 }
598 }
599 return def;
600 }
601
602 METHOD(settings_t, get_double, double,
603 private_settings_t *this, char *key, double def, ...)
604 {
605 char *value;
606 va_list args;
607
608 va_start(args, def);
609 value = find_value(this, this->top, key, args);
610 va_end(args);
611 return settings_value_as_double(value, def);
612 }
613
614 /**
615 * Described in header
616 */
617 inline u_int32_t settings_value_as_time(char *value, u_int32_t def)
618 {
619 char *endptr;
620 u_int32_t timeval;
621 if (value)
622 {
623 errno = 0;
624 timeval = strtoul(value, &endptr, 10);
625 if (errno == 0)
626 {
627 switch (*endptr)
628 {
629 case 'd': /* time in days */
630 timeval *= 24 * 3600;
631 break;
632 case 'h': /* time in hours */
633 timeval *= 3600;
634 break;
635 case 'm': /* time in minutes */
636 timeval *= 60;
637 break;
638 case 's': /* time in seconds */
639 default:
640 break;
641 }
642 return timeval;
643 }
644 }
645 return def;
646 }
647
648 METHOD(settings_t, get_time, u_int32_t,
649 private_settings_t *this, char *key, u_int32_t def, ...)
650 {
651 char *value;
652 va_list args;
653
654 va_start(args, def);
655 value = find_value(this, this->top, key, args);
656 va_end(args);
657 return settings_value_as_time(value, def);
658 }
659
660 METHOD(settings_t, set_str, void,
661 private_settings_t *this, char *key, char *value, ...)
662 {
663 va_list args;
664 va_start(args, value);
665 set_value(this, this->top, key, args, value);
666 va_end(args);
667 }
668
669 METHOD(settings_t, set_bool, void,
670 private_settings_t *this, char *key, bool value, ...)
671 {
672 va_list args;
673 va_start(args, value);
674 set_value(this, this->top, key, args, value ? "1" : "0");
675 va_end(args);
676 }
677
678 METHOD(settings_t, set_int, void,
679 private_settings_t *this, char *key, int value, ...)
680 {
681 char val[16];
682 va_list args;
683 va_start(args, value);
684 if (snprintf(val, sizeof(val), "%d", value) < sizeof(val))
685 {
686 set_value(this, this->top, key, args, val);
687 }
688 va_end(args);
689 }
690
691 METHOD(settings_t, set_double, void,
692 private_settings_t *this, char *key, double value, ...)
693 {
694 char val[64];
695 va_list args;
696 va_start(args, value);
697 if (snprintf(val, sizeof(val), "%f", value) < sizeof(val))
698 {
699 set_value(this, this->top, key, args, val);
700 }
701 va_end(args);
702 }
703
704 METHOD(settings_t, set_time, void,
705 private_settings_t *this, char *key, u_int32_t value, ...)
706 {
707 char val[16];
708 va_list args;
709 va_start(args, value);
710 if (snprintf(val, sizeof(val), "%u", value) < sizeof(val))
711 {
712 set_value(this, this->top, key, args, val);
713 }
714 va_end(args);
715 }
716
717 METHOD(settings_t, set_default_str, bool,
718 private_settings_t *this, char *key, char *value, ...)
719 {
720 char *old;
721 va_list args;
722
723 va_start(args, value);
724 old = find_value(this, this->top, key, args);
725 va_end(args);
726
727 if (!old)
728 {
729 va_start(args, value);
730 set_value(this, this->top, key, args, value);
731 va_end(args);
732 return TRUE;
733 }
734 return FALSE;
735 }
736
737 /**
738 * Data for enumerators
739 */
740 typedef struct {
741 /** settings_t instance */
742 private_settings_t *settings;
743 /** sections to enumerate */
744 array_t *sections;
745 /** sections/keys that were already enumerated */
746 hashtable_t *seen;
747 } enumerator_data_t;
748
749 /**
750 * Destroy enumerator data
751 */
752 static void enumerator_destroy(enumerator_data_t *this)
753 {
754 this->settings->lock->unlock(this->settings->lock);
755 this->seen->destroy(this->seen);
756 array_destroy(this->sections);
757 free(this);
758 }
759
760 /**
761 * Enumerate section names, not sections
762 */
763 static bool section_filter(hashtable_t *seen, section_t **in, char **out)
764 {
765 *out = (*in)->name;
766 if (seen->get(seen, *out))
767 {
768 return FALSE;
769 }
770 seen->put(seen, *out, *out);
771 return TRUE;
772 }
773
774 /**
775 * Enumerate sections of the given section
776 */
777 static enumerator_t *section_enumerator(section_t *section,
778 enumerator_data_t *data)
779 {
780 return enumerator_create_filter(array_create_enumerator(section->sections),
781 (void*)section_filter, data->seen, NULL);
782 }
783
784 METHOD(settings_t, create_section_enumerator, enumerator_t*,
785 private_settings_t *this, char *key, ...)
786 {
787 enumerator_data_t *data;
788 array_t *sections;
789 va_list args;
790
791 this->lock->read_lock(this->lock);
792 va_start(args, key);
793 sections = find_sections(this, this->top, key, args);
794 va_end(args);
795
796 if (!sections)
797 {
798 this->lock->unlock(this->lock);
799 return enumerator_create_empty();
800 }
801 INIT(data,
802 .settings = this,
803 .sections = sections,
804 .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
805 );
806 return enumerator_create_nested(array_create_enumerator(sections),
807 (void*)section_enumerator, data, (void*)enumerator_destroy);
808 }
809
810 /**
811 * Enumerate key and values, not kv_t entries
812 */
813 static bool kv_filter(hashtable_t *seen, kv_t **in, char **key,
814 void *none, char **value)
815 {
816 *key = (*in)->key;
817 if (seen->get(seen, *key) || !(*in)->value)
818 {
819 return FALSE;
820 }
821 *value = (*in)->value;
822 seen->put(seen, *key, *key);
823 return TRUE;
824 }
825
826 /**
827 * Enumerate key/value pairs of the given section
828 */
829 static enumerator_t *kv_enumerator(section_t *section, enumerator_data_t *data)
830 {
831 return enumerator_create_filter(array_create_enumerator(section->kv),
832 (void*)kv_filter, data->seen, NULL);
833 }
834
835 METHOD(settings_t, create_key_value_enumerator, enumerator_t*,
836 private_settings_t *this, char *key, ...)
837 {
838 enumerator_data_t *data;
839 array_t *sections;
840 va_list args;
841
842 this->lock->read_lock(this->lock);
843 va_start(args, key);
844 sections = find_sections(this, this->top, key, args);
845 va_end(args);
846
847 if (!sections)
848 {
849 this->lock->unlock(this->lock);
850 return enumerator_create_empty();
851 }
852 INIT(data,
853 .settings = this,
854 .sections = sections,
855 .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
856 );
857 return enumerator_create_nested(array_create_enumerator(sections),
858 (void*)kv_enumerator, data, (void*)enumerator_destroy);
859 }
860
861 METHOD(settings_t, add_fallback, void,
862 private_settings_t *this, const char *key, const char *fallback, ...)
863 {
864 section_t *section;
865 va_list args;
866
867 /* find/create the fallback */
868 va_start(args, fallback);
869 section = ensure_section(this, this->top, fallback, args);
870 va_end(args);
871
872 va_start(args, fallback);
873 add_fallback_to_section(this, this->top, key, args, section);
874 va_end(args);
875 }
876
877 /**
878 * Load settings from files matching the given file pattern.
879 * All sections and values are added relative to "parent".
880 * All files (even included ones) have to be loaded successfully.
881 * If merge is FALSE the contents of parent are replaced with the parsed
882 * contents, otherwise they are merged together.
883 */
884 static bool load_files_internal(private_settings_t *this, section_t *parent,
885 char *pattern, bool merge)
886 {
887 section_t *section;
888
889 if (pattern == NULL)
890 {
891 #ifdef STRONGSWAN_CONF
892 pattern = STRONGSWAN_CONF;
893 #else
894 return FALSE;
895 #endif
896 }
897
898 section = settings_section_create(NULL);
899 if (!settings_parser_parse_file(section, pattern))
900 {
901 settings_section_destroy(section, NULL);
902 return FALSE;
903 }
904
905 this->lock->write_lock(this->lock);
906 if (!merge)
907 {
908 section_purge(parent, this->contents);
909 }
910 /* extend parent section */
911 settings_section_extend(parent, section, this->contents);
912 this->lock->unlock(this->lock);
913
914 settings_section_destroy(section, NULL);
915 return TRUE;
916 }
917
918 METHOD(settings_t, load_files, bool,
919 private_settings_t *this, char *pattern, bool merge)
920 {
921 return load_files_internal(this, this->top, pattern, merge);
922 }
923
924 METHOD(settings_t, load_files_section, bool,
925 private_settings_t *this, char *pattern, bool merge, char *key, ...)
926 {
927 section_t *section;
928 va_list args;
929
930 va_start(args, key);
931 section = ensure_section(this, this->top, key, args);
932 va_end(args);
933
934 if (!section)
935 {
936 return FALSE;
937 }
938 return load_files_internal(this, section, pattern, merge);
939 }
940
941 METHOD(settings_t, destroy, void,
942 private_settings_t *this)
943 {
944 settings_section_destroy(this->top, NULL);
945 array_destroy_function(this->contents, (void*)free, NULL);
946 this->lock->destroy(this->lock);
947 free(this);
948 }
949
950 /*
951 * see header file
952 */
953 settings_t *settings_create(char *file)
954 {
955 private_settings_t *this;
956
957 INIT(this,
958 .public = {
959 .get_str = _get_str,
960 .get_int = _get_int,
961 .get_double = _get_double,
962 .get_time = _get_time,
963 .get_bool = _get_bool,
964 .set_str = _set_str,
965 .set_int = _set_int,
966 .set_double = _set_double,
967 .set_time = _set_time,
968 .set_bool = _set_bool,
969 .set_default_str = _set_default_str,
970 .create_section_enumerator = _create_section_enumerator,
971 .create_key_value_enumerator = _create_key_value_enumerator,
972 .add_fallback = _add_fallback,
973 .load_files = _load_files,
974 .load_files_section = _load_files_section,
975 .destroy = _destroy,
976 },
977 .top = settings_section_create(NULL),
978 .contents = array_create(0, 0),
979 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
980 );
981
982 load_files(this, file, FALSE);
983
984 return &this->public;
985 }