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