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 log_syntax(unit, LOG_WARNING, filename, line, 0,
219 ".include directives are deprecated, and support for them will be removed in a future version of elogind. "
220 "Please use drop-in files instead.");
222 fn = file_in_same_dir(filename, strstrip(l+9));
226 return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata);
237 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
241 n = strndup(l+1, k-2);
245 if (sections && !nulstr_contains(sections, n)) {
247 if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
248 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
251 *section = mfree(*section);
253 *section_ignored = true;
257 *section_line = line;
258 *section_ignored = false;
264 if (sections && !*section) {
266 if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
267 log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
274 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
281 return next_assignment(unit,
294 /* Go through the file and parse each line */
295 int config_parse(const char *unit,
296 const char *filename,
298 const char *sections,
299 ConfigItemLookup lookup,
301 ConfigParseFlags flags,
304 _cleanup_free_ char *section = NULL, *continuation = NULL;
305 _cleanup_fclose_ FILE *ours = NULL;
306 unsigned line = 0, section_line = 0;
307 bool section_ignored = false;
314 f = ours = fopen(filename, "re");
316 /* Only log on request, except for ENOENT,
317 * since we return 0 to the caller. */
318 if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
319 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
320 "Failed to open configuration file '%s': %m", filename);
321 return errno == ENOENT ? 0 : -errno;
325 fd_warn_permissions(filename, fileno(f));
328 _cleanup_free_ char *buf = NULL;
329 bool escaped = false;
332 r = read_line(f, LONG_LINE_MAX, &buf);
336 if (flags & CONFIG_PARSE_WARN)
337 log_error_errno(r, "%s:%u: Line too long", filename, line);
342 if (CONFIG_PARSE_WARN)
343 log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
349 if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
352 q = startswith(buf, UTF8_BYTE_ORDER_MARK);
355 flags |= CONFIG_PARSE_REFUSE_BOM;
360 if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) {
361 if (flags & CONFIG_PARSE_WARN)
362 log_error("%s:%u: Continuation line too long", filename, line);
366 if (!strextend(&continuation, l, NULL)) {
367 if (flags & CONFIG_PARSE_WARN)
376 for (e = p; *e; e++) {
387 continuation = strdup(l);
389 if (flags & CONFIG_PARSE_WARN)
411 if (flags & CONFIG_PARSE_WARN)
412 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
417 continuation = mfree(continuation);
423 static int config_parse_many_files(
424 const char *conf_file,
426 const char *sections,
427 ConfigItemLookup lookup,
429 ConfigParseFlags flags,
436 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
441 STRV_FOREACH(fn, files) {
442 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
450 /* Parse each config file in the directories specified as nulstr. */
451 int config_parse_many_nulstr(
452 const char *conf_file,
453 const char *conf_file_dirs,
454 const char *sections,
455 ConfigItemLookup lookup,
457 ConfigParseFlags flags,
460 _cleanup_strv_free_ char **files = NULL;
463 r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
467 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
470 #if 0 /// UNNEEDED by elogind
471 /* Parse each config file in the directories specified as strv. */
472 int config_parse_many(
473 const char *conf_file,
474 const char* const* conf_file_dirs,
475 const char *dropin_dirname,
476 const char *sections,
477 ConfigItemLookup lookup,
479 ConfigParseFlags flags,
482 _cleanup_strv_free_ char **dropin_dirs = NULL;
483 _cleanup_strv_free_ char **files = NULL;
487 suffix = strjoina("/", dropin_dirname);
488 r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
492 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
496 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
500 #define DEFINE_PARSER(type, vartype, conv_func) \
501 int config_parse_##type( \
503 const char *filename, \
505 const char *section, \
506 unsigned section_line, \
507 const char *lvalue, \
509 const char *rvalue, \
521 r = conv_func(rvalue, i); \
523 log_syntax(unit, LOG_ERR, filename, line, r, \
524 "Failed to parse %s value, ignoring: %s", \
530 DEFINE_PARSER(int, int, safe_atoi);
531 DEFINE_PARSER(long, long, safe_atoli);
532 #if 0 /// UNNEEDED by elogind
533 DEFINE_PARSER(uint8, uint8_t, safe_atou8);
534 DEFINE_PARSER(uint16, uint16_t, safe_atou16);
535 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
537 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
538 DEFINE_PARSER(unsigned, unsigned, safe_atou);
539 DEFINE_PARSER(double, double, safe_atod);
540 #if 0 /// UNNEEDED by elogind
541 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
543 DEFINE_PARSER(sec, usec_t, parse_sec);
544 DEFINE_PARSER(mode, mode_t, parse_mode);
546 int config_parse_iec_size(const char* unit,
547 const char *filename,
550 unsigned section_line,
566 r = parse_size(rvalue, 1024, &v);
567 if (r < 0 || (uint64_t) (size_t) v != v) {
568 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
576 #if 0 /// UNNEEDED by elogind
577 int config_parse_si_size(
579 const char *filename,
582 unsigned section_line,
598 r = parse_size(rvalue, 1000, &v);
599 if (r < 0 || (uint64_t) (size_t) v != v) {
600 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
608 int config_parse_iec_uint64(
610 const char *filename,
613 unsigned section_line,
620 uint64_t *bytes = data;
628 r = parse_size(rvalue, 1024, bytes);
630 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
636 int config_parse_bool(const char* unit,
637 const char *filename,
640 unsigned section_line,
656 k = parse_boolean(rvalue);
658 log_syntax(unit, LOG_ERR, filename, line, k,
659 "Failed to parse boolean value%s: %s",
660 fatal ? "" : ", ignoring", rvalue);
661 return fatal ? -ENOEXEC : 0;
668 #if 0 /// UNNEEDED by elogind
669 int config_parse_tristate(
671 const char *filename,
674 unsigned section_line,
688 /* A tristate is pretty much a boolean, except that it can
689 * also take the special value -1, indicating "uninitialized",
690 * much like NULL is for a pointer type. */
692 k = parse_boolean(rvalue);
694 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
703 int config_parse_string(
705 const char *filename,
708 unsigned section_line,
722 if (!utf8_is_valid(rvalue)) {
723 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
741 int config_parse_path(
743 const char *filename,
746 unsigned section_line,
761 if (isempty(rvalue)) {
766 if (!utf8_is_valid(rvalue)) {
767 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
768 return fatal ? -ENOEXEC : 0;
771 if (!path_is_absolute(rvalue)) {
772 log_syntax(unit, LOG_ERR, filename, line, 0,
773 "Not an absolute path%s: %s",
774 fatal ? "" : ", ignoring", rvalue);
775 return fatal ? -ENOEXEC : 0;
782 path_kill_slashes(n);
791 int config_parse_strv(
793 const char *filename,
796 unsigned section_line,
811 if (isempty(rvalue)) {
812 *sv = strv_free(*sv);
819 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
825 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
829 if (!utf8_is_valid(word)) {
830 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
835 r = strv_consume(sv, word);
843 int config_parse_warn_compat(
845 const char *filename,
848 unsigned section_line,
854 Disabled reason = ltype;
857 case DISABLED_CONFIGURATION:
858 log_syntax(unit, LOG_DEBUG, filename, line, 0,
859 "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
861 case DISABLED_LEGACY:
862 log_syntax(unit, LOG_INFO, filename, line, 0,
863 "Support for option %s= has been removed and it is ignored", lvalue);
865 case DISABLED_EXPERIMENTAL:
866 log_syntax(unit, LOG_INFO, filename, line, 0,
867 "Support for option %s= has not yet been enabled and it is ignored", lvalue);
874 #if 0 /// UNNEEDED by elogind
875 int config_parse_log_facility(
877 const char *filename,
880 unsigned section_line,
894 x = log_facility_unshifted_from_string(rvalue);
896 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
900 *o = (x << 3) | LOG_PRI(*o);
906 int config_parse_log_level(
908 const char *filename,
911 unsigned section_line,
925 x = log_level_from_string(rvalue);
927 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
931 if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
934 *o = (*o & LOG_FACMASK) | x;
939 int config_parse_signal(
941 const char *filename,
944 unsigned section_line,
958 r = signal_from_string_try_harder(rvalue);
960 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
968 #if 0 /// UNNEEDED by elogind
969 int config_parse_personality(
971 const char *filename,
974 unsigned section_line,
981 unsigned long *personality = data, p;
989 p = PERSONALITY_INVALID;
991 p = personality_from_string(rvalue);
992 if (p == PERSONALITY_INVALID) {
993 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
1002 int config_parse_ifname(
1004 const char *filename,
1006 const char *section,
1007 unsigned section_line,
1022 if (isempty(rvalue)) {
1027 if (!ifname_valid(rvalue)) {
1028 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
1032 r = free_and_strdup(s, rvalue);
1039 int config_parse_ip_port(
1041 const char *filename,
1043 const char *section,
1044 unsigned section_line,
1060 if (isempty(rvalue)) {
1065 r = parse_ip_port(rvalue, &port);
1067 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
1076 int config_parse_join_controllers(
1078 const char *filename,
1080 const char *section,
1081 unsigned section_line,
1088 char ****ret = data;
1089 const char *whole_rvalue = rvalue;
1091 _cleanup_(strv_free_freep) char ***controllers = NULL;
1099 _cleanup_free_ char *word = NULL;
1103 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
1105 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
1111 l = strv_split(word, ",");
1116 if (strv_length(l) <= 1) {
1122 controllers = new(char**, 2);
1129 controllers[1] = NULL;
1136 t = new0(char**, n+2);
1144 for (a = controllers; *a; a++)
1145 if (strv_overlap(*a, l)) {
1146 if (strv_extend_strv(&l, *a, false) < 0) {
1165 t[n++] = strv_uniq(l);
1167 strv_free_free(controllers);
1171 if (!isempty(rvalue))
1172 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
1174 /* As a special case, return a single empty strv, to override the default */
1176 controllers = new(char**, 2);
1179 controllers[0] = strv_new(NULL, NULL);
1180 if (!controllers[0])
1182 controllers[1] = NULL;
1185 strv_free_free(*ret);