]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/conf-parser.h
man/systemd-sysext: list ephemeral/ephemeral-import in the list of options
[thirdparty/systemd.git] / src / shared / conf-parser.h
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
c2f1db8f 2#pragma once
ed5bcfbe 3
1cf40697 4#include <syslog.h>
ed5bcfbe 5
69a283c5 6#include "alloc-util.h"
7a30bc9a 7#include "conf-parser-forward.h"
69a283c5 8#include "forward.h"
a8fbdf54 9#include "log.h"
e8e581bf 10
bcde742e
LP
11/* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */
12
13typedef enum ConfigParseFlags {
94a404cb 14 CONFIG_PARSE_RELAXED = 1 << 0, /* Do not warn about unknown non-extension fields */
7ade8982 15 CONFIG_PARSE_WARN = 1 << 1, /* Emit non-debug messages */
bcde742e 16} ConfigParseFlags;
ed5bcfbe 17
f975e971
LP
18/* Wraps information for parsing a specific configuration variable, to
19 * be stored in a simple array */
20typedef struct ConfigTableItem {
21 const char *section; /* Section */
22 const char *lvalue; /* Name of the variable */
23 ConfigParserCallback parse; /* Function that is called to parse the variable's value */
24 int ltype; /* Distinguish different variables passed to the same callback */
25 void *data; /* Where to store the variable's data */
26} ConfigTableItem;
27
28/* Wraps information for parsing a specific configuration variable, to
e5a7f173 29 * be stored in a gperf perfect hashtable */
f975e971
LP
30typedef struct ConfigPerfItem {
31 const char *section_and_lvalue; /* Section + "." + name of the variable */
32 ConfigParserCallback parse; /* Function that is called to parse the variable's value */
33 int ltype; /* Distinguish different variables passed to the same callback */
34 size_t offset; /* Offset where to store data, from the beginning of userdata */
35} ConfigPerfItem;
36
37/* Prototype for a low-level gperf lookup function */
5996cc34 38typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, GPERF_LEN_TYPE length);
f975e971
LP
39
40/* Prototype for a generic high-level lookup function */
41typedef int (*ConfigItemLookup)(
e9f3d2d5 42 const void *table,
f975e971
LP
43 const char *section,
44 const char *lvalue,
0b954099
LP
45 ConfigParserCallback *ret_func,
46 int *ret_ltype,
47 void **ret_data,
f975e971
LP
48 void *userdata);
49
50/* Linear table search implementation of ConfigItemLookup, based on
51 * ConfigTableItem arrays */
0b954099 52int config_item_table_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *ret_func, int *ret_ltype, void **ret_data, void *userdata);
f975e971
LP
53
54/* gperf implementation of ConfigItemLookup, based on gperf
55 * ConfigPerfItem tables */
0b954099 56int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *ret_func, int *ret_ltype, void **ret_data, void *userdata);
f975e971 57
43688c49
ZJS
58int config_parse(
59 const char *unit,
60 const char *filename,
61 FILE *f,
4f9ff96a 62 const char *sections, /* nulstr */
43688c49
ZJS
63 ConfigItemLookup lookup,
64 const void *table,
bcde742e 65 ConfigParseFlags flags,
4f9ff96a 66 void *userdata,
8524db50 67 struct stat *ret_stat); /* possibly NULL */
43688c49 68
23bb31aa 69int config_parse_many(
8b8024f1 70 const char* const* conf_files, /* possibly empty */
23bb31aa
ZJS
71 const char* const* conf_file_dirs,
72 const char *dropin_dirname,
947f59ba 73 const char *root,
d8a91c6b 74 const char *sections, /* nulstr */
23bb31aa
ZJS
75 ConfigItemLookup lookup,
76 const void *table,
bcde742e 77 ConfigParseFlags flags,
9f83091e 78 void *userdata,
ead3a3fc
RP
79 Hashmap **ret_stats_by_path, /* possibly NULL */
80 char ***ret_drop_in_files); /* possibly NULL */
23bb31aa 81
e7e52ff9
ZJS
82int config_parse_standard_file_with_dropins_full(
83 const char *root,
84 const char *main_file, /* A path like "systemd/frobnicator.conf" */
85 const char *sections,
86 ConfigItemLookup lookup,
87 const void *table,
88 ConfigParseFlags flags,
89 void *userdata,
90 Hashmap **ret_stats_by_path, /* possibly NULL */
91 char ***ret_dropin_files); /* possibly NULL */
92
6378f257
ZJS
93static inline int config_parse_standard_file_with_dropins(
94 const char *main_file, /* A path like "systemd/frobnicator.conf" */
95 const char *sections, /* nulstr */
96 ConfigItemLookup lookup,
97 const void *table,
98 ConfigParseFlags flags,
99 void *userdata) {
100 return config_parse_standard_file_with_dropins_full(
101 /* root= */ NULL,
102 main_file,
103 sections,
104 lookup,
105 table,
106 flags,
107 userdata,
108 /* ret_stats_by_path= */ NULL,
109 /* ret_dropin_files= */ NULL);
110}
111
bdb2d3c6
YW
112int config_get_stats_by_path(
113 const char *suffix,
114 const char *root,
115 unsigned flags,
116 const char* const* dirs,
3f4dfd9d 117 bool check_dropins,
bdb2d3c6
YW
118 Hashmap **ret);
119
acfbd71c 120int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st);
bdb2d3c6
YW
121bool stats_by_path_equal(Hashmap *a, Hashmap *b);
122
152b8a4e
YW
123typedef struct ConfigSectionParser {
124 ConfigParserCallback parser;
125 int ltype;
126 size_t offset;
127} ConfigSectionParser;
128
129int config_section_parse(
130 const ConfigSectionParser *parsers,
131 size_t n_parsers,
132 const char *unit,
133 const char *filename,
134 unsigned line,
135 const char *section,
136 unsigned section_line,
137 const char *lvalue,
138 int ltype,
139 const char *rvalue,
140 void *userdata);
141
307fe3cd
YW
142static inline ConfigSection* config_section_free(ConfigSection *cs) {
143 return mfree(cs);
144}
145DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection*, config_section_free);
146
08ca764d 147int config_section_new(const char *filename, unsigned line, ConfigSection **ret);
b46d1694
YW
148
149void config_section_hash_func(const ConfigSection *c, struct siphash *state);
150int config_section_compare_func(const ConfigSection *x, const ConfigSection *y);
307fe3cd 151extern const struct hash_ops config_section_hash_ops;
b46d1694 152
69a283c5
DDM
153int hashmap_by_section_find_unused_line(Hashmap *entries_by_section, const char *filename, unsigned *ret);
154int ordered_hashmap_by_section_find_unused_line(OrderedHashmap *entries_by_section, const char *filename, unsigned *ret);
307fe3cd
YW
155
156static inline bool section_is_invalid(ConfigSection *section) {
157 /* If this returns false, then it does _not_ mean the section is valid. */
158
159 if (!section)
160 return false;
161
162 return section->invalid;
163}
164
75f59c6e
YW
165#define log_section_full_errno_zerook(section, level, error, ...) \
166 ({ \
167 const ConfigSection *_s = (section); \
168 log_syntax(/* unit = */ NULL, \
169 level, \
170 _s ? _s->filename : NULL, \
171 _s ? _s->line : 0, \
172 error, \
173 __VA_ARGS__); \
174 })
175
176#define log_section_full_errno(section, level, error, ...) \
177 ({ \
178 int _error = (error); \
179 ASSERT_NON_ZERO(_error); \
180 log_section_full_errno_zerook(section, level, _error, __VA_ARGS__); \
181 })
182
183#define log_section_full(section, level, fmt, ...) \
184 ({ \
185 if (BUILD_MODE_DEVELOPER) \
186 assert(!strstr(fmt, "%m")); \
187 (void) log_section_full_errno_zerook(section, level, 0, fmt, ##__VA_ARGS__); \
188 })
189
190#define log_section_debug(section, ...) log_section_full(section, LOG_DEBUG, __VA_ARGS__)
191#define log_section_info(section, ...) log_section_full(section, LOG_INFO, __VA_ARGS__)
192#define log_section_notice(section, ...) log_section_full(section, LOG_NOTICE, __VA_ARGS__)
193#define log_section_warning(section, ...) log_section_full(section, LOG_WARNING, __VA_ARGS__)
194#define log_section_error(section, ...) log_section_full(section, LOG_ERR, __VA_ARGS__)
195
196#define log_section_debug_errno(section, error, ...) log_section_full_errno(section, LOG_DEBUG, error, __VA_ARGS__)
197#define log_section_info_errno(section, error, ...) log_section_full_errno(section, LOG_INFO, error, __VA_ARGS__)
198#define log_section_notice_errno(section, error, ...) log_section_full_errno(section, LOG_NOTICE, error, __VA_ARGS__)
199#define log_section_warning_errno(section, error, ...) log_section_full_errno(section, LOG_WARNING, error, __VA_ARGS__)
200#define log_section_error_errno(section, error, ...) log_section_full_errno(section, LOG_ERR, error, __VA_ARGS__)
201
1f12b48a
LP
202CONFIG_PARSER_PROTOTYPE(config_parse_int);
203CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);
204CONFIG_PARSER_PROTOTYPE(config_parse_long);
205CONFIG_PARSER_PROTOTYPE(config_parse_uint8);
206CONFIG_PARSER_PROTOTYPE(config_parse_uint16);
207CONFIG_PARSER_PROTOTYPE(config_parse_uint32);
b57ebc60 208CONFIG_PARSER_PROTOTYPE(config_parse_int32);
1f12b48a
LP
209CONFIG_PARSER_PROTOTYPE(config_parse_uint64);
210CONFIG_PARSER_PROTOTYPE(config_parse_double);
211CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
50299121 212CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64);
1f12b48a 213CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64);
6e8791a0 214CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64_infinity);
1f12b48a 215CONFIG_PARSER_PROTOTYPE(config_parse_bool);
6db311fd 216CONFIG_PARSER_PROTOTYPE(config_parse_uint32_flag);
ff616da4 217CONFIG_PARSER_PROTOTYPE(config_parse_uint32_invert_flag);
12963533 218CONFIG_PARSER_PROTOTYPE(config_parse_id128);
1f12b48a
LP
219CONFIG_PARSER_PROTOTYPE(config_parse_tristate);
220CONFIG_PARSER_PROTOTYPE(config_parse_string);
fa787a13
YW
221CONFIG_PARSER_PROTOTYPE(config_parse_dns_name);
222CONFIG_PARSER_PROTOTYPE(config_parse_hostname);
1f12b48a
LP
223CONFIG_PARSER_PROTOTYPE(config_parse_path);
224CONFIG_PARSER_PROTOTYPE(config_parse_strv);
225CONFIG_PARSER_PROTOTYPE(config_parse_sec);
7b61ce3c 226CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_infinity);
dc653bf4 227CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_unset);
1f12b48a
LP
228CONFIG_PARSER_PROTOTYPE(config_parse_nsec);
229CONFIG_PARSER_PROTOTYPE(config_parse_mode);
230CONFIG_PARSER_PROTOTYPE(config_parse_warn_compat);
231CONFIG_PARSER_PROTOTYPE(config_parse_log_facility);
232CONFIG_PARSER_PROTOTYPE(config_parse_log_level);
233CONFIG_PARSER_PROTOTYPE(config_parse_signal);
234CONFIG_PARSER_PROTOTYPE(config_parse_personality);
c07b23ca 235CONFIG_PARSER_PROTOTYPE(config_parse_permille);
1f12b48a 236CONFIG_PARSER_PROTOTYPE(config_parse_ifname);
a5053a15 237CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
1f12b48a 238CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
1f12b48a
LP
239CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
240CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
4df4df5b 241CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol);
99628f36
YW
242CONFIG_PARSER_PROTOTYPE(config_parse_hw_addr);
243CONFIG_PARSER_PROTOTYPE(config_parse_hw_addrs);
aa4f7653
YW
244CONFIG_PARSER_PROTOTYPE(config_parse_ether_addr);
245CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs);
cf074772 246CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
0ea6d55a 247CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_data);
8cde9f6c 248CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_prefix);
9de5e321 249CONFIG_PARSER_PROTOTYPE(config_parse_percent);
0a9f9344 250CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
65a0ede2 251CONFIG_PARSER_PROTOTYPE(config_parse_pid);
4ee8176f 252CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
f72e851f 253CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
0e10c3d8 254CONFIG_PARSER_PROTOTYPE(config_parse_calendar);
f7a1e57e 255CONFIG_PARSER_PROTOTYPE(config_parse_ip_protocol);
015a3b8c 256CONFIG_PARSER_PROTOTYPE(config_parse_loadavg);
e8e581bf 257
1e35c5ab
RP
258typedef enum Disabled {
259 DISABLED_CONFIGURATION,
260 DISABLED_LEGACY,
261 DISABLED_EXPERIMENTAL,
262} Disabled;
263
3f87eaa5
YW
264typedef enum ConfigParseStringFlags {
265 CONFIG_PARSE_STRING_SAFE = 1 << 0,
e289ce7f
YW
266 CONFIG_PARSE_STRING_ASCII = 1 << 1,
267
268 CONFIG_PARSE_STRING_SAFE_AND_ASCII = CONFIG_PARSE_STRING_SAFE | CONFIG_PARSE_STRING_ASCII,
3f87eaa5
YW
269} ConfigParseStringFlags;
270
42efe5be 271#define DEFINE_CONFIG_PARSE(function, parser) \
2d1729ca
YW
272 CONFIG_PARSER_PROTOTYPE(function) { \
273 int *i = data, r; \
274 \
275 assert(filename); \
276 assert(lvalue); \
277 assert(rvalue); \
278 assert(data); \
279 \
280 r = parser(rvalue); \
42efe5be
YW
281 if (r < 0) \
282 return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); \
2d1729ca
YW
283 \
284 *i = r; \
f4810fe2 285 return 1; \
2d1729ca
YW
286 }
287
42efe5be 288#define DEFINE_CONFIG_PARSE_PTR(function, parser, type) \
2d1729ca 289 CONFIG_PARSER_PROTOTYPE(function) { \
99534007 290 type *i = ASSERT_PTR(data); \
2d1729ca
YW
291 int r; \
292 \
293 assert(filename); \
294 assert(lvalue); \
295 assert(rvalue); \
2d1729ca
YW
296 \
297 r = parser(rvalue, i); \
298 if (r < 0) \
42efe5be 299 return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); \
2d1729ca 300 \
f4810fe2 301 return 1; \
2d1729ca
YW
302 }
303
42efe5be 304#define DEFINE_CONFIG_PARSE_ENUM_FULL(function, from_string, type) \
2d1729ca
YW
305 CONFIG_PARSER_PROTOTYPE(function) { \
306 type *i = data, x; \
307 \
308 assert(filename); \
309 assert(lvalue); \
310 assert(rvalue); \
311 assert(data); \
312 \
a2b06dbe 313 x = from_string(rvalue); \
42efe5be
YW
314 if (x < 0) \
315 return log_syntax_parse_error(unit, filename, line, x, lvalue, rvalue); \
2d1729ca
YW
316 \
317 *i = x; \
f4810fe2 318 return 1; \
2d1729ca
YW
319 }
320
42efe5be
YW
321#define DEFINE_CONFIG_PARSE_ENUM(function, name, type) \
322 DEFINE_CONFIG_PARSE_ENUM_FULL(function, name##_from_string, type)
a2b06dbe 323
42efe5be 324#define DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(function, name, type, default_value) \
1f12b48a 325 CONFIG_PARSER_PROTOTYPE(function) { \
487393e9
LP
326 type *i = data, x; \
327 \
328 assert(filename); \
329 assert(lvalue); \
330 assert(rvalue); \
331 assert(data); \
332 \
2d1729ca
YW
333 if (isempty(rvalue)) { \
334 *i = default_value; \
f4810fe2 335 return 1; \
2d1729ca
YW
336 } \
337 \
338 x = name##_from_string(rvalue); \
42efe5be
YW
339 if (x < 0) \
340 return log_syntax_parse_error(unit, filename, line, x, lvalue, rvalue); \
487393e9
LP
341 \
342 *i = x; \
f4810fe2 343 return 1; \
487393e9 344 }
916484f5 345
42efe5be 346#define DEFINE_CONFIG_PARSE_ENUMV(function, name, type, invalid) \
1f12b48a 347 CONFIG_PARSER_PROTOTYPE(function) { \
99534007 348 type **enums = ASSERT_PTR(data); \
77c10205 349 _cleanup_free_ type *xs = NULL; \
3c8dc3a3 350 size_t n = 0; \
ecaf258e 351 int r; \
916484f5 352 \
916484f5 353 assert(lvalue); \
916484f5 354 \
ecaf258e 355 for (const char *p = rvalue;;) { \
916484f5 356 _cleanup_free_ char *en = NULL; \
3c8dc3a3 357 type x; \
916484f5 358 \
ecaf258e 359 r = extract_first_word(&p, &en, NULL, 0); \
42efe5be
YW
360 if (r < 0) \
361 return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); \
ecaf258e
ZJS
362 if (r == 0) \
363 break; \
916484f5 364 \
0cbb768a
YW
365 x = name##_from_string(en); \
366 if (x < 0) { \
367 log_syntax(unit, LOG_WARNING, filename, line, x, \
42efe5be
YW
368 "Failed to parse %s in %s=, ignoring.", \
369 en, lvalue); \
916484f5
TG
370 continue; \
371 } \
372 \
3c8dc3a3
YW
373 FOREACH_ARRAY(i, xs, n) \
374 if (*i == x) { \
ecaf258e 375 log_syntax(unit, LOG_NOTICE, filename, line, 0, \
42efe5be
YW
376 "Duplicate entry %s in %s=, ignoring.", \
377 en, lvalue); \
916484f5 378 x = invalid; \
3c8dc3a3 379 break; \
916484f5 380 } \
916484f5
TG
381 \
382 if (x == invalid) \
383 continue; \
384 \
3c8dc3a3
YW
385 /* Allocate one more for the trailing 'invalid'. */ \
386 if (!GREEDY_REALLOC(xs, n + 2)) \
d96edb2c 387 return log_oom(); \
83e341a6 388 \
3c8dc3a3
YW
389 xs[n++] = x; \
390 } \
391 \
392 if (n <= 0) { \
393 /* An empty string, or invalid values only. */ \
394 *enums = mfree(*enums); \
395 return 1; \
916484f5
TG
396 } \
397 \
3c8dc3a3
YW
398 /* Terminate with 'invalid' */ \
399 xs[n] = invalid; \
f4810fe2
YW
400 free_and_replace(*enums, xs); \
401 return 1; \
916484f5 402 }
851cdffd
ZJS
403
404int config_parse_unsigned_bounded(
405 const char *unit,
406 const char *filename,
407 unsigned line,
408 const char *section,
409 unsigned section_line,
6f12cb91
YW
410 const char *lvalue,
411 const char *rvalue,
851cdffd
ZJS
412 unsigned min,
413 unsigned max,
414 bool ignoring,
415 unsigned *ret);
416
417static inline int config_parse_uint32_bounded(
418 const char *unit,
419 const char *filename,
420 unsigned line,
421 const char *section,
422 unsigned section_line,
6f12cb91
YW
423 const char *lvalue,
424 const char *rvalue,
851cdffd
ZJS
425 uint32_t min,
426 uint32_t max,
427 bool ignoring,
428 uint32_t *ret) {
429
430 unsigned t;
431 int r;
432
433 r = config_parse_unsigned_bounded(
6f12cb91 434 unit, filename, line, section, section_line, lvalue, rvalue,
851cdffd
ZJS
435 min, max, ignoring,
436 &t);
437 if (r <= 0)
438 return r;
439 assert(t <= UINT32_MAX);
440 *ret = t;
441 return 1;
442}
443
444static inline int config_parse_uint16_bounded(
445 const char *unit,
446 const char *filename,
447 unsigned line,
448 const char *section,
449 unsigned section_line,
6f12cb91
YW
450 const char *lvalue,
451 const char *rvalue,
851cdffd
ZJS
452 uint16_t min,
453 uint16_t max,
454 bool ignoring,
455 uint16_t *ret) {
456
457 unsigned t;
458 int r;
459
460 r = config_parse_unsigned_bounded(
6f12cb91 461 unit, filename, line, section, section_line, lvalue, rvalue,
851cdffd
ZJS
462 min, max, ignoring,
463 &t);
464 if (r <= 0)
465 return r;
466 assert(t <= UINT16_MAX);
467 *ret = t;
468 return 1;
469}
470
471static inline int config_parse_uint8_bounded(
472 const char *unit,
473 const char *filename,
474 unsigned line,
475 const char *section,
476 unsigned section_line,
6f12cb91
YW
477 const char *lvalue,
478 const char *rvalue,
851cdffd
ZJS
479 uint8_t min,
480 uint8_t max,
481 bool ignoring,
482 uint8_t *ret) {
483
484 unsigned t;
485 int r;
486
487 r = config_parse_unsigned_bounded(
6f12cb91 488 unit, filename, line, section, section_line, lvalue, rvalue,
851cdffd
ZJS
489 min, max, ignoring,
490 &t);
491 if (r <= 0)
492 return r;
493 assert(t <= UINT8_MAX);
494 *ret = t;
495 return 1;
496}