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
117 int config_get_stats_by_path(
118 const char *suffix,
119 const char *root,
120 unsigned flags,
121 const char* const* dirs,
122 Hashmap **ret);
123
124 bool stats_by_path_equal(Hashmap *a, Hashmap *b);
125
126 typedef struct ConfigSection {
127 unsigned line;
128 bool invalid;
129 char filename[];
130 } ConfigSection;
131
config_section_free(ConfigSection * cs)132 static inline ConfigSection* config_section_free(ConfigSection *cs) {
133 return mfree(cs);
134 }
135 DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection*, config_section_free);
136
137 int config_section_new(const char *filename, unsigned line, ConfigSection **s);
138 extern const struct hash_ops config_section_hash_ops;
139 unsigned hashmap_find_free_section_line(Hashmap *hashmap);
140
section_is_invalid(ConfigSection * section)141 static inline bool section_is_invalid(ConfigSection *section) {
142 /* If this returns false, then it does _not_ mean the section is valid. */
143
144 if (!section)
145 return false;
146
147 return section->invalid;
148 }
149
150 #define DEFINE_SECTION_CLEANUP_FUNCTIONS(type, free_func) \
151 static inline type* free_func##_or_set_invalid(type *p) { \
152 assert(p); \
153 \
154 if (p->section) \
155 p->section->invalid = true; \
156 else \
157 free_func(p); \
158 return NULL; \
159 } \
160 DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \
161 DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid);
162
163 CONFIG_PARSER_PROTOTYPE(config_parse_int);
164 CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);
165 CONFIG_PARSER_PROTOTYPE(config_parse_long);
166 CONFIG_PARSER_PROTOTYPE(config_parse_uint8);
167 CONFIG_PARSER_PROTOTYPE(config_parse_uint16);
168 CONFIG_PARSER_PROTOTYPE(config_parse_uint32);
169 CONFIG_PARSER_PROTOTYPE(config_parse_int32);
170 CONFIG_PARSER_PROTOTYPE(config_parse_uint64);
171 CONFIG_PARSER_PROTOTYPE(config_parse_double);
172 CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
173 CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64);
174 CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64);
175 CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64_infinity);
176 CONFIG_PARSER_PROTOTYPE(config_parse_bool);
177 CONFIG_PARSER_PROTOTYPE(config_parse_id128);
178 CONFIG_PARSER_PROTOTYPE(config_parse_tristate);
179 CONFIG_PARSER_PROTOTYPE(config_parse_string);
180 CONFIG_PARSER_PROTOTYPE(config_parse_dns_name);
181 CONFIG_PARSER_PROTOTYPE(config_parse_hostname);
182 CONFIG_PARSER_PROTOTYPE(config_parse_path);
183 CONFIG_PARSER_PROTOTYPE(config_parse_strv);
184 CONFIG_PARSER_PROTOTYPE(config_parse_sec);
185 CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_infinity);
186 CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_unset);
187 CONFIG_PARSER_PROTOTYPE(config_parse_nsec);
188 CONFIG_PARSER_PROTOTYPE(config_parse_mode);
189 CONFIG_PARSER_PROTOTYPE(config_parse_warn_compat);
190 CONFIG_PARSER_PROTOTYPE(config_parse_log_facility);
191 CONFIG_PARSER_PROTOTYPE(config_parse_log_level);
192 CONFIG_PARSER_PROTOTYPE(config_parse_signal);
193 CONFIG_PARSER_PROTOTYPE(config_parse_personality);
194 CONFIG_PARSER_PROTOTYPE(config_parse_permille);
195 CONFIG_PARSER_PROTOTYPE(config_parse_ifname);
196 CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
197 CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
198 CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
199 CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
200 CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol);
201 CONFIG_PARSER_PROTOTYPE(config_parse_hw_addr);
202 CONFIG_PARSER_PROTOTYPE(config_parse_hw_addrs);
203 CONFIG_PARSER_PROTOTYPE(config_parse_ether_addr);
204 CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs);
205 CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
206 CONFIG_PARSER_PROTOTYPE(config_parse_percent);
207 CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
208 CONFIG_PARSER_PROTOTYPE(config_parse_pid);
209
210 typedef enum Disabled {
211 DISABLED_CONFIGURATION,
212 DISABLED_LEGACY,
213 DISABLED_EXPERIMENTAL,
214 } Disabled;
215
216 typedef enum ConfigParseStringFlags {
217 CONFIG_PARSE_STRING_SAFE = 1 << 0,
218 CONFIG_PARSE_STRING_ASCII = 1 << 1,
219
220 CONFIG_PARSE_STRING_SAFE_AND_ASCII = CONFIG_PARSE_STRING_SAFE | CONFIG_PARSE_STRING_ASCII,
221 } ConfigParseStringFlags;
222
223 #define DEFINE_CONFIG_PARSE(function, parser, msg) \
224 CONFIG_PARSER_PROTOTYPE(function) { \
225 int *i = data, r; \
226 \
227 assert(filename); \
228 assert(lvalue); \
229 assert(rvalue); \
230 assert(data); \
231 \
232 r = parser(rvalue); \
233 if (r < 0) { \
234 log_syntax(unit, LOG_WARNING, filename, line, r, \
235 msg ", ignoring: %s", rvalue); \
236 return 0; \
237 } \
238 \
239 *i = r; \
240 return 0; \
241 }
242
243 #define DEFINE_CONFIG_PARSE_PTR(function, parser, type, msg) \
244 CONFIG_PARSER_PROTOTYPE(function) { \
245 type *i = data; \
246 int r; \
247 \
248 assert(filename); \
249 assert(lvalue); \
250 assert(rvalue); \
251 assert(data); \
252 \
253 r = parser(rvalue, i); \
254 if (r < 0) \
255 log_syntax(unit, LOG_WARNING, filename, line, r, \
256 msg ", ignoring: %s", rvalue); \
257 \
258 return 0; \
259 }
260
261 #define DEFINE_CONFIG_PARSE_ENUM_FULL(function, from_string, type, msg) \
262 CONFIG_PARSER_PROTOTYPE(function) { \
263 type *i = data, x; \
264 \
265 assert(filename); \
266 assert(lvalue); \
267 assert(rvalue); \
268 assert(data); \
269 \
270 x = from_string(rvalue); \
271 if (x < 0) { \
272 log_syntax(unit, LOG_WARNING, filename, line, x, \
273 msg ", ignoring: %s", rvalue); \
274 return 0; \
275 } \
276 \
277 *i = x; \
278 return 0; \
279 }
280
281 #define DEFINE_CONFIG_PARSE_ENUM(function, name, type, msg) \
282 DEFINE_CONFIG_PARSE_ENUM_FULL(function, name##_from_string, type, msg)
283
284 #define DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(function, name, type, default_value, msg) \
285 CONFIG_PARSER_PROTOTYPE(function) { \
286 type *i = data, x; \
287 \
288 assert(filename); \
289 assert(lvalue); \
290 assert(rvalue); \
291 assert(data); \
292 \
293 if (isempty(rvalue)) { \
294 *i = default_value; \
295 return 0; \
296 } \
297 \
298 x = name##_from_string(rvalue); \
299 if (x < 0) { \
300 log_syntax(unit, LOG_WARNING, filename, line, x, \
301 msg ", ignoring: %s", rvalue); \
302 return 0; \
303 } \
304 \
305 *i = x; \
306 return 0; \
307 }
308
309 #define DEFINE_CONFIG_PARSE_ENUMV(function, name, type, invalid, msg) \
310 CONFIG_PARSER_PROTOTYPE(function) { \
311 type **enums = data; \
312 _cleanup_free_ type *xs = NULL; \
313 size_t i = 0; \
314 int r; \
315 \
316 assert(filename); \
317 assert(lvalue); \
318 assert(rvalue); \
319 assert(data); \
320 \
321 xs = new0(type, 1); \
322 if (!xs) \
323 return -ENOMEM; \
324 \
325 *xs = invalid; \
326 \
327 for (const char *p = rvalue;;) { \
328 _cleanup_free_ char *en = NULL; \
329 type x, *new_xs; \
330 \
331 r = extract_first_word(&p, &en, NULL, 0); \
332 if (r == -ENOMEM) \
333 return log_oom(); \
334 if (r < 0) { \
335 log_syntax(unit, LOG_WARNING, filename, line, r, \
336 msg ", ignoring: %s", en); \
337 return 0; \
338 } \
339 if (r == 0) \
340 break; \
341 \
342 x = name##_from_string(en); \
343 if (x < 0) { \
344 log_syntax(unit, LOG_WARNING, filename, line, x, \
345 msg ", ignoring: %s", en); \
346 continue; \
347 } \
348 \
349 for (type *ys = xs; x != invalid && *ys != invalid; ys++) \
350 if (*ys == x) { \
351 log_syntax(unit, LOG_NOTICE, filename, line, 0, \
352 "Duplicate entry, ignoring: %s", \
353 en); \
354 x = invalid; \
355 } \
356 \
357 if (x == invalid) \
358 continue; \
359 \
360 *(xs + i) = x; \
361 new_xs = realloc(xs, (++i + 1) * sizeof(type)); \
362 if (new_xs) \
363 xs = new_xs; \
364 else \
365 return log_oom(); \
366 \
367 *(xs + i) = invalid; \
368 } \
369 \
370 return free_and_replace(*enums, xs); \
371 }
372