Coverage Report

Created: 2025-07-23 07:29

/src/suricata7/src/conf.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2007-2023 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Endace Technology Limited - Jason Ish <jason.ish@endace.com>
22
 *
23
 * This file provides a basic configuration system for the IDPS
24
 * engine.
25
 *
26
 * NOTE: Setting values should only be done from one thread during
27
 * engine initialization.  Multiple threads should be able access read
28
 * configuration data.  Allowing run time changes to the configuration
29
 * will require some locks.
30
 *
31
 * \todo Consider having the in-memory configuration database a direct
32
 *   reflection of the configuration file and moving command line
33
 *   parameters to a primary lookup table?
34
 *
35
 * \todo Get rid of allow override and go with a simpler first set,
36
 *   stays approach?
37
 */
38
39
#include "suricata-common.h"
40
#include "conf.h"
41
#include "util-unittest.h"
42
#include "util-debug.h"
43
#include "util-path.h"
44
#include "util-conf.h"
45
46
/** Maximum size of a complete domain name. */
47
#define NODE_NAME_MAX 1024
48
49
static ConfNode *root = NULL;
50
static ConfNode *root_backup = NULL;
51
52
/**
53
 * \brief Helper function to get a node, creating it if it does not
54
 * exist.
55
 *
56
 * This function exits on memory failure as creating configuration
57
 * nodes is usually part of application initialization.
58
 *
59
 * \param parent The node to use as the parent
60
 * \param name The name of the configuration node to get.
61
 * \param final Flag to set created nodes as final or not.
62
 *
63
 * \retval The existing configuration node if it exists, or a newly
64
 *   created node for the provided name.  On error, NULL will be returned.
65
 */
66
ConfNode *ConfNodeGetNodeOrCreate(ConfNode *parent, const char *name, int final)
67
10.3k
{
68
10.3k
    ConfNode *node = NULL;
69
10.3k
    char node_name[NODE_NAME_MAX];
70
10.3k
    char *key;
71
10.3k
    char *next;
72
73
10.3k
    if (strlcpy(node_name, name, sizeof(node_name)) >= sizeof(node_name)) {
74
26
        SCLogError("Configuration name too long: %s", name);
75
26
        return NULL;
76
26
    }
77
78
10.2k
    key = node_name;
79
80
219k
    do {
81
219k
        if ((next = strchr(key, '.')) != NULL)
82
208k
            *next++ = '\0';
83
219k
        if ((node = ConfNodeLookupChild(parent, key)) == NULL) {
84
184k
            node = ConfNodeNew();
85
184k
            if (unlikely(node == NULL)) {
86
0
                SCLogWarning("Failed to allocate memory for configuration.");
87
0
                goto end;
88
0
            }
89
184k
            node->name = SCStrdup(key);
90
184k
            if (unlikely(node->name == NULL)) {
91
0
                ConfNodeFree(node);
92
0
                node = NULL;
93
0
                SCLogWarning("Failed to allocate memory for configuration.");
94
0
                goto end;
95
0
            }
96
184k
            node->parent = parent;
97
184k
            node->final = final;
98
184k
            TAILQ_INSERT_TAIL(&parent->head, node, next);
99
184k
        }
100
219k
        key = next;
101
219k
        parent = node;
102
219k
    } while (next != NULL);
103
104
10.2k
end:
105
10.2k
    return node;
106
10.2k
}
107
108
/**
109
 * \brief Wrapper function for ConfNodeGetNodeOrCreate that operates
110
 *     on the current root node.
111
 */
112
static ConfNode *ConfGetNodeOrCreate(const char *name, int final)
113
66
{
114
66
    return ConfNodeGetNodeOrCreate(root, name, final);
115
66
}
116
117
/**
118
 * \brief Initialize the configuration system.
119
 */
120
void ConfInit(void)
121
37
{
122
37
    if (root != NULL) {
123
0
        SCLogDebug("already initialized");
124
0
        return;
125
0
    }
126
37
    root = ConfNodeNew();
127
37
    if (root == NULL) {
128
0
        FatalError("ERROR: Failed to allocate memory for root configuration node, "
129
0
                   "aborting.");
130
0
    }
131
37
    SCLogDebug("configuration module initialized");
132
37
}
133
134
/**
135
 * \brief Allocate a new configuration node.
136
 *
137
 * \retval An allocated configuration node on success, NULL on failure.
138
 */
139
ConfNode *ConfNodeNew(void)
140
413k
{
141
413k
    ConfNode *new;
142
143
413k
    new = SCCalloc(1, sizeof(*new));
144
413k
    if (unlikely(new == NULL)) {
145
0
        return NULL;
146
0
    }
147
413k
    TAILQ_INIT(&new->head);
148
149
413k
    return new;
150
413k
}
151
152
/**
153
 * \brief Free a ConfNode and all of its children.
154
 *
155
 * \param node The configuration node to SCFree.
156
 */
157
void ConfNodeFree(ConfNode *node)
158
0
{
159
0
    ConfNode *tmp;
160
161
0
    while ((tmp = TAILQ_FIRST(&node->head))) {
162
0
        TAILQ_REMOVE(&node->head, tmp, next);
163
0
        ConfNodeFree(tmp);
164
0
    }
165
166
0
    if (node->name != NULL)
167
0
        SCFree(node->name);
168
0
    if (node->val != NULL)
169
0
        SCFree(node->val);
170
0
    SCFree(node);
171
0
}
172
173
/**
174
 * \brief Get a ConfNode by name.
175
 *
176
 * \param name The full name of the configuration node to lookup.
177
 *
178
 * \retval A pointer to ConfNode is found or NULL if the configuration
179
 *    node does not exist.
180
 */
181
ConfNode *ConfGetNode(const char *name)
182
14.1M
{
183
14.1M
    ConfNode *node = root;
184
14.1M
    char node_name[NODE_NAME_MAX];
185
14.1M
    char *key;
186
14.1M
    char *next;
187
188
14.1M
    if (strlcpy(node_name, name, sizeof(node_name)) >= sizeof(node_name)) {
189
31
        SCLogError("Configuration name too long: %s", name);
190
31
        return NULL;
191
31
    }
192
193
14.1M
    key = node_name;
194
27.7M
    do {
195
27.7M
        if ((next = strchr(key, '.')) != NULL)
196
26.8M
            *next++ = '\0';
197
27.7M
        node = ConfNodeLookupChild(node, key);
198
27.7M
        key = next;
199
27.7M
    } while (next != NULL && node != NULL);
200
201
14.1M
    return node;
202
14.1M
}
203
204
/**
205
 * \brief Get the root configuration node.
206
 */
207
ConfNode *ConfGetRootNode(void)
208
8.54k
{
209
8.54k
    return root;
210
8.54k
}
211
212
/**
213
 * \brief Set a configuration value.
214
 *
215
 * Configuration values set with this function may be overridden by
216
 * subsequent calls, or if the value appears multiple times in a
217
 * configuration file.
218
 *
219
 * \param name The name of the configuration parameter to set.
220
 * \param val The value of the configuration parameter.
221
 *
222
 * \retval 1 if the value was set otherwise 0.
223
 */
