]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.h
2f1b5ffecc6bf2cce4b3a3d2fbefb9594901cd09
[thirdparty/systemd.git] / src / shared / conf-parser.h
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 #pragma once
3
4 #include <syslog.h>
5
6 #include "alloc-util.h"
7 #include "conf-parser-forward.h"
8 #include "forward.h"
9 #include "log.h"
10
11 /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */
12
13 typedef enum ConfigParseFlags {
14 CONFIG_PARSE_RELAXED = 1 << 0, /* Do not warn about unknown non-extension fields */
15 CONFIG_PARSE_WARN = 1 << 1, /* Emit non-debug messages */
16 } ConfigParseFlags;
17
18 /* Wraps information for parsing a specific configuration variable, to
19 * be stored in a simple array */
20 typedef 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
29 * be stored in a gperf perfect hashtable */
30 typedef 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 */
38 typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, GPERF_LEN_TYPE length);
39
40 /* Prototype for a generic high-level lookup function */
41 typedef int (*ConfigItemLookup)(
42 const void *table,
43 const char *section,
44 const char *lvalue,
45 ConfigParserCallback *ret_func,
46 int *ret_ltype,
47 void **ret_data,
48 void *userdata);
49
50 /* Linear table search implementation of ConfigItemLookup, based on
51 * ConfigTableItem arrays */
52 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);
53
54 /* gperf implementation of ConfigItemLookup, based on gperf
55 * ConfigPerfItem tables */
56 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);
57
58 int config_parse(
59 const char *unit,
60 const char *filename,
61 FILE *f,
62 const char *sections, /* nulstr */
63 ConfigItemLookup lookup,
64 const void *table,
65 ConfigParseFlags flags,
66 void *userdata,
67 struct stat *ret_stat); /* possibly NULL */
68
69 int config_parse_many(
70 const char* const* conf_files, /* possibly empty */
71 const char* const* conf_file_dirs,
72 const char *dropin_dirname,
73 const char *root,
74 const char *sections, /* nulstr */
75 ConfigItemLookup lookup,
76 const void *table,
77 ConfigParseFlags flags,
78 void *userdata,
79 Hashmap **ret_stats_by_path, /* possibly NULL */
80 char ***ret_drop_in_files); /* possibly NULL */
81
82 int 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
93 static 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
112 int config_get_stats_by_path(
113 const char *suffix,
114 const char *root,
115 unsigned flags,
116 const char* const* dirs,
117 bool check_dropins,
118 Hashmap **ret);
119
120 int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st);
121 bool stats_by_path_equal(Hashmap *a, Hashmap *b);
122
123 typedef struct ConfigSectionParser {
124 ConfigParserCallback parser;
125 int ltype;
126 size_t offset;
127 } ConfigSectionParser;
128
129 int 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
142 static inline ConfigSection* config_section_free(ConfigSection *cs) {
143 return mfree(cs);
144 }
145 DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection*, config_section_free);
146
147 int config_section_new(const char *filename, unsigned line, ConfigSection **ret);
148
149 void config_section_hash_func(const ConfigSection *c, struct siphash *state);
150 int config_section_compare_func(const ConfigSection *x, const ConfigSection *y);
151 extern const struct hash_ops config_section_hash_ops;
152
153 int hashmap_by_section_find_unused_line(Hashmap *entries_by_section, const char *filename, unsigned *ret);
154 int ordered_hashmap_by_section_find_unused_line(OrderedHashmap *entries_by_section, const char *filename, unsigned *ret);
155
156 static 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
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
202 CONFIG_PARSER_PROTOTYPE(config_parse_int);
203 CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);
204 CONFIG_PARSER_PROTOTYPE(config_parse_long);
205 CONFIG_PARSER_PROTOTYPE(config_parse_uint8);
206 CONFIG_PARSER_PROTOTYPE(config_parse_uint16);
207 CONFIG_PARSER_PROTOTYPE(config_parse_uint32);
208 CONFIG_PARSER_PROTOTYPE(config_parse_int32);
209 CONFIG_PARSER_PROTOTYPE(config_parse_uint64);
210 CONFIG_PARSER_PROTOTYPE(config_parse_double);
211 CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
212 CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64);
213 CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64);
214 CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64_infinity);
215 CONFIG_PARSER_PROTOTYPE(config_parse_bool);
216 CONFIG_PARSER_PROTOTYPE(config_parse_uint32_flag);
217 CONFIG_PARSER_PROTOTYPE(config_parse_uint32_invert_flag);
218 CONFIG_PARSER_PROTOTYPE(config_parse_id128);
219 CONFIG_PARSER_PROTOTYPE(config_parse_tristate);
220 CONFIG_PARSER_PROTOTYPE(config_parse_string);
221 CONFIG_PARSER_PROTOTYPE(config_parse_dns_name);
222 CONFIG_PARSER_PROTOTYPE(config_parse_hostname);
223 CONFIG_PARSER_PROTOTYPE(config_parse_path);
224 CONFIG_PARSER_PROTOTYPE(config_parse_strv);
225 CONFIG_PARSER_PROTOTYPE(config_parse_sec);
226 CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_infinity);
227 CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_unset);
228 CONFIG_PARSER_PROTOTYPE(config_parse_nsec);
229 CONFIG_PARSER_PROTOTYPE(config_parse_mode);
230 CONFIG_PARSER_PROTOTYPE(config_parse_warn_compat);
231 CONFIG_PARSER_PROTOTYPE(config_parse_log_facility);
232 CONFIG_PARSER_PROTOTYPE(config_parse_log_level);
233 CONFIG_PARSER_PROTOTYPE(config_parse_signal);
234 CONFIG_PARSER_PROTOTYPE(config_parse_personality);
235 CONFIG_PARSER_PROTOTYPE(config_parse_permille);
236 CONFIG_PARSER_PROTOTYPE(config_parse_ifname);
237 CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
238 CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
239 CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
240 CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
241 CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol);
242 CONFIG_PARSER_PROTOTYPE(config_parse_hw_addr);
243 CONFIG_PARSER_PROTOTYPE(config_parse_hw_addrs);
244 CONFIG_PARSER_PROTOTYPE(config_parse_ether_addr);
245 CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs);
246 CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
247 CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_data);
248 CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_prefix);
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 CONFIG_PARSER_PROTOTYPE(config_parse_calendar);
255 CONFIG_PARSER_PROTOTYPE(config_parse_ip_protocol);
256 CONFIG_PARSER_PROTOTYPE(config_parse_loadavg);
257
258 typedef enum Disabled {
259 DISABLED_CONFIGURATION,
260 DISABLED_LEGACY,
261 DISABLED_EXPERIMENTAL,
262 } Disabled;
263
264 typedef enum ConfigParseStringFlags {
265 CONFIG_PARSE_STRING_SAFE = 1 << 0,
266 CONFIG_PARSE_STRING_ASCII = 1 << 1,
267
268 CONFIG_PARSE_STRING_SAFE_AND_ASCII = CONFIG_PARSE_STRING_SAFE | CONFIG_PARSE_STRING_ASCII,
269 } ConfigParseStringFlags;
270
271 #define DEFINE_CONFIG_PARSE(function, parser) \
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); \
281 if (r < 0) \
282 return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); \
283 \
284 *i = r; \
285 return 1; \
286 }
287
288 #define DEFINE_CONFIG_PARSE_PTR(function, parser, type) \
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 return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); \
300 \
301 return 1; \
302 }
303
304 #define DEFINE_CONFIG_PARSE_ENUM_FULL(function, from_string, type) \
305 CONFIG_PARSER_PROTOTYPE(function) { \
306 type *i = data, x; \
307 \
308 assert(filename); \
309 assert(lvalue); \
310 assert(rvalue); \
311 assert(data); \
312 \
313 x = from_string(rvalue); \
314 if (x < 0) \
315 return log_syntax_parse_error(unit, filename, line, x, lvalue, rvalue); \
316 \
317 *i = x; \
318 return 1; \
319 }
320
321 #define DEFINE_CONFIG_PARSE_ENUM(function, name, type) \
322 DEFINE_CONFIG_PARSE_ENUM_FULL(function, name##_from_string, type)
323
324 #define DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(function, name, type, default_value) \
325 CONFIG_PARSER_PROTOTYPE(function) { \
326 type *i = data, x; \
327 \
328 assert(filename); \
329 assert(lvalue); \
330 assert(rvalue); \
331 assert(data); \
332 \
333 if (isempty(rvalue)) { \
334 *i = default_value; \
335 return 1; \
336 } \
337 \
338 x = name##_from_string(rvalue); \
339 if (x < 0) \
340 return log_syntax_parse_error(unit, filename, line, x, lvalue, rvalue); \
341 \
342 *i = x; \
343 return 1; \
344 }
345
346 #define DEFINE_CONFIG_PARSE_ENUMV(function, name, type, invalid) \
347 CONFIG_PARSER_PROTOTYPE(function) { \
348 type **enums = ASSERT_PTR(data); \
349 _cleanup_free_ type *xs = NULL; \
350 size_t n = 0; \
351 int r; \
352 \
353 assert(lvalue); \
354 \
355 for (const char *p = rvalue;;) { \
356 _cleanup_free_ char *en = NULL; \
357 type x; \
358 \
359 r = extract_first_word(&p, &en, NULL, 0); \
360 if (r < 0) \
361 return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); \
362 if (r == 0) \
363 break; \
364 \
365 x = name##_from_string(en); \
366 if (x < 0) { \
367 log_syntax(unit, LOG_WARNING, filename, line, x, \
368 "Failed to parse %s in %s=, ignoring.", \
369 en, lvalue); \
370 continue; \
371 } \
372 \
373 FOREACH_ARRAY(i, xs, n) \
374 if (*i == x) { \
375 log_syntax(unit, LOG_NOTICE, filename, line, 0, \
376 "Duplicate entry %s in %s=, ignoring.", \
377 en, lvalue); \
378 x = invalid; \
379 break; \
380 } \
381 \
382 if (x == invalid) \
383 continue; \
384 \
385 /* Allocate one more for the trailing 'invalid'. */ \
386 if (!GREEDY_REALLOC(xs, n + 2)) \
387 return log_oom(); \
388 \
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; \
396 } \
397 \
398 /* Terminate with 'invalid' */ \
399 xs[n] = invalid; \
400 free_and_replace(*enums, xs); \
401 return 1; \
402 }
403
404 int 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 *lvalue,
411 const char *rvalue,
412 unsigned min,
413 unsigned max,
414 bool ignoring,
415 unsigned *ret);
416
417 static 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 *lvalue,
424 const char *rvalue,
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, lvalue, rvalue,
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
444 static 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 *lvalue,
451 const char *rvalue,
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, lvalue, rvalue,
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
471 static 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 *lvalue,
478 const char *rvalue,
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, lvalue, rvalue,
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 }