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 if (startswith(l, ".include ")) {
202 _cleanup_free_ char *fn = NULL;
204 /* .includes are a bad idea, we only support them here
205 * for historical reasons. They create cyclic include
206 * problems and make it difficult to detect
207 * configuration file changes with an easy
208 * stat(). Better approaches, such as .d/ drop-in
211 * Support for them should be eventually removed. */
213 if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) {
214 log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
218 fn = file_in_same_dir(filename, strstrip(l+9));
222 return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata);
233 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
237 n = strndup(l+1, k-2);
241 if (sections && !nulstr_contains(sections, n)) {
243 if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
244 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
247 *section = mfree(*section);
249 *section_ignored = true;
253 *section_line = line;
254 *section_ignored = false;
260 if (sections && !*section) {
262 if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
263 log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
270 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
277 return next_assignment(unit,
290 /* Go through the file and parse each line */
291 int config_parse(const char *unit,
292 const char *filename,
294 const char *sections,
295 ConfigItemLookup lookup,
297 ConfigParseFlags flags,
300 _cleanup_free_ char *section = NULL, *continuation = NULL;
301 _cleanup_fclose_ FILE *ours = NULL;
302 unsigned line = 0, section_line = 0;
303 bool section_ignored = false;
310 f = ours = fopen(filename, "re");
312 /* Only log on request, except for ENOENT,
313 * since we return 0 to the caller. */
314 if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
315 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
316 "Failed to open configuration file '%s': %m", filename);
317 return errno == ENOENT ? 0 : -errno;
321 fd_warn_permissions(filename, fileno(f));
324 _cleanup_free_ char *buf = NULL;
325 bool escaped = false;
328 r = read_line(f, LONG_LINE_MAX, &buf);
332 if (flags & CONFIG_PARSE_WARN)
333 log_error_errno(r, "%s:%u: Line too long", filename, line);
338 if (CONFIG_PARSE_WARN)
339 log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
345 if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
348 q = startswith(buf, UTF8_BYTE_ORDER_MARK);
351 flags |= CONFIG_PARSE_REFUSE_BOM;
356 if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) {
357 if (flags & CONFIG_PARSE_WARN)
358 log_error("%s:%u: Continuation line too long", filename, line);
362 if (!strextend(&continuation, l, NULL)) {
363 if (flags & CONFIG_PARSE_WARN)
372 for (e = p; *e; e++) {
383 continuation = strdup(l);
385 if (flags & CONFIG_PARSE_WARN)
407 if (flags & CONFIG_PARSE_WARN)
408 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
413 continuation = mfree(continuation);
419 static int config_parse_many_files(
420 const char *conf_file,
422 const char *sections,
423 ConfigItemLookup lookup,
425 ConfigParseFlags flags,
432 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
437 STRV_FOREACH(fn, files) {
438 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
446 /* Parse each config file in the directories specified as nulstr. */
447 int config_parse_many_nulstr(
448 const char *conf_file,
449 const char *conf_file_dirs,
450 const char *sections,
451 ConfigItemLookup lookup,
453 ConfigParseFlags flags,
456 _cleanup_strv_free_ char **files = NULL;
459 r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
463 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
466 #if 0 /// UNNEEDED by elogind
467 /* Parse each config file in the directories specified as strv. */
468 int config_parse_many(
469 const char *conf_file,
470 const char* const* conf_file_dirs,
471 const char *dropin_dirname,
472 const char *sections,
473 ConfigItemLookup lookup,
475 ConfigParseFlags flags,
478 _cleanup_strv_free_ char **dropin_dirs = NULL;
479 _cleanup_strv_free_ char **files = NULL;
483 suffix = strjoina("/", dropin_dirname);
484 r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
488 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
492 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
496 #define DEFINE_PARSER(type, vartype, conv_func) \
497 int config_parse_##type( \
499 const char *filename, \
501 const char *section, \
502 unsigned section_line, \
503 const char *lvalue, \
505 const char *rvalue, \
517 r = conv_func(rvalue, i); \
519 log_syntax(unit, LOG_ERR, filename, line, r, \
520 "Failed to parse %s value, ignoring: %s", \
526 DEFINE_PARSER(int, int, safe_atoi);
527 DEFINE_PARSER(long, long, safe_atoli);
528 #if 0 /// UNNEEDED by elogind
529 DEFINE_PARSER(uint8, uint8_t, safe_atou8);
530 DEFINE_PARSER(uint16, uint16_t, safe_atou16);
531 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
533 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
534 DEFINE_PARSER(unsigned, unsigned, safe_atou);
535 DEFINE_PARSER(double, double, safe_atod);
536 #if 0 /// UNNEEDED by elogind
537 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
539 DEFINE_PARSER(sec, usec_t, parse_sec);
540 DEFINE_PARSER(mode, mode_t, parse_mode);
542 int config_parse_iec_size(const char* unit,
543 const char *filename,
546 unsigned section_line,
562 r = parse_size(rvalue, 1024, &v);
563 if (r < 0 || (uint64_t) (size_t) v != v) {
564 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
572 #if 0 /// UNNEEDED by elogind
573 int config_parse_si_size(
575 const char *filename,
578 unsigned section_line,
594 r = parse_size(rvalue, 1000, &v);
595 if (r < 0 || (uint64_t) (size_t) v != v) {
596 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
604 int config_parse_iec_uint64(
606 const char *filename,
609 unsigned section_line,
616 uint64_t *bytes = data;
624 r = parse_size(rvalue, 1024, bytes);
626 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
632 int config_parse_bool(const char* unit,
633 const char *filename,
636 unsigned section_line,
652 k = parse_boolean(rvalue);
654 log_syntax(unit, LOG_ERR, filename, line, k,
655 "Failed to parse boolean value%s: %s",
656 fatal ? "" : ", ignoring", rvalue);
657 return fatal ? -ENOEXEC : 0;
664 #if 0 /// UNNEEDED by elogind
665 int config_parse_tristate(
667 const char *filename,
670 unsigned section_line,
684 /* A tristate is pretty much a boolean, except that it can
685 * also take the special value -1, indicating "uninitialized",
686 * much like NULL is for a pointer type. */
688 k = parse_boolean(rvalue);
690 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
699 int config_parse_string(
701 const char *filename,
704 unsigned section_line,
718 if (!utf8_is_valid(rvalue)) {
719 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
737 int config_parse_path(
739 const char *filename,
742 unsigned section_line,
757 if (isempty(rvalue)) {
762 if (!utf8_is_valid(rvalue)) {
763 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
764 return fatal ? -ENOEXEC : 0;
767 if (!path_is_absolute(rvalue)) {
768 log_syntax(unit, LOG_ERR, filename, line, 0,
769 "Not an absolute path%s: %s",
770 fatal ? "" : ", ignoring", rvalue);
771 return fatal ? -ENOEXEC : 0;
778 path_kill_slashes(n);
787 int config_parse_strv(
789 const char *filename,
792 unsigned section_line,
807 if (isempty(rvalue)) {
808 *sv = strv_free(*sv);
815 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
821 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
825 if (!utf8_is_valid(word)) {
826 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
831 r = strv_consume(sv, word);
839 int config_parse_warn_compat(
841 const char *filename,
844 unsigned section_line,
850 Disabled reason = ltype;
853 case DISABLED_CONFIGURATION:
854 log_syntax(unit, LOG_DEBUG, filename, line, 0,
855 "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
857 case DISABLED_LEGACY:
858 log_syntax(unit, LOG_INFO, filename, line, 0,
859 "Support for option %s= has been removed and it is ignored", lvalue);
861 case DISABLED_EXPERIMENTAL:
862 log_syntax(unit, LOG_INFO, filename, line, 0,
863 "Support for option %s= has not yet been enabled and it is ignored", lvalue);
870 #if 0 /// UNNEEDED by elogind
871 int config_parse_log_facility(
873 const char *filename,
876 unsigned section_line,
890 x = log_facility_unshifted_from_string(rvalue);
892 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
896 *o = (x << 3) | LOG_PRI(*o);
902 int config_parse_log_level(
904 const char *filename,
907 unsigned section_line,
921 x = log_level_from_string(rvalue);
923 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
927 if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
930 *o = (*o & LOG_FACMASK) | x;
935 int config_parse_signal(
937 const char *filename,
940 unsigned section_line,
954 r = signal_from_string_try_harder(rvalue);
956 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
964 #if 0 /// UNNEEDED by elogind
965 int config_parse_personality(
967 const char *filename,
970 unsigned section_line,
977 unsigned long *personality = data, p;
985 p = PERSONALITY_INVALID;
987 p = personality_from_string(rvalue);
988 if (p == PERSONALITY_INVALID) {
989 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
998 int config_parse_ifname(
1000 const char *filename,
1002 const char *section,
1003 unsigned section_line,
1018 if (isempty(rvalue)) {
1023 if (!ifname_valid(rvalue)) {
1024 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
1028 r = free_and_strdup(s, rvalue);
1035 int config_parse_ip_port(
1037 const char *filename,
1039 const char *section,
1040 unsigned section_line,
1056 if (isempty(rvalue)) {
1061 r = parse_ip_port(rvalue, &port);
1063 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
1072 int config_parse_join_controllers(
1074 const char *filename,
1076 const char *section,
1077 unsigned section_line,
1084 char ****ret = data;
1085 const char *whole_rvalue = rvalue;
1087 _cleanup_(strv_free_freep) char ***controllers = NULL;
1095 _cleanup_free_ char *word = NULL;
1099 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
1101 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
1107 l = strv_split(word, ",");
1112 if (strv_length(l) <= 1) {
1118 controllers = new(char**, 2);
1125 controllers[1] = NULL;
1132 t = new0(char**, n+2);
1140 for (a = controllers; *a; a++)
1141 if (strv_overlap(*a, l)) {
1142 if (strv_extend_strv(&l, *a, false) < 0) {
1161 t[n++] = strv_uniq(l);
1163 strv_free_free(controllers);
1167 if (!isempty(rvalue))
1168 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
1170 /* As a special case, return a single empty strv, to override the default */
1172 controllers = new(char**, 2);
1175 controllers[0] = strv_new(NULL, NULL);
1176 if (!controllers[0])
1178 controllers[1] = NULL;
1181 strv_free_free(*ret);