]> git.proxmox.com Git - systemd.git/blame - src/shared/conf-parser.h
bump version to 252.11-pve1
[systemd.git] / src / shared / conf-parser.h
CommitLineData
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
19typedef 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
38typedef 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 */
47typedef 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
57typedef 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 */
65typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length);
66
67/* Prototype for a generic high-level lookup function */
68typedef 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 79int 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 83int 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
85int 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
96int 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
106int 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
118int 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 126int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st);
8f232108
MB
127bool stats_by_path_equal(Hashmap *a, Hashmap *b);
128
f5caa8fa
MB
129typedef struct ConfigSection {
130 unsigned line;
131 bool invalid;
132 char filename[];
133} ConfigSection;
134
135static inline ConfigSection* config_section_free(ConfigSection *cs) {
136 return mfree(cs);
137}
138DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection*, config_section_free);
139
140int config_section_new(const char *filename, unsigned line, ConfigSection **s);
141extern const struct hash_ops config_section_hash_ops;
142unsigned hashmap_find_free_section_line(Hashmap *hashmap);
143
144static 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
166CONFIG_PARSER_PROTOTYPE(config_parse_int);
167CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);
168CONFIG_PARSER_PROTOTYPE(config_parse_long);
169CONFIG_PARSER_PROTOTYPE(config_parse_uint8);
170CONFIG_PARSER_PROTOTYPE(config_parse_uint16);
171CONFIG_PARSER_PROTOTYPE(config_parse_uint32);
46cdbd49 172CONFIG_PARSER_PROTOTYPE(config_parse_int32);
b012e921
MB
173CONFIG_PARSER_PROTOTYPE(config_parse_uint64);
174CONFIG_PARSER_PROTOTYPE(config_parse_double);
175CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
46cdbd49 176CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64);
b012e921 177CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64);
ea0999c9 178CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64_infinity);
b012e921 179CONFIG_PARSER_PROTOTYPE(config_parse_bool);
a10f5d05 180CONFIG_PARSER_PROTOTYPE(config_parse_id128);
b012e921
MB
181CONFIG_PARSER_PROTOTYPE(config_parse_tristate);
182CONFIG_PARSER_PROTOTYPE(config_parse_string);
f5caa8fa
MB
183CONFIG_PARSER_PROTOTYPE(config_parse_dns_name);
184CONFIG_PARSER_PROTOTYPE(config_parse_hostname);
b012e921
MB
185CONFIG_PARSER_PROTOTYPE(config_parse_path);
186CONFIG_PARSER_PROTOTYPE(config_parse_strv);
187CONFIG_PARSER_PROTOTYPE(config_parse_sec);
bb4f798a 188CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_infinity);
f2dec872 189CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_unset);
b012e921
MB
190CONFIG_PARSER_PROTOTYPE(config_parse_nsec);
191CONFIG_PARSER_PROTOTYPE(config_parse_mode);
192CONFIG_PARSER_PROTOTYPE(config_parse_warn_compat);
193CONFIG_PARSER_PROTOTYPE(config_parse_log_facility);
194CONFIG_PARSER_PROTOTYPE(config_parse_log_level);
195CONFIG_PARSER_PROTOTYPE(config_parse_signal);
196CONFIG_PARSER_PROTOTYPE(config_parse_personality);
197CONFIG_PARSER_PROTOTYPE(config_parse_permille);
198CONFIG_PARSER_PROTOTYPE(config_parse_ifname);
46cdbd49 199CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
b012e921 200CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
b012e921
MB
201CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
202CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
a10f5d05 203CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol);
ea0999c9
MB
204CONFIG_PARSER_PROTOTYPE(config_parse_hw_addr);
205CONFIG_PARSER_PROTOTYPE(config_parse_hw_addrs);
206CONFIG_PARSER_PROTOTYPE(config_parse_ether_addr);
207CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs);
8b3d4ff0 208CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
a032b68d 209CONFIG_PARSER_PROTOTYPE(config_parse_percent);
3a6ce677 210CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
f5caa8fa 211CONFIG_PARSER_PROTOTYPE(config_parse_pid);
086111aa 212CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
b012e921
MB
213
214typedef enum Disabled {
215 DISABLED_CONFIGURATION,
216 DISABLED_LEGACY,
217 DISABLED_EXPERIMENTAL,
218} Disabled;
219
f5caa8fa
MB
220typedef 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 }