224
int ConfSet(const char *name, const char *val)
225
33
{
226
33
    ConfNode *node = ConfGetNodeOrCreate(name, 0);
227
33
    if (node == NULL || node->final) {
228
0
        return 0;
229
0
    }
230
33
    if (node->val != NULL)
231
32
        SCFree(node->val);
232
33
    node->val = SCStrdup(val);
233
33
    if (unlikely(node->val == NULL)) {
234
0
        return 0;
235
0
    }
236
33
    return 1;
237
33
}
238
239
/**
240
 * \brief Set a configuration parameter from a string.
241
 *
242
 * Where the input string is something like:
243
 *    stream.midstream=true
244
 *
245
 * \param input the input string to be parsed.
246
 *
247
 * \retval 1 if the value of set, otherwise 0.
248
 */
249
int ConfSetFromString(const char *input, int final)
250
0
{
251
0
    int retval = 0;
252
0
    char *name = SCStrdup(input), *val = NULL;
253
0
    if (unlikely(name == NULL)) {
254
0
        goto done;
255
0
    }
256
0
    val = strchr(name, '=');
257
0
    if (val == NULL) {
258
0
        goto done;
259
0
    }
260
0
    *val++ = '\0';
261
262
0
    while (isspace((int)name[strlen(name) - 1])) {
263
0
        name[strlen(name) - 1] = '\0';
264
0
    }
265
266
0
    while (isspace((int)*val)) {
267
0
        val++;
268
0
    }
269
270
0
    if (final) {
271
0
        if (!ConfSetFinal(name, val)) {
272
0
            goto done;
273
0
        }
274
0
    }
275
0
    else {
276
0
        if (!ConfSet(name, val)) {
277
0
            goto done;
278
0
        }
279
0
    }
280
281
0
    retval = 1;
282
0
done:
283
0
    if (name != NULL) {
284
0
        SCFree(name);
285
0
    }
286
0
    return retval;
287
0
}
288
289
/**
290
 * \brief Set a final configuration value.
291
 *
292
 * A final configuration value is a value that cannot be overridden by
293
 * the configuration file.  Its mainly useful for setting values that
294
 * are supplied on the command line prior to the configuration file
295
 * being loaded.  However, a subsequent call to this function can
296
 * override a previously set value.
297
 *
298
 * \param name The name of the configuration parameter to set.
299
 * \param val The value of the configuration parameter.
300
 *
301
 * \retval 1 if the value was set otherwise 0.
302
 */
303
int ConfSetFinal(const char *name, const char *val)
304
33
{
305
33
    ConfNode *node = ConfGetNodeOrCreate(name, 1);
306
33
    if (node == NULL) {
307
0
        return 0;
308
0
    }
309
33
    if (node->val != NULL)
310
0
        SCFree(node->val);
311
33
    node->val = SCStrdup(val);
312
33
    if (unlikely(node->val == NULL)) {
313
0
        return 0;
314
0
    }
315
33
    node->final = 1;
316
33
    return 1;
317
33
}
318
319
/**
320
 * \brief Retrieve the value of a configuration node.
321
 *
322
 * This function will return the value for a configuration node based
323
 * on the full name of the node.  It is possible that the value
324
 * returned could be NULL, this could happen if the requested node
325
 * does exist but is not a node that contains a value, but contains
326
 * children ConfNodes instead.
327
 *
328
 * \param name Name of configuration parameter to get.
329
 * \param vptr Pointer that will be set to the configuration value parameter.
330
 *   Note that this is just a reference to the actual value, not a copy.
331
 *
332
 * \retval 1 will be returned if the name is found, otherwise 0 will
333
 *   be returned.
334
 */
335
int ConfGet(const char *name, const char **vptr)
336
13.9M
{
337
13.9M
    ConfNode *node = ConfGetNode(name);
338
13.9M
    if (node == NULL) {
339
13.9M
        SCLogDebug("failed to lookup configuration parameter '%s'", name);
340
13.9M
        return 0;
341
13.9M
    }
342
70.9k
    else {
343
70.9k
        *vptr = node->val;
344
70.9k
        return 1;
345
70.9k
    }
346
13.9M
}
347
348
int ConfGetChildValue(const ConfNode *base, const char *name, const char **vptr)
349
4
{
350
4
    ConfNode *node = ConfNodeLookupChild(base, name);
351
352
4
    if (node == NULL) {
353
0
        SCLogDebug("failed to lookup configuration parameter '%s'", name);
354
0
        return 0;
355
0
    }
356
4
    else {
357
4
        if (node->val == NULL)
358
0
            return 0;
359
4
        *vptr = node->val;
360
4
        return 1;
361
4
    }
362
4
}
363
364
ConfNode *ConfGetChildWithDefault(const ConfNode *base, const ConfNode *dflt,
365
    const char *name)
366
0
{
367
0
    ConfNode *node = ConfNodeLookupChild(base, name);
368
0
    if (node != NULL)
369
0
        return node;
370
371
    /* Get 'default' value */
372
0
    if (dflt) {
373
0
        return ConfNodeLookupChild(dflt, name);
374
0
    }
375
0
    return NULL;
376
0
}
377
378
int ConfGetChildValueWithDefault(const ConfNode *base, const ConfNode *dflt,
379
    const char *name, const char **vptr)
380
0
{
381
0
    int ret = ConfGetChildValue(base, name, vptr);
382
    /* Get 'default' value */
383
0
    if (ret == 0 && dflt) {
384
0
        return ConfGetChildValue(dflt, name, vptr);
385
0
    }
386
0
    return ret;
387
0
}
388
389
/**
390
 * \brief Retrieve a configuration value as an integer.
391
 *
392
 * \param name Name of configuration parameter to get.
393
 * \param val Pointer to an intmax_t that will be set the
394
 * configuration value.
395
 *
396
 * \retval 1 will be returned if the name is found and was properly
397
 * converted to an integer, otherwise 0 will be returned.
398
 */
