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 #############################################################################*/
28 #include <sys/queue.h>
29 #include <sys/types.h>
38 struct nw_config_entry
{
39 STAILQ_ENTRY(nw_config_entry
) nodes
;
41 char key
[NETWORK_CONFIG_KEY_MAX_LENGTH
];
42 char value
[NETWORK_CONFIG_KEY_MAX_LENGTH
];
45 struct nw_config_option
{
46 STAILQ_ENTRY(nw_config_option
) nodes
;
53 nw_config_option_read_callback_t read_callback
;
54 nw_config_option_write_callback_t write_callback
;
61 STAILQ_HEAD(config_entries
, nw_config_entry
) entries
;
64 STAILQ_HEAD(parser_entries
, nw_config_option
) options
;
67 static void nw_config_entry_free(struct nw_config_entry
* entry
) {
71 static void nw_config_option_free(struct nw_config_option
* option
) {
75 static struct nw_config_entry
* nw_config_entry_create(
76 nw_config
* config
, const char* key
) {
85 // Allocate a new object
86 struct nw_config_entry
* entry
= calloc(1, sizeof(*entry
));
91 r
= nw_string_set(entry
->key
, key
);
95 // Append the new entry
96 STAILQ_INSERT_TAIL(&config
->entries
, entry
, nodes
);
101 nw_config_entry_free(entry
);
105 static void nw_config_free(nw_config
* config
) {
106 struct nw_config_option
* option
= NULL
;
109 nw_config_flush(config
);
112 while (!STAILQ_EMPTY(&config
->options
)) {
113 option
= STAILQ_FIRST(&config
->options
);
114 STAILQ_REMOVE_HEAD(&config
->options
, nodes
);
117 nw_config_option_free(option
);
123 int nw_config_create(nw_config
** config
, FILE* f
) {
126 nw_config
* c
= calloc(1, sizeof(*c
));
130 // Initialize reference counter
133 // Initialise entries
134 STAILQ_INIT(&c
->entries
);
136 // Initialise options
137 STAILQ_INIT(&c
->options
);
139 // Read configuration
141 r
= nw_config_read(c
, f
);
156 int nw_config_open(nw_config
** config
, const char* path
) {
161 f
= fopen(path
, "r");
165 // Create a new configuration
166 r
= nw_config_create(config
, f
);
174 nw_config
* nw_config_ref(nw_config
* config
) {
180 nw_config
* nw_config_unref(nw_config
* config
) {
181 if (--config
->nrefs
> 0)
184 nw_config_free(config
);
188 int nw_config_copy(nw_config
* config
, nw_config
** copy
) {
189 struct nw_config_entry
* entry
= NULL
;
193 // Create a new configuration
194 r
= nw_config_create(&c
, NULL
);
199 STAILQ_FOREACH(entry
, &config
->entries
, nodes
) {
200 r
= nw_config_set(c
, entry
->key
, entry
->value
);
215 int nw_config_flush(nw_config
* config
) {
216 struct nw_config_entry
* entry
= NULL
;
218 while (!STAILQ_EMPTY(&config
->entries
)) {
219 entry
= STAILQ_FIRST(&config
->entries
);
220 STAILQ_REMOVE_HEAD(&config
->entries
, nodes
);
223 nw_config_entry_free(entry
);
229 int nw_config_read(nw_config
* config
, FILE* f
) {
234 ssize_t bytes_read
= 0;
240 // Read the next line
241 bytes_read
= getline(&line
, &length
, f
);
245 // Key starts at the beginning of the line
248 // Value starts after '='
249 val
= strchr(line
, '=');
251 // Invalid line without a '=' character
258 // Strip any whitespace from value
259 r
= nw_string_strip(val
);
264 r
= nw_config_set(config
, key
, val
);
275 int nw_config_write(nw_config
* config
, FILE* f
) {
276 struct nw_config_entry
* entry
= NULL
;
279 STAILQ_FOREACH(entry
, &config
->entries
, nodes
) {
280 // Skip if value is NULL
285 r
= fprintf(f
, "%s=%s\n", entry
->key
, entry
->value
);
287 ERROR("Failed to write configuration: %m\n");
295 static struct nw_config_entry
* nw_config_find(nw_config
* config
, const char* key
) {
296 struct nw_config_entry
* entry
= NULL
;
298 STAILQ_FOREACH(entry
, &config
->entries
, nodes
) {
300 if (strcmp(entry
->key
, key
) != 0)
311 int nw_config_del(nw_config
* config
, const char* key
) {
312 struct nw_config_entry
* entry
= NULL
;
314 // Find an entry matching the key
315 entry
= nw_config_find(config
, key
);
317 // If there is no entry, there is nothing to do
321 // Otherwise remove the object
322 STAILQ_REMOVE(&config
->entries
, entry
, nw_config_entry
, nodes
);
325 nw_config_entry_free(entry
);
330 const char* nw_config_get(nw_config
* config
, const char* key
) {
331 struct nw_config_entry
* entry
= nw_config_find(config
, key
);
333 // Return the value if found and set
334 if (entry
&& *entry
->value
)
337 // Otherwise return NULL
341 int nw_config_set(nw_config
* config
, const char* key
, const char* value
) {
342 struct nw_config_entry
* entry
= NULL
;
345 DEBUG("%p: Setting %s = %s\n", config
, key
, value
);
347 // Delete the entry if val is NULL
349 return nw_config_del(config
, key
);
351 // Find any existing entries
352 entry
= nw_config_find(config
, key
);
354 // Create a new entry if it doesn't exist, yet
356 entry
= nw_config_entry_create(config
, key
);
361 // Store the new value
362 return nw_string_set(entry
->value
, value
);
365 int nw_config_get_int(nw_config
* config
, const char* key
, const int __default
) {
369 const char* value
= nw_config_get(config
, key
);
371 // Return zero if not set
376 r
= strtoul(value
, &p
, 10);
378 // If we have characters following the input, we throw it away
385 int nw_config_set_int(nw_config
* config
, const char* key
, const int value
) {
389 // Format the value as string
390 r
= nw_string_format(__value
, "%d", value
);
394 return nw_config_set(config
, key
, __value
);
397 static const char* nw_config_true
[] = {
404 int nw_config_get_bool(nw_config
* config
, const char* key
) {
405 const char* value
= nw_config_get(config
, key
);
407 // No value indicates false
411 // Check if we match any known true words
412 for (const char** s
= nw_config_true
; *s
; s
++) {
413 if (strcasecmp(value
, *s
) == 0)
417 // No match means false
421 int nw_config_set_bool(nw_config
* config
, const char* key
, const int value
) {
422 return nw_config_set(config
, key
, value
? "true" : "false");
436 static void nw_configd_free(nw_configd
* dir
) {
443 static int __nw_configd_create(nw_configd
** dir
, int fd
, const char* path
) {
444 nw_configd
* d
= NULL
;
447 // Allocate a new object
448 d
= calloc(1, sizeof(*d
));
452 // Initialize the reference counter
455 // Store the file descriptor
464 r
= nw_string_set(d
->path
, path
);
477 int nw_configd_create(nw_configd
** dir
, const char* path
) {
480 // Open the directory
481 fd
= open(path
, O_DIRECTORY
);
483 ERROR("Could not open %s: %m\n", path
);
487 return __nw_configd_create(dir
, fd
, path
);
490 nw_configd
* nw_configd_ref(nw_configd
* dir
) {
496 nw_configd
* nw_configd_unref(nw_configd
* dir
) {
497 if (--dir
->nrefs
> 0)
500 nw_configd_free(dir
);
504 static int nw_configd_open(nw_configd
* dir
, const char* path
, int flags
) {
505 return openat(dir
->fd
, path
, flags
);
508 FILE* nw_configd_fopen(nw_configd
* dir
, const char* path
, const char* mode
) {
512 fd
= nw_configd_open(dir
, path
, 0);
516 // Return a file handle
517 return fdopen(fd
, mode
);
520 int nw_configd_open_config(nw_config
** config
, nw_configd
* dir
, const char* path
) {
525 f
= nw_configd_fopen(dir
, path
, "r");
529 // Create configuration
530 r
= nw_config_create(config
, f
);
541 int nw_configd_unlink(nw_configd
* dir
, const char* path
, int flags
) {
542 return unlinkat(dir
->fd
, path
, flags
);
545 nw_configd
* nw_configd_descend(nw_configd
* dir
, const char* path
) {
546 nw_configd
* d
= NULL
;
552 r
= nw_path_join(p
, dir
->path
, path
);
557 fd
= nw_configd_open(dir
, path
, O_DIRECTORY
);
559 ERROR("Could not open %s: %m\n", p
);
563 // Create a new config directory object
564 r
= __nw_configd_create(&d
, fd
, p
);
575 int nw_configd_walk(nw_configd
* dir
, nw_configd_walk_callback callback
, void* data
) {
578 struct dirent
* e
= NULL
;
581 // Re-open the directory
582 d
= fdopendir(dir
->fd
);
588 // Walk trough everything
590 // Read the next entry
595 // Skip anything that is not a regular file
596 if (e
->d_type
!= DT_REG
)
600 if (e
->d_name
[0] == '.')
604 f
= nw_configd_fopen(dir
, e
->d_name
, "r");
611 r
= callback(e
, f
, data
);
631 int nw_config_options_read(nw_config
* config
) {
632 struct nw_config_option
* option
= NULL
;
635 STAILQ_FOREACH(option
, &config
->options
, nodes
) {
636 r
= option
->read_callback(config
,
637 option
->key
, option
->value
, option
->length
, option
->data
);
645 int nw_config_options_write(nw_config
* config
) {
646 struct nw_config_option
* option
= NULL
;
649 STAILQ_FOREACH(option
, &config
->options
, nodes
) {
650 r
= option
->write_callback(config
,
651 option
->key
, option
->value
, option
->length
, option
->data
);
659 int nw_config_option_add(nw_config
* config
,
660 const char* key
, void* value
, const size_t length
,
661 nw_config_option_read_callback_t read_callback
,
662 nw_config_option_write_callback_t write_callback
, void* data
) {
664 if (!key
|| !value
|| !read_callback
|| !write_callback
)
667 // Allocate a new option
668 struct nw_config_option
* option
= calloc(1, sizeof(*option
));
676 option
->value
= value
;
677 option
->length
= length
;
680 option
->read_callback
= read_callback
;
681 option
->write_callback
= write_callback
;
684 // Append the new option
685 STAILQ_INSERT_TAIL(&config
->options
, option
, nodes
);
690 int nw_config_read_int(nw_config
* config
,
691 const char* key
, void* value
, const size_t length
, void* data
) {
693 *(int*)value
= nw_config_get_int(config
, key
, -1);
698 int nw_config_write_int(nw_config
* config
,
699 const char* key
, const void* value
, const size_t length
, void* data
) {
705 int nw_config_read_string(nw_config
* config
,
706 const char* key
, void* value
, const size_t length
, void* data
) {
708 const char* p
= nw_config_get(config
, key
);
710 *(const char**)value
= p
;
715 int nw_config_write_string(nw_config
* config
,
716 const char* key
, const void* value
, const size_t length
, void* data
) {
717 return nw_config_set(config
, key
, *(const char**)value
);
722 int nw_config_read_string_buffer(nw_config
* config
,
723 const char* key
, void* value
, const size_t length
, void* data
) {
724 char* string
= (char*)value
;
727 const char* p
= nw_config_get(config
, key
);
729 return __nw_string_set(string
, length
, p
);
736 int nw_config_read_string_table(nw_config
* config
,
737 const char* key
, void* value
, const size_t length
, void* data
) {
738 const char* s
= NULL
;
739 int* v
= (int*)value
;
741 const nw_string_table_t
* table
= (nw_string_table_t
*)data
;
744 s
= nw_config_get(config
, key
);
748 // Lookup the string in the table
749 *v
= nw_string_table_lookup_id(table
, s
);
751 // If the result is negative, nothing was found
758 int nw_config_write_string_table(nw_config
* config
,
759 const char* key
, const void* value
, const size_t length
, void* data
) {
760 int* v
= (int*)value
;
762 const nw_string_table_t
* table
= (nw_string_table_t
*)data
;
765 const char* s
= nw_string_table_lookup_string(table
, *v
);
769 return nw_config_set(config
, key
, s
);
774 int nw_config_read_address(nw_config
* config
,
775 const char* key
, void* value
, const size_t length
, void* data
) {
776 nw_address_t
* address
= (nw_address_t
*)value
;
780 const char* p
= nw_config_get(config
, key
);
784 r
= nw_address_from_string(address
, p
);
786 ERROR("Could not parse address: %s\n", p
);
791 int nw_config_write_address(nw_config
* config
,
792 const char* key
, const void* value
, const size_t length
, void* data
) {
793 const nw_address_t
* address
= (nw_address_t
*)value
;
796 // Format the address to string
797 char* p
= nw_address_to_string(address
);
802 r
= nw_config_set(config
, key
, p
);