]> git.ipfire.org Git - people/ms/network.git/blame - src/networkd/config.c
config: Compare truthiness case-insensitively
[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
4237caa2 21#include <errno.h>
b076fa8b 22#include <limits.h>
c81a6335 23#include <stdio.h>
6b666d62 24#include <stdlib.h>
4237caa2
MT
25#include <string.h>
26#include <sys/queue.h>
c403eb4c 27#include <unistd.h>
6b666d62
MT
28
29#include "config.h"
c81a6335 30#include "logging.h"
4237caa2
MT
31#include "string.h"
32
33struct nw_config_entry {
34 STAILQ_ENTRY(nw_config_entry) nodes;
35
36 char key[NETWORK_CONFIG_KEY_MAX_LENGTH];
37 char value[NETWORK_CONFIG_KEY_MAX_LENGTH];
38};
6b666d62
MT
39
40struct nw_config {
41 int nrefs;
4237caa2 42
b076fa8b
MT
43 // The path to the configuration file
44 char path[PATH_MAX];
45
4237caa2 46 STAILQ_HEAD(entries, nw_config_entry) entries;
6b666d62
MT
47};
48
4237caa2
MT
49static void nw_config_entry_free(struct nw_config_entry* entry) {
50 free(entry);
51}
52
53static struct nw_config_entry* nw_config_entry_create(
2361667e 54 nw_config* config, const char* key) {
4237caa2
MT
55 int r;
56
57 // Check input value
58 if (!key) {
59 errno = EINVAL;
60 return NULL;
61 }
62
63 // Allocate a new object
64 struct nw_config_entry* entry = calloc(1, sizeof(*entry));
65 if (!entry)
66 return NULL;
67
68 // Store the key
69 r = nw_string_set(entry->key, key);
70 if (r)
71 goto ERROR;
72
73 // Append the new entry
74 STAILQ_INSERT_TAIL(&config->entries, entry, nodes);
75
82f84c5f
MT
76 return entry;
77
4237caa2
MT
78ERROR:
79 nw_config_entry_free(entry);
80 return NULL;
81}
82
2361667e 83static void nw_config_free(nw_config* config) {
91915f80
MT
84 // Flush all entries
85 nw_config_flush(config);
4237caa2 86
6b666d62
MT
87 free(config);
88}
89
2361667e 90int nw_config_create(nw_config** config, const char* path) {
b076fa8b
MT
91 int r;
92
2361667e 93 nw_config* c = calloc(1, sizeof(*c));
6b666d62
MT
94 if (!c)
95 return 1;
96
97 // Initialize reference counter
98 c->nrefs = 1;
99
4237caa2
MT
100 // Initialise entries
101 STAILQ_INIT(&c->entries);
102
b076fa8b
MT
103 // Store the path
104 if (path) {
105 r = nw_string_set(c->path, path);
106 if (r)
107 goto ERROR;
108
109 // Try to read the configuration from path
110 r = nw_config_read(c);
111 if (r)
112 goto ERROR;
113 }
114
6b666d62
MT
115 *config = c;
116
117 return 0;
b076fa8b
MT
118
119ERROR:
120 nw_config_free(c);
121
122 return r;
6b666d62
MT
123}
124
2361667e 125nw_config* nw_config_ref(nw_config* config) {
6b666d62
MT
126 config->nrefs++;
127
128 return config;
129}
130
2361667e 131nw_config* nw_config_unref(nw_config* config) {
6b666d62
MT
132 if (--config->nrefs > 0)
133 return config;
134
135 nw_config_free(config);
136 return NULL;
137}
c81a6335 138
c403eb4c
MT
139int nw_config_destroy(nw_config* config) {
140 int r;
141
142 // Drop all entries
143 r = nw_config_flush(config);
144 if (r)
145 return r;
146
147 return unlink(config->path);
148}
149
2361667e 150const char* nw_config_path(nw_config* config) {
b076fa8b
MT
151 if (*config->path)
152 return config->path;
153
154 return NULL;
155}
156
2361667e 157int nw_config_flush(nw_config* config) {
91915f80
MT
158 struct nw_config_entry* entry = NULL;
159
160 while (!STAILQ_EMPTY(&config->entries)) {
161 entry = STAILQ_FIRST(&config->entries);
162 STAILQ_REMOVE_HEAD(&config->entries, nodes);
163
164 // Free the entry
165 nw_config_entry_free(entry);
166 }
167
168 return 0;
169}
170
2361667e 171static int nw_config_readf(nw_config* config, FILE* f) {
5ef56cff
MT
172 char* line = NULL;
173 size_t length = 0;
174 int r;
c81a6335 175
5ef56cff
MT
176 ssize_t bytes_read = 0;
177
178 char* key = NULL;
179 char* val = NULL;
180
181 for (;;) {
182 // Read the next line
183 bytes_read = getline(&line, &length, f);
184 if (bytes_read < 0)
185 break;
186
187 // Key starts at the beginning of the line
188 key = line;
189
190 // Value starts after '='
191 val = strchr(line, '=');
192
193 // Invalid line without a '=' character
194 if (!val)
195 continue;
196
197 // Split the string
198 *val++ = '\0';
199
200 // Strip any whitespace from value
201 r = nw_string_strip(val);
202 if (r)
203 break;
204
205 // Store the setting
206 r = nw_config_set(config, key, val);
207 if (r)
208 break;
209 }
210
211 if (line)
212 free(line);
213
214 return r;
c81a6335
MT
215}
216
2361667e 217int nw_config_read(nw_config* config) {
c81a6335
MT
218 FILE* f = NULL;
219 int r;
220
b076fa8b
MT
221 // We cannot read if path is not set
222 if (!*config->path) {
223 errno = ENOTSUP;
224 return 1;
225 }
226
c81a6335 227 // Open the file
b076fa8b 228 f = fopen(config->path, "r");
c81a6335 229 if (!f) {
b076fa8b
MT
230 // Silently ignore if the file does not exist
231 if (errno == ENOENT)
232 return 0;
233
234 ERROR("Could not read configuration file %s: %m\n", config->path);
c81a6335
MT
235 r = 1;
236 goto ERROR;
237 }
238
239 // Read from file
240 r = nw_config_readf(config, f);
241
242ERROR:
243 if (f)
244 fclose(f);
245
246 return r;
247}
4237caa2 248
2361667e 249static int nw_config_writef(nw_config* config, FILE* f) {
d3dfdb77
MT
250 struct nw_config_entry* entry = NULL;
251 int r;
252
253 STAILQ_FOREACH(entry, &config->entries, nodes) {
254 // Skip if value is NULL
255 if (!*entry->value)
256 continue;
257
258 // Write the entry
5ef56cff 259 r = fprintf(f, "%s=%s\n", entry->key, entry->value);
d3dfdb77
MT
260 if (r < 0) {
261 ERROR("Failed to write configuration: %m\n");
262 return r;
263 }
264 }
265
266 return 0;
267}
268
2361667e 269int nw_config_write(nw_config* config) {
d3dfdb77
MT
270 int r;
271
b076fa8b
MT
272 // We cannot write if path is not set
273 if (!*config->path) {
274 errno = ENOTSUP;
275 return 1;
276 }
277
278 FILE* f = fopen(config->path, "w");
d3dfdb77 279 if (!f) {
b076fa8b 280 ERROR("Failed to open %s for writing: %m\n", config->path);
d3dfdb77
MT
281 r = 1;
282 goto ERROR;
283 }
284
285 // Write configuration
286 r = nw_config_writef(config, f);
287
288ERROR:
289 if (f)
290 fclose(f);
291
292 return r;
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
344 // Delete the entry if val is NULL
345 if (!value)
346 return nw_config_del(config, key);
347
348 // Find any existing entries
349 entry = nw_config_find(config, key);
350
351 // Create a new entry if it doesn't exist, yet
352 if (!entry) {
353 entry = nw_config_entry_create(config, key);
354 if (!entry)
355 return 1;
356 }
357
358 // Store the new value
359 return nw_string_set(entry->value, value);
360}
d39683a6 361
2361667e 362int nw_config_get_int(nw_config* config, const char* key, const int __default) {
92c8a4fe
MT
363 char* p = NULL;
364 int r;
365
d39683a6
MT
366 const char* value = nw_config_get(config, key);
367
368 // Return zero if not set
369 if (!value)
9368a163 370 return __default;
d39683a6 371
92c8a4fe
MT
372 // Parse the input
373 r = strtoul(value, &p, 10);
374
375 // If we have characters following the input, we throw it away
376 if (p)
377 return __default;
378
379 return r;
d39683a6 380}
9368a163 381
2361667e 382int nw_config_set_int(nw_config* config, const char* key, const int value) {
9368a163
MT
383 char __value[1024];
384 int r;
385
386 // Format the value as string
2d000517 387 r = nw_string_format(__value, "%d", value);
9368a163
MT
388 if (r)
389 return r;
390
391 return nw_config_set(config, key, __value);
392}
8b0b5c6d
MT
393
394static const char* nw_config_true[] = {
395 "true",
396 "yes",
397 "1",
398 NULL,
399};
400
401int nw_config_get_bool(nw_config* config, const char* key) {
402 const char* value = nw_config_get(config, key);
403
404 // No value indicates false
405 if (!value)
406 return 0;
407
408 // Check if we match any known true words
409 for (const char** s = nw_config_true; *s; s++) {
8923434c 410 if (strcasecmp(value, *s) == 0)
8b0b5c6d
MT
411 return 1;
412 }
413
414 // No match means false
415 return 0;
416}
417
418int nw_config_set_bool(nw_config* config, const char* key, const int value) {
419 return nw_config_set(config, key, value ? "true" : "false");
420}