]> git.ipfire.org Git - people/ms/network.git/blame - src/networkd/config.c
Makefile: Fix typo in localstatedir
[people/ms/network.git] / src / networkd / config.c
CommitLineData
6b666d62
MT
1/*#############################################################################
2# #
3# IPFire.org - A linux based firewall #
4# Copyright (C) 2023 IPFire Network Development Team #
5# #
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. #
10# #
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. #
15# #
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/>. #
18# #
19#############################################################################*/
20
25808f65 21#include <dirent.h>
4237caa2 22#include <errno.h>
25808f65 23#include <fcntl.h>
b076fa8b 24#include <limits.h>
c81a6335 25#include <stdio.h>
6b666d62 26#include <stdlib.h>
4237caa2
MT
27#include <string.h>
28#include <sys/queue.h>
25808f65
MT
29#include <sys/types.h>
30#include <sys/stat.h>
c403eb4c 31#include <unistd.h>
6b666d62 32
082d81a3 33#include "address.h"
6b666d62 34#include "config.h"
c81a6335 35#include "logging.h"
4237caa2
MT
36#include "string.h"
37
38struct nw_config_entry {
39 STAILQ_ENTRY(nw_config_entry) nodes;
40
41 char key[NETWORK_CONFIG_KEY_MAX_LENGTH];
42 char value[NETWORK_CONFIG_KEY_MAX_LENGTH];
43};
6b666d62 44
082d81a3
MT
45struct nw_config_option {
46 STAILQ_ENTRY(nw_config_option) nodes;
47
48 const char* key;
cc54472e 49 void* value;
e633147d 50 size_t length;
082d81a3
MT
51
52 // Callbacks
53 nw_config_option_read_callback_t read_callback;
54 nw_config_option_write_callback_t write_callback;
3eeb38b7 55 void* data;
082d81a3
MT
56};
57
6b666d62
MT
58struct nw_config {
59 int nrefs;
4237caa2 60
082d81a3
MT
61 STAILQ_HEAD(config_entries, nw_config_entry) entries;
62
63 // Options
64 STAILQ_HEAD(parser_entries, nw_config_option) options;
6b666d62
MT
65};
66
4237caa2
MT
67static void nw_config_entry_free(struct nw_config_entry* entry) {
68 free(entry);
69}
70
082d81a3
MT
71static void nw_config_option_free(struct nw_config_option* option) {
72 free(option);
73}
74
4237caa2 75static struct nw_config_entry* nw_config_entry_create(
2361667e 76 nw_config* config, const char* key) {
4237caa2
MT
77 int r;
78
79 // Check input value
80 if (!key) {
81 errno = EINVAL;
82 return NULL;
83 }
84
85 // Allocate a new object
86 struct nw_config_entry* entry = calloc(1, sizeof(*entry));
87 if (!entry)
88 return NULL;
89
90 // Store the key
91 r = nw_string_set(entry->key, key);
92 if (r)
93 goto ERROR;
94
95 // Append the new entry
96 STAILQ_INSERT_TAIL(&config->entries, entry, nodes);
97
82f84c5f
MT
98 return entry;
99
4237caa2
MT
100ERROR:
101 nw_config_entry_free(entry);
102 return NULL;
103}
104
2361667e 105static void nw_config_free(nw_config* config) {
082d81a3
MT
106 struct nw_config_option* option = NULL;
107
91915f80
MT
108 // Flush all entries
109 nw_config_flush(config);
4237caa2 110
082d81a3
MT
111 // Free all options
112 while (!STAILQ_EMPTY(&config->options)) {
113 option = STAILQ_FIRST(&config->options);
114 STAILQ_REMOVE_HEAD(&config->options, nodes);
115
116 // Free the options
117 nw_config_option_free(option);
118 }
119
6b666d62
MT
120 free(config);
121}
122
8edf3da1 123int nw_config_create(nw_config** config, FILE* f) {
b076fa8b
MT
124 int r;
125
2361667e 126 nw_config* c = calloc(1, sizeof(*c));
6b666d62
MT
127 if (!c)
128 return 1;
129
130 // Initialize reference counter
131 c->nrefs = 1;
132
4237caa2
MT
133 // Initialise entries
134 STAILQ_INIT(&c->entries);
135
082d81a3
MT
136 // Initialise options
137 STAILQ_INIT(&c->options);
138
8edf3da1
MT
139 // Read configuration
140 if (f) {
141 r = nw_config_read(c, f);
142 if (r < 0)
b076fa8b
MT
143 goto ERROR;
144 }
145
6b666d62
MT
146 *config = c;
147
148 return 0;
b076fa8b
MT
149
150ERROR:
151 nw_config_free(c);
152
153 return r;
6b666d62
MT
154}
155
8edf3da1
MT
156int nw_config_open(nw_config** config, const char* path) {
157 FILE* f = NULL;
158 int r;
159
160 // Open path
161 f = fopen(path, "r");
162 if (!f)
163 return -errno;
164
165 // Create a new configuration
166 r = nw_config_create(config, f);
167
8edf3da1
MT
168 if (f)
169 fclose(f);
170
171 return r;
172}
173
2361667e 174nw_config* nw_config_ref(nw_config* config) {
6b666d62
MT
175 config->nrefs++;
176
177 return config;
178}
179
2361667e 180nw_config* nw_config_unref(nw_config* config) {
6b666d62
MT
181 if (--config->nrefs > 0)
182 return config;
183
184 nw_config_free(config);
185 return NULL;
186}
c81a6335 187
082d81a3
MT
188int nw_config_copy(nw_config* config, nw_config** copy) {
189 struct nw_config_entry* entry = NULL;
190 nw_config* c = NULL;
191 int r;
192
193 // Create a new configuration
194 r = nw_config_create(&c, NULL);
195 if (r)
196 return r;
197
198 // Copy everything
199 STAILQ_FOREACH(entry, &config->entries, nodes) {
200 r = nw_config_set(c, entry->key, entry->value);
201 if (r)
202 goto ERROR;
203 }
204
205 *copy = c;
206 return 0;
207
208ERROR:
209 if (c)
210 nw_config_unref(c);
211
212 return r;
213}
214
2361667e 215int nw_config_flush(nw_config* config) {
91915f80
MT
216 struct nw_config_entry* entry = NULL;
217
218 while (!STAILQ_EMPTY(&config->entries)) {
219 entry = STAILQ_FIRST(&config->entries);
220 STAILQ_REMOVE_HEAD(&config->entries, nodes);
221
222 // Free the entry
223 nw_config_entry_free(entry);
224 }
225
226 return 0;
227}
228
8edf3da1 229int nw_config_read(nw_config* config, FILE* f) {
5ef56cff
MT
230 char* line = NULL;
231 size_t length = 0;
232 int r;
c81a6335 233
5ef56cff
MT
234 ssize_t bytes_read = 0;
235
236 char* key = NULL;
237 char* val = NULL;
238
239 for (;;) {
240 // Read the next line
241 bytes_read = getline(&line, &length, f);
242 if (bytes_read < 0)
243 break;
244
245 // Key starts at the beginning of the line
246 key = line;
247
248 // Value starts after '='
249 val = strchr(line, '=');
250
251 // Invalid line without a '=' character
252 if (!val)
253 continue;
254
255 // Split the string
256 *val++ = '\0';
257
258 // Strip any whitespace from value
259 r = nw_string_strip(val);
260 if (r)
261 break;
262
263 // Store the setting
264 r = nw_config_set(config, key, val);
265 if (r)
266 break;
267 }
268
269 if (line)
270 free(line);
271
272 return r;
c81a6335
MT
273}
274
8edf3da1 275int nw_config_write(nw_config* config, FILE* f) {
d3dfdb77
MT
276 struct nw_config_entry* entry = NULL;
277 int r;
278
279 STAILQ_FOREACH(entry, &config->entries, nodes) {
280 // Skip if value is NULL
281 if (!*entry->value)
282 continue;
283
284 // Write the entry
5ef56cff 285 r = fprintf(f, "%s=%s\n", entry->key, entry->value);
d3dfdb77
MT
286 if (r < 0) {
287 ERROR("Failed to write configuration: %m\n");
288 return r;
289 }
290 }
291
292 return 0;
293}
294
2361667e 295static struct nw_config_entry* nw_config_find(nw_config* config, const char* key) {
4237caa2
MT
296 struct nw_config_entry* entry = NULL;
297
298 STAILQ_FOREACH(entry, &config->entries, nodes) {
299 // Key must match
300 if (strcmp(entry->key, key) != 0)
301 continue;
302
303 // Match!
304 return entry;
305 }
306
307 // No match
308 return NULL;
309}
310
2361667e 311int nw_config_del(nw_config* config, const char* key) {
4237caa2
MT
312 struct nw_config_entry* entry = NULL;
313
314 // Find an entry matching the key
315 entry = nw_config_find(config, key);
316
317 // If there is no entry, there is nothing to do
318 if (!entry)
319 return 0;
320
321 // Otherwise remove the object
322 STAILQ_REMOVE(&config->entries, entry, nw_config_entry, nodes);
323
324 // Free the entry
325 nw_config_entry_free(entry);
326
327 return 0;
328}
329
2361667e 330const char* nw_config_get(nw_config* config, const char* key) {
9368a163
MT
331 struct nw_config_entry* entry = nw_config_find(config, key);
332
333 // Return the value if found and set
334 if (entry && *entry->value)
335 return entry->value;
336
337 // Otherwise return NULL
338 return NULL;
339}
340
2361667e 341int nw_config_set(nw_config* config, const char* key, const char* value) {
4237caa2
MT
342 struct nw_config_entry* entry = NULL;
343
082d81a3
MT
344 // Log the change
345 DEBUG("%p: Setting %s = %s\n", config, key, value);
346
4237caa2
MT
347 // Delete the entry if val is NULL
348 if (!value)
349 return nw_config_del(config, key);
350
351 // Find any existing entries
352 entry = nw_config_find(config, key);
353
354 // Create a new entry if it doesn't exist, yet
355 if (!entry) {
356 entry = nw_config_entry_create(config, key);
357 if (!entry)
358 return 1;
359 }
360
361 // Store the new value
362 return nw_string_set(entry->value, value);
363}
d39683a6 364
2361667e 365int nw_config_get_int(nw_config* config, const char* key, const int __default) {
92c8a4fe
MT
366 char* p = NULL;
367 int r;
368
d39683a6
MT
369 const char* value = nw_config_get(config, key);
370
371 // Return zero if not set
372 if (!value)
9368a163 373 return __default;
d39683a6 374
92c8a4fe
MT
375 // Parse the input
376 r = strtoul(value, &p, 10);
377
378 // If we have characters following the input, we throw it away
379 if (p)
380 return __default;
381
382 return r;
d39683a6 383}
9368a163 384
2361667e 385int nw_config_set_int(nw_config* config, const char* key, const int value) {
9368a163
MT
386 char __value[1024];
387 int r;
388
389 // Format the value as string
2d000517 390 r = nw_string_format(__value, "%d", value);
9368a163
MT
391 if (r)
392 return r;
393
394 return nw_config_set(config, key, __value);
395}
8b0b5c6d
MT
396
397static const char* nw_config_true[] = {
398 "true",
399 "yes",
400 "1",
401 NULL,
402};
403
404int nw_config_get_bool(nw_config* config, const char* key) {
405 const char* value = nw_config_get(config, key);
406
407 // No value indicates false
408 if (!value)
409 return 0;
410
411 // Check if we match any known true words
412 for (const char** s = nw_config_true; *s; s++) {
8923434c 413 if (strcasecmp(value, *s) == 0)
8b0b5c6d
MT
414 return 1;
415 }
416
417 // No match means false
418 return 0;
419}
420
421int nw_config_set_bool(nw_config* config, const char* key, const int value) {
422 return nw_config_set(config, key, value ? "true" : "false");
423}
082d81a3 424
25808f65
MT
425/*
426 Directory
427*/
428
429struct nw_configd {
430 int nrefs;
431
432 char path[PATH_MAX];
433 int fd;
434};
435
436static void nw_configd_free(nw_configd* dir) {
437 if (dir->fd >= 0)
438 close(dir->fd);
439
440 free(dir);
441}
442
443static int __nw_configd_create(nw_configd** dir, int fd, const char* path) {
444 nw_configd* d = NULL;
445 int r;
446
447 // Allocate a new object
448 d = calloc(1, sizeof(*d));
449 if (!d)
450 return -errno;
451
452 // Initialize the reference counter
453 d->nrefs = 1;
454
455 // Store the file descriptor
456 d->fd = dup(fd);
457 if (d->fd < 0) {
458 r = -errno;
459 goto ERROR;
460 }
461
462 // Store path
463 if (path) {
464 r = nw_string_set(d->path, path);
465 if (r < 0)
466 goto ERROR;
467 }
468
469 *dir = d;
470 return 0;
471
472ERROR:
473 nw_configd_free(d);
474 return r;
475}
476
477int nw_configd_create(nw_configd** dir, const char* path) {
478 int fd;
479
480 // Open the directory
481 fd = open(path, O_DIRECTORY);
482 if (fd < 0) {
483 ERROR("Could not open %s: %m\n", path);
484 return -errno;
485 }
486
487 return __nw_configd_create(dir, fd, path);
488}
489
490nw_configd* nw_configd_ref(nw_configd* dir) {
491 dir->nrefs++;
492
493 return dir;
494}
495
496nw_configd* nw_configd_unref(nw_configd* dir) {
497 if (--dir->nrefs > 0)
498 return dir;
499
500 nw_configd_free(dir);
501 return NULL;
502}
503
504static int nw_configd_open(nw_configd* dir, const char* path, int flags) {
505 return openat(dir->fd, path, flags);
506}
507
508FILE* nw_configd_fopen(nw_configd* dir, const char* path, const char* mode) {
509 int fd;
510
511 // Open file
512 fd = nw_configd_open(dir, path, 0);
513 if (fd < 0)
514 return NULL;
515
516 // Return a file handle
517 return fdopen(fd, mode);
518}
519
520int nw_configd_open_config(nw_config** config, nw_configd* dir, const char* path) {
521 FILE* f = NULL;
522 int r;
523
524 // Open the file
525 f = nw_configd_fopen(dir, path, "r");
526 if (!f)
527 return -errno;
528
529 // Create configuration
530 r = nw_config_create(config, f);
531 if (r < 0)
532 goto ERROR;
533
534ERROR:
535 if (f)
536 fclose(f);
537
538 return r;
539}
540
541int nw_configd_unlink(nw_configd* dir, const char* path, int flags) {
542 return unlinkat(dir->fd, path, flags);
543}
544
545nw_configd* nw_configd_descend(nw_configd* dir, const char* path) {
546 nw_configd* d = NULL;
547 char p[PATH_MAX];
548 int fd = -1;
549 int r;
550
551 // Join paths
552 r = nw_path_join(p, dir->path, path);
553 if (r < 0)
554 goto ERROR;
555
556 // Open directory
557 fd = nw_configd_open(dir, path, O_DIRECTORY);
558 if (fd < 0) {
559 ERROR("Could not open %s: %m\n", p);
560 goto ERROR;
561 }
562
563 // Create a new config directory object
564 r = __nw_configd_create(&d, fd, p);
565 if (r < 0)
566 goto ERROR;
567
568ERROR:
569 if (fd >= 0)
570 close(fd);
571
572 return d;
573}
574
575int nw_configd_walk(nw_configd* dir, nw_configd_walk_callback callback, void* data) {
576 FILE* f = NULL;
577 DIR* d = NULL;
578 struct dirent* e = NULL;
579 int r;
580
581 // Re-open the directory
582 d = fdopendir(dir->fd);
583 if (!d) {
584 r = -errno;
585 goto ERROR;
586 }
587
588 // Walk trough everything
589 for (;;) {
590 // Read the next entry
591 e = readdir(d);
592 if (!e)
593 break;
594
595 // Skip anything that is not a regular file
596 if (e->d_type != DT_REG)
597 continue;
598
599 // Skip hidden files
600 if (e->d_name[0] == '.')
601 continue;
602
603 // Open the file
604 f = nw_configd_fopen(dir, e->d_name, "r");
605 if (!f) {
606 r = -errno;
607 goto ERROR;
608 }
609
610 // Call the callback
611 r = callback(e, f, data);
612 fclose(f);
613
614 if (r < 0)
615 goto ERROR;
616 }
617
618 r = 0;
619
620ERROR:
621 if (d)
622 closedir(d);
623
624 return r;
625}
626
082d81a3
MT
627/*
628 Options
629*/
630
631int nw_config_options_read(nw_config* config) {
632 struct nw_config_option* option = NULL;
633 int r;
634
635 STAILQ_FOREACH(option, &config->options, nodes) {
e633147d
MT
636 r = option->read_callback(config,
637 option->key, option->value, option->length, option->data);
082d81a3
MT
638 if (r < 0)
639 return r;
640 }
641
642 return 0;
643}
644
645int nw_config_options_write(nw_config* config) {
646 struct nw_config_option* option = NULL;
647 int r;
648
649 STAILQ_FOREACH(option, &config->options, nodes) {
e633147d
MT
650 r = option->write_callback(config,
651 option->key, option->value, option->length, option->data);
082d81a3
MT
652 if (r < 0)
653 return r;
654 }
655
656 return 0;
657}
658
e633147d
MT
659int nw_config_option_add(nw_config* config,
660 const char* key, void* value, const size_t length,
082d81a3 661 nw_config_option_read_callback_t read_callback,
3eeb38b7 662 nw_config_option_write_callback_t write_callback, void* data) {
082d81a3 663 // Check input
cc54472e 664 if (!key || !value || !read_callback || !write_callback)
082d81a3
MT
665 return -EINVAL;
666
667 // Allocate a new option
668 struct nw_config_option* option = calloc(1, sizeof(*option));
669 if (!option)
670 return -errno;
671
672 // Set key
673 option->key = key;
674
cc54472e
MT
675 // Set value
676 option->value = value;
e633147d 677 option->length = length;
082d81a3
MT
678
679 // Set callbacks
680 option->read_callback = read_callback;
681 option->write_callback = write_callback;
3eeb38b7 682 option->data = data;
082d81a3
MT
683
684 // Append the new option
685 STAILQ_INSERT_TAIL(&config->options, option, nodes);
686
687 return 0;
688}
689
e633147d
MT
690int nw_config_read_int(nw_config* config,
691 const char* key, void* value, const size_t length, void* data) {
082d81a3 692 // Fetch the value
cc54472e 693 *(int*)value = nw_config_get_int(config, key, -1);
082d81a3
MT
694
695 return 0;
696}
697
3eeb38b7 698int nw_config_write_int(nw_config* config,
e633147d 699 const char* key, const void* value, const size_t length, void* data) {
082d81a3
MT
700 return 0;
701}
702
703// String
704
e633147d
MT
705int nw_config_read_string(nw_config* config,
706 const char* key, void* value, const size_t length, void* data) {
082d81a3 707 // Fetch the value
cc54472e
MT
708 const char* p = nw_config_get(config, key);
709 if (p)
710 *(const char**)value = p;
082d81a3
MT
711
712 return 0;
713}
714
3eeb38b7 715int nw_config_write_string(nw_config* config,
e633147d 716 const char* key, const void* value, const size_t length, void* data) {
cc54472e 717 return nw_config_set(config, key, *(const char**)value);
082d81a3
MT
718}
719
e633147d
MT
720// String Buffer
721
722int 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;
725
726 // Fetch the value
727 const char* p = nw_config_get(config, key);
728 if (p)
729 return __nw_string_set(string, length, p);
730
731 return 0;
732}
733
7442668a
MT
734// String Table
735
e633147d
MT
736int nw_config_read_string_table(nw_config* config,
737 const char* key, void* value, const size_t length, void* data) {
7442668a
MT
738 const char* s = NULL;
739 int* v = (int*)value;
740
741 const nw_string_table_t* table = (nw_string_table_t*)data;
742
743 // Fetch the string
744 s = nw_config_get(config, key);
745 if (!s)
746 return -errno;
747
748 // Lookup the string in the table
749 *v = nw_string_table_lookup_id(table, s);
750
751 // If the result is negative, nothing was found
752 if (*v < 0)
753 return -EINVAL;
754
755 return 0;
756}
757
758int nw_config_write_string_table(nw_config* config,
e633147d 759 const char* key, const void* value, const size_t length, void* data) {
7442668a
MT
760 int* v = (int*)value;
761
762 const nw_string_table_t* table = (nw_string_table_t*)data;
763
764 // Lookup the string
765 const char* s = nw_string_table_lookup_string(table, *v);
766 if (!s)
767 return -errno;
768
769 return nw_config_set(config, key, s);
770}
771
082d81a3
MT
772// Address
773
e633147d
MT
774int nw_config_read_address(nw_config* config,
775 const char* key, void* value, const size_t length, void* data) {
cc54472e 776 nw_address_t* address = (nw_address_t*)value;
082d81a3
MT
777 int r;
778
779 // Fetch the value
cc54472e
MT
780 const char* p = nw_config_get(config, key);
781 if (!p)
082d81a3
MT
782 return -EINVAL;
783
cc54472e 784 r = nw_address_from_string(address, p);
082d81a3 785 if (r < 0)
cc54472e 786 ERROR("Could not parse address: %s\n", p);
082d81a3
MT
787
788 return r;
789}
790
3eeb38b7 791int nw_config_write_address(nw_config* config,
e633147d 792 const char* key, const void* value, const size_t length, void* data) {
cc54472e 793 const nw_address_t* address = (nw_address_t*)value;
082d81a3
MT
794 int r;
795
796 // Format the address to string
cc54472e
MT
797 char* p = nw_address_to_string(address);
798 if (!p)
082d81a3
MT
799 return -errno;
800
801 // Store the value
cc54472e 802 r = nw_config_set(config, key, p);
082d81a3
MT
803 if (r < 0)
804 goto ERROR;
805
806 // Success
807 r = 0;
808
809ERROR:
cc54472e
MT
810 if (p)
811 free(p);
082d81a3
MT
812
813 return r;
814}