]> git.ipfire.org Git - network.git/blob - src/networkd/config.c
networkd: Use typedef to keep type names shorter
[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
28 #include "config.h"
29 #include "logging.h"
30 #include "string.h"
31
32 struct 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 };
38
39 struct nw_config {
40 int nrefs;
41
42 // The path to the configuration file
43 char path[PATH_MAX];
44
45 STAILQ_HEAD(entries, nw_config_entry) entries;
46 };
47
48 static void nw_config_entry_free(struct nw_config_entry* entry) {
49 free(entry);
50 }
51
52 static struct nw_config_entry* nw_config_entry_create(
53 nw_config* config, const char* key) {
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
75 return entry;
76
77 ERROR:
78 nw_config_entry_free(entry);
79 return NULL;
80 }
81
82 static void nw_config_free(nw_config* config) {
83 // Flush all entries
84 nw_config_flush(config);
85
86 free(config);
87 }
88
89 int nw_config_create(nw_config** config, const char* path) {
90 int r;
91
92 nw_config* c = calloc(1, sizeof(*c));
93 if (!c)
94 return 1;
95
96 // Initialize reference counter
97 c->nrefs = 1;
98
99 // Initialise entries
100 STAILQ_INIT(&c->entries);
101
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
114 *config = c;
115
116 return 0;
117
118 ERROR:
119 nw_config_free(c);
120
121 return r;
122 }
123
124 nw_config* nw_config_ref(nw_config* config) {
125 config->nrefs++;
126
127 return config;
128 }
129
130 nw_config* nw_config_unref(nw_config* config) {
131 if (--config->nrefs > 0)
132 return config;
133
134 nw_config_free(config);
135 return NULL;
136 }
137
138 const char* nw_config_path(nw_config* config) {
139 if (*config->path)
140 return config->path;
141
142 return NULL;
143 }
144
145 int nw_config_flush(nw_config* config) {
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
159 static int nw_config_readf(nw_config* config, FILE* f) {
160 char* line = NULL;
161 size_t length = 0;
162 int r;
163
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;
203 }
204
205 int nw_config_read(nw_config* config) {
206 FILE* f = NULL;
207 int r;
208
209 // We cannot read if path is not set
210 if (!*config->path) {
211 errno = ENOTSUP;
212 return 1;
213 }
214
215 // Open the file
216 f = fopen(config->path, "r");
217 if (!f) {
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);
223 r = 1;
224 goto ERROR;
225 }
226
227 // Read from file
228 r = nw_config_readf(config, f);
229
230 ERROR:
231 if (f)
232 fclose(f);
233
234 return r;
235 }
236
237 static int nw_config_writef(nw_config* config, FILE* f) {
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
247 r = fprintf(f, "%s=%s\n", entry->key, entry->value);
248 if (r < 0) {
249 ERROR("Failed to write configuration: %m\n");
250 return r;
251 }
252 }
253
254 return 0;
255 }
256
257 int nw_config_write(nw_config* config) {
258 int r;
259
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");
267 if (!f) {
268 ERROR("Failed to open %s for writing: %m\n", config->path);
269 r = 1;
270 goto ERROR;
271 }
272
273 // Write configuration
274 r = nw_config_writef(config, f);
275
276 ERROR:
277 if (f)
278 fclose(f);
279
280 return r;
281 }
282
283 static struct nw_config_entry* nw_config_find(nw_config* config, const char* key) {
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
299 int nw_config_del(nw_config* config, const char* key) {
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
318 const char* nw_config_get(nw_config* config, const char* key) {
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
329 int nw_config_set(nw_config* config, const char* key, const char* value) {
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 }
349
350 int nw_config_get_int(nw_config* config, const char* key, const int __default) {
351 const char* value = nw_config_get(config, key);
352
353 // Return zero if not set
354 if (!value)
355 return __default;
356
357 return strtoul(value, NULL, 10);
358 }
359
360 int nw_config_set_int(nw_config* config, const char* key, const int value) {
361 char __value[1024];
362 int r;
363
364 // Format the value as string
365 r = nw_string_format(__value, "%d\n", value);
366 if (r)
367 return r;
368
369 return nw_config_set(config, key, __value);
370 }