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