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