]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.h
Merge pull request #33352 from YHNdnzj/freeconp-void
[thirdparty/systemd.git] / src / shared / conf-parser.h
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 #pragma once
3
4 #include <errno.h>
5 #include <stdbool.h>
6 #include <stddef.h>
7 #include <stdio.h>
8 #include <syslog.h>
9 #include <sys/stat.h>
10
11 #include "alloc-util.h"
12 #include "hashmap.h"
13 #include "log.h"
14 #include "macro.h"
15 #include "time-util.h"
16
17 /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */
18
19 typedef enum ConfigParseFlags {
20 CONFIG_PARSE_RELAXED = 1 << 0, /* Do not warn about unknown non-extension fields */
21 CONFIG_PARSE_WARN = 1 << 1, /* Emit non-debug messages */
22 } ConfigParseFlags;
23
24 /* Argument list for parsers of specific configuration settings. */
25 #define CONFIG_PARSER_ARGUMENTS \
26 const char *unit, \
27 const char *filename, \
28 unsigned line, \
29 const char *section, \
30 unsigned section_line, \
31 const char *lvalue, \
32 int ltype, \
33 const char *rvalue, \
34 void *data, \
35 void *userdata
36
37 /* Prototype for a parser for a specific configuration setting */
38 typedef int (*ConfigParserCallback)(CONFIG_PARSER_ARGUMENTS);
39
40 /* A macro declaring a function prototype, following the typedef above, simply because it's so cumbersomely long
41 * otherwise. (And current emacs gets irritatingly slow when editing files that contain lots of very long function
42 * prototypes on the same screen…) */
43 #define CONFIG_PARSER_PROTOTYPE(name) int name(CONFIG_PARSER_ARGUMENTS)
44
45 /* Wraps information for parsing a specific configuration variable, to
46 * be stored in a simple array */
47 typedef struct ConfigTableItem {
48 const char *section; /* Section */
49 const char *lvalue; /* Name of the variable */
50 ConfigParserCallback parse; /* Function that is called to parse the variable's value */
51 int ltype; /* Distinguish different variables passed to the same callback */
52 void *data; /* Where to store the variable's data */
53 } ConfigTableItem;
54
55 /* Wraps information for parsing a specific configuration variable, to
56 * be stored in a gperf perfect hashtable */
57 typedef struct ConfigPerfItem {
58 const char *section_and_lvalue; /* Section + "." + name of the variable */
59 ConfigParserCallback parse; /* Function that is called to parse the variable's value */
60 int ltype; /* Distinguish different variables passed to the same callback */
61 size_t offset; /* Offset where to store data, from the beginning of userdata */
62 } ConfigPerfItem;
63
64 /* Prototype for a low-level gperf lookup function */
65 typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, GPERF_LEN_TYPE length);
66
67 /* Prototype for a generic high-level lookup function */
68 typedef int (*ConfigItemLookup)(
69 const void *table,
70 const char *section,
71 const char *lvalue,
72 ConfigParserCallback *ret_func,
73 int *ret_ltype,
74 void **ret_data,
75 void *userdata);
76
77 /* Linear table search implementation of ConfigItemLookup, based on
78 * ConfigTableItem arrays */
79 int config_item_table_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *ret_func, int *ret_ltype, void **ret_data, void *userdata);
80
81 /* gperf implementation of ConfigItemLookup, based on gperf
82 * ConfigPerfItem tables */
83 int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *ret_func, int *ret_ltype, void **ret_data, void *userdata);
84
85 int config_parse(
86 const char *unit,
87 const char *filename,
88 FILE *f,
89 const char *sections, /* nulstr */
90 ConfigItemLookup lookup,
91 const void *table,
92 ConfigParseFlags flags,
93 void *userdata,
94 struct stat *ret_stat); /* possibly NULL */
95
96 int config_parse_many(
97 const char* const* conf_files, /* possibly empty */
98 const char* const* conf_file_dirs,
99 const char *dropin_dirname,
100 const char *root,
101 const char *sections, /* nulstr */
102 ConfigItemLookup lookup,
103 const void *table,
104 ConfigParseFlags flags,
105 void *userdata,
106 Hashmap **ret_stats_by_path, /* possibly NULL */
107 char ***ret_drop_in_files); /* possibly NULL */
108
109 int config_parse_standard_file_with_dropins_full(
110 const char *root,
111 const char *main_file, /* A path like "systemd/frobnicator.conf" */
112 const char *sections,
113 ConfigItemLookup lookup,
114 const void *table,
115 ConfigParseFlags flags,
116 void *userdata,
117 Hashmap **ret_stats_by_path, /* possibly NULL */
118 char ***ret_dropin_files); /* possibly NULL */
119
120 static inline int config_parse_standard_file_with_dropins(
121 const char *main_file, /* A path like "systemd/frobnicator.conf" */
122 const char *sections, /* nulstr */
123 ConfigItemLookup lookup,
124 const void *table,
125 ConfigParseFlags flags,
126 void *userdata) {
127 return config_parse_standard_file_with_dropins_full(
128 /* root= */ NULL,
129 main_file,
130 sections,
131 lookup,
132 table,
133 flags,
134 userdata,
135 /* ret_stats_by_path= */ NULL,
136 /* ret_dropin_files= */ NULL);
137 }
138
139 int config_get_stats_by_path(
140 const char *suffix,
141 const char *root,
142 unsigned flags,
143 const char* const* dirs,
144 bool check_dropins,
145 Hashmap **ret);
146
147 int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st);
148 bool stats_by_path_equal(Hashmap *a, Hashmap *b);
149
150 typedef struct ConfigSection {
151 unsigned line;
152 bool invalid;
153 char filename[];
154 } ConfigSection;
155
156 static inline ConfigSection* config_section_free(ConfigSection *cs) {
157 return mfree(cs);
158 }
159 DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection*, config_section_free);
160
161 int config_section_new(const char *filename, unsigned line, ConfigSection **ret);
162
163 void config_section_hash_func(const ConfigSection *c, struct siphash *state);
164 int config_section_compare_func(const ConfigSection *x, const ConfigSection *y);
165 extern const struct hash_ops config_section_hash_ops;
166
167 int _hashmap_by_section_find_unused_line(
168 HashmapBase *entries_by_section,
169 const char *filename,
170 unsigned *ret);
171 static inline int hashmap_by_section_find_unused_line(
172 Hashmap *entries_by_section,
173 const char *filename,
174 unsigned *ret) {
175 return _hashmap_by_section_find_unused_line(HASHMAP_BASE(entries_by_section), filename, ret);
176 }
177 static inline int ordered_hashmap_by_section_find_unused_line(
178 OrderedHashmap *entries_by_section,
179 const char *filename,
180 unsigned *ret) {
181 return _hashmap_by_section_find_unused_line(HASHMAP_BASE(entries_by_section), filename, ret);
182 }
183
184 static inline bool section_is_invalid(ConfigSection *section) {
185 /* If this returns false, then it does _not_ mean the section is valid. */
186
187 if (!section)
188 return false;
189
190 return section->invalid;
191 }
192
193 #define DEFINE_SECTION_CLEANUP_FUNCTIONS(type, free_func) \
194 static inline type* free_func##_or_set_invalid(type *p) { \
195 assert(p); \
196 \
197 if (p->section) \
198 p->section->invalid = true; \
199 else \
200 free_func(p); \
201 return NULL; \
202 } \
203 DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \
204 DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid);
205
206 CONFIG_PARSER_PROTOTYPE(config_parse_int);
207 CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);
208 CONFIG_PARSER_PROTOTYPE(config_parse_long);
209 CONFIG_PARSER_PROTOTYPE(config_parse_uint8);
210 CONFIG_PARSER_PROTOTYPE(config_parse_uint16);
211 CONFIG_PARSER_PROTOTYPE(config_parse_uint32);
212 CONFIG_PARSER_PROTOTYPE(config_parse_int32);
213 CONFIG_PARSER_PROTOTYPE(config_parse_uint64);
214 CONFIG_PARSER_PROTOTYPE(config_parse_double);
215 CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
216 CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64);
217 CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64);
218 CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64_infinity);
219 CONFIG_PARSER_PROTOTYPE(config_parse_bool);
220 CONFIG_PARSER_PROTOTYPE(config_parse_id128);
221 CONFIG_PARSER_PROTOTYPE(config_parse_tristate);
222 CONFIG_PARSER_PROTOTYPE(config_parse_string);
223 CONFIG_PARSER_PROTOTYPE(config_parse_dns_name);
224 CONFIG_PARSER_PROTOTYPE(config_parse_hostname);
225 CONFIG_PARSER_PROTOTYPE(config_parse_path);
226 CONFIG_PARSER_PROTOTYPE(config_parse_strv);
227 CONFIG_PARSER_PROTOTYPE(config_parse_sec);
228 CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_infinity);
229 CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_unset);
230 CONFIG_PARSER_PROTOTYPE(config_parse_nsec);
231 CONFIG_PARSER_PROTOTYPE(config_parse_mode);
232 CONFIG_PARSER_PROTOTYPE(config_parse_warn_compat);
233 CONFIG_PARSER_PROTOTYPE(config_parse_log_facility);
234 CONFIG_PARSER_PROTOTYPE(config_parse_log_level);
235 CONFIG_PARSER_PROTOTYPE(config_parse_signal);
236 CONFIG_PARSER_PROTOTYPE(config_parse_personality);
237 CONFIG_PARSER_PROTOTYPE(config_parse_permille);
238 CONFIG_PARSER_PROTOTYPE(config_parse_ifname);
239 CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
240 CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
241 CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
242 CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
243 CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol);
244 CONFIG_PARSER_PROTOTYPE(config_parse_hw_addr);
245 CONFIG_PARSER_PROTOTYPE(config_parse_hw_addrs);
246 CONFIG_PARSER_PROTOTYPE(config_parse_ether_addr);
247 CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs);
248 CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
249 CONFIG_PARSER_PROTOTYPE(config_parse_percent);
250 CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
251 CONFIG_PARSER_PROTOTYPE(config_parse_pid);
252 CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
253 CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
254
255 typedef enum Disabled {
256 DISABLED_CONFIGURATION,
257 DISABLED_LEGACY,
258 DISABLED_EXPERIMENTAL,
259 } Disabled;
260
261 typedef enum ConfigParseStringFlags {
262 CONFIG_PARSE_STRING_SAFE = 1 << 0,
263 CONFIG_PARSE_STRING_ASCII = 1 << 1,
264
265 CONFIG_PARSE_STRING_SAFE_AND_ASCII = CONFIG_PARSE_STRING_SAFE | CONFIG_PARSE_STRING_ASCII,
266 } ConfigParseStringFlags;
267
268 #define DEFINE_CONFIG_PARSE(function, parser, msg) \
269 CONFIG_PARSER_PROTOTYPE(function) { \
270 int *i = data, r; \
271 \
272 assert(filename); \
273 assert(lvalue); \
274 assert(rvalue); \
275 assert(data); \
276 \
277 r = parser(rvalue); \
278 if (r < 0) { \
279 log_syntax(unit, LOG_WARNING, filename, line, r, \
280 msg ", ignoring: %s", rvalue); \
281 return 0; \
282 } \
283 \
284 *i = r; \
285 return 0; \
286 }
287
288 #define DEFINE_CONFIG_PARSE_PTR(function, parser, type, msg) \
289 CONFIG_PARSER_PROTOTYPE(function) { \
290 type *i = ASSERT_PTR(data); \
291 int r; \
292 \
293 assert(filename); \
294 assert(lvalue); \
295 assert(rvalue); \
296 \
297 r = parser(rvalue, i); \
298 if (r < 0) \
299 log_syntax(unit, LOG_WARNING, filename, line, r, \
300 msg ", ignoring: %s", rvalue); \
301 \
302 return 0; \
303 }
304
305 #define DEFINE_CONFIG_PARSE_ENUM_FULL(function, from_string, type, msg) \
306 CONFIG_PARSER_PROTOTYPE(function) { \
307 type *i = data, x; \
308 \
309 assert(filename); \
310 assert(lvalue); \
311 assert(rvalue); \
312 assert(data); \
313 \
314 x = from_string(rvalue); \
315 if (x < 0) { \
316 log_syntax(unit, LOG_WARNING, filename, line, x, \
317 msg ", ignoring: %s", rvalue); \
318 return 0; \
319 } \
320 \
321 *i = x; \
322 return 0; \
323 }
324
325 #define DEFINE_CONFIG_PARSE_ENUM(function, name, type, msg) \
326 DEFINE_CONFIG_PARSE_ENUM_FULL(function, name##_from_string, type, msg)
327
328 #define DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(function, name, type, default_value, msg) \
329 CONFIG_PARSER_PROTOTYPE(function) { \
330 type *i = data, x; \
331 \
332 assert(filename); \
333 assert(lvalue); \
334 assert(rvalue); \
335 assert(data); \
336 \
337 if (isempty(rvalue)) { \
338 *i = default_value; \
339 return 0; \
340 } \
341 \
342 x = name##_from_string(rvalue); \
343 if (x < 0) { \
344 log_syntax(unit, LOG_WARNING, filename, line, x, \
345 msg ", ignoring: %s", rvalue); \
346 return 0; \
347 } \
348 \
349 *i = x; \
350 return 0; \
351 }
352
353 #define DEFINE_CONFIG_PARSE_ENUMV(function, name, type, invalid, msg) \
354 CONFIG_PARSER_PROTOTYPE(function) { \
355 type **enums = ASSERT_PTR(data); \
356 _cleanup_free_ type *xs = NULL; \
357 size_t i = 0; \
358 int r; \
359 \
360 assert(filename); \
361 assert(lvalue); \
362 assert(rvalue); \
363 \
364 xs = new0(type, 1); \
365 if (!xs) \
366 return -ENOMEM; \
367 \
368 *xs = invalid; \
369 \
370 for (const char *p = rvalue;;) { \
371 _cleanup_free_ char *en = NULL; \
372 type x, *new_xs; \
373 \
374 r = extract_first_word(&p, &en, NULL, 0); \
375 if (r == -ENOMEM) \
376 return log_oom(); \
377 if (r < 0) { \
378 log_syntax(unit, LOG_WARNING, filename, line, r, \
379 msg ", ignoring: %s", en); \
380 return 0; \
381 } \
382 if (r == 0) \
383 break; \
384 \
385 x = name##_from_string(en); \
386 if (x < 0) { \
387 log_syntax(unit, LOG_WARNING, filename, line, x, \
388 msg ", ignoring: %s", en); \
389 continue; \
390 } \
391 \
392 for (type *ys = xs; x != invalid && *ys != invalid; ys++) \
393 if (*ys == x) { \
394 log_syntax(unit, LOG_NOTICE, filename, line, 0, \
395 "Duplicate entry, ignoring: %s", \
396 en); \
397 x = invalid; \
398 } \
399 \
400 if (x == invalid) \
401 continue; \
402 \
403 *(xs + i) = x; \
404 new_xs = realloc(xs, (++i + 1) * sizeof(type)); \
405 if (new_xs) \
406 xs = new_xs; \
407 else \
408 return log_oom(); \
409 \
410 *(xs + i) = invalid; \
411 } \
412 \
413 return free_and_replace(*enums, xs); \
414 }
415
416 int config_parse_unsigned_bounded(
417 const char *unit,
418 const char *filename,
419 unsigned line,
420 const char *section,
421 unsigned section_line,
422 const char *name,
423 const char *value,
424 unsigned min,
425 unsigned max,
426 bool ignoring,
427 unsigned *ret);
428
429 static inline int config_parse_uint32_bounded(
430 const char *unit,
431 const char *filename,
432 unsigned line,
433 const char *section,
434 unsigned section_line,
435 const char *name,
436 const char *value,
437 uint32_t min,
438 uint32_t max,
439 bool ignoring,
440 uint32_t *ret) {
441
442 unsigned t;
443 int r;
444
445 r = config_parse_unsigned_bounded(
446 unit, filename, line, section, section_line, name, value,
447 min, max, ignoring,
448 &t);
449 if (r <= 0)
450 return r;
451 assert(t <= UINT32_MAX);
452 *ret = t;
453 return 1;
454 }
455
456 static inline int config_parse_uint16_bounded(
457 const char *unit,
458 const char *filename,
459 unsigned line,
460 const char *section,
461 unsigned section_line,
462 const char *name,
463 const char *value,
464 uint16_t min,
465 uint16_t max,
466 bool ignoring,
467 uint16_t *ret) {
468
469 unsigned t;
470 int r;
471
472 r = config_parse_unsigned_bounded(
473 unit, filename, line, section, section_line, name, value,
474 min, max, ignoring,
475 &t);
476 if (r <= 0)
477 return r;
478 assert(t <= UINT16_MAX);
479 *ret = t;
480 return 1;
481 }
482
483 static inline int config_parse_uint8_bounded(
484 const char *unit,
485 const char *filename,
486 unsigned line,
487 const char *section,
488 unsigned section_line,
489 const char *name,
490 const char *value,
491 uint8_t min,
492 uint8_t max,
493 bool ignoring,
494 uint8_t *ret) {
495
496 unsigned t;
497 int r;
498
499 r = config_parse_unsigned_bounded(
500 unit, filename, line, section, section_line, name, value,
501 min, max, ignoring,
502 &t);
503 if (r <= 0)
504 return r;
505 assert(t <= UINT8_MAX);
506 *ret = t;
507 return 1;
508 }