chiark / gitweb /
tree-wide: remove Lennart's copyright lines
[elogind.git] / src / shared / conf-parser.h
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
10 #include "alloc-util.h"
11 #include "log.h"
12 #include "macro.h"
13
14 /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */
15
16 typedef enum ConfigParseFlags {
17         CONFIG_PARSE_RELAXED       = 1 << 0,
18         CONFIG_PARSE_ALLOW_INCLUDE = 1 << 1,
19         CONFIG_PARSE_WARN          = 1 << 2,
20         CONFIG_PARSE_REFUSE_BOM    = 1 << 3,
21 } ConfigParseFlags;
22
23 /* Argument list for parsers of specific configuration settings. */
24 #define CONFIG_PARSER_ARGUMENTS                 \
25         const char *unit,                       \
26         const char *filename,                   \
27         unsigned line,                          \
28         const char *section,                    \
29         unsigned section_line,                  \
30         const char *lvalue,                     \
31         int ltype,                              \
32         const char *rvalue,                     \
33         void *data,                             \
34         void *userdata
35
36 /* Prototype for a parser for a specific configuration setting */
37 typedef int (*ConfigParserCallback)(CONFIG_PARSER_ARGUMENTS);
38
39 /* A macro declaring the a function prototype, following the typedef above, simply because it's so cumbersomely long
40  * otherwise. (And current emacs gets irritatingly slow when editing files that contain lots of very long function
41  * prototypes on the same screen…) */
42 #define CONFIG_PARSER_PROTOTYPE(name) int name(CONFIG_PARSER_ARGUMENTS)
43
44 /* Wraps information for parsing a specific configuration variable, to
45  * be stored in a simple array */
46 typedef struct ConfigTableItem {
47         const char *section;            /* Section */
48         const char *lvalue;             /* Name of the variable */
49         ConfigParserCallback parse;     /* Function that is called to parse the variable's value */
50         int ltype;                      /* Distinguish different variables passed to the same callback */
51         void *data;                     /* Where to store the variable's data */
52 } ConfigTableItem;
53
54 /* Wraps information for parsing a specific configuration variable, to
55  * be stored in a gperf perfect hashtable */
56 typedef struct ConfigPerfItem {
57         const char *section_and_lvalue; /* Section + "." + name of the variable */
58         ConfigParserCallback parse;     /* Function that is called to parse the variable's value */
59         int ltype;                      /* Distinguish different variables passed to the same callback */
60         size_t offset;                  /* Offset where to store data, from the beginning of userdata */
61 } ConfigPerfItem;
62
63 /* Prototype for a low-level gperf lookup function */
64 typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length);
65
66 /* Prototype for a generic high-level lookup function */
67 typedef int (*ConfigItemLookup)(
68                 const void *table,
69                 const char *section,
70                 const char *lvalue,
71                 ConfigParserCallback *func,
72                 int *ltype,
73                 void **data,
74                 void *userdata);
75
76 /* Linear table search implementation of ConfigItemLookup, based on
77  * ConfigTableItem arrays */
78 int config_item_table_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
79
80 /* gperf implementation of ConfigItemLookup, based on gperf
81  * ConfigPerfItem tables */
82 int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
83
84 int config_parse(
85                 const char *unit,
86                 const char *filename,
87                 FILE *f,
88                 const char *sections,  /* nulstr */
89                 ConfigItemLookup lookup,
90                 const void *table,
91                 ConfigParseFlags flags,
92                 void *userdata);
93
94 int config_parse_many_nulstr(
95                 const char *conf_file,      /* possibly NULL */
96                 const char *conf_file_dirs, /* nulstr */
97                 const char *sections,       /* nulstr */
98                 ConfigItemLookup lookup,
99                 const void *table,
100                 ConfigParseFlags flags,
101                 void *userdata);
102
103 #if 0 /// UNNEEDED by elogind
104 int config_parse_many(
105                 const char *conf_file,      /* possibly NULL */
106                 const char* const* conf_file_dirs,
107                 const char *dropin_dirname,
108                 const char *sections,       /* nulstr */
109                 ConfigItemLookup lookup,
110                 const void *table,
111                 ConfigParseFlags flags,
112                 void *userdata);
113 #endif // 0
114
115 #if 0 /// UNNEEDED by elogind
116 #endif // 0
117 #if 0 /// UNNEEDED by elogind
118 #endif // 0
119 #if 0 /// UNNEEDED by elogind
120 #endif // 0
121 #if 0 /// UNNEEDED by elogind
122 #endif // 0
123 #if 0 /// UNNEEDED by elogind
124 #endif // 0
125 #if 0 /// UNNEEDED by elogind
126 #endif // 0
127 #if 0 /// UNNEEDED by elogind
128 #endif // 0
129 CONFIG_PARSER_PROTOTYPE(config_parse_int);
130 CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);
131 CONFIG_PARSER_PROTOTYPE(config_parse_long);
132 CONFIG_PARSER_PROTOTYPE(config_parse_uint8);
133 CONFIG_PARSER_PROTOTYPE(config_parse_uint16);
134 CONFIG_PARSER_PROTOTYPE(config_parse_uint32);
135 CONFIG_PARSER_PROTOTYPE(config_parse_uint64);
136 CONFIG_PARSER_PROTOTYPE(config_parse_double);
137 CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
138 CONFIG_PARSER_PROTOTYPE(config_parse_si_size);
139 CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64);
140 CONFIG_PARSER_PROTOTYPE(config_parse_bool);
141 CONFIG_PARSER_PROTOTYPE(config_parse_tristate);
142 CONFIG_PARSER_PROTOTYPE(config_parse_string);
143 CONFIG_PARSER_PROTOTYPE(config_parse_path);
144 CONFIG_PARSER_PROTOTYPE(config_parse_strv);
145 CONFIG_PARSER_PROTOTYPE(config_parse_sec);
146 CONFIG_PARSER_PROTOTYPE(config_parse_nsec);
147 CONFIG_PARSER_PROTOTYPE(config_parse_mode);
148 CONFIG_PARSER_PROTOTYPE(config_parse_warn_compat);
149 CONFIG_PARSER_PROTOTYPE(config_parse_log_facility);
150 CONFIG_PARSER_PROTOTYPE(config_parse_log_level);
151 CONFIG_PARSER_PROTOTYPE(config_parse_signal);
152 CONFIG_PARSER_PROTOTYPE(config_parse_personality);
153 CONFIG_PARSER_PROTOTYPE(config_parse_permille);
154 CONFIG_PARSER_PROTOTYPE(config_parse_ifname);
155 CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
156 CONFIG_PARSER_PROTOTYPE(config_parse_join_controllers);
157 CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
158 CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
159
160 typedef enum Disabled {
161         DISABLED_CONFIGURATION,
162         DISABLED_LEGACY,
163         DISABLED_EXPERIMENTAL,
164 } Disabled;
165
166 #define DEFINE_CONFIG_PARSE(function, parser, msg)                      \
167         CONFIG_PARSER_PROTOTYPE(function) {                             \
168                 int *i = data, r;                                       \
169                                                                         \
170                 assert(filename);                                       \
171                 assert(lvalue);                                         \
172                 assert(rvalue);                                         \
173                 assert(data);                                           \
174                                                                         \
175                 r = parser(rvalue);                                     \
176                 if (r < 0) {                                            \
177                         log_syntax(unit, LOG_ERR, filename, line, r,    \
178                                    msg ", ignoring: %s", rvalue);       \
179                         return 0;                                       \
180                 }                                                       \
181                                                                         \
182                 *i = r;                                                 \
183                 return 0;                                               \
184         }
185
186 #define DEFINE_CONFIG_PARSE_PTR(function, parser, type, msg)            \
187         CONFIG_PARSER_PROTOTYPE(function) {                             \
188                 type *i = data;                                         \
189                 int r;                                                  \
190                                                                         \
191                 assert(filename);                                       \
192                 assert(lvalue);                                         \
193                 assert(rvalue);                                         \
194                 assert(data);                                           \
195                                                                         \
196                 r = parser(rvalue, i);                                  \
197                 if (r < 0)                                              \
198                         log_syntax(unit, LOG_ERR, filename, line, r,    \
199                                    msg ", ignoring: %s", rvalue);       \
200                                                                         \
201                 return 0;                                               \
202         }
203
204 #define DEFINE_CONFIG_PARSE_ENUM(function, name, type, msg)             \
205         CONFIG_PARSER_PROTOTYPE(function) {                             \
206                 type *i = data, x;                                      \
207                                                                         \
208                 assert(filename);                                       \
209                 assert(lvalue);                                         \
210                 assert(rvalue);                                         \
211                 assert(data);                                           \
212                                                                         \
213                 x = name##_from_string(rvalue);                         \
214                 if (x < 0) {                                            \
215                         log_syntax(unit, LOG_ERR, filename, line, 0,    \
216                                    msg ", ignoring: %s", rvalue);       \
217                         return 0;                                       \
218                 }                                                       \
219                                                                         \
220                 *i = x;                                                 \
221                 return 0;                                               \
222         }
223
224 #define DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(function, name, type, default_value, msg) \
225         CONFIG_PARSER_PROTOTYPE(function) {                             \
226                 type *i = data, x;                                      \
227                                                                         \
228                 assert(filename);                                       \
229                 assert(lvalue);                                         \
230                 assert(rvalue);                                         \
231                 assert(data);                                           \
232                                                                         \
233                 if (isempty(rvalue)) {                                  \
234                         *i = default_value;                             \
235                         return 0;                                       \
236                 }                                                       \
237                                                                         \
238                 x = name##_from_string(rvalue);                         \
239                 if (x < 0) {                                            \
240                         log_syntax(unit, LOG_ERR, filename, line, 0,    \
241                                    msg ", ignoring: %s", rvalue);       \
242                         return 0;                                       \
243                 }                                                       \
244                                                                         \
245                 *i = x;                                                 \
246                 return 0;                                               \
247         }
248
249 #define DEFINE_CONFIG_PARSE_ENUMV(function, name, type, invalid, msg)          \
250         CONFIG_PARSER_PROTOTYPE(function) {                                    \
251                 type **enums = data, x, *ys;                                   \
252                 _cleanup_free_ type *xs = NULL;                                \
253                 const char *word, *state;                                      \
254                 size_t l, i = 0;                                               \
255                                                                                \
256                 assert(filename);                                              \
257                 assert(lvalue);                                                \
258                 assert(rvalue);                                                \
259                 assert(data);                                                  \
260                                                                                \
261                 xs = new0(type, 1);                                            \
262                 if (!xs)                                                       \
263                         return -ENOMEM;                                        \
264                                                                                \
265                 *xs = invalid;                                                 \
266                                                                                \
267                 FOREACH_WORD(word, l, rvalue, state) {                         \
268                         _cleanup_free_ char *en = NULL;                        \
269                         type *new_xs;                                          \
270                                                                                \
271                         en = strndup(word, l);                                 \
272                         if (!en)                                               \
273                                 return -ENOMEM;                                \
274                                                                                \
275                         if ((x = name##_from_string(en)) < 0) {                \
276                                 log_syntax(unit, LOG_ERR, filename, line, 0,   \
277                                            msg ", ignoring: %s", en);          \
278                                 continue;                                      \
279                         }                                                      \
280                                                                                \
281                         for (ys = xs; x != invalid && *ys != invalid; ys++) {  \
282                                 if (*ys == x) {                                \
283                                         log_syntax(unit, LOG_NOTICE, filename, \
284                                                    line, 0,                    \
285                                                    "Duplicate entry, ignoring: %s", \
286                                                    en);                        \
287                                         x = invalid;                           \
288                                 }                                              \
289                         }                                                      \
290                                                                                \
291                         if (x == invalid)                                      \
292                                 continue;                                      \
293                                                                                \
294                         *(xs + i) = x;                                         \
295                         new_xs = realloc(xs, (++i + 1) * sizeof(type));        \
296                         if (new_xs)                                            \
297                                 xs = new_xs;                                   \
298                         else                                                   \
299                                 return -ENOMEM;                                \
300                                                                                \
301                         *(xs + i) = invalid;                                   \
302                 }                                                              \
303                                                                                \
304                 free_and_replace(*enums, xs);                                  \
305                 return 0;                                                      \
306         }