399
int ConfGetInt(const char *name, intmax_t *val)
400
73.1k
{
401
73.1k
    const char *strval = NULL;
402
73.1k
    intmax_t tmpint;
403
73.1k
    char *endptr;
404
405
73.1k
    if (ConfGet(name, &strval) == 0)
406
11.3k
        return 0;
407
408
61.8k
    if (strval == NULL) {
409
0
        SCLogError("malformed integer value "
410
0
                   "for %s: NULL",
411
0
                name);
412
0
        return 0;
413
0
    }
414
415
61.8k
    errno = 0;
416
61.8k
    tmpint = strtoimax(strval, &endptr, 0);
417
61.8k
    if (strval[0] == '\0' || *endptr != '\0') {
418
0
        SCLogError("malformed integer value "
419
0
                   "for %s: '%s'",
420
0
                name, strval);
421
0
        return 0;
422
0
    }
423
61.8k
    if (errno == ERANGE && (tmpint == INTMAX_MAX || tmpint == INTMAX_MIN)) {
424
0
        SCLogError("integer value for %s out "
425
0
                   "of range: '%s'",
426
0
                name, strval);
427
0
        return 0;
428
0
    }
429
430
61.8k
    *val = tmpint;
431
61.8k
    return 1;
432
61.8k
}
433
434
int ConfGetChildValueInt(const ConfNode *base, const char *name, intmax_t *val)
435
2
{
436
2
    const char *strval = NULL;
437
2
    intmax_t tmpint;
438
2
    char *endptr;
439
440
2
    if (ConfGetChildValue(base, name, &strval) == 0)
441
0
        return 0;
442
2
    errno = 0;
443
2
    tmpint = strtoimax(strval, &endptr, 0);
444
2
    if (strval[0] == '\0' || *endptr != '\0') {
445
0
        SCLogError("malformed integer value "
446
0
                   "for %s with base %s: '%s'",
447
0
                name, base->name, strval);
448
0
        return 0;
449
0
    }
450
2
    if (errno == ERANGE && (tmpint == INTMAX_MAX || tmpint == INTMAX_MIN)) {
451
0
        SCLogError("integer value for %s with "
452
0
                   " base %s out of range: '%s'",
453
0
                name, base->name, strval);
454
0
        return 0;
455
0
    }
456
457
2
    *val = tmpint;
458
2
    return 1;
459
460
2
}
461
462
int ConfGetChildValueIntWithDefault(const ConfNode *base, const ConfNode *dflt,
463
    const char *name, intmax_t *val)
464
0
{
465
0
    int ret = ConfGetChildValueInt(base, name, val);
466
    /* Get 'default' value */
467
0
    if (ret == 0 && dflt) {
468
0
        return ConfGetChildValueInt(dflt, name, val);
469
0
    }
470
0
    return ret;
471
0
}
472
473
/**
474
 * \brief Retrieve a configuration value as a boolean.
475
 *
476
 * \param name Name of configuration parameter to get.
477
 * \param val Pointer to an int that will be set to 1 for true, or 0
478
 * for false.
479
 *
480
 * \retval 1 will be returned if the name is found and was properly
481
 * converted to a boolean, otherwise 0 will be returned.
482
 */
483
int ConfGetBool(const char *name, int *val)
484
13.1M
{
485
13.1M
    const char *strval = NULL;
486
487
13.1M
    *val = 0;
488
13.1M
    if (ConfGet(name, &strval) != 1)
489
13.1M
        return 0;
490
491
65
    *val = ConfValIsTrue(strval);
492
493
65
    return 1;
494
13.1M
}
495
496
/**
497
 * Get a boolean value from the provided ConfNode.
498
 *
499
 * \retval 1 If the value exists, 0 if not.
500
 */
501
int ConfGetChildValueBool(const ConfNode *base, const char *name, int *val)
502
2
{
503
2
    const char *strval = NULL;
504
505
2
    *val = 0;
506
2
    if (ConfGetChildValue(base, name, &strval) == 0)
507
0
        return 0;
508
509
2
    *val = ConfValIsTrue(strval);
510
511
2
    return 1;
512
2
}
513
514
int ConfGetChildValueBoolWithDefault(const ConfNode *base, const ConfNode *dflt,
515
    const char *name, int *val)
516
0
{
517
0
    int ret = ConfGetChildValueBool(base, name, val);
518
    /* Get 'default' value */
519
0
    if (ret == 0 && dflt) {
520
0
        return ConfGetChildValueBool(dflt, name, val);
521
0
    }
522
0
    return ret;
523
0
}
524
525
526
/**
527
 * \brief Check if a value is true.
528
 *
529
 * The value is considered true if it is a string with the value of 1,
530
 * yes, true or on.  The test is not case sensitive, any other value
531
 * is false.
532
 *
533
 * \param val The string to test for a true value.
534
 *
535
 * \retval 1 If the value is true, 0 if not.
536
 */
537
int ConfValIsTrue(const char *val)
538
9.99k
{
539
9.99k
    const char *trues[] = {"1", "yes", "true", "on"};
540
9.99k
    size_t u;
541
542
46.8k
    for (u = 0; u < sizeof(trues) / sizeof(trues[0]); u++) {
543
37.8k
        if (strcasecmp(val, trues[u]) == 0) {
544
1.05k
            return 1;
545
1.05k
        }
546
37.8k
    }
547
548
8.93k
    return 0;
549
9.99k
}
550
551
/**
552
 * \brief Check if a value is false.
553
 *
554
 * The value is considered false if it is a string with the value of 0,
555
 * no, false or off.  The test is not case sensitive, any other value
556
 * is not false.
557
 *
558
 * \param val The string to test for a false value.
559
 *
560
 * \retval 1 If the value is false, 0 if not.
561
 */
562
int ConfValIsFalse(const char *val)
563
8.93k
{
564
8.93k
    const char *falses[] = {"0", "no", "false", "off"};
565
8.93k
    size_t u;
566
567
17.9k
    for (u = 0; u < sizeof(falses) / sizeof(falses[0]); u++) {
568
17.9k
        if (strcasecmp(val, falses[u]) == 0) {
569
8.90k
            return 1;
570
8.90k
        }
571
17.9k
    }
572
573
35
    return 0;
574
8.93k
}
575
576
/**
577
 * \brief Retrieve a configuration value as a double
578
 *
579
 * \param name Name of configuration parameter to get.
580
 * \param val Pointer to an double that will be set the
581
 * configuration value.
582
 *
583
 * \retval 1 will be returned if the name is found and was properly
584
 * converted to a double, otherwise 0 will be returned.
585
 */
586
int ConfGetDouble(const char *name, double *val)
587
0
{
588
0
    const char *strval = NULL;
589
0
    double tmpdo;
590
0
    char *endptr;
591
592
0
    if (ConfGet(name, &strval) == 0)
593
0
        return 0;
594
595
0
    errno = 0;
596
0
    tmpdo = strtod(strval, &endptr);
597
0
    if (strval[0] == '\0' || *endptr != '\0')
598
0
        return 0;
599
0
    if (errno == ERANGE)
600
0
        return 0;
601
602
0
    *val = tmpdo;
603
0
    return 1;
604
0
}
605
606
/**
607
 * \brief Retrieve a configuration value as a float
608
 *
609
 * \param name Name of configuration parameter to get.
610
 * \param val Pointer to an float that will be set the
611
 * configuration value.
612
 *
613
 * \retval 1 will be returned if the name is found and was properly
614
 * converted to a double, otherwise 0 will be returned.
615
 */
