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 variable assignment 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);
430 if (flags & CONFIG_PARSE_WARN)
431 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
440 static int config_parse_many_files(
441 const char *conf_file,
443 const char *sections,
444 ConfigItemLookup lookup,
446 ConfigParseFlags flags,
453 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
458 STRV_FOREACH(fn, files) {
459 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
467 /* Parse each config file in the directories specified as nulstr. */
468 int config_parse_many_nulstr(
469 const char *conf_file,
470 const char *conf_file_dirs,
471 const char *sections,
472 ConfigItemLookup lookup,
474 ConfigParseFlags flags,
477 _cleanup_strv_free_ char **files = NULL;
480 r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
484 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
487 #if 0 /// UNNEEDED by elogind
488 /* Parse each config file in the directories specified as strv. */
489 int config_parse_many(
490 const char *conf_file,
491 const char* const* conf_file_dirs,
492 const char *dropin_dirname,
493 const char *sections,
494 ConfigItemLookup lookup,
496 ConfigParseFlags flags,
499 _cleanup_strv_free_ char **dropin_dirs = NULL;
500 _cleanup_strv_free_ char **files = NULL;
504 suffix = strjoina("/", dropin_dirname);
505 r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
509 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
513 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
517 #define DEFINE_PARSER(type, vartype, conv_func) \
518 int config_parse_##type( \
520 const char *filename, \
522 const char *section, \
523 unsigned section_line, \
524 const char *lvalue, \
526 const char *rvalue, \
538 r = conv_func(rvalue, i); \
540 log_syntax(unit, LOG_ERR, filename, line, r, \
541 "Failed to parse %s value, ignoring: %s", \
546 struct __useless_struct_to_allow_trailing_semicolon__
548 DEFINE_PARSER(int, int, safe_atoi);
549 DEFINE_PARSER(long, long, safe_atoli);
550 #if 0 /// UNNEEDED by elogind
551 DEFINE_PARSER(uint8, uint8_t, safe_atou8);
552 DEFINE_PARSER(uint16, uint16_t, safe_atou16);
553 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
555 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
556 DEFINE_PARSER(unsigned, unsigned, safe_atou);
557 DEFINE_PARSER(double, double, safe_atod);
558 #if 0 /// UNNEEDED by elogind
559 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
561 DEFINE_PARSER(sec, usec_t, parse_sec);
562 DEFINE_PARSER(mode, mode_t, parse_mode);
564 int config_parse_iec_size(const char* unit,
565 const char *filename,
568 unsigned section_line,
584 r = parse_size(rvalue, 1024, &v);
585 if (r < 0 || (uint64_t) (size_t) v != v) {
586 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
594 #if 0 /// UNNEEDED by elogind
595 int config_parse_si_size(
597 const char *filename,
600 unsigned section_line,
616 r = parse_size(rvalue, 1000, &v);
617 if (r < 0 || (uint64_t) (size_t) v != v) {
618 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
626 int config_parse_iec_uint64(
628 const char *filename,
631 unsigned section_line,
638 uint64_t *bytes = data;
646 r = parse_size(rvalue, 1024, bytes);
648 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
654 int config_parse_bool(const char* unit,
655 const char *filename,
658 unsigned section_line,
674 k = parse_boolean(rvalue);
676 log_syntax(unit, LOG_ERR, filename, line, k,
677 "Failed to parse boolean value%s: %s",
678 fatal ? "" : ", ignoring", rvalue);
679 return fatal ? -ENOEXEC : 0;
686 #if 0 /// UNNEEDED by elogind
687 int config_parse_tristate(
689 const char *filename,
692 unsigned section_line,
706 /* A tristate is pretty much a boolean, except that it can
707 * also take the special value -1, indicating "uninitialized",
708 * much like NULL is for a pointer type. */
710 k = parse_boolean(rvalue);
712 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
721 int config_parse_string(
723 const char *filename,
726 unsigned section_line,
740 if (!utf8_is_valid(rvalue)) {
741 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
759 int config_parse_path(
761 const char *filename,
764 unsigned section_line,
779 if (isempty(rvalue)) {
784 if (!utf8_is_valid(rvalue)) {
785 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
786 return fatal ? -ENOEXEC : 0;
789 if (!path_is_absolute(rvalue)) {
790 log_syntax(unit, LOG_ERR, filename, line, 0,
791 "Not an absolute path%s: %s",
792 fatal ? "" : ", ignoring", rvalue);
793 return fatal ? -ENOEXEC : 0;
800 path_kill_slashes(n);
809 int config_parse_strv(
811 const char *filename,
814 unsigned section_line,
829 if (isempty(rvalue)) {
830 *sv = strv_free(*sv);
837 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
843 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
847 if (!utf8_is_valid(word)) {
848 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
853 r = strv_consume(sv, word);
861 #if 0 /// UNNEEDED by elogind
862 int config_parse_log_facility(
864 const char *filename,
867 unsigned section_line,
881 x = log_facility_unshifted_from_string(rvalue);
883 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
887 *o = (x << 3) | LOG_PRI(*o);
893 int config_parse_log_level(
895 const char *filename,
898 unsigned section_line,
912 x = log_level_from_string(rvalue);
914 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
918 if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
921 *o = (*o & LOG_FACMASK) | x;
926 int config_parse_signal(
928 const char *filename,
931 unsigned section_line,
945 r = signal_from_string_try_harder(rvalue);
947 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
955 #if 0 /// UNNEEDED by elogind
956 int config_parse_personality(
958 const char *filename,
961 unsigned section_line,
968 unsigned long *personality = data, p;
976 p = PERSONALITY_INVALID;
978 p = personality_from_string(rvalue);
979 if (p == PERSONALITY_INVALID) {
980 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
989 int config_parse_ifname(
991 const char *filename,
994 unsigned section_line,
1009 if (isempty(rvalue)) {
1014 if (!ifname_valid(rvalue)) {
1015 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
1019 r = free_and_strdup(s, rvalue);
1026 int config_parse_ip_port(
1028 const char *filename,
1030 const char *section,
1031 unsigned section_line,
1047 if (isempty(rvalue)) {
1052 r = parse_ip_port(rvalue, &port);
1054 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
1063 int config_parse_join_controllers(
1065 const char *filename,
1067 const char *section,
1068 unsigned section_line,
1075 char ****ret = data;
1076 const char *whole_rvalue = rvalue;
1078 _cleanup_(strv_free_freep) char ***controllers = NULL;
1086 _cleanup_free_ char *word = NULL;
1090 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
1092 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
1098 l = strv_split(word, ",");
1103 if (strv_length(l) <= 1) {
1109 controllers = new(char**, 2);
1116 controllers[1] = NULL;
1123 t = new0(char**, n+2);
1131 for (a = controllers; *a; a++)
1132 if (strv_overlap(*a, l)) {
1133 if (strv_extend_strv(&l, *a, false) < 0) {
1152 t[n++] = strv_uniq(l);
1154 strv_free_free(controllers);
1158 if (!isempty(rvalue))
1159 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
1161 /* As a special case, return a single empty strv, to override the default */
1163 controllers = new(char**, 2);
1166 controllers[0] = strv_new(NULL, NULL);
1167 if (!controllers[0])
1169 controllers[1] = NULL;
1172 strv_free_free(*ret);