Coverage Report

Created: 2025-12-14 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/strongswan/src/libstrongswan/settings/settings.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2010-2018 Tobias Brunner
3
 * Copyright (C) 2008 Martin Willi
4
 *
5
 * Copyright (C) secunet Security Networks AG
6
 *
7
 * This program is free software; you can redistribute it and/or modify it
8
 * under the terms of the GNU General Public License as published by the
9
 * Free Software Foundation; either version 2 of the License, or (at your
10
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15
 * for more details.
16
 */
17
18
#define _GNU_SOURCE
19
#include <string.h>
20
#include <stdarg.h>
21
#include <stdio.h>
22
#include <errno.h>
23
#include <limits.h>
24
#include <sys/types.h>
25
#include <sys/stat.h>
26
#include <unistd.h>
27
#include <ctype.h>
28
29
#include "settings.h"
30
#include "settings_types.h"
31
32
#include "collections/array.h"
33
#include "collections/hashtable.h"
34
#include "collections/linked_list.h"
35
#include "threading/rwlock.h"
36
#include "utils/debug.h"
37
38
typedef struct private_settings_t private_settings_t;
39
40
/**
41
 * Parse functions provided by the generated parser.
42
 */
43
bool settings_parser_parse_file(section_t *root, char *name);
44
bool settings_parser_parse_string(section_t *root, char *settings);
45
46
/**
47
 * Buffer size for complete key name/pattern when looking up settings.
48
 */
49
#define KEY_FULL_BUF_LEN 2048
50
51
/**
52
 * Buffer size for a single part of a key name (matches the maximum length
53
 * for names in VICI).
54
 */
55
#define KEY_PART_BUF_LEN 257
56
57
/**
58
 * Private data of settings
59
 */
60
struct private_settings_t {
61
62
  /**
63
   * Public interface
64
   */
65
  settings_t public;
66
67
  /**
68
   * Top level section
69
   */
70
  section_t *top;
71
72
  /**
73
   * Contents of replaced settings (char*)
74
   *
75
   * FIXME: This is required because the pointer returned by get_str()
76
   * is not refcounted.  Might cause ever increasing usage stats.
77
   */
78
  array_t *contents;
79
80
  /**
81
   * Lock to safely access the settings
82
   */
83
  rwlock_t *lock;
84
};
85
86
/**
87
 * Print a format key, but consume already processed arguments
88
 * Note that key and start point into the same string
89
 */
90
static bool print_key(char *buf, int len, char *start, char *key, va_list args)
91
81.7k
{
92
81.7k
  va_list copy;
93
81.7k
  char *pos = start;
94
81.7k
  bool res;
95
96
81.7k
  va_copy(copy, args);
97
81.7k
  while (TRUE)
98
108k
  {
99
108k
    pos = memchr(pos, '%', key - pos);
100
108k
    if (!pos)
101
81.7k
    {
102
81.7k
      break;
103
81.7k
    }
104
26.6k
    pos++;
105
26.6k
    switch (*pos)
106
26.6k
    {
107
0
      case 'd':
108
0
        va_arg(copy, int);
109
0
        break;
110
26.6k
      case 's':
111
26.6k
        va_arg(copy, char*);
112
26.6k
        break;
113
0
      case 'N':
114
0
        va_arg(copy, enum_name_t*);
115
0
        va_arg(copy, int);
116
0
        break;
117
0
      case '%':
118
0
        break;
119
0
      default:
120
0
        DBG1(DBG_CFG, "settings with %%%c not supported!", *pos);
121
0
        break;
122
26.6k
    }
123
26.6k
    pos++;
124
26.6k
  }
125
81.7k
  res = vsnprintf(buf, len, key, copy) < len;
126
81.7k
  va_end(copy);
127
81.7k
  return res;
128
81.7k
}
129
130
/**
131
 * Check if the given section is contained in the given array.
132
 */
133
static bool has_section(array_t *array, section_t *section)
134
0
{
135
0
  section_t *current;
136
0
  int i;
137
138
0
  for (i = 0; i < array_count(array); i++)
139
0
  {
140
0
    array_get(array, i, &current);
141
0
    if (current == section)
142
0
    {
143
0
      return TRUE;
144
0
    }
145
0
  }
146
0
  return FALSE;
147
0
}
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
1.87k
{
157
1.87k
  char *pos;
158
1.87k
  section_t *found = NULL;
159
160
1.87k
  if (section == NULL)
161
0
  {
162
0
    return NULL;
163
0
  }
164
1.87k
  pos = strchr(key, '.');
165
1.87k
  if (pos)
166
0
  {
167
0
    *pos = '\0';
168
0
    pos++;
169
0
  }
170
1.87k
  if (!print_key(buf, len, start, key, args))
171
0
  {
172
0
    return NULL;
173
0
  }
174
1.87k
  if (!strlen(buf))
175
0
  {
176
0
    found = section;
177
0
  }
178
1.87k
  else if (array_bsearch(section->sections, buf, settings_section_find,
179
1.87k
               &found) == -1)
180
1.87k
  {
181
1.87k
    if (ensure)
182
1.87k
    {
183
1.87k
      found = settings_section_create(strdup(buf));
184
1.87k
      settings_section_add(section, found, NULL);
185
1.87k
    }
186
1.87k
  }
187
1.87k
  if (found && pos)
188
0
  {
189
0
    return find_section_buffered(found, start, pos, args, buf, len, ensure);
190
0
  }
191
1.87k
  return found;
192
1.87k
}
193
194
/**
195
 * Forward declaration
196
 */
197
static array_t *find_sections(private_settings_t *this, section_t *section,
198
                char *key, va_list args, array_t **sections);
199
200
/**
201
 * Resolve the given reference. Not thread-safe.
202
 * Only a vararg function to get an empty va_list.
203
 */
204
static void resolve_reference(private_settings_t *this, section_ref_t *ref,
205
            array_t **sections, ...)
206
26.6k
{
207
26.6k
  va_list args;
208
209
26.6k
  va_start(args, sections);
210
26.6k
  find_sections(this, this->top, ref->name, args, sections);
211
26.6k
  va_end(args);
212
26.6k
}
213
214
/**
215
 * Find all sections via a given key considering references, using buffered key,
216
 * reusable buffer.
217
 */
218
static void find_sections_buffered(private_settings_t *this, section_t *section,
219
                   char *start, char *key, va_list args,
220
                   char *buf, int len, bool ignore_refs,
221
                   array_t **sections)
222
26.6k
{
223
26.6k
  section_t *found = NULL, *reference;
224
26.6k
  array_t *references;
225
26.6k
  section_ref_t *ref;
226
26.6k
  char *pos;
227
26.6k
  int i, j;
228
229
26.6k
  if (!section)
230
0
  {
231
0
    return;
232
0
  }
233
26.6k
  pos = strchr(key, '.');
234
26.6k
  if (pos)
235
0
  {
236
0
    *pos = '\0';
237
0
  }
238
26.6k
  if (!print_key(buf, len, start, key, args))
239
0
  {
240
0
    return;
241
0
  }
242
26.6k
  if (pos)
243
0
  { /* restore so we can follow references */
244
0
    *pos = '.';
245
0
  }
246
26.6k
  if (!strlen(buf))
247
0
  {
248
0
    found = section;
249
0
  }
250
26.6k
  else
251
26.6k
  {
252
26.6k
    array_bsearch(section->sections, buf, settings_section_find, &found);
253
26.6k
  }
254
26.6k
  if (found)
255
0
  {
256
0
    if (pos)
257
0
    {
258
0
      find_sections_buffered(this, found, start, pos+1, args, buf, len,
259
0
                   FALSE, sections);
260
0
    }
261
0
    else if (!has_section(*sections, found))
262
0
    {
263
      /* ignore if already added to avoid loops */
264
0
      array_insert_create(sections, ARRAY_TAIL, found);
265
      /* add all sections that are referenced here (also resolves
266
       * references in parent sections of the referenced section) */
267
0
      for (i = 0; i < array_count(found->references); i++)
268
0
      {
269
0
        array_get(found->references, i, &ref);
270
0
        resolve_reference(this, ref, sections);
271
0
      }
272
0
    }
273
0
  }
274
26.6k
  if (!ignore_refs && section != found && section->references)
275
0
  {
276
    /* find matching sub-sections relative to the referenced sections */
277
0
    for (i = 0; i < array_count(section->references); i++)
278
0
    {
279
0
      array_get(section->references, i, &ref);
280
0
      references = NULL;
281
0
      resolve_reference(this, ref, &references);
282
0
      for (j = 0; j < array_count(references); j++)
283
0
      {
284
0
        array_get(references, j, &reference);
285
        /* ignore references in this referenced section, they were
286
         * resolved via resolve_reference() */
287
0
        find_sections_buffered(this, reference, start, key, args,
288
0
                     buf, len, TRUE, sections);
289
0
      }
290
0
      array_destroy(references);
291
0
    }
292
0
  }
293
26.6k
}
294
295
/**
296
 * Ensure that the section with the given key exists (not thread-safe).
297
 */
298
static section_t *ensure_section(private_settings_t *this, section_t *section,
299
                 const char *key, va_list args)
300
1.87k
{
301
1.87k
  char buf[KEY_PART_BUF_LEN], keybuf[KEY_FULL_BUF_LEN];
302
303
1.87k
  if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
304
0
  {
305
0
    return NULL;
306
0
  }
307
1.87k
  return find_section_buffered(section, keybuf, keybuf, args, buf,
308
1.87k
                 sizeof(buf), TRUE);
309
1.87k
}
310
311
/**
312
 * Find a section by a given key with resolved references (not thread-safe!).
313
 * The array is allocated. NULL is returned if no sections are found.
314
 */
315
static array_t *find_sections(private_settings_t *this, section_t *section,
316
                char *key, va_list args, array_t **sections)
317
26.6k
{
318
26.6k
  char buf[KEY_PART_BUF_LEN], keybuf[KEY_FULL_BUF_LEN];
319
320
26.6k
  if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
321
0
  {
322
0
    return NULL;
323
0
  }
324
26.6k
  find_sections_buffered(this, section, keybuf, keybuf, args, buf,
325
26.6k
               sizeof(buf), FALSE, sections);
326
26.6k
  return *sections;
327
26.6k
}
328
329
/**
330
 * Find the key/value pair for a key, using buffered key, reusable buffer
331
 * There are two modes: 1. To find a key at an exact location and create the
332
 * sections (and key/value pair) if necessary, don't pass an array for sections.
333
 * 2. To find a key and follow references pass a pointer to an array to store
334
 * visited sections. NULL is returned in this case if the key is not found.
335
 */
336
static kv_t *find_value_buffered(private_settings_t *this, section_t *section,
337
                 char *start, char *key, va_list args,
338
                 char *buf, int len, bool ignore_refs,
339
                 array_t **sections)
340
53.2k
{
341
53.2k
  section_t *found = NULL;
342
53.2k
  kv_t *kv = NULL;
343
53.2k
  section_ref_t *ref;
344
53.2k
  array_t *references;
345
53.2k
  char *pos;
346
53.2k
  int i, j;
347
348
53.2k
  if (!section)
349
0
  {
350
0
    return NULL;
351
0
  }
352
53.2k
  pos = strchr(key, '.');
353
53.2k
  if (pos)
354
45.3k
  {
355
45.3k
    *pos = '\0';
356
45.3k
    if (!print_key(buf, len, start, key, args))
357
0
    {
358
0
      return NULL;
359
0
    }
360
    /* restore so we can follow references */
361
45.3k
    *pos = '.';
362
45.3k
    if (!strlen(buf))
363
0
    {
364
0
      found = section;
365
0
    }
366
45.3k
    else if (array_bsearch(section->sections, buf, settings_section_find,
367
45.3k
                 &found) == -1)
368
18.7k
    {
369
18.7k
      if (!sections)
370
0
      {
371
0
        found = settings_section_create(strdup(buf));
372
0
        settings_section_add(section, found, NULL);
373
0
      }
374
18.7k
    }
375
45.3k
    if (found)
376
26.6k
    {
377
26.6k
      kv = find_value_buffered(this, found, start, pos+1, args, buf, len,
378
26.6k
                   FALSE, sections);
379
26.6k
    }
380
45.3k
  }
381
7.89k
  else
382
7.89k
  {
383
7.89k
    if (sections)
384
7.89k
    {
385
7.89k
      array_insert_create(sections, ARRAY_TAIL, section);
386
7.89k
    }
387
7.89k
    if (!print_key(buf, len, start, key, args))
388
0
    {
389
0
      return NULL;
390
0
    }
391
7.89k
    if (array_bsearch(section->kv, buf, settings_kv_find, &kv) == -1)
392
7.89k
    {
393
7.89k
      if (!sections)
394
0
      {
395
0
        kv = settings_kv_create(strdup(buf), NULL);
396
0
        settings_kv_add(section, kv, NULL);
397
0
      }
398
7.89k
    }
399
7.89k
  }
400
53.2k
  if (!kv && !ignore_refs && sections && section->references)
401
26.6k
  {
402
    /* find key relative to the referenced sections */
403
53.2k
    for (i = 0; !kv && i < array_count(section->references); i++)
404
26.6k
    {
405
26.6k
      array_get(section->references, i, &ref);
406
26.6k
      references = NULL;
407
26.6k
      resolve_reference(this, ref, &references);
408
26.6k
      for (j = 0; !kv && j < array_count(references); j++)
409
0
      {
410
0
        array_get(references, j, &found);
411
        /* ignore if already added to avoid loops */
412
0
        if (!has_section(*sections, found))
413
0
        {
414
          /* ignore references in this referenced section, they were
415
           * resolved via resolve_reference() */
416
0
          kv = find_value_buffered(this, found, start, key, args,
417
0
                       buf, len, TRUE, sections);
418
0
        }
419
0
      }
420
26.6k
      array_destroy(references);
421
26.6k
    }
422
26.6k
  }
423
53.2k
  return kv;
424
53.2k
}
425
426
/**
427
 * Remove the key/value pair for a key, using buffered key, reusable buffer
428
 */
429
static void remove_value_buffered(private_settings_t *this, section_t *section,
430
                  char *start, char *key, va_list args,
431
                  char *buf, int len)
432
0
{
433
0
  section_t *found = NULL;
434
0
  kv_t *kv = NULL, *ordered = NULL;
435
0
  char *pos;
436
0
  int idx, i;
437
438
0
  if (!section)
439
0
  {
440
0
    return;
441
0
  }
442
0
  pos = strchr(key, '.');
443
0
  if (pos)
444
0
  {
445
0
    *pos = '\0';
446
0
    pos++;
447
0
  }
448
0
  if (!print_key(buf, len, start, key, args))
449
0
  {
450
0
    return;
451
0
  }
452
0
  if (!strlen(buf))
453
0
  {
454
0
    found = section;
455
0
  }
456
0
  if (pos)
457
0
  {
458
0
    if (array_bsearch(section->sections, buf, settings_section_find,
459
0
              &found) != -1)
460
0
    {
461
0
      remove_value_buffered(this, found, start, pos, args, buf, len);
462
0
    }
463
0
  }
464
0
  else
465
0
  {
466
0
    idx = array_bsearch(section->kv, buf, settings_kv_find, &kv);
467
0
    if (idx != -1)
468
0
    {
469
0
      array_remove(section->kv, idx, NULL);
470
0
      for (i = 0; i < array_count(section->kv_order); i++)
471
0
      {
472
0
        array_get(section->kv_order, i, &ordered);
473
0
        if (kv == ordered)
474
0
        {
475
0
          array_remove(section->kv_order, i, NULL);
476
0
          settings_kv_destroy(kv, this->contents);
477
0
          break;
478
0
        }
479
0
      }
480
0
    }
481
0
  }
482
0
}
483
484
/*
485
 * Described in header
486
 */
487
void settings_remove_value(settings_t *settings, char *key, ...)
488
0
{
489
0
  private_settings_t *this = (private_settings_t*)settings;
490
0
  char buf[KEY_PART_BUF_LEN], keybuf[KEY_FULL_BUF_LEN];
491
0
  va_list args;
492
493
0
  if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
494
0
  {
495
0
    return;
496
0
  }
497
0
  va_start(args, key);
498
499
0
  this->lock->read_lock(this->lock);
500
0
  remove_value_buffered(this, this->top, keybuf, keybuf, args, buf,
501
0
              sizeof(buf));
502
0
  this->lock->unlock(this->lock);
503
504
0
  va_end(args);
505
0
}
506
507
/**
508
 * Find the string value for a key (thread-safe).
509
 */
510
static char *find_value(private_settings_t *this, section_t *section,
511
            char *key, va_list args)
512
26.6k
{
513
26.6k
  char buf[KEY_PART_BUF_LEN], keybuf[KEY_FULL_BUF_LEN], *value = NULL;
514
26.6k
  array_t *sections = NULL;
515
26.6k
  kv_t *kv;
516
517
26.6k
  if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
518
0
  {
519
0
    return NULL;
520
0
  }
521
26.6k
  this->lock->read_lock(this->lock);
522
26.6k
  kv = find_value_buffered(this, section, keybuf, keybuf, args,
523
26.6k
               buf, sizeof(buf), FALSE, &sections);
524
26.6k
  if (kv)
525
0
  {
526
0
    value = kv->value;
527
0
  }
528
26.6k
  this->lock->unlock(this->lock);
529
26.6k
  array_destroy(sections);
530
26.6k
  return value;
531
26.6k
}
532
533
/**
534
 * Set a value to a copy of the given string (thread-safe).
535
 */
536
static void set_value(private_settings_t *this, section_t *section,
537
            char *key, va_list args, char *value)
538
0
{
539
0
  char buf[KEY_PART_BUF_LEN], keybuf[KEY_FULL_BUF_LEN];
540
0
  kv_t *kv;
541
542
0
  if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
543
0
  {
544
0
    return;
545
0
  }
546
0
  this->lock->write_lock(this->lock);
547
0
  kv = find_value_buffered(this, section, keybuf, keybuf, args,
548
0
               buf, sizeof(buf), FALSE, NULL);
549
0
  if (kv)
550
0
  {
551
0
    settings_kv_set(kv, strdupnull(value), this->contents);
552
0
  }
553
0
  this->lock->unlock(this->lock);
554
0
}
555
556
METHOD(settings_t, get_str, char*,
557
  private_settings_t *this, char *key, char *def, ...)
558
400
{
559
400
  char *value;
560
400
  va_list args;
561
562
400
  va_start(args, def);
563
400
  value = find_value(this, this->top, key, args);
564
400
  va_end(args);
565
400
  if (value)
566
0
  {
567
0
    return value;
568
0
  }
569
400
  return def;
570
400
}
571
572
/**
573
 * Described in header
574
 */
575
inline bool settings_value_as_bool(char *value, bool def)
576
18.7k
{
577
18.7k
  if (value)
578
0
  {
579
0
    if (strcaseeq(value, "1") ||
580
0
      strcaseeq(value, "yes") ||
581
0
      strcaseeq(value, "true") ||
582
0
      strcaseeq(value, "enabled"))
583
0
    {
584
0
      return TRUE;
585
0
    }
586
0
    else if (strcaseeq(value, "0") ||
587
0
         strcaseeq(value, "no") ||
588
0
         strcaseeq(value, "false") ||
589
0
         strcaseeq(value, "disabled"))
590
0
    {
591
0
      return FALSE;
592
0
    }
593
0
  }
594
18.7k
  return def;
595
18.7k
}
596
597
METHOD(settings_t, get_bool, bool,
598
  private_settings_t *this, char *key, int def, ...)
599
18.7k
{
600
18.7k
  char *value;
601
18.7k
  va_list args;
602
603
  /* we can't use bool for def due to this call */
604
18.7k
  va_start(args, def);
605
18.7k
  value = find_value(this, this->top, key, args);
606
18.7k
  va_end(args);
607
18.7k
  return settings_value_as_bool(value, def);
608
18.7k
}
609
610
/**
611
 * Described in header
612
 */
613
inline int settings_value_as_int(char *value, int def)
614
7.49k
{
615
7.49k
  int intval;
616
7.49k
  char *end;
617
7.49k
  int base = 10;
618
619
7.49k
  if (value)
620
0
  {
621
0
    errno = 0;
622
0
    if (value[0] == '0' && value[1] == 'x')
623
0
    { /* manually detect 0x prefix as we want to avoid octal encoding */
624
0
      base = 16;
625
0
    }
626
0
    intval = strtoul(value, &end, base);
627
0
    if (errno == 0 && *end == 0 && end != value)
628
0
    {
629
0
      return intval;
630
0
    }
631
0
  }
632
7.49k
  return def;
633
7.49k
}
634
635
METHOD(settings_t, get_int, int,
636
  private_settings_t *this, char *key, int def, ...)
637
7.49k
{
638
7.49k
  char *value;
639
7.49k
  va_list args;
640
641
7.49k
  va_start(args, def);
642
7.49k
  value = find_value(this, this->top, key, args);
643
7.49k
  va_end(args);
644
7.49k
  return settings_value_as_int(value, def);
645
7.49k
}
646
647
/**
648
 * Described in header
649
 */
650
inline uint64_t settings_value_as_uint64(char *value, uint64_t def)
651
0
{
652
0
  uint64_t intval;
653
0
  char *end;
654
0
  int base = 10;
655
656
0
  if (value)
657
0
  {
658
0
    errno = 0;
659
0
    if (value[0] == '0' && value[1] == 'x')
660
0
    { /* manually detect 0x prefix as we want to avoid octal encoding */
661
0
      base = 16;
662
0
    }
663
0
    intval = strtoull(value, &end, base);
664
0
    if (errno == 0 && *end == 0 && end != value)
665
0
    {
666
0
      return intval;
667
0
    }
668
0
  }
669
0
  return def;
670
0
}
671
672
/**
673
 * Described in header
674
 */
675
inline double settings_value_as_double(char *value, double def)
676
0
{
677
0
  double dval;
678
0
  char *end;
679
680
0
  if (value)
681
0
  {
682
0
    errno = 0;
683
0
    dval = strtod(value, &end);
684
0
    if (errno == 0 && *end == 0 && end != value)
685
0
    {
686
0
      return dval;
687
0
    }
688
0
  }
689
0
  return def;
690
0
}
691
692
METHOD(settings_t, get_double, double,
693
  private_settings_t *this, char *key, double def, ...)
694
0
{
695
0
  char *value;
696
0
  va_list args;
697
698
0
  va_start(args, def);
699
0
  value = find_value(this, this->top, key, args);
700
0
  va_end(args);
701
0
  return settings_value_as_double(value, def);
702
0
}
703
704
/**
705
 * Described in header
706
 */
707
inline uint32_t settings_value_as_time(char *value, uint32_t def)
708
0
{
709
0
  time_t val;
710
711
0
  if (timespan_from_string(value, NULL, &val))
712
0
  {
713
0
    return val;
714
0
  }
715
0
  return def;
716
0
}
717
718
METHOD(settings_t, get_time, uint32_t,
719
  private_settings_t *this, char *key, uint32_t def, ...)
720
0
{
721
0
  char *value;
722
0
  va_list args;
723
724
0
  va_start(args, def);
725
0
  value = find_value(this, this->top, key, args);
726
0
  va_end(args);
727
0
  return settings_value_as_time(value, def);
728
0
}
729
730
METHOD(settings_t, set_str, void,
731
  private_settings_t *this, char *key, char *value, ...)
732
0
{
733
0
  va_list args;
734
0
  va_start(args, value);
735
0
  set_value(this, this->top, key, args, value);
736
0
  va_end(args);
737
0
}
738
739
METHOD(settings_t, set_bool, void,
740
  private_settings_t *this, char *key, int value, ...)
741
0
{
742
0
  va_list args;
743
  /* we can't use bool for value due to this call */
744
0
  va_start(args, value);
745
0
  set_value(this, this->top, key, args, value ? "1" : "0");
746
0
  va_end(args);
747
0
}
748
749
METHOD(settings_t, set_int, void,
750
  private_settings_t *this, char *key, int value, ...)
751
0
{
752
0
  char val[16];
753
0
  va_list args;
754
0
  va_start(args, value);
755
0
  if (snprintf(val, sizeof(val), "%d", value) < sizeof(val))
756
0
  {
757
0
    set_value(this, this->top, key, args, val);
758
0
  }
759
0
  va_end(args);
760
0
}
761
762
METHOD(settings_t, set_double, void,
763
  private_settings_t *this, char *key, double value, ...)
764
0
{
765
0
  char val[64];
766
0
  va_list args;
767
0
  va_start(args, value);
768
0
  if (snprintf(val, sizeof(val), "%f", value) < sizeof(val))
769
0
  {
770
0
    set_value(this, this->top, key, args, val);
771
0
  }
772
0
  va_end(args);
773
0
}
774
775
METHOD(settings_t, set_time, void,
776
  private_settings_t *this, char *key, uint32_t value, ...)
777
0
{
778
0
  char val[16];
779
0
  va_list args;
780
0
  va_start(args, value);
781
0
  if (snprintf(val, sizeof(val), "%u", value) < sizeof(val))
782
0
  {
783
0
    set_value(this, this->top, key, args, val);
784
0
  }
785
0
  va_end(args);
786
0
}
787
788
METHOD(settings_t, set_default_str, bool,
789
  private_settings_t *this, char *key, char *value, ...)
790
0
{
791
0
  char *old;
792
0
  va_list args;
793
794
0
  va_start(args, value);
795
0
  old = find_value(this, this->top, key, args);
796
0
  va_end(args);
797
798
0
  if (!old)
799
0
  {
800
0
    va_start(args, value);
801
0
    set_value(this, this->top, key, args, value);
802
0
    va_end(args);
803
0
    return TRUE;
804
0
  }
805
0
  return FALSE;
806
0
}
807
808
/**
809
 * Data for enumerators
810
 */
811
typedef struct {
812
  /** settings_t instance */
813
  private_settings_t *settings;
814
  /** sections to enumerate */
815
  array_t *sections;
816
  /** sections/keys that were already enumerated */
817
  hashtable_t *seen;
818
} enumerator_data_t;
819
820
CALLBACK(enumerator_destroy, void,
821
  enumerator_data_t *this)
822
0
{
823
0
  this->settings->lock->unlock(this->settings->lock);
824
0
  this->seen->destroy(this->seen);
825
0
  array_destroy(this->sections);
826
0
  free(this);
827
0
}
828
829
CALLBACK(section_filter, bool,
830
  hashtable_t *seen, enumerator_t *orig, va_list args)
831
0
{
832
0
  section_t *section;
833
0
  char **out;
834
835
0
  VA_ARGS_VGET(args, out);
836
837
0
  while (orig->enumerate(orig, &section))
838
0
  {
839
0
    if (seen->get(seen, section->name))
840
0
    {
841
0
      continue;
842
0
    }
843
0
    *out = section->name;
844
0
    seen->put(seen, section->name, section->name);
845
0
    return TRUE;
846
0
  }
847
0
  return FALSE;
848
0
}
849
850
/**
851
 * Enumerate sections of the given section
852
 */
853
static enumerator_t *section_enumerator(section_t *section,
854
                    enumerator_data_t *data)
855
0
{
856
0
  return enumerator_create_filter(
857
0
              array_create_enumerator(section->sections_order),
858
0
              section_filter, data->seen, NULL);
859
0
}
860
861
METHOD(settings_t, create_section_enumerator, enumerator_t*,
862
  private_settings_t *this, char *key, ...)
863
0
{
864
0
  enumerator_data_t *data;
865
0
  array_t *sections = NULL;
866
0
  va_list args;
867
868
0
  this->lock->read_lock(this->lock);
869
0
  va_start(args, key);
870
0
  sections = find_sections(this, this->top, key, args, &sections);
871
0
  va_end(args);
872
873
0
  if (!sections)
874
0
  {
875
0
    this->lock->unlock(this->lock);
876
0
    return enumerator_create_empty();
877
0
  }
878
0
  INIT(data,
879
0
    .settings = this,
880
0
    .sections = sections,
881
0
    .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
882
0
  );
883
0
  return enumerator_create_nested(array_create_enumerator(sections),
884
0
            (void*)section_enumerator, data, enumerator_destroy);
885
0
}
886
887
CALLBACK(kv_filter, bool,
888
  hashtable_t *seen, enumerator_t *orig, va_list args)
889
0
{
890
0
  kv_t *kv;
891
0
  char **key, **value;
892
893
0
  VA_ARGS_VGET(args, key, value);
894
895
0
  while (orig->enumerate(orig, &kv))
896
0
  {
897
0
    if (seen->get(seen, kv->key))
898
0
    {
899
0
      continue;
900
0
    }
901
0
    seen->put(seen, kv->key, kv->key);
902
0
    if (!kv->value)
903
0
    {
904
0
      continue;
905
0
    }
906
0
    *key = kv->key;
907
0
    *value = kv->value;
908
0
    return TRUE;
909
0
  }
910
0
  return FALSE;
911
0
}
912
913
/**
914
 * Enumerate key/value pairs of the given section
915
 */
916
static enumerator_t *kv_enumerator(section_t *section, enumerator_data_t *data)
917
0
{
918
0
  return enumerator_create_filter(array_create_enumerator(section->kv_order),
919
0
                  kv_filter, data->seen, NULL);
920
0
}
921
922
METHOD(settings_t, create_key_value_enumerator, enumerator_t*,
923
  private_settings_t *this, char *key, ...)
924
0
{
925
0
  enumerator_data_t *data;
926
0
  array_t *sections = NULL;
927
0
  va_list args;
928
929
0
  this->lock->read_lock(this->lock);
930
0
  va_start(args, key);
931
0
  sections = find_sections(this, this->top, key, args, &sections);
932
0
  va_end(args);
933
934
0
  if (!sections)
935
0
  {
936
0
    this->lock->unlock(this->lock);
937
0
    return enumerator_create_empty();
938
0
  }
939
0
  INIT(data,
940
0
    .settings = this,
941
0
    .sections = sections,
942
0
    .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
943
0
  );
944
0
  return enumerator_create_nested(array_create_enumerator(sections),
945
0
          (void*)kv_enumerator, data, (void*)enumerator_destroy);
946
0
}
947
948
METHOD(settings_t, add_fallback, void,
949
  private_settings_t *this, const char *key, const char *fallback, ...)
950
1.87k
{
951
1.87k
  section_t *section;
952
1.87k
  va_list args;
953
1.87k
  char buf[KEY_FULL_BUF_LEN];
954
955
1.87k
  this->lock->write_lock(this->lock);
956
1.87k
  va_start(args, fallback);
957
1.87k
  section = ensure_section(this, this->top, key, args);
958
1.87k
  va_end(args);
959
960
1.87k
  va_start(args, fallback);
961
1.87k
  if (section && vsnprintf(buf, sizeof(buf), fallback, args) < sizeof(buf))
962
1.87k
  {
963
1.87k
    settings_reference_add(section, strdup(buf), TRUE);
964
1.87k
  }
965
1.87k
  va_end(args);
966
1.87k
  this->lock->unlock(this->lock);
967
1.87k
}
968
969
/**
970
 * Load settings from files matching the given file pattern or from a string.
971
 * All files (even included ones) have to be loaded successfully.
972
 */
973
static section_t *load_internal(char *pattern, bool string)
974
3.74k
{
975
3.74k
  section_t *section;
976
3.74k
  bool loaded;
977
978
3.74k
  if (pattern == NULL || !pattern[0])
979
1.87k
  {
980
1.87k
    return settings_section_create(NULL);
981
1.87k
  }
982
983
1.87k
  section = settings_section_create(NULL);
984
1.87k
  loaded = string ? settings_parser_parse_string(section, pattern) :
985
1.87k
            settings_parser_parse_file(section, pattern);
986
1.87k
  if (!loaded)
987
1.87k
  {
988
1.87k
    settings_section_destroy(section, NULL);
989
1.87k
    section = NULL;
990
1.87k
  }
991
1.87k
  return section;
992
3.74k
}
993
994
/**
995
 * Add sections and values in "section" relative to "parent".
996
 * If merge is FALSE the contents of parent are replaced with the parsed
997
 * contents, otherwise they are merged together.
998
 *
999
 * Releases the write lock and destroys the given section.
1000
 * If parent is NULL this is all that happens.
1001
 */
1002
static bool extend_section(private_settings_t *this, section_t *parent,
1003
               section_t *section, bool merge)
1004
1.87k
{
1005
1.87k
  if (parent)
1006
1.87k
  {
1007
1.87k
    settings_section_extend(parent, section, this->contents, !merge);
1008
1.87k
  }
1009
1.87k
  this->lock->unlock(this->lock);
1010
1.87k
  settings_section_destroy(section, NULL);
1011
1.87k
  return parent != NULL;
1012
1.87k
}
1013
1014
METHOD(settings_t, load_files, bool,
1015
  private_settings_t *this, char *pattern, bool merge)
1016
3.74k
{
1017
3.74k
  section_t *section;
1018
1019
3.74k
  section = load_internal(pattern, FALSE);
1020
3.74k
  if (!section)
1021
1.87k
  {
1022
1.87k
    return FALSE;
1023
1.87k
  }
1024
1025
1.87k
  this->lock->write_lock(this->lock);
1026
1.87k
  return extend_section(this, this->top, section, merge);
1027
3.74k
}
1028
1029
METHOD(settings_t, load_files_section, bool,
1030
  private_settings_t *this, char *pattern, bool merge, char *key, ...)
1031
0
{
1032
0
  section_t *section, *parent;
1033
0
  va_list args;
1034
1035
0
  section = load_internal(pattern, FALSE);
1036
0
  if (!section)
1037
0
  {
1038
0
    return FALSE;
1039
0
  }
1040
1041
0
  this->lock->write_lock(this->lock);
1042
1043
0
  va_start(args, key);
1044
0
  parent = ensure_section(this, this->top, key, args);
1045
0
  va_end(args);
1046
1047
0
  return extend_section(this, parent, section, merge);
1048
0
}
1049
1050
METHOD(settings_t, load_string, bool,
1051
  private_settings_t *this, char *settings, bool merge)
1052
0
{
1053
0
  section_t *section;
1054
1055
0
  section = load_internal(settings, TRUE);
1056
0
  if (!section)
1057
0
  {
1058
0
    return FALSE;
1059
0
  }
1060
1061
0
  this->lock->write_lock(this->lock);
1062
0
  return extend_section(this, this->top, section, merge);
1063
0
}
1064
1065
METHOD(settings_t, load_string_section, bool,
1066
  private_settings_t *this, char *settings, bool merge, char *key, ...)
1067
0
{
1068
0
  section_t *section, *parent;
1069
0
  va_list args;
1070
1071
0
  section = load_internal(settings, TRUE);
1072
0
  if (!section)
1073
0
  {
1074
0
    return FALSE;
1075
0
  }
1076
1077
0
  this->lock->write_lock(this->lock);
1078
1079
0
  va_start(args, key);
1080
0
  parent = ensure_section(this, this->top, key, args);
1081
0
  va_end(args);
1082
1083
0
  return extend_section(this, parent, section, merge);
1084
0
}
1085
1086
CALLBACK(clear_content, void,
1087
  char *str, int idx, void *clear)
1088
0
{
1089
0
  if (*(bool*)clear)
1090
0
  {
1091
0
    memwipe(str, strlen(str));
1092
0
  }
1093
0
  free(str);
1094
0
}
1095
1096
/**
1097
 * Destroy the settings object and optionally clear content memory
1098
 */
1099
static void destroy_settings(private_settings_t *this, bool clear)
1100
1.87k
{
1101
1.87k
  settings_section_destroy(this->top, clear ? this->contents : NULL);
1102
1.87k
  array_destroy_function(this->contents, clear_content, &clear);
1103
1.87k
  this->lock->destroy(this->lock);
1104
1.87k
  free(this);
1105
1.87k
}
1106
1107
METHOD(settings_t, destroy, void,
1108
  private_settings_t *this)
1109
1.87k
{
1110
1.87k
  destroy_settings(this, FALSE);
1111
1.87k
}
1112
1113
METHOD(settings_t, destroy_clear, void,
1114
  private_settings_t *this)
1115
0
{
1116
0
  destroy_settings(this, TRUE);
1117
0
}
1118
1119
static private_settings_t *settings_create_base()
1120
1.87k
{
1121
1.87k
  private_settings_t *this;
1122
1123
1.87k
  INIT(this,
1124
1.87k
    .public = {
1125
1.87k
      .get_str = _get_str,
1126
1.87k
      .get_int = _get_int,
1127
1.87k
      .get_double = _get_double,
1128
1.87k
      .get_time = _get_time,
1129
1.87k
      .get_bool = _get_bool,
1130
1.87k
      .set_str = _set_str,
1131
1.87k
      .set_int = _set_int,
1132
1.87k
      .set_double = _set_double,
1133
1.87k
      .set_time = _set_time,
1134
1.87k
      .set_bool = _set_bool,
1135
1.87k
      .set_default_str = _set_default_str,
1136
1.87k
      .create_section_enumerator = _create_section_enumerator,
1137
1.87k
      .create_key_value_enumerator = _create_key_value_enumerator,
1138
1.87k
      .add_fallback = _add_fallback,
1139
1.87k
      .load_files = _load_files,
1140
1.87k
      .load_files_section = _load_files_section,
1141
1.87k
      .load_string = _load_string,
1142
1.87k
      .load_string_section = _load_string_section,
1143
1.87k
      .destroy = _destroy,
1144
1.87k
      .destroy_clear = _destroy_clear,
1145
1.87k
    },
1146
1.87k
    .top = settings_section_create(NULL),
1147
1.87k
    .contents = array_create(0, 0),
1148
1.87k
    .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
1149
1.87k
  );
1150
1.87k
  return this;
1151
1.87k
}
1152
1153
/*
1154
 * see header file
1155
 */
1156
settings_t *settings_create(char *file)
1157
1.87k
{
1158
1.87k
  private_settings_t *this = settings_create_base();
1159
1160
1.87k
  load_files(this, file, FALSE);
1161
1162
1.87k
  return &this->public;
1163
1.87k
}
1164
1165
/*
1166
 * see header file
1167
 */
1168
settings_t *settings_create_string(char *settings)
1169
0
{
1170
0
  private_settings_t *this = settings_create_base();
1171
1172
0
  load_string(this, settings, FALSE);
1173
1174
0
  return &this->public;
1175
0
}