1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
7 #include "conf-parser-forward.h"
11 /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */
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 */
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 */
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 */
37 /* Prototype for a low-level gperf lookup function */
38 typedef const ConfigPerfItem
* (*ConfigPerfItemLookup
)(const char *section_and_lvalue
, GPERF_LEN_TYPE length
);
40 /* Prototype for a generic high-level lookup function */
41 typedef int (*ConfigItemLookup
)(
45 ConfigParserCallback
*ret_func
,
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
);
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
);
62 const char *sections
, /* nulstr */
63 ConfigItemLookup lookup
,
65 ConfigParseFlags flags
,
67 struct stat
*ret_stat
); /* possibly NULL */
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
,
74 const char *sections
, /* nulstr */
75 ConfigItemLookup lookup
,
77 ConfigParseFlags flags
,
79 Hashmap
**ret_stats_by_path
, /* possibly NULL */
80 char ***ret_drop_in_files
); /* possibly NULL */
82 int config_parse_standard_file_with_dropins_full(
84 const char *main_file
, /* A path like "systemd/frobnicator.conf" */
86 ConfigItemLookup lookup
,
88 ConfigParseFlags flags
,
90 Hashmap
**ret_stats_by_path
, /* possibly NULL */
91 char ***ret_dropin_files
); /* possibly NULL */
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
,
98 ConfigParseFlags flags
,
100 return config_parse_standard_file_with_dropins_full(
108 /* ret_stats_by_path= */ NULL
,
109 /* ret_dropin_files= */ NULL
);
112 int config_get_stats_by_path(
116 const char* const* dirs
,
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
);
123 typedef struct ConfigSectionParser
{
124 ConfigParserCallback parser
;
127 } ConfigSectionParser
;
129 int config_section_parse(
130 const ConfigSectionParser
*parsers
,
133 const char *filename
,
136 unsigned section_line
,
142 static inline ConfigSection
* config_section_free(ConfigSection
*cs
) {
145 DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection
*, config_section_free
);
147 int config_section_new(const char *filename
, unsigned line
, ConfigSection
**ret
);
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
;
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
);
156 static inline bool section_is_invalid(ConfigSection
*section
) {
157 /* If this returns false, then it does _not_ mean the section is valid. */
162 return section
->invalid
;
165 #define log_section_full_errno_zerook(section, level, error, ...) \
167 const ConfigSection *_s = (section); \
168 log_syntax(/* unit = */ NULL, \
170 _s ? _s->filename : NULL, \
176 #define log_section_full_errno(section, level, error, ...) \
178 int _error = (error); \
179 ASSERT_NON_ZERO(_error); \
180 log_section_full_errno_zerook(section, level, _error, __VA_ARGS__); \
183 #define log_section_full(section, level, fmt, ...) \
185 if (BUILD_MODE_DEVELOPER) \
186 assert(!strstr(fmt, "%m")); \
187 (void) log_section_full_errno_zerook(section, level, 0, fmt, ##__VA_ARGS__); \
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__)
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__)
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
);
258 typedef enum Disabled
{
259 DISABLED_CONFIGURATION
,
261 DISABLED_EXPERIMENTAL
,
264 typedef enum ConfigParseStringFlags
{
265 CONFIG_PARSE_STRING_SAFE
= 1 << 0,
266 CONFIG_PARSE_STRING_ASCII
= 1 << 1,
268 CONFIG_PARSE_STRING_SAFE_AND_ASCII
= CONFIG_PARSE_STRING_SAFE
| CONFIG_PARSE_STRING_ASCII
,
269 } ConfigParseStringFlags
;
271 #define DEFINE_CONFIG_PARSE(function, parser) \
272 CONFIG_PARSER_PROTOTYPE(function) { \
280 r = parser(rvalue); \
282 return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); \
288 #define DEFINE_CONFIG_PARSE_PTR(function, parser, type) \
289 CONFIG_PARSER_PROTOTYPE(function) { \
290 type *i = ASSERT_PTR(data); \
297 r = parser(rvalue, i); \
299 return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); \
304 #define DEFINE_CONFIG_PARSE_ENUM_FULL(function, from_string, type) \
305 CONFIG_PARSER_PROTOTYPE(function) { \
313 x = from_string(rvalue); \
315 return log_syntax_parse_error(unit, filename, line, x, lvalue, rvalue); \
321 #define DEFINE_CONFIG_PARSE_ENUM(function, name, type) \
322 DEFINE_CONFIG_PARSE_ENUM_FULL(function, name##_from_string, type)
324 #define DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(function, name, type, default_value) \
325 CONFIG_PARSER_PROTOTYPE(function) { \
333 if (isempty(rvalue)) { \
334 *i = default_value; \
338 x = name##_from_string(rvalue); \
340 return log_syntax_parse_error(unit, filename, line, x, lvalue, rvalue); \
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; \
355 for (const char *p = rvalue;;) { \
356 _cleanup_free_ char *en = NULL; \
359 r = extract_first_word(&p, &en, NULL, 0); \
361 return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); \
365 x = name##_from_string(en); \
367 log_syntax(unit, LOG_WARNING, filename, line, x, \
368 "Failed to parse %s in %s=, ignoring.", \
373 FOREACH_ARRAY(i, xs, n) \
375 log_syntax(unit, LOG_NOTICE, filename, line, 0, \
376 "Duplicate entry %s in %s=, ignoring.", \
385 /* Allocate one more for the trailing 'invalid'. */ \
386 if (!GREEDY_REALLOC(xs, n + 2)) \
393 /* An empty string, or invalid values only. */ \
394 *enums = mfree(*enums); \
398 /* Terminate with 'invalid' */ \
400 free_and_replace(*enums, xs); \
404 int config_parse_unsigned_bounded(
406 const char *filename
,
409 unsigned section_line
,
417 static inline int config_parse_uint32_bounded(
419 const char *filename
,
422 unsigned section_line
,
433 r
= config_parse_unsigned_bounded(
434 unit
, filename
, line
, section
, section_line
, lvalue
, rvalue
,
439 assert(t
<= UINT32_MAX
);
444 static inline int config_parse_uint16_bounded(
446 const char *filename
,
449 unsigned section_line
,
460 r
= config_parse_unsigned_bounded(
461 unit
, filename
, line
, section
, section_line
, lvalue
, rvalue
,
466 assert(t
<= UINT16_MAX
);
471 static inline int config_parse_uint8_bounded(
473 const char *filename
,
476 unsigned section_line
,
487 r
= config_parse_unsigned_bounded(
488 unit
, filename
, line
, section
, section_line
, lvalue
, rvalue
,
493 assert(t
<= UINT8_MAX
);