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