]>
Commit | Line | Data |
---|---|---|
a032b68d | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
663996b3 MS |
2 | #pragma once |
3 | ||
4c89c718 | 4 | #include <errno.h> |
663996b3 | 5 | #include <stdbool.h> |
4c89c718 MP |
6 | #include <stddef.h> |
7 | #include <stdio.h> | |
8 | #include <syslog.h> | |
ea0999c9 | 9 | #include <sys/stat.h> |
663996b3 | 10 | |
4c89c718 | 11 | #include "alloc-util.h" |
ea0999c9 | 12 | #include "hashmap.h" |
4c89c718 | 13 | #include "log.h" |
663996b3 | 14 | #include "macro.h" |
a10f5d05 | 15 | #include "time-util.h" |
663996b3 | 16 | |
52ad194e MB |
17 | /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */ |
18 | ||
19 | typedef enum ConfigParseFlags { | |
46cdbd49 | 20 | CONFIG_PARSE_RELAXED = 1 << 0, /* Do not warn about unknown non-extension fields */ |
a10f5d05 | 21 | CONFIG_PARSE_WARN = 1 << 1, /* Emit non-debug messages */ |
52ad194e | 22 | } ConfigParseFlags; |
663996b3 | 23 | |
b012e921 MB |
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 | ||
663996b3 | 37 | /* Prototype for a parser for a specific configuration setting */ |
b012e921 MB |
38 | typedef int (*ConfigParserCallback)(CONFIG_PARSER_ARGUMENTS); |
39 | ||
9e294e28 | 40 | /* A macro declaring a function prototype, following the typedef above, simply because it's so cumbersomely long |
b012e921 MB |
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) | |
663996b3 MS |
44 | |
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 */ | |
53 | } ConfigTableItem; | |
54 | ||
55 | /* Wraps information for parsing a specific configuration variable, to | |
60f067b4 | 56 | * be stored in a gperf perfect hashtable */ |
663996b3 MS |
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 */ | |
62 | } ConfigPerfItem; | |
63 | ||
64 | /* Prototype for a low-level gperf lookup function */ | |
65 | typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length); | |
66 | ||
67 | /* Prototype for a generic high-level lookup function */ | |
68 | typedef int (*ConfigItemLookup)( | |
5eef597e | 69 | const void *table, |
663996b3 MS |
70 | const char *section, |
71 | const char *lvalue, | |
f5caa8fa MB |
72 | ConfigParserCallback *ret_func, |
73 | int *ret_ltype, | |
74 | void **ret_data, | |
663996b3 MS |
75 | void *userdata); |
76 | ||
77 | /* Linear table search implementation of ConfigItemLookup, based on | |
78 | * ConfigTableItem arrays */ | |
f5caa8fa | 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); |
663996b3 MS |
80 | |
81 | /* gperf implementation of ConfigItemLookup, based on gperf | |
82 | * ConfigPerfItem tables */ | |
f5caa8fa | 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); |
663996b3 | 84 | |
8a584da2 MP |
85 | int config_parse( |
86 | const char *unit, | |
87 | const char *filename, | |
88 | FILE *f, | |
a10f5d05 | 89 | const char *sections, /* nulstr */ |
8a584da2 MP |
90 | ConfigItemLookup lookup, |
91 | const void *table, | |
52ad194e | 92 | ConfigParseFlags flags, |
a10f5d05 | 93 | void *userdata, |
ea0999c9 | 94 | struct stat *ret_stat); /* possibly NULL */ |
8a584da2 MP |
95 | |
96 | int config_parse_many_nulstr( | |
97 | const char *conf_file, /* possibly NULL */ | |
98 | const char *conf_file_dirs, /* nulstr */ | |
99 | const char *sections, /* nulstr */ | |
100 | ConfigItemLookup lookup, | |
101 | const void *table, | |
52ad194e | 102 | ConfigParseFlags flags, |
a10f5d05 | 103 | void *userdata, |
ea0999c9 | 104 | Hashmap **ret_stats_by_path); /* possibly NULL */ |
8a584da2 MP |
105 | |
106 | int config_parse_many( | |
3a6ce677 | 107 | const char* const* conf_files, /* possibly empty */ |
8a584da2 MP |
108 | const char* const* conf_file_dirs, |
109 | const char *dropin_dirname, | |
110 | const char *sections, /* nulstr */ | |
111 | ConfigItemLookup lookup, | |
112 | const void *table, | |
52ad194e | 113 | ConfigParseFlags flags, |
478ed938 | 114 | void *userdata, |
086111aa LB |
115 | Hashmap **ret_stats_by_path, /* possibly NULL */ |
116 | char ***ret_drop_in_files); /* possibly NULL */ | |
f47781d8 | 117 | |
8f232108 MB |
118 | int config_get_stats_by_path( |
119 | const char *suffix, | |
120 | const char *root, | |
121 | unsigned flags, | |
122 | const char* const* dirs, | |
086111aa | 123 | bool check_dropins, |
8f232108 MB |
124 | Hashmap **ret); |
125 | ||
086111aa | 126 | int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st); |
8f232108 MB |
127 | bool stats_by_path_equal(Hashmap *a, Hashmap *b); |
128 | ||
f5caa8fa MB |
129 | typedef struct ConfigSection { |
130 | unsigned line; | |
131 | bool invalid; | |
132 | char filename[]; | |
133 | } ConfigSection; | |
134 | ||
135 | static inline ConfigSection* config_section_free(ConfigSection *cs) { | |
136 | return mfree(cs); | |
137 | } | |
138 | DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection*, config_section_free); | |
139 | ||
140 | int config_section_new(const char *filename, unsigned line, ConfigSection **s); | |
141 | extern const struct hash_ops config_section_hash_ops; | |
142 | unsigned hashmap_find_free_section_line(Hashmap *hashmap); | |
143 | ||
144 | static inline bool section_is_invalid(ConfigSection *section) { | |
145 | /* If this returns false, then it does _not_ mean the section is valid. */ | |
146 | ||
147 | if (!section) | |
148 | return false; | |
149 | ||
150 | return section->invalid; | |
151 | } | |
152 | ||
153 | #define DEFINE_SECTION_CLEANUP_FUNCTIONS(type, free_func) \ | |
154 | static inline type* free_func##_or_set_invalid(type *p) { \ | |
155 | assert(p); \ | |
156 | \ | |
157 | if (p->section) \ | |
158 | p->section->invalid = true; \ | |
159 | else \ | |
160 | free_func(p); \ | |
161 | return NULL; \ | |
162 | } \ | |
163 | DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \ | |
164 | DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid); | |
165 | ||
b012e921 MB |
166 | CONFIG_PARSER_PROTOTYPE(config_parse_int); |
167 | CONFIG_PARSER_PROTOTYPE(config_parse_unsigned); | |
168 | CONFIG_PARSER_PROTOTYPE(config_parse_long); | |
169 | CONFIG_PARSER_PROTOTYPE(config_parse_uint8); | |
170 | CONFIG_PARSER_PROTOTYPE(config_parse_uint16); | |
171 | CONFIG_PARSER_PROTOTYPE(config_parse_uint32); | |
46cdbd49 | 172 | CONFIG_PARSER_PROTOTYPE(config_parse_int32); |
b012e921 MB |
173 | CONFIG_PARSER_PROTOTYPE(config_parse_uint64); |
174 | CONFIG_PARSER_PROTOTYPE(config_parse_double); | |
175 | CONFIG_PARSER_PROTOTYPE(config_parse_iec_size); | |
46cdbd49 | 176 | CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64); |
b012e921 | 177 | CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64); |
ea0999c9 | 178 | CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64_infinity); |
b012e921 | 179 | CONFIG_PARSER_PROTOTYPE(config_parse_bool); |
a10f5d05 | 180 | CONFIG_PARSER_PROTOTYPE(config_parse_id128); |
b012e921 MB |
181 | CONFIG_PARSER_PROTOTYPE(config_parse_tristate); |
182 | CONFIG_PARSER_PROTOTYPE(config_parse_string); | |
f5caa8fa MB |
183 | CONFIG_PARSER_PROTOTYPE(config_parse_dns_name); |
184 | CONFIG_PARSER_PROTOTYPE(config_parse_hostname); | |
b012e921 MB |
185 | CONFIG_PARSER_PROTOTYPE(config_parse_path); |
186 | CONFIG_PARSER_PROTOTYPE(config_parse_strv); | |
187 | CONFIG_PARSER_PROTOTYPE(config_parse_sec); | |
bb4f798a | 188 | CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_infinity); |
f2dec872 | 189 | CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_unset); |
b012e921 MB |
190 | CONFIG_PARSER_PROTOTYPE(config_parse_nsec); |
191 | CONFIG_PARSER_PROTOTYPE(config_parse_mode); | |
192 | CONFIG_PARSER_PROTOTYPE(config_parse_warn_compat); | |
193 | CONFIG_PARSER_PROTOTYPE(config_parse_log_facility); | |
194 | CONFIG_PARSER_PROTOTYPE(config_parse_log_level); | |
195 | CONFIG_PARSER_PROTOTYPE(config_parse_signal); | |
196 | CONFIG_PARSER_PROTOTYPE(config_parse_personality); | |
197 | CONFIG_PARSER_PROTOTYPE(config_parse_permille); | |
198 | CONFIG_PARSER_PROTOTYPE(config_parse_ifname); | |
46cdbd49 | 199 | CONFIG_PARSER_PROTOTYPE(config_parse_ifnames); |
b012e921 | 200 | CONFIG_PARSER_PROTOTYPE(config_parse_ip_port); |
b012e921 MB |
201 | CONFIG_PARSER_PROTOTYPE(config_parse_mtu); |
202 | CONFIG_PARSER_PROTOTYPE(config_parse_rlimit); | |
a10f5d05 | 203 | CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol); |
ea0999c9 MB |
204 | CONFIG_PARSER_PROTOTYPE(config_parse_hw_addr); |
205 | CONFIG_PARSER_PROTOTYPE(config_parse_hw_addrs); | |
206 | CONFIG_PARSER_PROTOTYPE(config_parse_ether_addr); | |
207 | CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs); | |
8b3d4ff0 | 208 | CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null); |
a032b68d | 209 | CONFIG_PARSER_PROTOTYPE(config_parse_percent); |
3a6ce677 | 210 | CONFIG_PARSER_PROTOTYPE(config_parse_permyriad); |
f5caa8fa | 211 | CONFIG_PARSER_PROTOTYPE(config_parse_pid); |
086111aa | 212 | CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0); |
b012e921 MB |
213 | |
214 | typedef enum Disabled { | |
215 | DISABLED_CONFIGURATION, | |
216 | DISABLED_LEGACY, | |
217 | DISABLED_EXPERIMENTAL, | |
218 | } Disabled; | |
219 | ||
f5caa8fa MB |
220 | typedef enum ConfigParseStringFlags { |
221 | CONFIG_PARSE_STRING_SAFE = 1 << 0, | |
222 | CONFIG_PARSE_STRING_ASCII = 1 << 1, | |
223 | ||
224 | CONFIG_PARSE_STRING_SAFE_AND_ASCII = CONFIG_PARSE_STRING_SAFE | CONFIG_PARSE_STRING_ASCII, | |
225 | } ConfigParseStringFlags; | |
226 | ||
b012e921 MB |
227 | #define DEFINE_CONFIG_PARSE(function, parser, msg) \ |
228 | CONFIG_PARSER_PROTOTYPE(function) { \ | |
229 | int *i = data, r; \ | |
230 | \ | |
231 | assert(filename); \ | |
232 | assert(lvalue); \ | |
233 | assert(rvalue); \ | |
234 | assert(data); \ | |
235 | \ | |
236 | r = parser(rvalue); \ | |
237 | if (r < 0) { \ | |
a10f5d05 | 238 | log_syntax(unit, LOG_WARNING, filename, line, r, \ |
b012e921 MB |
239 | msg ", ignoring: %s", rvalue); \ |
240 | return 0; \ | |
241 | } \ | |
242 | \ | |
243 | *i = r; \ | |
244 | return 0; \ | |
245 | } | |
246 | ||
247 | #define DEFINE_CONFIG_PARSE_PTR(function, parser, type, msg) \ | |
248 | CONFIG_PARSER_PROTOTYPE(function) { \ | |
086111aa | 249 | type *i = ASSERT_PTR(data); \ |
b012e921 MB |
250 | int r; \ |
251 | \ | |
252 | assert(filename); \ | |
253 | assert(lvalue); \ | |
254 | assert(rvalue); \ | |
b012e921 MB |
255 | \ |
256 | r = parser(rvalue, i); \ | |
257 | if (r < 0) \ | |
a10f5d05 | 258 | log_syntax(unit, LOG_WARNING, filename, line, r, \ |
b012e921 MB |
259 | msg ", ignoring: %s", rvalue); \ |
260 | \ | |
261 | return 0; \ | |
262 | } | |
263 | ||
a032b68d | 264 | #define DEFINE_CONFIG_PARSE_ENUM_FULL(function, from_string, type, msg) \ |
b012e921 MB |
265 | CONFIG_PARSER_PROTOTYPE(function) { \ |
266 | type *i = data, x; \ | |
267 | \ | |
268 | assert(filename); \ | |
269 | assert(lvalue); \ | |
270 | assert(rvalue); \ | |
271 | assert(data); \ | |
272 | \ | |
a032b68d | 273 | x = from_string(rvalue); \ |
b012e921 | 274 | if (x < 0) { \ |
3a6ce677 | 275 | log_syntax(unit, LOG_WARNING, filename, line, x, \ |
b012e921 MB |
276 | msg ", ignoring: %s", rvalue); \ |
277 | return 0; \ | |
278 | } \ | |
279 | \ | |
280 | *i = x; \ | |
281 | return 0; \ | |
282 | } | |
283 | ||
a032b68d MB |
284 | #define DEFINE_CONFIG_PARSE_ENUM(function, name, type, msg) \ |
285 | DEFINE_CONFIG_PARSE_ENUM_FULL(function, name##_from_string, type, msg) | |
286 | ||
b012e921 MB |
287 | #define DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(function, name, type, default_value, msg) \ |
288 | CONFIG_PARSER_PROTOTYPE(function) { \ | |
663996b3 MS |
289 | type *i = data, x; \ |
290 | \ | |
291 | assert(filename); \ | |
292 | assert(lvalue); \ | |
293 | assert(rvalue); \ | |
294 | assert(data); \ | |
295 | \ | |
b012e921 MB |
296 | if (isempty(rvalue)) { \ |
297 | *i = default_value; \ | |
298 | return 0; \ | |
299 | } \ | |
300 | \ | |
301 | x = name##_from_string(rvalue); \ | |
302 | if (x < 0) { \ | |
3a6ce677 | 303 | log_syntax(unit, LOG_WARNING, filename, line, x, \ |
663996b3 MS |
304 | msg ", ignoring: %s", rvalue); \ |
305 | return 0; \ | |
306 | } \ | |
307 | \ | |
308 | *i = x; \ | |
309 | return 0; \ | |
310 | } | |
60f067b4 | 311 | |
b012e921 MB |
312 | #define DEFINE_CONFIG_PARSE_ENUMV(function, name, type, invalid, msg) \ |
313 | CONFIG_PARSER_PROTOTYPE(function) { \ | |
086111aa | 314 | type **enums = ASSERT_PTR(data); \ |
5eef597e | 315 | _cleanup_free_ type *xs = NULL; \ |
a032b68d MB |
316 | size_t i = 0; \ |
317 | int r; \ | |
60f067b4 JS |
318 | \ |
319 | assert(filename); \ | |
320 | assert(lvalue); \ | |
321 | assert(rvalue); \ | |
60f067b4 JS |
322 | \ |
323 | xs = new0(type, 1); \ | |
aa27b158 | 324 | if (!xs) \ |
5eef597e MP |
325 | return -ENOMEM; \ |
326 | \ | |
60f067b4 JS |
327 | *xs = invalid; \ |
328 | \ | |
a032b68d | 329 | for (const char *p = rvalue;;) { \ |
60f067b4 | 330 | _cleanup_free_ char *en = NULL; \ |
a032b68d | 331 | type x, *new_xs; \ |
60f067b4 | 332 | \ |
a032b68d MB |
333 | r = extract_first_word(&p, &en, NULL, 0); \ |
334 | if (r == -ENOMEM) \ | |
a10f5d05 | 335 | return log_oom(); \ |
3a6ce677 BR |
336 | if (r < 0) { \ |
337 | log_syntax(unit, LOG_WARNING, filename, line, r, \ | |
338 | msg ", ignoring: %s", en); \ | |
339 | return 0; \ | |
340 | } \ | |
a032b68d MB |
341 | if (r == 0) \ |
342 | break; \ | |
60f067b4 | 343 | \ |
3a6ce677 BR |
344 | x = name##_from_string(en); \ |
345 | if (x < 0) { \ | |
346 | log_syntax(unit, LOG_WARNING, filename, line, x, \ | |
b012e921 | 347 | msg ", ignoring: %s", en); \ |
60f067b4 JS |
348 | continue; \ |
349 | } \ | |
350 | \ | |
a032b68d MB |
351 | for (type *ys = xs; x != invalid && *ys != invalid; ys++) \ |
352 | if (*ys == x) { \ | |
353 | log_syntax(unit, LOG_NOTICE, filename, line, 0, \ | |
354 | "Duplicate entry, ignoring: %s", \ | |
b012e921 | 355 | en); \ |
60f067b4 JS |
356 | x = invalid; \ |
357 | } \ | |
60f067b4 JS |
358 | \ |
359 | if (x == invalid) \ | |
360 | continue; \ | |
361 | \ | |
362 | *(xs + i) = x; \ | |
5eef597e MP |
363 | new_xs = realloc(xs, (++i + 1) * sizeof(type)); \ |
364 | if (new_xs) \ | |
365 | xs = new_xs; \ | |
366 | else \ | |
a10f5d05 | 367 | return log_oom(); \ |
5eef597e | 368 | \ |
60f067b4 JS |
369 | *(xs + i) = invalid; \ |
370 | } \ | |
371 | \ | |
a032b68d | 372 | return free_and_replace(*enums, xs); \ |
60f067b4 | 373 | } |