/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 */ |