]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/config.c
Makefile: Fix typo in localstatedir
[people/ms/network.git] / src / networkd / config.c
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
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/queue.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32
33 #include "address.h"
34 #include "config.h"
35 #include "logging.h"
36 #include "string.h"
37
38 struct 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 };
44
45 struct nw_config_option {
46 STAILQ_ENTRY(nw_config_option) nodes;
47
48 const char* key;
49 void* value;
50 size_t length;
51
52 // Callbacks
53 nw_config_option_read_callback_t read_callback;
54 nw_config_option_write_callback_t write_callback;
55 void* data;
56 };
57
58 struct nw_config {
59 int nrefs;
60
61 STAILQ_HEAD(config_entries, nw_config_entry) entries;
62
63 // Options
64 STAILQ_HEAD(parser_entries, nw_config_option) options;
65 };
66
67 static void nw_config_entry_free(struct nw_config_entry* entry) {
68 free(entry);
69 }
70
71 static void nw_config_option_free(struct nw_config_option* option) {
72 free(option);
73 }
74
75 static struct nw_config_entry* nw_config_entry_create(
76 nw_config* config, const char* key) {
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
98 return entry;
99
100 ERROR:
101 nw_config_entry_free(entry);
102 return NULL;
103 }
104
105 static void nw_config_free(nw_config* config) {
106 struct nw_config_option* option = NULL;
107
108 // Flush all entries
109 nw_config_flush(config);
110
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
120 free(config);
121 }
122
123 int nw_config_create(nw_config** config, FILE* f) {
124 int r;
125
126 nw_config* c = calloc(1, sizeof(*c));
127 if (!c)
128 return 1;
129
130 // Initialize reference counter
131 c->nrefs = 1;
132
133 // Initialise entries
134 STAILQ_INIT(&c->entries);
135
136 // Initialise options
137 STAILQ_INIT(&c->options);
138
139 // Read configuration
140 if (f) {
141 r = nw_config_read(c, f);
142 if (r < 0)
143 goto ERROR;
144 }
145
146 *config = c;
147
148 return 0;
149
150 ERROR:
151 nw_config_free(c);
152
153 return r;
154 }
155
156 int 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
168 if (f)
169 fclose(f);
170
171 return r;
172 }
173
174 nw_config* nw_config_ref(nw_config* config) {
175 config->nrefs++;
176
177 return config;
178 }
179
180 nw_config* nw_config_unref(nw_config* config) {
181 if (--config->nrefs > 0)
182 return config;
183
184 nw_config_free(config);
185 return NULL;
186 }
187
188 int 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
208 ERROR:
209 if (c)
210 nw_config_unref(c);
211
212 return r;
213 }
214
215 int nw_config_flush(nw_config* config) {
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
229 int nw_config_read(nw_config* config, FILE* f) {
230 char* line = NULL;
231 size_t length = 0;
232 int r;
233
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;
273 }
274
275 int nw_config_write(nw_config* config, FILE* f) {
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
285 r = fprintf(f, "%s=%s\n", entry->key, entry->value);
286 if (r < 0) {
287 ERROR("Failed to write configuration: %m\n");
288 return r;
289 }
290 }
291
292 return 0;
293 }
294
295 static struct nw_config_entry* nw_config_find(nw_config* config, const char* key) {
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
311 int nw_config_del(nw_config* config, const char* key) {
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
330 const char* nw_config_get(nw_config* config, const char* key) {
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
341 int nw_config_set(nw_config* config, const char* key, const char* value) {
342 struct nw_config_entry* entry = NULL;
343
344 // Log the change
345 DEBUG("%p: Setting %s = %s\n", config, key, value);
346
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 }
364
365 int nw_config_get_int(nw_config* config, const char* key, const int __default) {
366 char* p = NULL;
367 int r;
368
369 const char* value = nw_config_get(config, key);
370
371 // Return zero if not set
372 if (!value)
373 return __default;
374
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;
383 }
384
385 int nw_config_set_int(nw_config* config, const char* key, const int value) {
386 char __value[1024];
387 int r;
388
389 // Format the value as string
390 r = nw_string_format(__value, "%d", value);
391 if (r)
392 return r;
393
394 return nw_config_set(config, key, __value);
395 }
396
397 static const char* nw_config_true[] = {
398 "true",
399 "yes",
400 "1",
401 NULL,
402 };
403
404 int 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++) {
413 if (strcasecmp(value, *s) == 0)
414 return 1;
415 }
416
417 // No match means false
418 return 0;
419 }
420
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");
423 }
424
425 /*
426 Directory
427 */
428
429 struct nw_configd {
430 int nrefs;
431
432 char path[PATH_MAX];
433 int fd;
434 };
435
436 static void nw_configd_free(nw_configd* dir) {
437 if (dir->fd >= 0)
438 close(dir->fd);
439
440 free(dir);
441 }
442
443 static 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
472 ERROR:
473 nw_configd_free(d);
474 return r;
475 }
476
477 int 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
490 nw_configd* nw_configd_ref(nw_configd* dir) {
491 dir->nrefs++;
492
493 return dir;
494 }
495
496 nw_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
504 static int nw_configd_open(nw_configd* dir, const char* path, int flags) {
505 return openat(dir->fd, path, flags);
506 }
507
508 FILE* 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
520 int 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
534 ERROR:
535 if (f)
536 fclose(f);
537
538 return r;
539 }
540
541 int nw_configd_unlink(nw_configd* dir, const char* path, int flags) {
542 return unlinkat(dir->fd, path, flags);
543 }
544
545 nw_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
568 ERROR:
569 if (fd >= 0)
570 close(fd);
571
572 return d;
573 }
574
575 int 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
620 ERROR:
621 if (d)
622 closedir(d);
623
624 return r;
625 }
626
627 /*
628 Options
629 */
630
631 int 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) {
636 r = option->read_callback(config,
637 option->key, option->value, option->length, option->data);
638 if (r < 0)
639 return r;
640 }
641
642 return 0;
643 }
644
645 int 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) {
650 r = option->write_callback(config,
651 option->key, option->value, option->length, option->data);
652 if (r < 0)
653 return r;
654 }
655
656 return 0;
657 }
658
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) {
663 // Check input
664 if (!key || !value || !read_callback || !write_callback)
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
675 // Set value
676 option->value = value;
677 option->length = length;
678
679 // Set callbacks
680 option->read_callback = read_callback;
681 option->write_callback = write_callback;
682 option->data = data;
683
684 // Append the new option
685 STAILQ_INSERT_TAIL(&config->options, option, nodes);
686
687 return 0;
688 }
689
690 int nw_config_read_int(nw_config* config,
691 const char* key, void* value, const size_t length, void* data) {
692 // Fetch the value
693 *(int*)value = nw_config_get_int(config, key, -1);
694
695 return 0;
696 }
697
698 int nw_config_write_int(nw_config* config,
699 const char* key, const void* value, const size_t length, void* data) {
700 return 0;
701 }
702
703 // String
704
705 int nw_config_read_string(nw_config* config,
706 const char* key, void* value, const size_t length, void* data) {
707 // Fetch the value
708 const char* p = nw_config_get(config, key);
709 if (p)
710 *(const char**)value = p;
711
712 return 0;
713 }
714
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);
718 }
719
720 // String Buffer
721
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;
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
734 // String Table
735
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;
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
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;
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
772 // Address
773
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;
777 int r;
778
779 // Fetch the value
780 const char* p = nw_config_get(config, key);
781 if (!p)
782 return -EINVAL;
783
784 r = nw_address_from_string(address, p);
785 if (r < 0)
786 ERROR("Could not parse address: %s\n", p);
787
788 return r;
789 }
790
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;
794 int r;
795
796 // Format the address to string
797 char* p = nw_address_to_string(address);
798 if (!p)
799 return -errno;
800
801 // Store the value
802 r = nw_config_set(config, key, p);
803 if (r < 0)
804 goto ERROR;
805
806 // Success
807 r = 0;
808
809 ERROR:
810 if (p)
811 free(p);
812
813 return r;
814 }