1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
11 #include "alloc-util.h"
15 #include "time-util.h"
17 /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */
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 */
24 /* Argument list for parsers of specific configuration settings. */
25 #define CONFIG_PARSER_ARGUMENTS \
27 const char *filename, \
29 const char *section, \
30 unsigned section_line, \
37 /* Prototype for a parser for a specific configuration setting */
38 typedef int (*ConfigParserCallback
)(CONFIG_PARSER_ARGUMENTS
);
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)
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 */
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 */
64 /* Prototype for a low-level gperf lookup function */
65 typedef const ConfigPerfItem
* (*ConfigPerfItemLookup
)(const char *section_and_lvalue
, GPERF_LEN_TYPE length
);
67 /* Prototype for a generic high-level lookup function */
68 typedef int (*ConfigItemLookup
)(
72 ConfigParserCallback
*ret_func
,
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
);
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
);
89 const char *sections
, /* nulstr */
90 ConfigItemLookup lookup
,
92 ConfigParseFlags flags
,
94 struct stat
*ret_stat
); /* possibly NULL */
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
,
101 const char *sections
, /* nulstr */
102 ConfigItemLookup lookup
,
104 ConfigParseFlags flags
,
106 Hashmap
**ret_stats_by_path
, /* possibly NULL */
107 char ***ret_drop_in_files
); /* possibly NULL */
109 int config_parse_standard_file_with_dropins_full(
111 const char *main_file
, /* A path like "systemd/frobnicator.conf" */
112 const char *sections
,
113 ConfigItemLookup lookup
,
115 ConfigParseFlags flags
,
117 Hashmap
**ret_stats_by_path
, /* possibly NULL */
118 char ***ret_dropin_files
); /* possibly NULL */
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
,
125 ConfigParseFlags flags
,
127 return config_parse_standard_file_with_dropins_full(
135 /* ret_stats_by_path= */ NULL
,
136 /* ret_dropin_files= */ NULL
);
139 int config_get_stats_by_path(
143 const char* const* dirs
,
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
);
150 typedef struct ConfigSection
{
156 static inline ConfigSection
* config_section_free(ConfigSection
*cs
) {
159 DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection
*, config_section_free
);
161 int config_section_new(const char *filename
, unsigned line
, ConfigSection
**ret
);
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
;
167 int _hashmap_by_section_find_unused_line(
168 HashmapBase
*entries_by_section
,
169 const char *filename
,
171 static inline int hashmap_by_section_find_unused_line(
172 Hashmap
*entries_by_section
,
173 const char *filename
,
175 return _hashmap_by_section_find_unused_line(HASHMAP_BASE(entries_by_section
), filename
, ret
);
177 static inline int ordered_hashmap_by_section_find_unused_line(
178 OrderedHashmap
*entries_by_section
,
179 const char *filename
,
181 return _hashmap_by_section_find_unused_line(HASHMAP_BASE(entries_by_section
), filename
, ret
);
184 static inline bool section_is_invalid(ConfigSection
*section
) {
185 /* If this returns false, then it does _not_ mean the section is valid. */
190 return section
->invalid
;
193 #define DEFINE_SECTION_CLEANUP_FUNCTIONS(type, free_func) \
194 static inline type* free_func##_or_set_invalid(type *p) { \
198 p->section->invalid = true; \
203 DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \
204 DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid);
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
);
255 typedef enum Disabled
{
256 DISABLED_CONFIGURATION
,
258 DISABLED_EXPERIMENTAL
,
261 typedef enum ConfigParseStringFlags
{
262 CONFIG_PARSE_STRING_SAFE
= 1 << 0,
263 CONFIG_PARSE_STRING_ASCII
= 1 << 1,
265 CONFIG_PARSE_STRING_SAFE_AND_ASCII
= CONFIG_PARSE_STRING_SAFE
| CONFIG_PARSE_STRING_ASCII
,
266 } ConfigParseStringFlags
;
268 #define DEFINE_CONFIG_PARSE(function, parser, msg) \
269 CONFIG_PARSER_PROTOTYPE(function) { \
277 r = parser(rvalue); \
279 log_syntax(unit, LOG_WARNING, filename, line, r, \
280 msg ", ignoring: %s", rvalue); \
288 #define DEFINE_CONFIG_PARSE_PTR(function, parser, type, msg) \
289 CONFIG_PARSER_PROTOTYPE(function) { \
290 type *i = ASSERT_PTR(data); \
297 r = parser(rvalue, i); \
299 log_syntax(unit, LOG_WARNING, filename, line, r, \
300 msg ", ignoring: %s", rvalue); \
305 #define DEFINE_CONFIG_PARSE_ENUM_FULL(function, from_string, type, msg) \
306 CONFIG_PARSER_PROTOTYPE(function) { \
314 x = from_string(rvalue); \
316 log_syntax(unit, LOG_WARNING, filename, line, x, \
317 msg ", ignoring: %s", rvalue); \
325 #define DEFINE_CONFIG_PARSE_ENUM(function, name, type, msg) \
326 DEFINE_CONFIG_PARSE_ENUM_FULL(function, name##_from_string, type, msg)
328 #define DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(function, name, type, default_value, msg) \
329 CONFIG_PARSER_PROTOTYPE(function) { \
337 if (isempty(rvalue)) { \
338 *i = default_value; \
342 x = name##_from_string(rvalue); \
344 log_syntax(unit, LOG_WARNING, filename, line, x, \
345 msg ", ignoring: %s", rvalue); \
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; \
364 xs = new0(type, 1); \
370 for (const char *p = rvalue;;) { \
371 _cleanup_free_ char *en = NULL; \
374 r = extract_first_word(&p, &en, NULL, 0); \
378 log_syntax(unit, LOG_WARNING, filename, line, r, \
379 msg ", ignoring: %s", en); \
385 x = name##_from_string(en); \
387 log_syntax(unit, LOG_WARNING, filename, line, x, \
388 msg ", ignoring: %s", en); \
392 for (type *ys = xs; x != invalid && *ys != invalid; ys++) \
394 log_syntax(unit, LOG_NOTICE, filename, line, 0, \
395 "Duplicate entry, ignoring: %s", \
404 new_xs = realloc(xs, (++i + 1) * sizeof(type)); \
410 *(xs + i) = invalid; \
413 return free_and_replace(*enums, xs); \
416 int config_parse_unsigned_bounded(
418 const char *filename
,
421 unsigned section_line
,
429 static inline int config_parse_uint32_bounded(
431 const char *filename
,
434 unsigned section_line
,
445 r
= config_parse_unsigned_bounded(
446 unit
, filename
, line
, section
, section_line
, name
, value
,
451 assert(t
<= UINT32_MAX
);
456 static inline int config_parse_uint16_bounded(
458 const char *filename
,
461 unsigned section_line
,
472 r
= config_parse_unsigned_bounded(
473 unit
, filename
, line
, section
, section_line
, name
, value
,
478 assert(t
<= UINT16_MAX
);
483 static inline int config_parse_uint8_bounded(
485 const char *filename
,
488 unsigned section_line
,
499 r
= config_parse_unsigned_bounded(
500 unit
, filename
, line
, section
, section_line
, name
, value
,
505 assert(t
<= UINT8_MAX
);