]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/conf-parser.h
cryptenroll: allow to use a public key on a token
[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
a8fbdf54 4#include <errno.h>
10e87ee7 5#include <stdbool.h>
a8fbdf54 6#include <stddef.h>
71d35b6b 7#include <stdio.h>
a8fbdf54 8#include <syslog.h>
8524db50 9#include <sys/stat.h>
ed5bcfbe 10
a8fbdf54 11#include "alloc-util.h"
8524db50 12#include "hashmap.h"
a8fbdf54 13#include "log.h"
e8e581bf 14#include "macro.h"
4f9ff96a 15#include "time-util.h"
e8e581bf 16
bcde742e
LP
17/* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */
18
19typedef enum ConfigParseFlags {
94a404cb 20 CONFIG_PARSE_RELAXED = 1 << 0, /* Do not warn about unknown non-extension fields */
7ade8982 21 CONFIG_PARSE_WARN = 1 << 1, /* Emit non-debug messages */
bcde742e 22} ConfigParseFlags;
ed5bcfbe 23
1f12b48a
LP
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
f975e971 37/* Prototype for a parser for a specific configuration setting */
1f12b48a
LP
38typedef int (*ConfigParserCallback)(CONFIG_PARSER_ARGUMENTS);
39
d51c4fca 40/* A macro declaring a function prototype, following the typedef above, simply because it's so cumbersomely long
1f12b48a
LP
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)
f975e971
LP
44
45/* Wraps information for parsing a specific configuration variable, to
46 * be stored in a simple array */
47typedef 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
e5a7f173 56 * be stored in a gperf perfect hashtable */
f975e971
LP
57typedef 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 */
5996cc34 65typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, GPERF_LEN_TYPE length);
f975e971
LP
66
67/* Prototype for a generic high-level lookup function */
68typedef int (*ConfigItemLookup)(
e9f3d2d5 69 const void *table,
f975e971
LP
70 const char *section,
71 const char *lvalue,
0b954099
LP
72 ConfigParserCallback *ret_func,
73 int *ret_ltype,
74 void **ret_data,
f975e971
LP
75 void *userdata);
76
77/* Linear table search implementation of ConfigItemLookup, based on
78 * ConfigTableItem arrays */
0b954099 79int 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
80
81/* gperf implementation of ConfigItemLookup, based on gperf
82 * ConfigPerfItem tables */
0b954099 83int 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 84
43688c49
ZJS
85int config_parse(
86 const char *unit,
87 const char *filename,
88 FILE *f,
4f9ff96a 89 const char *sections, /* nulstr */
43688c49
ZJS
90 ConfigItemLookup lookup,
91 const void *table,
bcde742e 92 ConfigParseFlags flags,
4f9ff96a 93 void *userdata,
8524db50 94 struct stat *ret_stat); /* possibly NULL */
43688c49 95
5656cdfe 96int config_parse_config_file_full(
07e0ffc8 97 const char *conf_file,
5656cdfe 98 const char *pkgdir,
43688c49
ZJS
99 const char *sections, /* nulstr */
100 ConfigItemLookup lookup,
101 const void *table,
bcde742e 102 ConfigParseFlags flags,
07e0ffc8 103 void *userdata);
e8461023 104
5656cdfe
DT
105static inline int config_parse_config_file(
106 const char *conf_file,
107 const char *sections, /* nulstr */
108 ConfigItemLookup lookup,
109 const void *table,
110 ConfigParseFlags flags,
111 void *userdata) {
112 return config_parse_config_file_full(conf_file, "systemd", sections, lookup, table, flags, userdata);
113}
114
23bb31aa 115int config_parse_many(
8b8024f1 116 const char* const* conf_files, /* possibly empty */
23bb31aa
ZJS
117 const char* const* conf_file_dirs,
118 const char *dropin_dirname,
947f59ba 119 const char *root,
23bb31aa
ZJS
120 const char *sections, /* nulstr */
121 ConfigItemLookup lookup,
122 const void *table,
bcde742e 123 ConfigParseFlags flags,
9f83091e 124 void *userdata,
ead3a3fc
RP
125 Hashmap **ret_stats_by_path, /* possibly NULL */
126 char ***ret_drop_in_files); /* possibly NULL */
23bb31aa 127
bdb2d3c6
YW
128int config_get_stats_by_path(
129 const char *suffix,
130 const char *root,
131 unsigned flags,
132 const char* const* dirs,
3f4dfd9d 133 bool check_dropins,
bdb2d3c6
YW
134 Hashmap **ret);
135
acfbd71c 136int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st);
bdb2d3c6
YW
137bool stats_by_path_equal(Hashmap *a, Hashmap *b);
138
307fe3cd
YW
139typedef struct ConfigSection {
140 unsigned line;
141 bool invalid;
142 char filename[];
143} ConfigSection;
144
145static inline ConfigSection* config_section_free(ConfigSection *cs) {
146 return mfree(cs);
147}
148DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection*, config_section_free);
149
08ca764d 150int config_section_new(const char *filename, unsigned line, ConfigSection **ret);
b46d1694
YW
151
152void config_section_hash_func(const ConfigSection *c, struct siphash *state);
153int config_section_compare_func(const ConfigSection *x, const ConfigSection *y);
307fe3cd 154extern const struct hash_ops config_section_hash_ops;
b46d1694 155
e63c6e9f
YW
156int _hashmap_by_section_find_unused_line(
157 HashmapBase *entries_by_section,
d9171a23
YW
158 const char *filename,
159 unsigned *ret);
e63c6e9f
YW
160static inline int hashmap_by_section_find_unused_line(
161 Hashmap *entries_by_section,
162 const char *filename,
163 unsigned *ret) {
164 return _hashmap_by_section_find_unused_line(HASHMAP_BASE(entries_by_section), filename, ret);
165}
166static inline int ordered_hashmap_by_section_find_unused_line(
167 OrderedHashmap *entries_by_section,
168 const char *filename,
169 unsigned *ret) {
170 return _hashmap_by_section_find_unused_line(HASHMAP_BASE(entries_by_section), filename, ret);
171}
307fe3cd
YW
172
173static inline bool section_is_invalid(ConfigSection *section) {
174 /* If this returns false, then it does _not_ mean the section is valid. */
175
176 if (!section)
177 return false;
178
179 return section->invalid;
180}
181
182#define DEFINE_SECTION_CLEANUP_FUNCTIONS(type, free_func) \
183 static inline type* free_func##_or_set_invalid(type *p) { \
184 assert(p); \
185 \
186 if (p->section) \
187 p->section->invalid = true; \
188 else \
189 free_func(p); \
190 return NULL; \
191 } \
192 DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \
193 DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid);
194
1f12b48a
LP
195CONFIG_PARSER_PROTOTYPE(config_parse_int);
196CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);
197CONFIG_PARSER_PROTOTYPE(config_parse_long);
198CONFIG_PARSER_PROTOTYPE(config_parse_uint8);
199CONFIG_PARSER_PROTOTYPE(config_parse_uint16);
200CONFIG_PARSER_PROTOTYPE(config_parse_uint32);
b57ebc60 201CONFIG_PARSER_PROTOTYPE(config_parse_int32);
1f12b48a
LP
202CONFIG_PARSER_PROTOTYPE(config_parse_uint64);
203CONFIG_PARSER_PROTOTYPE(config_parse_double);
204CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
50299121 205CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64);
1f12b48a 206CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64);
6e8791a0 207CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64_infinity);
1f12b48a 208CONFIG_PARSER_PROTOTYPE(config_parse_bool);
12963533 209CONFIG_PARSER_PROTOTYPE(config_parse_id128);
1f12b48a
LP
210CONFIG_PARSER_PROTOTYPE(config_parse_tristate);
211CONFIG_PARSER_PROTOTYPE(config_parse_string);
fa787a13
YW
212CONFIG_PARSER_PROTOTYPE(config_parse_dns_name);
213CONFIG_PARSER_PROTOTYPE(config_parse_hostname);
1f12b48a
LP
214CONFIG_PARSER_PROTOTYPE(config_parse_path);
215CONFIG_PARSER_PROTOTYPE(config_parse_strv);
216CONFIG_PARSER_PROTOTYPE(config_parse_sec);
7b61ce3c 217CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_infinity);
dc653bf4 218CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_unset);
1f12b48a
LP
219CONFIG_PARSER_PROTOTYPE(config_parse_nsec);
220CONFIG_PARSER_PROTOTYPE(config_parse_mode);
221CONFIG_PARSER_PROTOTYPE(config_parse_warn_compat);
222CONFIG_PARSER_PROTOTYPE(config_parse_log_facility);
223CONFIG_PARSER_PROTOTYPE(config_parse_log_level);
224CONFIG_PARSER_PROTOTYPE(config_parse_signal);
225CONFIG_PARSER_PROTOTYPE(config_parse_personality);
c07b23ca 226CONFIG_PARSER_PROTOTYPE(config_parse_permille);
1f12b48a 227CONFIG_PARSER_PROTOTYPE(config_parse_ifname);
a5053a15 228CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
1f12b48a 229CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
1f12b48a
LP
230CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
231CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
4df4df5b 232CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol);
99628f36
YW
233CONFIG_PARSER_PROTOTYPE(config_parse_hw_addr);
234CONFIG_PARSER_PROTOTYPE(config_parse_hw_addrs);
aa4f7653
YW
235CONFIG_PARSER_PROTOTYPE(config_parse_ether_addr);
236CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs);
cf074772 237CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
9de5e321 238CONFIG_PARSER_PROTOTYPE(config_parse_percent);
0a9f9344 239CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
65a0ede2 240CONFIG_PARSER_PROTOTYPE(config_parse_pid);
4ee8176f 241CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
e8e581bf 242
1e35c5ab
RP
243typedef enum Disabled {
244 DISABLED_CONFIGURATION,
245 DISABLED_LEGACY,
246 DISABLED_EXPERIMENTAL,
247} Disabled;
248
3f87eaa5
YW
249typedef enum ConfigParseStringFlags {
250 CONFIG_PARSE_STRING_SAFE = 1 << 0,
e289ce7f
YW
251 CONFIG_PARSE_STRING_ASCII = 1 << 1,
252
253 CONFIG_PARSE_STRING_SAFE_AND_ASCII = CONFIG_PARSE_STRING_SAFE | CONFIG_PARSE_STRING_ASCII,
3f87eaa5
YW
254} ConfigParseStringFlags;
255
2d1729ca
YW
256#define DEFINE_CONFIG_PARSE(function, parser, msg) \
257 CONFIG_PARSER_PROTOTYPE(function) { \
258 int *i = data, r; \
259 \
260 assert(filename); \
261 assert(lvalue); \
262 assert(rvalue); \
263 assert(data); \
264 \
265 r = parser(rvalue); \
266 if (r < 0) { \
d96edb2c 267 log_syntax(unit, LOG_WARNING, filename, line, r, \
2d1729ca
YW
268 msg ", ignoring: %s", rvalue); \
269 return 0; \
270 } \
271 \
272 *i = r; \
273 return 0; \
274 }
275
276#define DEFINE_CONFIG_PARSE_PTR(function, parser, type, msg) \
277 CONFIG_PARSER_PROTOTYPE(function) { \
99534007 278 type *i = ASSERT_PTR(data); \
2d1729ca
YW
279 int r; \
280 \
281 assert(filename); \
282 assert(lvalue); \
283 assert(rvalue); \
2d1729ca
YW
284 \
285 r = parser(rvalue, i); \
286 if (r < 0) \
d96edb2c 287 log_syntax(unit, LOG_WARNING, filename, line, r, \
2d1729ca
YW
288 msg ", ignoring: %s", rvalue); \
289 \
290 return 0; \
291 }
292
a2b06dbe 293#define DEFINE_CONFIG_PARSE_ENUM_FULL(function, from_string, type, msg) \
2d1729ca
YW
294 CONFIG_PARSER_PROTOTYPE(function) { \
295 type *i = data, x; \
296 \
297 assert(filename); \
298 assert(lvalue); \
299 assert(rvalue); \
300 assert(data); \
301 \
a2b06dbe 302 x = from_string(rvalue); \
2d1729ca 303 if (x < 0) { \
0cbb768a 304 log_syntax(unit, LOG_WARNING, filename, line, x, \
2d1729ca
YW
305 msg ", ignoring: %s", rvalue); \
306 return 0; \
307 } \
308 \
309 *i = x; \
310 return 0; \
311 }
312
a2b06dbe
LP
313#define DEFINE_CONFIG_PARSE_ENUM(function, name, type, msg) \
314 DEFINE_CONFIG_PARSE_ENUM_FULL(function, name##_from_string, type, msg)
315
2d1729ca 316#define DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(function, name, type, default_value, msg) \
1f12b48a 317 CONFIG_PARSER_PROTOTYPE(function) { \
487393e9
LP
318 type *i = data, x; \
319 \
320 assert(filename); \
321 assert(lvalue); \
322 assert(rvalue); \
323 assert(data); \
324 \
2d1729ca
YW
325 if (isempty(rvalue)) { \
326 *i = default_value; \
327 return 0; \
328 } \
329 \
330 x = name##_from_string(rvalue); \
331 if (x < 0) { \
0cbb768a 332 log_syntax(unit, LOG_WARNING, filename, line, x, \
e8e581bf 333 msg ", ignoring: %s", rvalue); \
c0b34696 334 return 0; \
487393e9
LP
335 } \
336 \
337 *i = x; \
487393e9
LP
338 return 0; \
339 }
916484f5 340
2d1729ca 341#define DEFINE_CONFIG_PARSE_ENUMV(function, name, type, invalid, msg) \
1f12b48a 342 CONFIG_PARSER_PROTOTYPE(function) { \
99534007 343 type **enums = ASSERT_PTR(data); \
77c10205 344 _cleanup_free_ type *xs = NULL; \
ecaf258e
ZJS
345 size_t i = 0; \
346 int r; \
916484f5
TG
347 \
348 assert(filename); \
349 assert(lvalue); \
350 assert(rvalue); \
916484f5
TG
351 \
352 xs = new0(type, 1); \
9ed794a3 353 if (!xs) \
83e341a6
TG
354 return -ENOMEM; \
355 \
916484f5
TG
356 *xs = invalid; \
357 \
ecaf258e 358 for (const char *p = rvalue;;) { \
916484f5 359 _cleanup_free_ char *en = NULL; \
ecaf258e 360 type x, *new_xs; \
916484f5 361 \
ecaf258e
ZJS
362 r = extract_first_word(&p, &en, NULL, 0); \
363 if (r == -ENOMEM) \
d96edb2c 364 return log_oom(); \
0cbb768a
YW
365 if (r < 0) { \
366 log_syntax(unit, LOG_WARNING, filename, line, r, \
367 msg ", ignoring: %s", en); \
368 return 0; \
369 } \
ecaf258e
ZJS
370 if (r == 0) \
371 break; \
916484f5 372 \
0cbb768a
YW
373 x = name##_from_string(en); \
374 if (x < 0) { \
375 log_syntax(unit, LOG_WARNING, filename, line, x, \
2d1729ca 376 msg ", ignoring: %s", en); \
916484f5
TG
377 continue; \
378 } \
379 \
ecaf258e
ZJS
380 for (type *ys = xs; x != invalid && *ys != invalid; ys++) \
381 if (*ys == x) { \
382 log_syntax(unit, LOG_NOTICE, filename, line, 0, \
383 "Duplicate entry, ignoring: %s", \
2d1729ca 384 en); \
916484f5
TG
385 x = invalid; \
386 } \
916484f5
TG
387 \
388 if (x == invalid) \
389 continue; \
390 \
391 *(xs + i) = x; \
77c10205
TG
392 new_xs = realloc(xs, (++i + 1) * sizeof(type)); \
393 if (new_xs) \
394 xs = new_xs; \
395 else \
d96edb2c 396 return log_oom(); \
83e341a6 397 \
916484f5
TG
398 *(xs + i) = invalid; \
399 } \
400 \
ecaf258e 401 return free_and_replace(*enums, xs); \
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,
410 const char *name,
411 const char *value,
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,
423 const char *name,
424 const char *value,
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(
434 unit, filename, line, section, section_line, name, value,
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,
450 const char *name,
451 const char *value,
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(
461 unit, filename, line, section, section_line, name, value,
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,
477 const char *name,
478 const char *value,
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(
488 unit, filename, line, section, section_line, name, value,
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}