616
int ConfGetFloat(const char *name, float *val)
617
1
{
618
1
    const char *strval = NULL;
619
1
    double tmpfl;
620
1
    char *endptr;
621
622
1
    if (ConfGet(name, &strval) == 0)
623
1
        return 0;
624
625
0
    errno = 0;
626
0
    tmpfl = strtof(strval, &endptr);
627
0
    if (strval[0] == '\0' || *endptr != '\0')
628
0
        return 0;
629
0
    if (errno == ERANGE)
630
0
        return 0;
631
632
0
    *val = tmpfl;
633
0
    return 1;
634
0
}
635
636
/**
637
 * \brief Remove (and SCFree) the provided configuration node.
638
 */
639
void ConfNodeRemove(ConfNode *node)
640
0
{
641
0
    if (node->parent != NULL)
642
0
        TAILQ_REMOVE(&node->parent->head, node, next);
643
0
    ConfNodeFree(node);
644
0
}
645
646
/**
647
 * \brief Remove a configuration parameter from the configuration db.
648
 *
649
 * \param name The name of the configuration parameter to remove.
650
 *
651
 * \retval Returns 1 if the parameter was removed, otherwise 0 is returned
652
 *   most likely indicating the parameter was not set.
653
 */
654
int ConfRemove(const char *name)
655
0
{
656
0
    ConfNode *node;
657
658
0
    node = ConfGetNode(name);
659
0
    if (node == NULL)
660
0
        return 0;
661
0
    else {
662
0
        ConfNodeRemove(node);
663
0
        return 1;
664
0
    }
665
0
}
666
667
/**
668
 * \brief Creates a backup of the conf_hash hash_table used by the conf API.
669
 */
670
void ConfCreateContextBackup(void)
671
0
{
672
0
    root_backup = root;
673
0
    root = NULL;
674
675
0
    return;
676
0
}
677
678
/**
679
 * \brief Restores the backup of the hash_table present in backup_conf_hash
680
 *        back to conf_hash.
681
 */
682
void ConfRestoreContextBackup(void)
683
0
{
684
0
    root = root_backup;
685
0
    root_backup = NULL;
686
687
0
    return;
688
0
}
689
690
/**
691
 * \brief De-initializes the configuration system.
692
 */
693
void ConfDeInit(void)
694
0
{
695
0
    if (root != NULL) {
696
0
        ConfNodeFree(root);
697
0
        root = NULL;
698
0
    }
699
700
0
    SCLogDebug("configuration module de-initialized");
701
0
}
702
703
static char *ConfPrintNameArray(char **name_arr, int level)
704
0
{
705
0
    static char name[128*128];
706
0
    int i;
707
708
0
    name[0] = '\0';
709
0
    for (i = 0; i <= level; i++) {
710
0
        strlcat(name, name_arr[i], sizeof(name));
711
0
        if (i < level)
712
0
            strlcat(name, ".", sizeof(name));
713
0
    }
714
715
0
    return name;
716
0
}
717
718
/**
719
 * \brief Dump a configuration node and all its children.
720
 */
721
void ConfNodeDump(const ConfNode *node, const char *prefix)
722
0
{
723
0
    ConfNode *child;
724
725
0
    static char *name[128];
726
0
    static int level = -1;
727
728
0
    level++;
729
0
    TAILQ_FOREACH(child, &node->head, next) {
730
0
        name[level] = SCStrdup(child->name);
731
0
        if (unlikely(name[level] == NULL)) {
732
0
            continue;
733
0
        }
734
0
        if (prefix == NULL) {
735
0
            printf("%s = %s\n", ConfPrintNameArray(name, level),
736
0
                child->val);
737
0
        }
738
0
        else {
739
0
            printf("%s.%s = %s\n", prefix,
740
0
                ConfPrintNameArray(name, level), child->val);
741
0
        }
742
0
        ConfNodeDump(child, prefix);
743
0
        SCFree(name[level]);
744
0
    }
745
0
    level--;
746
0
}
747
748
/**
749
 * \brief Dump configuration to stdout.
750
 */
751
void ConfDump(void)
752
0
{
753
0
    ConfNodeDump(root, NULL);
754
0
}
755
756
/**
757
 * \brief Check if a node has any children.
758
 *
759
 * Checks if the provided node has any children. Any node that is a
760
 * YAML map or array will have children.
761
 *
762
 * \param node The node to check.
763
 *
764
 * \retval true if node has children
765
 * \retval false if node does not have children
766
 */
767
bool ConfNodeHasChildren(const ConfNode *node)
768
2
{
769
2
    if (TAILQ_EMPTY(&node->head)) {
770
2
        return false;
771
2
    }
772
0
    return true;
773
2
}
774
775
/**
776
 * \brief Lookup a child configuration node by name.
777
 *
778
 * Given a ConfNode this function will lookup an immediate child
779
 * ConfNode by name and return the child ConfNode.
780
 *
781
 * \param node The parent configuration node.
782
 * \param name The name of the child node to lookup.
783
 *
784
 * \retval A pointer the child ConfNode if found otherwise NULL.
785
 */
786
ConfNode *ConfNodeLookupChild(const ConfNode *node, const char *name)
787
29.5M
{
788
29.5M
    ConfNode *child;
789
790
29.5M
    if (node == NULL || name == NULL) {
791
6
        return NULL;
792
6
    }
793
794
2.52G
    TAILQ_FOREACH(child, &node->head, next) {
795
2.52G
        if (child->name != NULL && strcmp(child->name, name) == 0)
796
15.0M
            return child;
797
2.52G
    }
798
799
14.5M
    return NULL;
800
29.5M
}
801
802
/**
803
 * \brief Lookup the value of a child configuration node by name.
804
 *
805
 * Given a parent ConfNode this function will return the value of a
806
 * child configuration node by name returning a reference to that
807
 * value.
808
 *
809
 * \param node The parent configuration node.
810
 * \param name The name of the child node to lookup.
811
 *
812
 * \retval A pointer the child ConfNodes value if found otherwise NULL.
813
 */
814
const char *ConfNodeLookupChildValue(const ConfNode *node, const char *name)
815
240
{
816
240
    ConfNode *child;
817
818
240
    child = ConfNodeLookupChild(node, name);
819
240
    if (child != NULL)
820
110
        return child->val;
821
822
130
    return NULL;
823
240
}
824
825
/**
826
 * \brief Lookup for a key value under a specific node
827
 *
828
 * \return the ConfNode matching or NULL
829
 */
830
831
ConfNode *ConfNodeLookupKeyValue(const ConfNode *base, const char *key,
832
    const char *value)
