]>
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> | |
6b666d62 MT |
27 | |
28 | #include "config.h" | |
c81a6335 | 29 | #include "logging.h" |
4237caa2 MT |
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 | }; | |
6b666d62 MT |
38 | |
39 | struct 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 |
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( | |
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 |
77 | ERROR: |
78 | nw_config_entry_free(entry); | |
79 | return NULL; | |
80 | } | |
81 | ||
2361667e | 82 | static 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 | 89 | int 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 | |
118 | ERROR: | |
119 | nw_config_free(c); | |
120 | ||
121 | return r; | |
6b666d62 MT |
122 | } |
123 | ||
2361667e | 124 | nw_config* nw_config_ref(nw_config* config) { |
6b666d62 MT |
125 | config->nrefs++; |
126 | ||
127 | return config; | |
128 | } | |
129 | ||
2361667e | 130 | nw_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 | 138 | const char* nw_config_path(nw_config* config) { |
b076fa8b MT |
139 | if (*config->path) |
140 | return config->path; | |
141 | ||
142 | return NULL; | |
143 | } | |
144 | ||
2361667e | 145 | int 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 | 159 | static 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 | 205 | int 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 | ||
230 | ERROR: | |
231 | if (f) | |
232 | fclose(f); | |
233 | ||
234 | return r; | |
235 | } | |
4237caa2 | 236 | |
2361667e | 237 | static 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 | 257 | int 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 | ||
276 | ERROR: | |
277 | if (f) | |
278 | fclose(f); | |
279 | ||
280 | return r; | |
281 | } | |
282 | ||
2361667e | 283 | static 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 | 299 | int 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 | 318 | const 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 | 329 | int 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 | 350 | int 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 | 370 | int 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 | |
382 | static const char* nw_config_true[] = { | |
383 | "true", | |
384 | "yes", | |
385 | "1", | |
386 | NULL, | |
387 | }; | |
388 | ||
389 | int 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 | ||
406 | int nw_config_set_bool(nw_config* config, const char* key, const int value) { | |
407 | return nw_config_set(config, key, value ? "true" : "false"); | |
408 | } |