settings: Use generated parser instead of our own
[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 array_insert_create(&section->sections, ARRAY_TAIL, found);
180 array_sort(section->sections, settings_section_sort, NULL);
181 }
182 }
183 if (found && pos)
184 {
185 return find_section_buffered(found, start, pos, args, buf, len, ensure);
186 }
187 return found;
188 }
189
190 /**
191 * Find all sections via a given key considering fallbacks, using buffered key,
192 * reusable buffer.
193 */
194 static void find_sections_buffered(section_t *section, char *start, char *key,
195 va_list args, char *buf, int len, array_t **sections)
196 {
197 section_t *found = NULL, *fallback;
198 char *pos;
199 int i;
200
201 if (!section)
202 {
203 return;
204 }
205 pos = strchr(key, '.');
206 if (pos)
207 {
208 *pos = '\0';
209 }
210 if (!print_key(buf, len, start, key, args))
211 {
212 return;
213 }
214 if (pos)
215 { /* restore so we can follow fallbacks */
216 *pos = '.';
217 }
218 if (!strlen(buf))
219 {
220 found = section;
221 }
222 else
223 {
224 array_bsearch(section->sections, buf, settings_section_find, &found);
225 }
226 if (found)
227 {
228 if (pos)
229 {
230 find_sections_buffered(found, start, pos+1, args, buf, len,
231 sections);
232 }
233 else
234 {
235 array_insert_create(sections, ARRAY_TAIL, found);
236 for (i = 0; i < array_count(found->fallbacks); i++)
237 {
238 array_get(found->fallbacks, i, &fallback);
239 array_insert_create(sections, ARRAY_TAIL, fallback);
240 }
241 }
242 }
243 if (section->fallbacks)
244 {
245 for (i = 0; i < array_count(section->fallbacks); i++)
246 {
247 array_get(section->fallbacks, i, &fallback);
248 find_sections_buffered(fallback, start, key, args, buf, len,
249 sections);
250 }
251 }
252 }
253
254 /**
255 * Ensure that the section with the given key exists (thread-safe).
256 */
257 static section_t *ensure_section(private_settings_t *this, section_t *section,
258 const char *key, va_list args)
259 {
260 char buf[128], keybuf[512];
261 section_t *found;
262
263 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
264 {
265 return NULL;
266 }
267 /* we might have to change the tree */
268 this->lock->write_lock(this->lock);
269 found = find_section_buffered(section, keybuf, keybuf, args, buf,
270 sizeof(buf), TRUE);
271 this->lock->unlock(this->lock);
272 return found;
273 }
274
275 /**
276 * Find a section by a given key with its fallbacks (not thread-safe!).
277 * Sections are returned in depth-first order (array is allocated). NULL is
278 * returned if no sections are found.
279 */
280 static array_t *find_sections(private_settings_t *this, section_t *section,
281 char *key, va_list args)
282 {
283 char buf[128], keybuf[512];
284 array_t *sections = NULL;
285
286 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
287 {
288 return NULL;
289 }
290 find_sections_buffered(section, keybuf, keybuf, args, buf,
291 sizeof(buf), &sections);
292 return sections;
293 }
294
295 /**
296 * Check if the given fallback section already exists
297 */
298 static bool fallback_exists(section_t *section, section_t *fallback)
299 {
300 if (section == fallback)
301 {
302 return TRUE;
303 }
304 else if (section->fallbacks)
305 {
306 section_t *existing;
307 int i;
308
309 for (i = 0; i < array_count(section->fallbacks); i++)
310 {
311 array_get(section->fallbacks, i, &existing);
312 if (existing == fallback)
313 {
314 return TRUE;
315 }
316 }
317 }
318 return FALSE;
319 }
320
321 /**
322 * Ensure that the section with the given key exists and add the given fallback
323 * section (thread-safe).
324 */
325 static void add_fallback_to_section(private_settings_t *this,
326 section_t *section, const char *key, va_list args,
327 section_t *fallback)
328 {
329 char buf[128], keybuf[512];
330 section_t *found;
331
332 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
333 {
334 return;
335 }
336 this->lock->write_lock(this->lock);
337 found = find_section_buffered(section, keybuf, keybuf, args, buf,
338 sizeof(buf), TRUE);
339 if (!fallback_exists(found, fallback))
340 {
341 /* to ensure sections referred to as fallback are not purged, we create
342 * the array there too */
343 if (!fallback->fallbacks)
344 {
345 fallback->fallbacks = array_create(0, 0);
346 }
347 array_insert_create(&found->fallbacks, ARRAY_TAIL, fallback);
348 }
349 this->lock->unlock(this->lock);
350 }
351
352 /**
353 * Find the key/value pair for a key, using buffered key, reusable buffer
354 * If "ensure" is TRUE, the sections (and key/value pair) are created if they
355 * don't exist.
356 * Fallbacks are only considered if "ensure" is FALSE.
357 */
358 static kv_t *find_value_buffered(section_t *section, char *start, char *key,
359 va_list args, char *buf, int len, bool ensure)
360 {
361 int i;
362 char *pos;
363 kv_t *kv = NULL;
364 section_t *found = NULL;
365
366 if (section == NULL)
367 {
368 return NULL;
369 }
370
371 pos = strchr(key, '.');
372 if (pos)
373 {
374 *pos = '\0';
375 if (!print_key(buf, len, start, key, args))
376 {
377 return NULL;
378 }
379 /* restore so we can retry for fallbacks */
380 *pos = '.';
381 if (!strlen(buf))
382 {
383 found = section;
384 }
385 else if (array_bsearch(section->sections, buf, settings_section_find,
386 &found) == -1)
387 {
388 if (ensure)
389 {
390 found = settings_section_create(strdup(buf));
391 array_insert_create(&section->sections, ARRAY_TAIL, found);
392 array_sort(section->sections, settings_section_sort, NULL);
393 }
394 }
395 if (found)
396 {
397 kv = find_value_buffered(found, start, pos+1, args, buf, len,
398 ensure);
399 }
400 if (!kv && !ensure && section->fallbacks)
401 {
402 for (i = 0; !kv && i < array_count(section->fallbacks); i++)
403 {
404 array_get(section->fallbacks, i, &found);
405 kv = find_value_buffered(found, start, key, args, buf, len,
406 ensure);
407 }
408 }
409 }
410 else
411 {
412 if (!print_key(buf, len, start, key, args))
413 {
414 return NULL;
415 }
416 if (array_bsearch(section->kv, buf, settings_kv_find, &kv) == -1)
417 {
418 if (ensure)
419 {
420 kv = settings_kv_create(strdup(buf), NULL);
421 array_insert_create(&section->kv, ARRAY_TAIL, kv);
422 array_sort(section->kv, settings_kv_sort, NULL);
423 }
424 else if (section->fallbacks)
425 {
426 for (i = 0; !kv && i < array_count(section->fallbacks); i++)
427 {
428 array_get(section->fallbacks, i, &found);
429 kv = find_value_buffered(found, start, key, args, buf, len,
430 ensure);
431 }
432 }
433 }
434 }
435 return kv;
436 }
437
438 /**
439 * Find the string value for a key (thread-safe).
440 */
441 static char *find_value(private_settings_t *this, section_t *section,
442 char *key, va_list args)
443 {
444 char buf[128], keybuf[512], *value = NULL;
445 kv_t *kv;
446
447 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
448 {
449 return NULL;
450 }
451 this->lock->read_lock(this->lock);
452 kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
453 FALSE);
454 if (kv)
455 {
456 value = kv->value;
457 }
458 this->lock->unlock(this->lock);
459 return value;
460 }
461
462 /**
463 * Set a value to a copy of the given string (thread-safe).
464 */
465 static void set_value(private_settings_t *this, section_t *section,
466 char *key, va_list args, char *value)
467 {
468 char buf[128], keybuf[512];
469 kv_t *kv;
470
471 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
472 {
473 return;
474 }
475 this->lock->write_lock(this->lock);
476 kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
477 TRUE);
478 if (kv)
479 {
480 if (!value)
481 {
482 if (kv->value)
483 {
484 array_insert(this->contents, ARRAY_TAIL, kv->value);
485 }
486 kv->value = NULL;
487 }
488 else if (kv->value && (strlen(value) <= strlen(kv->value)))
489 { /* overwrite in-place, if possible */
490 strcpy(kv->value, value);
491 }
492 else
493 { /* otherwise clone the string and cache the replaced one */
494 if (kv->value)
495 {
496 array_insert(this->contents, ARRAY_TAIL, kv->value);
497 }
498 kv->value = strdup(value);
499 }
500 }
501 this->lock->unlock(this->lock);
502 }
503
504 METHOD(settings_t, get_str, char*,
505 private_settings_t *this, char *key, char *def, ...)
506 {
507 char *value;
508 va_list args;
509
510 va_start(args, def);
511 value = find_value(this, this->top, key, args);
512 va_end(args);
513 if (value)
514 {
515 return value;
516 }
517 return def;
518 }
519
520 /**
521 * Described in header
522 */
523 inline bool settings_value_as_bool(char *value, bool def)
524 {
525 if (value)
526 {
527 if (strcaseeq(value, "1") ||
528 strcaseeq(value, "yes") ||
529 strcaseeq(value, "true") ||
530 strcaseeq(value, "enabled"))
531 {
532 return TRUE;
533 }
534 else if (strcaseeq(value, "0") ||
535 strcaseeq(value, "no") ||
536 strcaseeq(value, "false") ||
537 strcaseeq(value, "disabled"))
538 {
539 return FALSE;
540 }
541 }
542 return def;
543 }
544
545 METHOD(settings_t, get_bool, bool,
546 private_settings_t *this, char *key, bool def, ...)
547 {
548 char *value;
549 va_list args;
550
551 va_start(args, def);
552 value = find_value(this, this->top, key, args);
553 va_end(args);
554 return settings_value_as_bool(value, def);
555 }
556
557 /**
558 * Described in header
559 */
560 inline int settings_value_as_int(char *value, int def)
561 {
562 int intval;
563 if (value)
564 {
565 errno = 0;
566 intval = strtol(value, NULL, 10);
567 if (errno == 0)
568 {
569 return intval;
570 }
571 }
572 return def;
573 }
574
575 METHOD(settings_t, get_int, int,
576 private_settings_t *this, char *key, int def, ...)
577 {
578 char *value;
579 va_list args;
580
581 va_start(args, def);
582 value = find_value(this, this->top, key, args);
583 va_end(args);
584 return settings_value_as_int(value, def);
585 }
586
587 /**
588 * Described in header
589 */
590 inline double settings_value_as_double(char *value, double def)
591 {
592 double dval;
593 if (value)
594 {
595 errno = 0;
596 dval = strtod(value, NULL);
597 if (errno == 0)
598 {
599 return dval;
600 }
601 }
602 return def;
603 }
604
605 METHOD(settings_t, get_double, double,
606 private_settings_t *this, char *key, double def, ...)
607 {
608 char *value;
609 va_list args;
610
611 va_start(args, def);
612 value = find_value(this, this->top, key, args);
613 va_end(args);
614 return settings_value_as_double(value, def);
615 }
616
617 /**
618 * Described in header
619 */
620 inline u_int32_t settings_value_as_time(char *value, u_int32_t def)
621 {
622 char *endptr;
623 u_int32_t timeval;
624 if (value)
625 {
626 errno = 0;
627 timeval = strtoul(value, &endptr, 10);
628 if (errno == 0)
629 {
630 switch (*endptr)
631 {
632 case 'd': /* time in days */
633 timeval *= 24 * 3600;
634 break;
635 case 'h': /* time in hours */
636 timeval *= 3600;
637 break;
638 case 'm': /* time in minutes */
639 timeval *= 60;
640 break;
641 case 's': /* time in seconds */
642 default:
643 break;
644 }
645 return timeval;
646 }
647 }
648 return def;
649 }
650
651 METHOD(settings_t, get_time, u_int32_t,
652 private_settings_t *this, char *key, u_int32_t def, ...)
653 {
654 char *value;
655 va_list args;
656
657 va_start(args, def);
658 value = find_value(this, this->top, key, args);
659 va_end(args);
660 return settings_value_as_time(value, def);
661 }
662
663 METHOD(settings_t, set_str, void,
664 private_settings_t *this, char *key, char *value, ...)
665 {
666 va_list args;
667 va_start(args, value);
668 set_value(this, this->top, key, args, value);
669 va_end(args);
670 }
671
672 METHOD(settings_t, set_bool, void,
673 private_settings_t *this, char *key, bool value, ...)
674 {
675 va_list args;
676 va_start(args, value);
677 set_value(this, this->top, key, args, value ? "1" : "0");
678 va_end(args);
679 }
680
681 METHOD(settings_t, set_int, void,
682 private_settings_t *this, char *key, int value, ...)
683 {
684 char val[16];
685 va_list args;
686 va_start(args, value);
687 if (snprintf(val, sizeof(val), "%d", value) < sizeof(val))
688 {
689 set_value(this, this->top, key, args, val);
690 }
691 va_end(args);
692 }
693
694 METHOD(settings_t, set_double, void,
695 private_settings_t *this, char *key, double value, ...)
696 {
697 char val[64];
698 va_list args;
699 va_start(args, value);
700 if (snprintf(val, sizeof(val), "%f", value) < sizeof(val))
701 {
702 set_value(this, this->top, key, args, val);
703 }
704 va_end(args);
705 }
706
707 METHOD(settings_t, set_time, void,
708 private_settings_t *this, char *key, u_int32_t value, ...)
709 {
710 char val[16];
711 va_list args;
712 va_start(args, value);
713 if (snprintf(val, sizeof(val), "%u", value) < sizeof(val))
714 {
715 set_value(this, this->top, key, args, val);
716 }
717 va_end(args);
718 }
719
720 METHOD(settings_t, set_default_str, bool,
721 private_settings_t *this, char *key, char *value, ...)
722 {
723 char *old;
724 va_list args;
725
726 va_start(args, value);
727 old = find_value(this, this->top, key, args);
728 va_end(args);
729
730 if (!old)
731 {
732 va_start(args, value);
733 set_value(this, this->top, key, args, value);
734 va_end(args);
735 return TRUE;
736 }
737 return FALSE;
738 }
739
740 /**
741 * Data for enumerators
742 */
743 typedef struct {
744 /** settings_t instance */
745 private_settings_t *settings;
746 /** sections to enumerate */
747 array_t *sections;
748 /** sections/keys that were already enumerated */
749 hashtable_t *seen;
750 } enumerator_data_t;
751
752 /**
753 * Destroy enumerator data
754 */
755 static void enumerator_destroy(enumerator_data_t *this)
756 {
757 this->settings->lock->unlock(this->settings->lock);
758 this->seen->destroy(this->seen);
759 array_destroy(this->sections);
760 free(this);
761 }
762
763 /**
764 * Enumerate section names, not sections
765 */
766 static bool section_filter(hashtable_t *seen, section_t **in, char **out)
767 {
768 *out = (*in)->name;
769 if (seen->get(seen, *out))
770 {
771 return FALSE;
772 }
773 seen->put(seen, *out, *out);
774 return TRUE;
775 }
776
777 /**
778 * Enumerate sections of the given section
779 */
780 static enumerator_t *section_enumerator(section_t *section,
781 enumerator_data_t *data)
782 {
783 return enumerator_create_filter(array_create_enumerator(section->sections),
784 (void*)section_filter, data->seen, NULL);
785 }
786
787 METHOD(settings_t, create_section_enumerator, enumerator_t*,
788 private_settings_t *this, char *key, ...)
789 {
790 enumerator_data_t *data;
791 array_t *sections;
792 va_list args;
793
794 this->lock->read_lock(this->lock);
795 va_start(args, key);
796 sections = find_sections(this, this->top, key, args);
797 va_end(args);
798
799 if (!sections)
800 {
801 this->lock->unlock(this->lock);
802 return enumerator_create_empty();
803 }
804 INIT(data,
805 .settings = this,
806 .sections = sections,
807 .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
808 );
809 return enumerator_create_nested(array_create_enumerator(sections),
810 (void*)section_enumerator, data, (void*)enumerator_destroy);
811 }
812
813 /**
814 * Enumerate key and values, not kv_t entries
815 */
816 static bool kv_filter(hashtable_t *seen, kv_t **in, char **key,
817 void *none, char **value)
818 {
819 *key = (*in)->key;
820 if (seen->get(seen, *key))
821 {
822 return FALSE;
823 }
824 *value = (*in)->value;
825 seen->put(seen, *key, *key);
826 return TRUE;
827 }
828
829 /**
830 * Enumerate key/value pairs of the given section
831 */
832 static enumerator_t *kv_enumerator(section_t *section, enumerator_data_t *data)
833 {
834 return enumerator_create_filter(array_create_enumerator(section->kv),
835 (void*)kv_filter, data->seen, NULL);
836 }
837
838 METHOD(settings_t, create_key_value_enumerator, enumerator_t*,
839 private_settings_t *this, char *key, ...)
840 {
841 enumerator_data_t *data;
842 array_t *sections;
843 va_list args;
844
845 this->lock->read_lock(this->lock);
846 va_start(args, key);
847 sections = find_sections(this, this->top, key, args);
848 va_end(args);
849
850 if (!sections)
851 {
852 this->lock->unlock(this->lock);
853 return enumerator_create_empty();
854 }
855 INIT(data,
856 .settings = this,
857 .sections = sections,
858 .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
859 );
860 return enumerator_create_nested(array_create_enumerator(sections),
861 (void*)kv_enumerator, data, (void*)enumerator_destroy);
862 }
863
864 METHOD(settings_t, add_fallback, void,
865 private_settings_t *this, const char *key, const char *fallback, ...)
866 {
867 section_t *section;
868 va_list args;
869
870 /* find/create the fallback */
871 va_start(args, fallback);
872 section = ensure_section(this, this->top, fallback, args);
873 va_end(args);
874
875 va_start(args, fallback);
876 add_fallback_to_section(this, this->top, key, args, section);
877 va_end(args);
878 }
879
880 /**
881 * Load settings from files matching the given file pattern.
882 * All sections and values are added relative to "parent".
883 * All files (even included ones) have to be loaded successfully.
884 * If merge is FALSE the contents of parent are replaced with the parsed
885 * contents, otherwise they are merged together.
886 */
887 static bool load_files_internal(private_settings_t *this, section_t *parent,
888 char *pattern, bool merge)
889 {
890 section_t *section;
891
892 if (pattern == NULL)
893 {
894 #ifdef STRONGSWAN_CONF
895 pattern = STRONGSWAN_CONF;
896 #else
897 return FALSE;
898 #endif
899 }
900
901 section = settings_section_create(NULL);
902 if (!settings_parser_parse_file(section, pattern))
903 {
904 settings_section_destroy(section, NULL);
905 return FALSE;
906 }
907
908 this->lock->write_lock(this->lock);
909 if (!merge)
910 {
911 section_purge(parent, this->contents);
912 }
913 /* extend parent section */
914 settings_section_extend(parent, section, this->contents);
915 this->lock->unlock(this->lock);
916
917 settings_section_destroy(section, NULL);
918 return TRUE;
919 }
920
921 METHOD(settings_t, load_files, bool,
922 private_settings_t *this, char *pattern, bool merge)
923 {
924 return load_files_internal(this, this->top, pattern, merge);
925 }
926
927 METHOD(settings_t, load_files_section, bool,
928 private_settings_t *this, char *pattern, bool merge, char *key, ...)
929 {
930 section_t *section;
931 va_list args;
932
933 va_start(args, key);
934 section = ensure_section(this, this->top, key, args);
935 va_end(args);
936
937 if (!section)
938 {
939 return FALSE;
940 }
941 return load_files_internal(this, section, pattern, merge);
942 }
943
944 METHOD(settings_t, destroy, void,
945 private_settings_t *this)
946 {
947 settings_section_destroy(this->top, NULL);
948 array_destroy_function(this->contents, (void*)free, NULL);
949 this->lock->destroy(this->lock);
950 free(this);
951 }
952
953 /*
954 * see header file
955 */
956 settings_t *settings_create(char *file)
957 {
958 private_settings_t *this;
959
960 INIT(this,
961 .public = {
962 .get_str = _get_str,
963 .get_int = _get_int,
964 .get_double = _get_double,
965 .get_time = _get_time,
966 .get_bool = _get_bool,
967 .set_str = _set_str,
968 .set_int = _set_int,
969 .set_double = _set_double,
970 .set_time = _set_time,
971 .set_bool = _set_bool,
972 .set_default_str = _set_default_str,
973 .create_section_enumerator = _create_section_enumerator,
974 .create_key_value_enumerator = _create_key_value_enumerator,
975 .add_fallback = _add_fallback,
976 .load_files = _load_files,
977 .load_files_section = _load_files_section,
978 .destroy = _destroy,
979 },
980 .top = settings_section_create(NULL),
981 .contents = array_create(0, 0),
982 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
983 );
984
985 load_files(this, file, FALSE);
986
987 return &this->public;
988 }