833
0
{
834
0
    ConfNode *child;
835
836
0
    TAILQ_FOREACH(child, &base->head, next) {
837
0
        if (!strncmp(child->val, key, strlen(child->val))) {
838
0
            ConfNode *subchild;
839
0
            TAILQ_FOREACH(subchild, &child->head, next) {
840
0
                if ((!strcmp(subchild->name, key)) && (!strcmp(subchild->val, value))) {
841
0
                    return child;
842
0
                }
843
0
            }
844
0
        }
845
0
    }
846
847
0
    return NULL;
848
0
}
849
850
/**
851
 * \brief Test if a configuration node has a true value.
852
 *
853
 * \param node The parent configuration node.
854
 * \param name The name of the child node to test.
855
 *
856
 * \retval 1 if the child node has a true value, otherwise 0 is
857
 *     returned, even if the child node does not exist.
858
 */
859
int ConfNodeChildValueIsTrue(const ConfNode *node, const char *key)
860
2
{
861
2
    const char *val;
862
863
2
    val = ConfNodeLookupChildValue(node, key);
864
865
2
    return val != NULL ? ConfValIsTrue(val) : 0;
866
2
}
867
868
/**
869
 *  \brief Create the path for an include entry
870
 *  \param file The name of the file
871
 *  \retval str Pointer to the string path + sig_file
872
 */
873
char *ConfLoadCompleteIncludePath(const char *file)
874
0
{
875
0
    const char *defaultpath = NULL;
876
0
    char *path = NULL;
877
878
    /* Path not specified */
879
0
    if (PathIsRelative(file)) {
880
0
        if (ConfGet("include-path", &defaultpath) == 1) {
881
0
            SCLogDebug("Default path: %s", defaultpath);
882
0
            size_t path_len = sizeof(char) * (strlen(defaultpath) +
883
0
                          strlen(file) + 2);
884
0
            path = SCMalloc(path_len);
885
0
            if (unlikely(path == NULL))
886
0
                return NULL;
887
0
            strlcpy(path, defaultpath, path_len);
888
0
            if (path[strlen(path) - 1] != '/')
889
0
                strlcat(path, "/", path_len);
890
0
            strlcat(path, file, path_len);
891
0
       } else {
892
0
            path = SCStrdup(file);
893
0
            if (unlikely(path == NULL))
894
0
                return NULL;
895
0
        }
896
0
    } else {
897
0
        path = SCStrdup(file);
898
0
        if (unlikely(path == NULL))
899
0
            return NULL;
900
0
    }
901
0
    return path;
902
0
}
903
904
/**
905
 * \brief Prune a configuration node.
906
 *
907
 * Pruning a configuration is similar to freeing, but only fields that
908
 * may be overridden are, leaving final type parameters.  Additional
909
 * the value of the provided node is also free'd, but the node itself
910
 * is left.
911
 *
912
 * \param node The configuration node to prune.
913
 */
914
void ConfNodePrune(ConfNode *node)
915
1.39M
{
916
1.39M
    ConfNode *item, *it;
917
918
1.72M
    for (item = TAILQ_FIRST(&node->head); item != NULL; item = it) {
919
328k
        it = TAILQ_NEXT(item, next);
920
328k
        if (!item->final) {
921
328k
            ConfNodePrune(item);
922
328k
            if (TAILQ_EMPTY(&item->head)) {
923
328k
                TAILQ_REMOVE(&node->head, item, next);
924
328k
                if (item->name != NULL)
925
328k
                    SCFree(item->name);
926
328k
                if (item->val != NULL)
927
0
                    SCFree(item->val);
928
328k
                SCFree(item);
929
328k
            }
930
328k
        }
931
328k
    }
932
933
1.39M
    if (node->val != NULL) {
934
330k
        SCFree(node->val);
935
330k
        node->val = NULL;
936
330k
    }
937
1.39M
}
938
939
/**
940
 * \brief Check if a node is a sequence or node.
941
 *
942
 * \param node the node to check.
943
 *
944
 * \return 1 if node is a sequence, otherwise 0.
945
 */
946
int ConfNodeIsSequence(const ConfNode *node)
947
0
{
948
0
    return node->is_seq == 0 ? 0 : 1;
949
0
}
950
951
/**
952
 * @brief Finds an interface from the list of interfaces.
953
 * @param ifaces_node_name - name of the node which holds a list of interfaces
954
 * @param iface - interfaces name
955
 * @return NULL on failure otherwise a valid pointer
956
 */
957
ConfNode *ConfSetIfaceNode(const char *ifaces_node_name, const char *iface)
958
0
{
959
0
    ConfNode *if_node;
960
0
    ConfNode *ifaces_list_node;
961
    /* Find initial node which holds all interfaces */
962
0
    ifaces_list_node = ConfGetNode(ifaces_node_name);
963
0
    if (ifaces_list_node == NULL) {
964
0
        SCLogError("unable to find %s config", ifaces_node_name);
965
0
        return NULL;
966
0
    }
967
968
0
    if_node = ConfFindDeviceConfig(ifaces_list_node, iface);
969
0
    if (if_node == NULL)
970
0
        SCLogNotice("unable to find interface %s in DPDK config", iface);
971
972
0
    return if_node;
973
0
}
974
975
/**
976
 * @brief Finds and sets root and default node of the interface.
977
 * @param ifaces_node_name Node which holds list of interfaces
978
 * @param iface Name of the interface e.g. eth3
979
 * @param if_root Node which will hold the interface configuration
980
 * @param if_default Node which is the default configuration in the given list of interfaces
981
 * @return 0 on success, -ENODEV when neither the root interface nor the default interface was found
982
 */
983
int ConfSetRootAndDefaultNodes(
984
        const char *ifaces_node_name, const char *iface, ConfNode **if_root, ConfNode **if_default)
985
0
{
986
0
    const char *default_iface = "default";
987
0
    *if_root = ConfSetIfaceNode(ifaces_node_name, iface);
988
0
    *if_default = ConfSetIfaceNode(ifaces_node_name, default_iface);
989
990
0
    if (*if_root == NULL && *if_default == NULL) {
991
0
        SCLogError("unable to find configuration for the interface \"%s\" or the default "
992
0
                   "configuration (\"%s\")",
993
0
                iface, default_iface);
994
0
        return (-ENODEV);
995
0
    }
996
997
    /* If there is no setting for current interface use default one as main iface */
998
0
    if (*if_root == NULL) {
999
0
        *if_root = *if_default;
1000
0
        *if_default = NULL;
1001
0
    }
1002
0
    return 0;
1003
0
}
1004
1005
#ifdef UNITTESTS
1006
1007
/**
1008
 * Lookup a non-existant value.
1009
 */
1010
static int ConfTestGetNonExistant(void)
1011
{
1012
    char name[] = "non-existant-value";
1013
    const char *value;
1014
1015
    FAIL_IF(ConfGet(name, &value));
1016
    PASS;
1017
}
1018
1019
/**
1020
 * Set then lookup a value.
1021
 */
