]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/config.c
39e98818dcf740db9b14ad0a23773931297ba60e
[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 <errno.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/queue.h>
27 #include <unistd.h>
28
29 #include "address.h"
30 #include "config.h"
31 #include "logging.h"
32 #include "string.h"
33
34 struct nw_config_entry {
35 STAILQ_ENTRY(nw_config_entry) nodes;
36
37 char key[NETWORK_CONFIG_KEY_MAX_LENGTH];
38 char value[NETWORK_CONFIG_KEY_MAX_LENGTH];
39 };
40
41 struct nw_config_option {
42 STAILQ_ENTRY(nw_config_option) nodes;
43
44 const char* key;
45 void* value;
46
47 // Callbacks
48 nw_config_option_read_callback_t read_callback;
49 nw_config_option_write_callback_t write_callback;
50 void* data;
51 };
52
53 struct nw_config {
54 int nrefs;
55
56 // The path to the configuration file
57 char path[PATH_MAX];
58
59 STAILQ_HEAD(config_entries, nw_config_entry) entries;
60
61 // Options
62 STAILQ_HEAD(parser_entries, nw_config_option) options;
63 };
64
65 static void nw_config_entry_free(struct nw_config_entry* entry) {
66 free(entry);
67 }
68
69 static void nw_config_option_free(struct nw_config_option* option) {
70 free(option);
71 }
72
73 static struct nw_config_entry* nw_config_entry_create(
74 nw_config* config, const char* key) {
75 int r;
76
77 // Check input value
78 if (!key) {
79 errno = EINVAL;
80 return NULL;
81 }
82
83 // Allocate a new object
84 struct nw_config_entry* entry = calloc(1, sizeof(*entry));
85 if (!entry)
86 return NULL;
87
88 // Store the key
89 r = nw_string_set(entry->key, key);
90 if (r)
91 goto ERROR;
92
93 // Append the new entry
94 STAILQ_INSERT_TAIL(&config->entries, entry, nodes);
95
96 return entry;
97
98 ERROR:
99 nw_config_entry_free(entry);
100 return NULL;
101 }
102
103 static void nw_config_free(nw_config* config) {
104 struct nw_config_option* option = NULL;
105
106 // Flush all entries
107 nw_config_flush(config);
108
109 // Free all options
110 while (!STAILQ_EMPTY(&config->options)) {
111 option = STAILQ_FIRST(&config->options);
112 STAILQ_REMOVE_HEAD(&config->options, nodes);
113
114 // Free the options
115 nw_config_option_free(option);
116 }
117
118 free(config);
119 }
120
121 int nw_config_create(nw_config** config, const char* path) {
122 int r;
123
124 nw_config* c = calloc(1, sizeof(*c));
125 if (!c)
126 return 1;
127
128 // Initialize reference counter
129 c->nrefs = 1;
130
131 // Initialise entries
132 STAILQ_INIT(&c->entries);
133
134 // Initialise options
135 STAILQ_INIT(&c->options);
136
137 // Store the path
138 if (path) {
139 r = nw_string_set(c->path, path);
140 if (r)
141 goto ERROR;
142
143 // Try to read the configuration from path
144 r = nw_config_read(c);
145 if (r)
146 goto ERROR;
147 }
148
149 *config = c;
150
151 return 0;
152
153 ERROR:
154 nw_config_free(c);
155
156 return r;
157 }
158
159 nw_config* nw_config_ref(nw_config* config) {
160 config->nrefs++;
161
162 return config;
163 }
164
165 nw_config* nw_config_unref(nw_config* config) {
166 if (--config->nrefs > 0)
167 return config;
168
169 nw_config_free(config);
170 return NULL;
171 }
172
173 int nw_config_destroy(nw_config* config) {
174 int r;
175
176 // Drop all entries
177 r = nw_config_flush(config);
178 if (r)
179 return r;
180
181 return unlink(config->path);
182 }
183
184 int nw_config_copy(nw_config* config, nw_config** copy) {
185 struct nw_config_entry* entry = NULL;
186 nw_config* c = NULL;
187 int r;
188
189 // Create a new configuration
190 r = nw_config_create(&c, NULL);
191 if (r)
192 return r;
193
194 // Copy everything
195 STAILQ_FOREACH(entry, &config->entries, nodes) {
196 r = nw_config_set(c, entry->key, entry->value);
197 if (r)
198 goto ERROR;
199 }
200
201 *copy = c;
202 return 0;
203
204 ERROR:
205 if (c)
206 nw_config_unref(c);
207
208 return r;
209 }
210
211 const char* nw_config_path(nw_config* config) {
212 if (*config->path)
213 return config->path;
214
215 return NULL;
216 }
217
218 int nw_config_flush(nw_config* config) {
219 struct nw_config_entry* entry = NULL;
220
221 while (!STAILQ_EMPTY(&config->entries)) {
222 entry = STAILQ_FIRST(&config->entries);
223 STAILQ_REMOVE_HEAD(&config->entries, nodes);
224
225 // Free the entry
226 nw_config_entry_free(entry);
227 }
228
229 return 0;
230 }
231
232 static int nw_config_readf(nw_config* config, FILE* f) {
233 char* line = NULL;
234 size_t length = 0;
235 int r;
236
237 ssize_t bytes_read = 0;
238
239 char* key = NULL;
240 char* val = NULL;
241
242 for (;;) {
243 // Read the next line
244 bytes_read = getline(&line, &length, f);
245 if (bytes_read < 0)
246 break;
247
248 // Key starts at the beginning of the line
249 key = line;
250
251 // Value starts after '='
252 val = strchr(line, '=');
253
254 // Invalid line without a '=' character
255 if (!val)
256 continue;
257
258 // Split the string
259 *val++ = '\0';
260
261 // Strip any whitespace from value
262 r = nw_string_strip(val);
263 if (r)
264 break;
265
266 // Store the setting
267 r = nw_config_set(config, key, val);
268 if (r)
269 break;
270 }
271
272 if (line)
273 free(line);
274
275 return r;
276 }
277
278 int nw_config_read(nw_config* config) {
279 FILE* f = NULL;
280 int r;
281
282 // We cannot read if path is not set
283 if (!*config->path) {
284 errno = ENOTSUP;
285 return 1;
286 }
287
288 // Open the file
289 f = fopen(config->path, "r");
290 if (!f) {
291 // Silently ignore if the file does not exist
292 if (errno == ENOENT)
293 return 0;
294
295 ERROR("Could not read configuration file %s: %m\n", config->path);
296 r = 1;
297 goto ERROR;
298 }
299
300 // Read from file
301 r = nw_config_readf(config, f);
302
303 ERROR:
304 if (f)
305 fclose(f);
306
307 return r;
308 }
309
310 static int nw_config_writef(nw_config* config, FILE* f) {
311 struct nw_config_entry* entry = NULL;
312 int r;
313
314 STAILQ_FOREACH(entry, &config->entries, nodes) {
315 // Skip if value is NULL
316 if (!*entry->value)
317 continue;
318
319 // Write the entry
320 r = fprintf(f, "%s=%s\n", entry->key, entry->value);
321 if (r < 0) {
322 ERROR("Failed to write configuration: %m\n");
323 return r;
324 }
325 }
326
327 return 0;
328 }
329
330 int nw_config_write(nw_config* config) {
331 int r;
332
333 // We cannot write if path is not set
334 if (!*config->path) {
335 errno = ENOTSUP;
336 return 1;
337 }
338
339 FILE* f = fopen(config->path, "w");
340 if (!f) {
341 ERROR("Failed to open %s for writing: %m\n", config->path);
342 r = 1;
343 goto ERROR;
344 }
345
346 // Write configuration
347 r = nw_config_writef(config, f);
348
349 ERROR:
350 if (f)
351 fclose(f);
352
353 return r;
354 }
355
356 static struct nw_config_entry* nw_config_find(nw_config* config, const char* key) {
357 struct nw_config_entry* entry = NULL;
358
359 STAILQ_FOREACH(entry, &config->entries, nodes) {
360 // Key must match
361 if (strcmp(entry->key, key) != 0)
362 continue;
363
364 // Match!
365 return entry;
366 }
367
368 // No match
369 return NULL;
370 }
371
372 int nw_config_del(nw_config* config, const char* key) {
373 struct nw_config_entry* entry = NULL;
374
375 // Find an entry matching the key
376 entry = nw_config_find(config, key);
377
378 // If there is no entry, there is nothing to do
379 if (!entry)
380 return 0;
381
382 // Otherwise remove the object
383 STAILQ_REMOVE(&config->entries, entry, nw_config_entry, nodes);
384
385 // Free the entry
386 nw_config_entry_free(entry);
387
388 return 0;
389 }
390
391 const char* nw_config_get(nw_config* config, const char* key) {
392 struct nw_config_entry* entry = nw_config_find(config, key);
393
394 // Return the value if found and set
395 if (entry && *entry->value)
396 return entry->value;
397
398 // Otherwise return NULL
399 return NULL;
400 }
401
402 int nw_config_set(nw_config* config, const char* key, const char* value) {
403 struct nw_config_entry* entry = NULL;
404
405 // Log the change
406 DEBUG("%p: Setting %s = %s\n", config, key, value);
407
408 // Delete the entry if val is NULL
409 if (!value)
410 return nw_config_del(config, key);
411
412 // Find any existing entries
413 entry = nw_config_find(config, key);
414
415 // Create a new entry if it doesn't exist, yet
416 if (!entry) {
417 entry = nw_config_entry_create(config, key);
418 if (!entry)
419 return 1;
420 }
421
422 // Store the new value
423 return nw_string_set(entry->value, value);
424 }
425
426 int nw_config_get_int(nw_config* config, const char* key, const int __default) {
427 char* p = NULL;
428 int r;
429
430 const char* value = nw_config_get(config, key);
431
432 // Return zero if not set
433 if (!value)
434 return __default;
435
436 // Parse the input
437 r = strtoul(value, &p, 10);
438
439 // If we have characters following the input, we throw it away
440 if (p)
441 return __default;
442
443 return r;
444 }
445
446 int nw_config_set_int(nw_config* config, const char* key, const int value) {
447 char __value[1024];
448 int r;
449
450 // Format the value as string
451 r = nw_string_format(__value, "%d", value);
452 if (r)
453 return r;
454
455 return nw_config_set(config, key, __value);
456 }
457
458 static const char* nw_config_true[] = {
459 "true",
460 "yes",
461 "1",
462 NULL,
463 };
464
465 int nw_config_get_bool(nw_config* config, const char* key) {
466 const char* value = nw_config_get(config, key);
467
468 // No value indicates false
469 if (!value)
470 return 0;
471
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)
475 return 1;
476 }
477
478 // No match means false
479 return 0;
480 }
481
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");
484 }
485
486 /*
487 Options
488 */
489
490 int nw_config_options_read(nw_config* config) {
491 struct nw_config_option* option = NULL;
492 int r;
493
494 STAILQ_FOREACH(option, &config->options, nodes) {
495 r = option->read_callback(config, option->key, option->value, option->data);
496 if (r < 0)
497 return r;
498 }
499
500 return 0;
501 }
502
503 int nw_config_options_write(nw_config* config) {
504 struct nw_config_option* option = NULL;
505 int r;
506
507 STAILQ_FOREACH(option, &config->options, nodes) {
508 r = option->write_callback(config, option->key, option->value, option->data);
509 if (r < 0)
510 return r;
511 }
512
513 return 0;
514 }
515
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) {
519 // Check input
520 if (!key || !value || !read_callback || !write_callback)
521 return -EINVAL;
522
523 // Allocate a new option
524 struct nw_config_option* option = calloc(1, sizeof(*option));
525 if (!option)
526 return -errno;
527
528 // Set key
529 option->key = key;
530
531 // Set value
532 option->value = value;
533
534 // Set callbacks
535 option->read_callback = read_callback;
536 option->write_callback = write_callback;
537 option->data = data;
538
539 // Append the new option
540 STAILQ_INSERT_TAIL(&config->options, option, nodes);
541
542 return 0;
543 }
544
545 int nw_config_read_int(nw_config* config, const char* key, void* value, void* data) {
546 // Fetch the value
547 *(int*)value = nw_config_get_int(config, key, -1);
548
549 return 0;
550 }
551
552 int nw_config_write_int(nw_config* config,
553 const char* key, const void* value, void* data) {
554 return 0;
555 }
556
557 // String
558
559 int nw_config_read_string(nw_config* config, const char* key, void* value, void* data) {
560 // Fetch the value
561 const char* p = nw_config_get(config, key);
562 if (p)
563 *(const char**)value = p;
564
565 return 0;
566 }
567
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);
571 }
572
573 // Address
574
575 int nw_config_read_address(nw_config* config, const char* key, void* value, void* data) {
576 nw_address_t* address = (nw_address_t*)value;
577 int r;
578
579 // Fetch the value
580 const char* p = nw_config_get(config, key);
581 if (!p)
582 return -EINVAL;
583
584 r = nw_address_from_string(address, p);
585 if (r < 0)
586 ERROR("Could not parse address: %s\n", p);
587
588 return r;
589 }
590
591 int nw_config_write_address(nw_config* config,
592 const char* key, const void* value, void* data) {
593 const nw_address_t* address = (nw_address_t*)value;
594 int r;
595
596 // Format the address to string
597 char* p = nw_address_to_string(address);
598 if (!p)
599 return -errno;
600
601 // Store the value
602 r = nw_config_set(config, key, p);
603 if (r < 0)
604 goto ERROR;
605
606 // Success
607 r = 0;
608
609 ERROR:
610 if (p)
611 free(p);
612
613 return r;
614 }