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