1022
static int ConfTestSetAndGet(void)
1023
{
1024
    char name[] = "some-name";
1025
    char value[] = "some-value";
1026
    const char *value0 = NULL;
1027
1028
    FAIL_IF(ConfSet(name, value) != 1);
1029
    FAIL_IF(ConfGet(name, &value0) != 1);
1030
    FAIL_IF(value0 == NULL);
1031
    FAIL_IF(strcmp(value, value0) != 0);
1032
1033
    /* Cleanup. */
1034
    ConfRemove(name);
1035
1036
    PASS;
1037
}
1038
1039
/**
1040
 * Test that overriding a value is allowed provided allow_override is
1041
 * true and that the config parameter gets the new value.
1042
 */
1043
static int ConfTestOverrideValue1(void)
1044
{
1045
    char name[] = "some-name";
1046
    char value0[] = "some-value";
1047
    char value1[] = "new-value";
1048
    const char *val = NULL;
1049
1050
    FAIL_IF(ConfSet(name, value0) != 1);
1051
    FAIL_IF(ConfSet(name, value1) != 1);
1052
    FAIL_IF(ConfGet(name, &val) != 1);
1053
    FAIL_IF(val == NULL);
1054
    FAIL_IF(strcmp(val, value1) != 0);
1055
1056
    /* Cleanup. */
1057
    ConfRemove(name);
1058
1059
    PASS;
1060
}
1061
1062
/**
1063
 * Test that a final value will not be overridden by a ConfSet.
1064
 */
1065
static int ConfTestOverrideValue2(void)
1066
{
1067
    char name[] = "some-name";
1068
    char value0[] = "some-value";
1069
    char value1[] = "new-value";
1070
    const char *val = NULL;
1071
1072
    FAIL_IF(ConfSetFinal(name, value0) != 1);
1073
    FAIL_IF(ConfSet(name, value1) != 0);
1074
    FAIL_IF(ConfGet(name, &val) != 1);
1075
    FAIL_IF(val == NULL);
1076
    FAIL_IF(strcmp(val, value0) != 0);
1077
1078
    /* Cleanup. */
1079
    ConfRemove(name);
1080
1081
    PASS;
1082
}
1083
1084
/**
1085
 * Test retrieving an integer value from the configuration db.
1086
 */
1087
static int ConfTestGetInt(void)
1088
{
1089
    char name[] = "some-int.x";
1090
    intmax_t val;
1091
1092
    FAIL_IF(ConfSet(name, "0") != 1);
1093
    FAIL_IF(ConfGetInt(name, &val) != 1);
1094
    FAIL_IF(val != 0);
1095
1096
    FAIL_IF(ConfSet(name, "-1") != 1);
1097
    FAIL_IF(ConfGetInt(name, &val) != 1);
1098
    FAIL_IF(val != -1);
1099
1100
    FAIL_IF(ConfSet(name, "0xffff") != 1);
1101
    FAIL_IF(ConfGetInt(name, &val) != 1);
1102
    FAIL_IF(val != 0xffff);
1103
1104
    FAIL_IF(ConfSet(name, "not-an-int") != 1);
1105
    FAIL_IF(ConfGetInt(name, &val) != 0);
1106
1107
    PASS;
1108
}
1109
1110
/**
1111
 * Test retrieving a boolean value from the configuration db.
1112
 */
1113
static int ConfTestGetBool(void)
1114
{
1115
    char name[] = "some-bool";
1116
    const char *trues[] = {
1117
        "1",
1118
        "on", "ON",
1119
        "yes", "YeS",
1120
        "true", "TRUE",
1121
    };
1122
    const char *falses[] = {
1123
        "0",
1124
        "something",
1125
        "off", "OFF",
1126
        "false", "FalSE",
1127
        "no", "NO",
1128
    };
1129
    int val;
1130
    size_t u;
1131
1132
    for (u = 0; u < sizeof(trues) / sizeof(trues[0]); u++) {
1133
        FAIL_IF(ConfSet(name, trues[u]) != 1);
1134
        FAIL_IF(ConfGetBool(name, &val) != 1);
1135
        FAIL_IF(val != 1);
1136
    }
1137
1138
    for (u = 0; u < sizeof(falses) / sizeof(falses[0]); u++) {
1139
        FAIL_IF(ConfSet(name, falses[u]) != 1);
1140
        FAIL_IF(ConfGetBool(name, &val) != 1);
1141
        FAIL_IF(val != 0);
1142
    }
1143
1144
    PASS;
1145
}
1146
1147
static int ConfNodeLookupChildTest(void)
1148
{
1149
    const char *test_vals[] = { "one", "two", "three" };
1150
    size_t u;
1151
1152
    ConfNode *parent = ConfNodeNew();
1153
    ConfNode *child;
1154
1155
    for (u = 0; u < sizeof(test_vals)/sizeof(test_vals[0]); u++) {
1156
        child = ConfNodeNew();
1157
        child->name = SCStrdup(test_vals[u]);
1158
        child->val = SCStrdup(test_vals[u]);
1159
        TAILQ_INSERT_TAIL(&parent->head, child, next);
1160
    }
1161
1162
    child = ConfNodeLookupChild(parent, "one");
1163
    FAIL_IF(child == NULL);
1164
    FAIL_IF(strcmp(child->name, "one") != 0);
1165
    FAIL_IF(strcmp(child->val, "one") != 0);
1166
1167
    child = ConfNodeLookupChild(parent, "two");
1168
    FAIL_IF(child == NULL);
1169
    FAIL_IF(strcmp(child->name, "two") != 0);
1170
    FAIL_IF(strcmp(child->val, "two") != 0);
1171
1172
    child = ConfNodeLookupChild(parent, "three");
1173
    FAIL_IF(child == NULL);
1174
    FAIL_IF(strcmp(child->name, "three") != 0);
1175
    FAIL_IF(strcmp(child->val, "three") != 0);
1176
1177
    child = ConfNodeLookupChild(parent, "four");
1178
    FAIL_IF(child != NULL);
1179
1180
    FAIL_IF(ConfNodeLookupChild(NULL, NULL) != NULL);
1181
1182
    if (parent != NULL) {
1183
        ConfNodeFree(parent);
1184
    }
1185
1186
    PASS;
1187
}
1188
1189
static int ConfNodeLookupChildValueTest(void)
1190
{
1191
    const char *test_vals[] = { "one", "two", "three" };
1192
    size_t u;
1193
1194
    ConfNode *parent = ConfNodeNew();
1195
    ConfNode *child;
1196
    const char *value;
1197
1198
    for (u = 0; u < sizeof(test_vals)/sizeof(test_vals[0]); u++) {
1199
        child = ConfNodeNew();
1200
        child->name = SCStrdup(test_vals[u]);
1201
        child->val = SCStrdup(test_vals[u]);
1202
        TAILQ_INSERT_TAIL(&parent->head, child, next);
1203
    }
1204
1205
    value = (char *)ConfNodeLookupChildValue(parent, "one");
1206
    FAIL_IF(value == NULL);
1207
    FAIL_IF(strcmp(value, "one") != 0);
1208
1209
    value = (char *)ConfNodeLookupChildValue(parent, "two");
1210
    FAIL_IF(value == NULL);
1211
    FAIL_IF(strcmp(value, "two") != 0);
1212
1213
    value = (char *)ConfNodeLookupChildValue(parent, "three");
1214
    FAIL_IF(value == NULL);
1215
    FAIL_IF(strcmp(value, "three") != 0);
1216
1217
    value = (char *)ConfNodeLookupChildValue(parent, "four");
1218
    FAIL_IF(value != NULL);
1219
1220
    ConfNodeFree(parent);
1221
1222
    PASS;
1223
}
1224
1225
static int ConfGetChildValueWithDefaultTest(void)
1226
{
1227
    const char  *val = "";
1228
    ConfCreateContextBackup();
1229
    ConfInit();
1230
    ConfSet("af-packet.0.interface", "eth0");
1231
    ConfSet("af-packet.1.interface", "default");
1232
    ConfSet("af-packet.1.cluster-type", "cluster_cpu");
1233
1234
    ConfNode *myroot = ConfGetNode("af-packet.0");
1235
    ConfNode *dflt = ConfGetNode("af-packet.1");
1236
    ConfGetChildValueWithDefault(myroot, dflt, "cluster-type", &val);
1237
    FAIL_IF(strcmp(val, "cluster_cpu"));
1238
1239
    ConfSet("af-packet.0.cluster-type", "cluster_flow");
1240
    ConfGetChildValueWithDefault(myroot, dflt, "cluster-type", &val);
1241
1242
    FAIL_IF(strcmp(val, "cluster_flow"));
1243
1244
    ConfDeInit();
1245
    ConfRestoreContextBackup();
1246
    PASS;
1247
}
1248
1249
static int ConfGetChildValueIntWithDefaultTest(void)
1250
{
1251
    intmax_t val = 0;
1252
    ConfCreateContextBackup();
1253
    ConfInit();
1254
    ConfSet("af-packet.0.interface", "eth0");
1255
    ConfSet("af-packet.1.interface", "default");
1256
    ConfSet("af-packet.1.threads", "2");
1257
1258
    ConfNode *myroot = ConfGetNode("af-packet.0");
1259
    ConfNode *dflt = ConfGetNode("af-packet.1");
1260
    ConfGetChildValueIntWithDefault(myroot, dflt, "threads", &val);
1261
    FAIL_IF(val != 2);
1262
1263
    ConfSet("af-packet.0.threads", "1");
1264
    ConfGetChildValueIntWithDefault(myroot, dflt, "threads", &val);
1265
    FAIL_IF(val != 1);
1266
1267
    ConfDeInit();
1268
    ConfRestoreContextBackup();
1269
1270
    PASS;
1271
}
1272
1273
static int ConfGetChildValueBoolWithDefaultTest(void)
1274
{
1275
    int val;
1276
    ConfCreateContextBackup();
1277
    ConfInit();
1278
    ConfSet("af-packet.0.interface", "eth0");
1279
    ConfSet("af-packet.1.interface", "default");
1280
    ConfSet("af-packet.1.use-mmap", "yes");
1281
1282
    ConfNode *myroot = ConfGetNode("af-packet.0");
1283
    ConfNode *dflt = ConfGetNode("af-packet.1");
1284
    ConfGetChildValueBoolWithDefault(myroot, dflt, "use-mmap", &val);
1285
    FAIL_IF(val == 0);
1286
1287
    ConfSet("af-packet.0.use-mmap", "no");
1288
    ConfGetChildValueBoolWithDefault(myroot, dflt, "use-mmap", &val);
1289
    FAIL_IF(val);
1290
1291
    ConfDeInit();
1292
    ConfRestoreContextBackup();
1293
1294
    PASS;
1295
}
1296
1297
/**
1298
 * Test the removal of a configuration node.
1299
 */
