1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include <sys/types.h>
29 #include "alloc-util.h"
30 #include "conf-files.h"
31 #include "conf-parser.h"
33 #include "extract-word.h"
39 #include "parse-util.h"
40 #include "path-util.h"
41 #include "process-util.h"
42 #include "signal-util.h"
43 #include "socket-util.h"
44 #include "string-util.h"
46 #include "syslog-util.h"
47 #include "time-util.h"
50 /// Additional includes needed by elogind
54 int config_item_table_lookup(
58 ConfigParserCallback *func,
63 const ConfigTableItem *t;
71 for (t = table; t->lvalue; t++) {
73 if (!streq(lvalue, t->lvalue))
76 if (!streq_ptr(section, t->section))
88 int config_item_perf_lookup(
92 ConfigParserCallback *func,
97 ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
98 const ConfigPerfItem *p;
107 p = lookup(lvalue, strlen(lvalue));
111 key = strjoin(section, ".", lvalue);
115 p = lookup(key, strlen(key));
124 *data = (uint8_t*) userdata + p->offset;
128 /* Run the user supplied parser for an assignment */
129 static int next_assignment(
131 const char *filename,
133 ConfigItemLookup lookup,
136 unsigned section_line,
139 ConfigParseFlags flags,
142 ConfigParserCallback func = NULL;
153 r = lookup(table, section, lvalue, &func, <ype, &data, userdata);
159 return func(unit, filename, line, section, section_line,
160 lvalue, ltype, rvalue, data, userdata);
165 /* Warn about unknown non-extension fields. */
166 if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-"))
167 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section);
172 /* Parse a single logical line */
173 static int parse_line(
175 const char *filename,
177 const char *sections,
178 ConfigItemLookup lookup,
180 ConfigParseFlags flags,
182 unsigned *section_line,
183 bool *section_ignored,
198 if (strchr(COMMENTS "\n", *l))
201 include = first_word(l, ".include");
203 _cleanup_free_ char *fn = NULL;
205 /* .includes are a bad idea, we only support them here
206 * for historical reasons. They create cyclic include
207 * problems and make it difficult to detect
208 * configuration file changes with an easy
209 * stat(). Better approaches, such as .d/ drop-in
212 * Support for them should be eventually removed. */
214 if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) {
215 log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
219 log_syntax(unit, LOG_WARNING, filename, line, 0,
220 ".include directives are deprecated, and support for them will be removed in a future version of elogind. "
221 "Please use drop-in files instead.");
223 fn = file_in_same_dir(filename, strstrip(include));
227 return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata);
238 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
242 n = strndup(l+1, k-2);
246 if (sections && !nulstr_contains(sections, n)) {
248 if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
249 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
252 *section = mfree(*section);
254 *section_ignored = true;
258 *section_line = line;
259 *section_ignored = false;
265 if (sections && !*section) {
267 if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
268 log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
275 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
282 return next_assignment(unit,
295 /* Go through the file and parse each line */
296 int config_parse(const char *unit,
297 const char *filename,
299 const char *sections,
300 ConfigItemLookup lookup,
302 ConfigParseFlags flags,
305 _cleanup_free_ char *section = NULL, *continuation = NULL;
306 _cleanup_fclose_ FILE *ours = NULL;
307 unsigned line = 0, section_line = 0;
308 bool section_ignored = false;
315 f = ours = fopen(filename, "re");
317 /* Only log on request, except for ENOENT,
318 * since we return 0 to the caller. */
319 if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
320 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
321 "Failed to open configuration file '%s': %m", filename);
322 return errno == ENOENT ? 0 : -errno;
326 fd_warn_permissions(filename, fileno(f));
329 _cleanup_free_ char *buf = NULL;
330 bool escaped = false;
333 r = read_line(f, LONG_LINE_MAX, &buf);
337 if (flags & CONFIG_PARSE_WARN)
338 log_error_errno(r, "%s:%u: Line too long", filename, line);
343 if (CONFIG_PARSE_WARN)
344 log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
350 if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
353 q = startswith(buf, UTF8_BYTE_ORDER_MARK);
356 flags |= CONFIG_PARSE_REFUSE_BOM;
361 if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) {
362 if (flags & CONFIG_PARSE_WARN)
363 log_error("%s:%u: Continuation line too long", filename, line);
367 if (!strextend(&continuation, l, NULL)) {
368 if (flags & CONFIG_PARSE_WARN)
377 for (e = p; *e; e++) {
388 continuation = strdup(l);
390 if (flags & CONFIG_PARSE_WARN)
412 if (flags & CONFIG_PARSE_WARN)
413 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
418 continuation = mfree(continuation);
424 static int config_parse_many_files(
425 const char *conf_file,
427 const char *sections,
428 ConfigItemLookup lookup,
430 ConfigParseFlags flags,
437 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
442 STRV_FOREACH(fn, files) {
443 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
451 /* Parse each config file in the directories specified as nulstr. */
452 int config_parse_many_nulstr(
453 const char *conf_file,
454 const char *conf_file_dirs,
455 const char *sections,
456 ConfigItemLookup lookup,
458 ConfigParseFlags flags,
461 _cleanup_strv_free_ char **files = NULL;
464 r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
468 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
471 #if 0 /// UNNEEDED by elogind
472 /* Parse each config file in the directories specified as strv. */
473 int config_parse_many(
474 const char *conf_file,
475 const char* const* conf_file_dirs,
476 const char *dropin_dirname,
477 const char *sections,
478 ConfigItemLookup lookup,
480 ConfigParseFlags flags,
483 _cleanup_strv_free_ char **dropin_dirs = NULL;
484 _cleanup_strv_free_ char **files = NULL;
488 suffix = strjoina("/", dropin_dirname);
489 r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
493 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
497 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
501 #define DEFINE_PARSER(type, vartype, conv_func) \
502 int config_parse_##type( \
504 const char *filename, \
506 const char *section, \
507 unsigned section_line, \
508 const char *lvalue, \
510 const char *rvalue, \
522 r = conv_func(rvalue, i); \
524 log_syntax(unit, LOG_ERR, filename, line, r, \
525 "Failed to parse %s value, ignoring: %s", \
531 DEFINE_PARSER(int, int, safe_atoi);
532 DEFINE_PARSER(long, long, safe_atoli);
533 #if 0 /// UNNEEDED by elogind
534 DEFINE_PARSER(uint8, uint8_t, safe_atou8);
535 DEFINE_PARSER(uint16, uint16_t, safe_atou16);
536 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
538 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
539 DEFINE_PARSER(unsigned, unsigned, safe_atou);
540 DEFINE_PARSER(double, double, safe_atod);
541 #if 0 /// UNNEEDED by elogind
542 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
544 DEFINE_PARSER(sec, usec_t, parse_sec);
545 DEFINE_PARSER(mode, mode_t, parse_mode);
547 int config_parse_iec_size(const char* unit,
548 const char *filename,
551 unsigned section_line,
567 r = parse_size(rvalue, 1024, &v);
568 if (r < 0 || (uint64_t) (size_t) v != v) {
569 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
577 #if 0 /// UNNEEDED by elogind
578 int config_parse_si_size(
580 const char *filename,
583 unsigned section_line,
599 r = parse_size(rvalue, 1000, &v);
600 if (r < 0 || (uint64_t) (size_t) v != v) {
601 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
609 int config_parse_iec_uint64(
611 const char *filename,
614 unsigned section_line,
621 uint64_t *bytes = data;
629 r = parse_size(rvalue, 1024, bytes);
631 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
637 int config_parse_bool(const char* unit,
638 const char *filename,
641 unsigned section_line,
657 k = parse_boolean(rvalue);
659 log_syntax(unit, LOG_ERR, filename, line, k,
660 "Failed to parse boolean value%s: %s",
661 fatal ? "" : ", ignoring", rvalue);
662 return fatal ? -ENOEXEC : 0;
669 #if 0 /// UNNEEDED by elogind
670 int config_parse_tristate(
672 const char *filename,
675 unsigned section_line,
689 /* A tristate is pretty much a boolean, except that it can
690 * also take the special value -1, indicating "uninitialized",
691 * much like NULL is for a pointer type. */
693 k = parse_boolean(rvalue);
695 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
704 int config_parse_string(
706 const char *filename,
709 unsigned section_line,
723 if (!utf8_is_valid(rvalue)) {
724 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
742 int config_parse_path(
744 const char *filename,
747 unsigned section_line,
762 if (isempty(rvalue)) {
767 if (!utf8_is_valid(rvalue)) {
768 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
769 return fatal ? -ENOEXEC : 0;
772 if (!path_is_absolute(rvalue)) {
773 log_syntax(unit, LOG_ERR, filename, line, 0,
774 "Not an absolute path%s: %s",
775 fatal ? "" : ", ignoring", rvalue);
776 return fatal ? -ENOEXEC : 0;
783 path_kill_slashes(n);
792 int config_parse_strv(
794 const char *filename,
797 unsigned section_line,
812 if (isempty(rvalue)) {
813 *sv = strv_free(*sv);
820 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
826 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
830 if (!utf8_is_valid(word)) {
831 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
836 r = strv_consume(sv, word);
844 int config_parse_warn_compat(
846 const char *filename,
849 unsigned section_line,
855 Disabled reason = ltype;
858 case DISABLED_CONFIGURATION:
859 log_syntax(unit, LOG_DEBUG, filename, line, 0,
860 "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
862 case DISABLED_LEGACY:
863 log_syntax(unit, LOG_INFO, filename, line, 0,
864 "Support for option %s= has been removed and it is ignored", lvalue);
866 case DISABLED_EXPERIMENTAL:
867 log_syntax(unit, LOG_INFO, filename, line, 0,
868 "Support for option %s= has not yet been enabled and it is ignored", lvalue);
875 #if 0 /// UNNEEDED by elogind
876 int config_parse_log_facility(
878 const char *filename,
881 unsigned section_line,
895 x = log_facility_unshifted_from_string(rvalue);
897 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
901 *o = (x << 3) | LOG_PRI(*o);
907 int config_parse_log_level(
909 const char *filename,
912 unsigned section_line,
926 x = log_level_from_string(rvalue);
928 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
932 if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
935 *o = (*o & LOG_FACMASK) | x;
940 int config_parse_signal(
942 const char *filename,
945 unsigned section_line,
959 r = signal_from_string_try_harder(rvalue);
961 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
969 #if 0 /// UNNEEDED by elogind
970 int config_parse_personality(
972 const char *filename,
975 unsigned section_line,
982 unsigned long *personality = data, p;
990 p = PERSONALITY_INVALID;
992 p = personality_from_string(rvalue);
993 if (p == PERSONALITY_INVALID) {
994 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
1003 int config_parse_ifname(
1005 const char *filename,
1007 const char *section,
1008 unsigned section_line,
1023 if (isempty(rvalue)) {
1028 if (!ifname_valid(rvalue)) {
1029 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
1033 r = free_and_strdup(s, rvalue);
1040 int config_parse_ip_port(
1042 const char *filename,
1044 const char *section,
1045 unsigned section_line,
1061 if (isempty(rvalue)) {
1066 r = parse_ip_port(rvalue, &port);
1068 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
1077 int config_parse_join_controllers(
1079 const char *filename,
1081 const char *section,
1082 unsigned section_line,
1089 char ****ret = data;
1090 const char *whole_rvalue = rvalue;
1092 _cleanup_(strv_free_freep) char ***controllers = NULL;
1100 _cleanup_free_ char *word = NULL;
1104 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
1106 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
1112 l = strv_split(word, ",");
1117 if (strv_length(l) <= 1) {
1123 controllers = new(char**, 2);
1130 controllers[1] = NULL;
1137 t = new0(char**, n+2);
1145 for (a = controllers; *a; a++)
1146 if (strv_overlap(*a, l)) {
1147 if (strv_extend_strv(&l, *a, false) < 0) {
1166 t[n++] = strv_uniq(l);
1168 strv_free_free(controllers);
1172 if (!isempty(rvalue))
1173 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
1175 /* As a special case, return a single empty strv, to override the default */
1177 controllers = new(char**, 2);
1180 controllers[0] = strv_new(NULL, NULL);
1181 if (!controllers[0])
1183 controllers[1] = NULL;
1186 strv_free_free(*ret);
1187 *ret = TAKE_PTR(controllers);