]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.h
6d9d243492d792cb0488735312a5812200396445
[thirdparty/systemd.git] / src / shared / conf-parser.h
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 #pragma once
3
4 #include <errno.h>
5 #include <stdbool.h>
6 #include <stddef.h>
7 #include <stdio.h>
8 #include <syslog.h>
9 #include <sys/stat.h>
10
11 #include "alloc-util.h"
12 #include "hashmap.h"
13 #include "log.h"
14 #include "macro.h"
15 #include "time-util.h"
16
17 /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */
18
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 */
22 } ConfigParseFlags;
23
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
37 /* Prototype for a parser for a specific configuration setting */
38 typedef int (*ConfigParserCallback)(CONFIG_PARSER_ARGUMENTS);
39
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)
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
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 */
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)(
69 const void *table,
70 const char *section,
71 const char *lvalue,
72 ConfigParserCallback *ret_func,
73 int *ret_ltype,
74 void **ret_data,
75 void *userdata);
76
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);
80
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);
84
85 int config_parse(
86 const char *unit,
87 const char *filename,
88 FILE *f,
89 const char *sections, /* nulstr */
90 ConfigItemLookup lookup,
91 const void *table,
92 ConfigParseFlags flags,
93 void *userdata,
94 struct stat *ret_stat); /* possibly NULL */
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,
102 ConfigParseFlags flags,
103 void *userdata,
104 Hashmap **ret_stats_by_path); /* possibly NULL */
105
106 int config_parse_many(
107 const char* const* conf_files, /* possibly empty */
108 const char* const* conf_file_dirs,
109 const char *dropin_dirname,
110 const char *sections, /* nulstr */
111 ConfigItemLookup lookup,
112 const void *table,
113 ConfigParseFlags flags,
114 void *userdata,
115 Hashmap **ret_stats_by_path, /* possibly NULL */
116 char ***ret_drop_in_files); /* possibly NULL */
117
118 int config_get_stats_by_path(
119 const char *suffix,
120 const char *root,
121 unsigned flags,
122 const char* const* dirs,
123 bool check_dropins,
124 Hashmap **ret);
125
126 int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st);
127 bool stats_by_path_equal(Hashmap *a, Hashmap *b);
128
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
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);
172 CONFIG_PARSER_PROTOTYPE(config_parse_int32);
173 CONFIG_PARSER_PROTOTYPE(config_parse_uint64);
174 CONFIG_PARSER_PROTOTYPE(config_parse_double);
175 CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
176 CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64);
177 CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64);
178 CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64_infinity);
179 CONFIG_PARSER_PROTOTYPE(config_parse_bool);
180 CONFIG_PARSER_PROTOTYPE(config_parse_id128);
181 CONFIG_PARSER_PROTOTYPE(config_parse_tristate);
182 CONFIG_PARSER_PROTOTYPE(config_parse_string);
183 CONFIG_PARSER_PROTOTYPE(config_parse_dns_name);
184 CONFIG_PARSER_PROTOTYPE(config_parse_hostname);
185 CONFIG_PARSER_PROTOTYPE(config_parse_path);
186 CONFIG_PARSER_PROTOTYPE(config_parse_strv);
187 CONFIG_PARSER_PROTOTYPE(config_parse_sec);
188 CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_infinity);
189 CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_unset);
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);
199 CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
200 CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
201 CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
202 CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
203 CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol);
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);
208 CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
209 CONFIG_PARSER_PROTOTYPE(config_parse_percent);
210 CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
211 CONFIG_PARSER_PROTOTYPE(config_parse_pid);
212 CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
213
214 typedef enum Disabled {
215 DISABLED_CONFIGURATION,
216 DISABLED_LEGACY,
217 DISABLED_EXPERIMENTAL,
218 } Disabled;
219
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
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) { \
238 log_syntax(unit, LOG_WARNING, filename, line, r, \
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) { \
249 type *i = data; \
250 int r; \
251 \
252 assert(filename); \
253 assert(lvalue); \
254 assert(rvalue); \
255 assert(data); \
256 \
257 r = parser(rvalue, i); \
258 if (r < 0) \
259 log_syntax(unit, LOG_WARNING, filename, line, r, \
260 msg ", ignoring: %s", rvalue); \
261 \
262 return 0; \
263 }
264
265 #define DEFINE_CONFIG_PARSE_ENUM_FULL(function, from_string, type, msg) \
266 CONFIG_PARSER_PROTOTYPE(function) { \
267 type *i = data, x; \
268 \
269 assert(filename); \
270 assert(lvalue); \
271 assert(rvalue); \
272 assert(data); \
273 \
274 x = from_string(rvalue); \
275 if (x < 0) { \
276 log_syntax(unit, LOG_WARNING, filename, line, x, \
277 msg ", ignoring: %s", rvalue); \
278 return 0; \
279 } \
280 \
281 *i = x; \
282 return 0; \
283 }
284
285 #define DEFINE_CONFIG_PARSE_ENUM(function, name, type, msg) \
286 DEFINE_CONFIG_PARSE_ENUM_FULL(function, name##_from_string, type, msg)
287
288 #define DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(function, name, type, default_value, msg) \
289 CONFIG_PARSER_PROTOTYPE(function) { \
290 type *i = data, x; \
291 \
292 assert(filename); \
293 assert(lvalue); \
294 assert(rvalue); \
295 assert(data); \
296 \
297 if (isempty(rvalue)) { \
298 *i = default_value; \
299 return 0; \
300 } \
301 \
302 x = name##_from_string(rvalue); \
303 if (x < 0) { \
304 log_syntax(unit, LOG_WARNING, filename, line, x, \
305 msg ", ignoring: %s", rvalue); \
306 return 0; \
307 } \
308 \
309 *i = x; \
310 return 0; \
311 }
312
313 #define DEFINE_CONFIG_PARSE_ENUMV(function, name, type, invalid, msg) \
314 CONFIG_PARSER_PROTOTYPE(function) { \
315 type **enums = data; \
316 _cleanup_free_ type *xs = NULL; \
317 size_t i = 0; \
318 int r; \
319 \
320 assert(filename); \
321 assert(lvalue); \
322 assert(rvalue); \
323 assert(data); \
324 \
325 xs = new0(type, 1); \
326 if (!xs) \
327 return -ENOMEM; \
328 \
329 *xs = invalid; \
330 \
331 for (const char *p = rvalue;;) { \
332 _cleanup_free_ char *en = NULL; \
333 type x, *new_xs; \
334 \
335 r = extract_first_word(&p, &en, NULL, 0); \
336 if (r == -ENOMEM) \
337 return log_oom(); \
338 if (r < 0) { \
339 log_syntax(unit, LOG_WARNING, filename, line, r, \
340 msg ", ignoring: %s", en); \
341 return 0; \
342 } \
343 if (r == 0) \
344 break; \
345 \
346 x = name##_from_string(en); \
347 if (x < 0) { \
348 log_syntax(unit, LOG_WARNING, filename, line, x, \
349 msg ", ignoring: %s", en); \
350 continue; \
351 } \
352 \
353 for (type *ys = xs; x != invalid && *ys != invalid; ys++) \
354 if (*ys == x) { \
355 log_syntax(unit, LOG_NOTICE, filename, line, 0, \
356 "Duplicate entry, ignoring: %s", \
357 en); \
358 x = invalid; \
359 } \
360 \
361 if (x == invalid) \
362 continue; \
363 \
364 *(xs + i) = x; \
365 new_xs = realloc(xs, (++i + 1) * sizeof(type)); \
366 if (new_xs) \
367 xs = new_xs; \
368 else \
369 return log_oom(); \
370 \
371 *(xs + i) = invalid; \
372 } \
373 \
374 return free_and_replace(*enums, xs); \
375 }