1300
static int ConfNodeRemoveTest(void)
1301
{
1302
    ConfCreateContextBackup();
1303
    ConfInit();
1304
1305
    FAIL_IF(ConfSet("some.nested.parameter", "blah") != 1);
1306
1307
    ConfNode *node = ConfGetNode("some.nested.parameter");
1308
    FAIL_IF(node == NULL);
1309
    ConfNodeRemove(node);
1310
1311
    node = ConfGetNode("some.nested.parameter");
1312
    FAIL_IF(node != NULL);
1313
1314
    ConfDeInit();
1315
    ConfRestoreContextBackup();
1316
1317
    PASS;
1318
}
1319
1320
static int ConfSetTest(void)
1321
{
1322
    ConfCreateContextBackup();
1323
    ConfInit();
1324
1325
    /* Set some value with 2 levels. */
1326
    FAIL_IF(ConfSet("one.two", "three") != 1);
1327
    ConfNode *n = ConfGetNode("one.two");
1328
    FAIL_IF(n == NULL);
1329
1330
    /* Set another 2 level parameter with the same first level, this
1331
     * used to trigger a bug that caused the second level of the name
1332
     * to become a first level node. */
1333
    FAIL_IF(ConfSet("one.three", "four") != 1);
1334
1335
    n = ConfGetNode("one.three");
1336
    FAIL_IF(n == NULL);
1337
1338
    /* A top level node of "three" should not exist. */
1339
    n = ConfGetNode("three");
1340
    FAIL_IF(n != NULL);
1341
1342
    ConfDeInit();
1343
    ConfRestoreContextBackup();
1344
1345
    PASS;
1346
}
1347
1348
static int ConfGetNodeOrCreateTest(void)
1349
{
1350
    ConfNode *node;
1351
1352
    ConfCreateContextBackup();
1353
    ConfInit();
1354
1355
    /* Get a node that should not exist, give it a value, re-get it
1356
     * and make sure the second time it returns the existing node. */
1357
    node = ConfGetNodeOrCreate("node0", 0);
1358
    FAIL_IF(node == NULL);
1359
    FAIL_IF(node->parent == NULL || node->parent != root);
1360
    FAIL_IF(node->val != NULL);
1361
    node->val = SCStrdup("node0");
1362
    node = ConfGetNodeOrCreate("node0", 0);
1363
    FAIL_IF(node == NULL);
1364
    FAIL_IF(node->val == NULL);
1365
    FAIL_IF(strcmp(node->val, "node0") != 0);
1366
1367
    /* Do the same, but for something deeply nested. */
1368
    node = ConfGetNodeOrCreate("parent.child.grandchild", 0);
1369
    FAIL_IF(node == NULL);
1370
    FAIL_IF(node->parent == NULL || node->parent == root);
1371
    FAIL_IF(node->val != NULL);
1372
    node->val = SCStrdup("parent.child.grandchild");
1373
    node = ConfGetNodeOrCreate("parent.child.grandchild", 0);
1374
    FAIL_IF(node == NULL);
1375
    FAIL_IF(node->val == NULL);
1376
    FAIL_IF(strcmp(node->val, "parent.child.grandchild") != 0);
1377
1378
    /* Test that 2 child nodes have the same root. */
1379
    ConfNode *child1 = ConfGetNodeOrCreate("parent.kids.child1", 0);
1380
    ConfNode *child2 = ConfGetNodeOrCreate("parent.kids.child2", 0);
1381
    FAIL_IF(child1 == NULL || child2 == NULL);
1382
    FAIL_IF(child1->parent != child2->parent);
1383
    FAIL_IF(strcmp(child1->parent->name, "kids") != 0);
1384
1385
    ConfDeInit();
1386
    ConfRestoreContextBackup();
1387
1388
    PASS;
1389
}
1390
1391
static int ConfNodePruneTest(void)
1392
{
1393
    ConfNode *node;
1394
1395
    ConfCreateContextBackup();
1396
    ConfInit();
1397
1398
    /* Test that final nodes exist after a prune. */
1399
    FAIL_IF(ConfSet("node.notfinal", "notfinal") != 1);
1400
    FAIL_IF(ConfSetFinal("node.final", "final") != 1);
1401
    FAIL_IF(ConfGetNode("node.notfinal") == NULL);
1402
    FAIL_IF(ConfGetNode("node.final") == NULL);
1403
    FAIL_IF((node = ConfGetNode("node")) == NULL);
1404
    ConfNodePrune(node);
1405
    FAIL_IF(ConfGetNode("node.notfinal") != NULL);
1406
    FAIL_IF(ConfGetNode("node.final") == NULL);
1407
1408
    /* Test that everything under a final node exists after a prune. */
1409
    FAIL_IF(ConfSet("node.final.one", "one") != 1);
1410
    FAIL_IF(ConfSet("node.final.two", "two") != 1);
1411
    ConfNodePrune(node);
1412
    FAIL_IF(ConfNodeLookupChild(node, "final") == NULL);
1413
    FAIL_IF(ConfGetNode("node.final.one") == NULL);
1414
    FAIL_IF(ConfGetNode("node.final.two") == NULL);
1415
1416
    ConfDeInit();
1417
    ConfRestoreContextBackup();
1418
1419
    PASS;
1420
}
1421
1422
static int ConfNodeIsSequenceTest(void)
1423
{
1424
    ConfNode *node = ConfNodeNew();
1425
    FAIL_IF(node == NULL);
1426
    FAIL_IF(ConfNodeIsSequence(node));
1427
    node->is_seq = 1;
1428
    FAIL_IF(!ConfNodeIsSequence(node));
1429
1430
    if (node != NULL) {
1431
        ConfNodeFree(node);
1432
    }
1433
    PASS;
1434
}
1435
1436
static int ConfSetFromStringTest(void)
1437
{
1438
    ConfNode *n;
1439
1440
    ConfCreateContextBackup();
1441
    ConfInit();
1442
1443
    FAIL_IF_NOT(ConfSetFromString("stream.midstream=true", 0));
1444
    n = ConfGetNode("stream.midstream");
1445
    FAIL_IF_NULL(n);
1446
    FAIL_IF_NULL(n->val);
1447
    FAIL_IF(strcmp("true", n->val));
1448
1449
    FAIL_IF_NOT(ConfSetFromString("stream.midstream =false", 0));
1450
    n = ConfGetNode("stream.midstream");
1451
    FAIL_IF_NULL(n);
1452
    FAIL_IF(n->val == NULL || strcmp("false", n->val));
1453
1454
    FAIL_IF_NOT(ConfSetFromString("stream.midstream= true", 0));
1455
    n = ConfGetNode("stream.midstream");
1456
    FAIL_IF_NULL(n);
1457
    FAIL_IF(n->val == NULL || strcmp("true", n->val));
1458
1459
    FAIL_IF_NOT(ConfSetFromString("stream.midstream = false", 0));
1460
    n = ConfGetNode("stream.midstream");
1461
    FAIL_IF_NULL(n);
1462
    FAIL_IF(n->val == NULL || strcmp("false", n->val));
1463
1464
    ConfDeInit();
1465
    ConfRestoreContextBackup();
1466
    PASS;
1467
}
1468
1469
static int ConfNodeHasChildrenTest(void)
1470
{
1471
    ConfCreateContextBackup();
1472
    ConfInit();
1473
1474
    /* Set a plain key with value. */
1475
    ConfSet("no-children", "value");
1476
    ConfNode *n = ConfGetNode("no-children");
1477
    FAIL_IF_NULL(n);
1478
    FAIL_IF(ConfNodeHasChildren(n));
1479
1480
    /* Set a key with a sub key to a value. This makes the first key a
1481
     * map. */
1482
    ConfSet("parent.child", "value");
1483
    n = ConfGetNode("parent");
1484
    FAIL_IF_NULL(n);
1485
    FAIL_IF(!ConfNodeHasChildren(n));
1486
1487
    ConfDeInit();
1488
    ConfRestoreContextBackup();
1489
    PASS;
1490
}
1491
1492
void ConfRegisterTests(void)
1493
{
1494
    UtRegisterTest("ConfTestGetNonExistant", ConfTestGetNonExistant);
1495
    UtRegisterTest("ConfSetTest", ConfSetTest);
1496
    UtRegisterTest("ConfTestSetAndGet", ConfTestSetAndGet);
1497
    UtRegisterTest("ConfTestOverrideValue1", ConfTestOverrideValue1);
1498
    UtRegisterTest("ConfTestOverrideValue2", ConfTestOverrideValue2);
1499
    UtRegisterTest("ConfTestGetInt", ConfTestGetInt);
1500
    UtRegisterTest("ConfTestGetBool", ConfTestGetBool);
1501
    UtRegisterTest("ConfNodeLookupChildTest", ConfNodeLookupChildTest);
1502
    UtRegisterTest("ConfNodeLookupChildValueTest",
1503
                   ConfNodeLookupChildValueTest);
1504
    UtRegisterTest("ConfNodeRemoveTest", ConfNodeRemoveTest);
1505
    UtRegisterTest("ConfGetChildValueWithDefaultTest",
1506
                   ConfGetChildValueWithDefaultTest);
1507
    UtRegisterTest("ConfGetChildValueIntWithDefaultTest",
1508
                   ConfGetChildValueIntWithDefaultTest);
1509
    UtRegisterTest("ConfGetChildValueBoolWithDefaultTest",
1510
                   ConfGetChildValueBoolWithDefaultTest);
1511
    UtRegisterTest("ConfGetNodeOrCreateTest", ConfGetNodeOrCreateTest);
1512
    UtRegisterTest("ConfNodePruneTest", ConfNodePruneTest);
1513
    UtRegisterTest("ConfNodeIsSequenceTest", ConfNodeIsSequenceTest);
1514
    UtRegisterTest("ConfSetFromStringTest", ConfSetFromStringTest);
1515
    UtRegisterTest("ConfNodeHasChildrenTest", ConfNodeHasChildrenTest);
1516
}
1517
1518
#endif /* UNITTESTS */