1 /*#############################################################################
3 # IPFire.org - A linux based firewall #
4 # Copyright (C) 2023 IPFire Network Development Team #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 #############################################################################*/
26 #include <sys/queue.h>
34 struct nw_config_entry
{
35 STAILQ_ENTRY(nw_config_entry
) nodes
;
37 char key
[NETWORK_CONFIG_KEY_MAX_LENGTH
];
38 char value
[NETWORK_CONFIG_KEY_MAX_LENGTH
];
41 struct nw_config_option
{
42 STAILQ_ENTRY(nw_config_option
) nodes
;
48 nw_config_option_read_callback_t read_callback
;
49 nw_config_option_write_callback_t write_callback
;
56 // The path to the configuration file
59 STAILQ_HEAD(config_entries
, nw_config_entry
) entries
;
62 STAILQ_HEAD(parser_entries
, nw_config_option
) options
;
65 static void nw_config_entry_free(struct nw_config_entry
* entry
) {
69 static void nw_config_option_free(struct nw_config_option
* option
) {
73 static struct nw_config_entry
* nw_config_entry_create(
74 nw_config
* config
, const char* key
) {
83 // Allocate a new object
84 struct nw_config_entry
* entry
= calloc(1, sizeof(*entry
));
89 r
= nw_string_set(entry
->key
, key
);
93 // Append the new entry
94 STAILQ_INSERT_TAIL(&config
->entries
, entry
, nodes
);
99 nw_config_entry_free(entry
);
103 static void nw_config_free(nw_config
* config
) {
104 struct nw_config_option
* option
= NULL
;
107 nw_config_flush(config
);
110 while (!STAILQ_EMPTY(&config
->options
)) {
111 option
= STAILQ_FIRST(&config
->options
);
112 STAILQ_REMOVE_HEAD(&config
->options
, nodes
);
115 nw_config_option_free(option
);
121 int nw_config_create(nw_config
** config
, const char* path
) {
124 nw_config
* c
= calloc(1, sizeof(*c
));
128 // Initialize reference counter
131 // Initialise entries
132 STAILQ_INIT(&c
->entries
);
134 // Initialise options
135 STAILQ_INIT(&c
->options
);
139 r
= nw_string_set(c
->path
, path
);
143 // Try to read the configuration from path
144 r
= nw_config_read(c
);
159 nw_config
* nw_config_ref(nw_config
* config
) {
165 nw_config
* nw_config_unref(nw_config
* config
) {
166 if (--config
->nrefs
> 0)
169 nw_config_free(config
);
173 int nw_config_destroy(nw_config
* config
) {
177 r
= nw_config_flush(config
);
181 return unlink(config
->path
);
184 int nw_config_copy(nw_config
* config
, nw_config
** copy
) {
185 struct nw_config_entry
* entry
= NULL
;
189 // Create a new configuration
190 r
= nw_config_create(&c
, NULL
);
195 STAILQ_FOREACH(entry
, &config
->entries
, nodes
) {
196 r
= nw_config_set(c
, entry
->key
, entry
->value
);
211 const char* nw_config_path(nw_config
* config
) {
218 int nw_config_flush(nw_config
* config
) {
219 struct nw_config_entry
* entry
= NULL
;
221 while (!STAILQ_EMPTY(&config
->entries
)) {
222 entry
= STAILQ_FIRST(&config
->entries
);
223 STAILQ_REMOVE_HEAD(&config
->entries
, nodes
);
226 nw_config_entry_free(entry
);
232 static int nw_config_readf(nw_config
* config
, FILE* f
) {
237 ssize_t bytes_read
= 0;
243 // Read the next line
244 bytes_read
= getline(&line
, &length
, f
);
248 // Key starts at the beginning of the line
251 // Value starts after '='
252 val
= strchr(line
, '=');
254 // Invalid line without a '=' character
261 // Strip any whitespace from value
262 r
= nw_string_strip(val
);
267 r
= nw_config_set(config
, key
, val
);
278 int nw_config_read(nw_config
* config
) {
282 // We cannot read if path is not set
283 if (!*config
->path
) {
289 f
= fopen(config
->path
, "r");
291 // Silently ignore if the file does not exist
295 ERROR("Could not read configuration file %s: %m\n", config
->path
);
301 r
= nw_config_readf(config
, f
);
310 static int nw_config_writef(nw_config
* config
, FILE* f
) {
311 struct nw_config_entry
* entry
= NULL
;
314 STAILQ_FOREACH(entry
, &config
->entries
, nodes
) {
315 // Skip if value is NULL
320 r
= fprintf(f
, "%s=%s\n", entry
->key
, entry
->value
);
322 ERROR("Failed to write configuration: %m\n");
330 int nw_config_write(nw_config
* config
) {
333 // We cannot write if path is not set
334 if (!*config
->path
) {
339 FILE* f
= fopen(config
->path
, "w");
341 ERROR("Failed to open %s for writing: %m\n", config
->path
);
346 // Write configuration
347 r
= nw_config_writef(config
, f
);
356 static struct nw_config_entry
* nw_config_find(nw_config
* config
, const char* key
) {
357 struct nw_config_entry
* entry
= NULL
;
359 STAILQ_FOREACH(entry
, &config
->entries
, nodes
) {
361 if (strcmp(entry
->key
, key
) != 0)
372 int nw_config_del(nw_config
* config
, const char* key
) {
373 struct nw_config_entry
* entry
= NULL
;
375 // Find an entry matching the key
376 entry
= nw_config_find(config
, key
);
378 // If there is no entry, there is nothing to do
382 // Otherwise remove the object
383 STAILQ_REMOVE(&config
->entries
, entry
, nw_config_entry
, nodes
);
386 nw_config_entry_free(entry
);
391 const char* nw_config_get(nw_config
* config
, const char* key
) {
392 struct nw_config_entry
* entry
= nw_config_find(config
, key
);
394 // Return the value if found and set
395 if (entry
&& *entry
->value
)
398 // Otherwise return NULL
402 int nw_config_set(nw_config
* config
, const char* key
, const char* value
) {
403 struct nw_config_entry
* entry
= NULL
;
406 DEBUG("%p: Setting %s = %s\n", config
, key
, value
);
408 // Delete the entry if val is NULL
410 return nw_config_del(config
, key
);
412 // Find any existing entries
413 entry
= nw_config_find(config
, key
);
415 // Create a new entry if it doesn't exist, yet
417 entry
= nw_config_entry_create(config
, key
);
422 // Store the new value
423 return nw_string_set(entry
->value
, value
);
426 int nw_config_get_int(nw_config
* config
, const char* key
, const int __default
) {
430 const char* value
= nw_config_get(config
, key
);
432 // Return zero if not set
437 r
= strtoul(value
, &p
, 10);
439 // If we have characters following the input, we throw it away
446 int nw_config_set_int(nw_config
* config
, const char* key
, const int value
) {
450 // Format the value as string
451 r
= nw_string_format(__value
, "%d", value
);
455 return nw_config_set(config
, key
, __value
);
458 static const char* nw_config_true
[] = {
465 int nw_config_get_bool(nw_config
* config
, const char* key
) {
466 const char* value
= nw_config_get(config
, key
);
468 // No value indicates false
472 // Check if we match any known true words
473 for (const char** s
= nw_config_true
; *s
; s
++) {
474 if (strcasecmp(value
, *s
) == 0)
478 // No match means false
482 int nw_config_set_bool(nw_config
* config
, const char* key
, const int value
) {
483 return nw_config_set(config
, key
, value
? "true" : "false");
490 int nw_config_options_read(nw_config
* config
) {
491 struct nw_config_option
* option
= NULL
;
494 STAILQ_FOREACH(option
, &config
->options
, nodes
) {
495 r
= option
->read_callback(config
, option
->key
, option
->value
, option
->data
);
503 int nw_config_options_write(nw_config
* config
) {
504 struct nw_config_option
* option
= NULL
;
507 STAILQ_FOREACH(option
, &config
->options
, nodes
) {
508 r
= option
->write_callback(config
, option
->key
, option
->value
, option
->data
);
516 int nw_config_option_add(nw_config
* config
, const char* key
, void* value
,
517 nw_config_option_read_callback_t read_callback
,
518 nw_config_option_write_callback_t write_callback
, void* data
) {
520 if (!key
|| !value
|| !read_callback
|| !write_callback
)
523 // Allocate a new option
524 struct nw_config_option
* option
= calloc(1, sizeof(*option
));
532 option
->value
= value
;
535 option
->read_callback
= read_callback
;
536 option
->write_callback
= write_callback
;
539 // Append the new option
540 STAILQ_INSERT_TAIL(&config
->options
, option
, nodes
);
545 int nw_config_read_int(nw_config
* config
, const char* key
, void* value
, void* data
) {
547 *(int*)value
= nw_config_get_int(config
, key
, -1);
552 int nw_config_write_int(nw_config
* config
,
553 const char* key
, const void* value
, void* data
) {
559 int nw_config_read_string(nw_config
* config
, const char* key
, void* value
, void* data
) {
561 const char* p
= nw_config_get(config
, key
);
563 *(const char**)value
= p
;
568 int nw_config_write_string(nw_config
* config
,
569 const char* key
, const void* value
, void* data
) {
570 return nw_config_set(config
, key
, *(const char**)value
);
575 int nw_config_read_string_table(nw_config
* config
, const char* key
, void* value
, void* data
) {
576 const char* s
= NULL
;
577 int* v
= (int*)value
;
579 const nw_string_table_t
* table
= (nw_string_table_t
*)data
;
582 s
= nw_config_get(config
, key
);
586 // Lookup the string in the table
587 *v
= nw_string_table_lookup_id(table
, s
);
589 // If the result is negative, nothing was found
596 int nw_config_write_string_table(nw_config
* config
,
597 const char* key
, const void* value
, void* data
) {
598 int* v
= (int*)value
;
600 const nw_string_table_t
* table
= (nw_string_table_t
*)data
;
603 const char* s
= nw_string_table_lookup_string(table
, *v
);
607 return nw_config_set(config
, key
, s
);
612 int nw_config_read_address(nw_config
* config
, const char* key
, void* value
, void* data
) {
613 nw_address_t
* address
= (nw_address_t
*)value
;
617 const char* p
= nw_config_get(config
, key
);
621 r
= nw_address_from_string(address
, p
);
623 ERROR("Could not parse address: %s\n", p
);
628 int nw_config_write_address(nw_config
* config
,
629 const char* key
, const void* value
, void* data
) {
630 const nw_address_t
* address
= (nw_address_t
*)value
;
633 // Format the address to string
634 char* p
= nw_address_to_string(address
);
639 r
= nw_config_set(config
, key
, p
);