]>
Commit | Line | Data |
---|---|---|
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 | ||
33 | struct 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 | |
40 | struct 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 |
49 | static void nw_config_entry_free(struct nw_config_entry* entry) { |
50 | free(entry); | |
51 | } | |
52 | ||
53 | static 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 |
78 | ERROR: |
79 | nw_config_entry_free(entry); | |
80 | return NULL; | |
81 | } | |
82 | ||
2361667e | 83 | static 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 | 90 | int 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 | |
119 | ERROR: | |
120 | nw_config_free(c); | |
121 | ||
122 | return r; | |
6b666d62 MT |
123 | } |
124 | ||
2361667e | 125 | nw_config* nw_config_ref(nw_config* config) { |
6b666d62 MT |
126 | config->nrefs++; |
127 | ||
128 | return config; | |
129 | } | |
130 | ||
2361667e | 131 | nw_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 |
139 | int 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 | 150 | const char* nw_config_path(nw_config* config) { |
b076fa8b MT |
151 | if (*config->path) |
152 | return config->path; | |
153 | ||
154 | return NULL; | |
155 | } | |
156 | ||
2361667e | 157 | int 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 | 171 | static 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 | 217 | int 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 | ||
242 | ERROR: | |
243 | if (f) | |
244 | fclose(f); | |
245 | ||
246 | return r; | |
247 | } | |
4237caa2 | 248 | |
2361667e | 249 | static 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 | 269 | int 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 | ||
288 | ERROR: | |
289 | if (f) | |
290 | fclose(f); | |
291 | ||
292 | return r; | |
293 | } | |
294 | ||
2361667e | 295 | static 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 | 311 | int 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 | 330 | const 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 | 341 | int 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 | 362 | int 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 | 382 | int 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 | |
394 | static const char* nw_config_true[] = { | |
395 | "true", | |
396 | "yes", | |
397 | "1", | |
398 | NULL, | |
399 | }; | |
400 | ||
401 | int 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 | ||
418 | int nw_config_set_bool(nw_config* config, const char* key, const int value) { | |
419 | return nw_config_set(config, key, value ? "true" : "false"); | |
420 | } |