]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/conf-parser.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / shared / conf-parser.c
index 44df7493e2ba9979e84a9c8e20656ab2588abbcf..daddb7cf20b20b42b9d210160921c01c9c98f18c 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
 #include "alloc-util.h"
 #include "conf-files.h"
 #include "conf-parser.h"
+#include "def.h"
 #include "extract-word.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "fs-util.h"
 #include "log.h"
 #include "macro.h"
@@ -119,17 +122,18 @@ int config_item_perf_lookup(
 }
 
 /* Run the user supplied parser for an assignment */
-static int next_assignment(const char *unit,
-                           const char *filename,
-                           unsigned line,
-                           ConfigItemLookup lookup,
-                           const void *table,
-                           const char *section,
-                           unsigned section_line,
-                           const char *lvalue,
-                           const char *rvalue,
-                           bool relaxed,
-                           void *userdata) {
+static int next_assignment(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                ConfigItemLookup lookup,
+                const void *table,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                const char *rvalue,
+                ConfigParseFlags flags,
+                void *userdata) {
 
         ConfigParserCallback func = NULL;
         int ltype = 0;
@@ -155,26 +159,26 @@ static int next_assignment(const char *unit,
         }
 
         /* Warn about unknown non-extension fields. */
-        if (!relaxed && !startswith(lvalue, "X-"))
+        if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-"))
                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section);
 
         return 0;
 }
 
 /* Parse a variable assignment line */
-static int parse_line(const char* unit,
-                      const char *filename,
-                      unsigned line,
-                      const char *sections,
-                      ConfigItemLookup lookup,
-                      const void *table,
-                      bool relaxed,
-                      bool allow_include,
-                      char **section,
-                      unsigned *section_line,
-                      bool *section_ignored,
-                      char *l,
-                      void *userdata) {
+static int parse_line(
+                const char* unit,
+                const char *filename,
+                unsigned line,
+                const char *sections,
+                ConfigItemLookup lookup,
+                const void *table,
+                ConfigParseFlags flags,
+                char **section,
+                unsigned *section_line,
+                bool *section_ignored,
+                char *l,
+                void *userdata) {
 
         char *e;
 
@@ -184,7 +188,6 @@ static int parse_line(const char* unit,
         assert(l);
 
         l = strstrip(l);
-
         if (!*l)
                 return 0;
 
@@ -203,7 +206,7 @@ static int parse_line(const char* unit,
                  *
                  * Support for them should be eventually removed. */
 
-                if (!allow_include) {
+                if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) {
                         log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
                         return 0;
                 }
@@ -212,7 +215,7 @@ static int parse_line(const char* unit,
                 if (!fn)
                         return -ENOMEM;
 
-                return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata);
+                return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata);
         }
 
         if (*l == '[') {
@@ -233,7 +236,7 @@ static int parse_line(const char* unit,
 
                 if (sections && !nulstr_contains(sections, n)) {
 
-                        if (!relaxed && !startswith(n, "X-"))
+                        if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
                                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
 
                         free(n);
@@ -252,7 +255,7 @@ static int parse_line(const char* unit,
 
         if (sections && !*section) {
 
-                if (!relaxed && !*section_ignored)
+                if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
 
                 return 0;
@@ -276,7 +279,7 @@ static int parse_line(const char* unit,
                                *section_line,
                                strstrip(l),
                                strstrip(e),
-                               relaxed,
+                               flags,
                                userdata);
 }
 
@@ -287,15 +290,13 @@ int config_parse(const char *unit,
                  const char *sections,
                  ConfigItemLookup lookup,
                  const void *table,
-                 bool relaxed,
-                 bool allow_include,
-                 bool warn,
+                 ConfigParseFlags flags,
                  void *userdata) {
 
         _cleanup_free_ char *section = NULL, *continuation = NULL;
         _cleanup_fclose_ FILE *ours = NULL;
         unsigned line = 0, section_line = 0;
-        bool section_ignored = false, allow_bom = true;
+        bool section_ignored = false;
         int r;
 
         assert(filename);
@@ -306,7 +307,7 @@ int config_parse(const char *unit,
                 if (!f) {
                         /* Only log on request, except for ENOENT,
                          * since we return 0 to the caller. */
-                        if (warn || errno == ENOENT)
+                        if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
                                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
                                          "Failed to open configuration file '%s': %m", filename);
                         return errno == ENOENT ? 0 : -errno;
@@ -316,33 +317,51 @@ int config_parse(const char *unit,
         fd_warn_permissions(filename, fileno(f));
 
         for (;;) {
-                char buf[LINE_MAX], *l, *p, *c = NULL, *e;
+                _cleanup_free_ char *buf = NULL;
                 bool escaped = false;
+                char *l, *p, *e;
+
+                r = read_line(f, LONG_LINE_MAX, &buf);
+                if (r == 0)
+                        break;
+                if (r == -ENOBUFS) {
+                        if (flags & CONFIG_PARSE_WARN)
+                                log_error_errno(r, "%s:%u: Line too long", filename, line);
 
-                if (!fgets(buf, sizeof buf, f)) {
-                        if (feof(f))
-                                break;
+                        return r;
+                }
+                if (r < 0) {
+                        if (CONFIG_PARSE_WARN)
+                                log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
 
-                        return log_error_errno(errno, "Failed to read configuration file '%s': %m", filename);
+                        return r;
                 }
 
                 l = buf;
-                if (allow_bom && startswith(l, UTF8_BYTE_ORDER_MARK))
-                        l += strlen(UTF8_BYTE_ORDER_MARK);
-                allow_bom = false;
+                if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
+                        char *q;
 
-                truncate_nl(l);
+                        q = startswith(buf, UTF8_BYTE_ORDER_MARK);
+                        if (q) {
+                                l = q;
+                                flags |= CONFIG_PARSE_REFUSE_BOM;
+                        }
+                }
 
                 if (continuation) {
-                        c = strappend(continuation, l);
-                        if (!c) {
-                                if (warn)
+                        if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) {
+                                if (flags & CONFIG_PARSE_WARN)
+                                        log_error("%s:%u: Continuation line too long", filename, line);
+                                return -ENOBUFS;
+                        }
+
+                        if (!strextend(&continuation, l, NULL)) {
+                                if (flags & CONFIG_PARSE_WARN)
                                         log_oom();
                                 return -ENOMEM;
                         }
 
-                        continuation = mfree(continuation);
-                        p = c;
+                        p = continuation;
                 } else
                         p = l;
 
@@ -356,12 +375,10 @@ int config_parse(const char *unit,
                 if (escaped) {
                         *(e-1) = ' ';
 
-                        if (c)
-                                continuation = c;
-                        else {
+                        if (!continuation) {
                                 continuation = strdup(l);
                                 if (!continuation) {
-                                        if (warn)
+                                        if (flags & CONFIG_PARSE_WARN)
                                                 log_oom();
                                         return -ENOMEM;
                                 }
@@ -376,21 +393,20 @@ int config_parse(const char *unit,
                                sections,
                                lookup,
                                table,
-                               relaxed,
-                               allow_include,
+                               flags,
                                &section,
                                &section_line,
                                &section_ignored,
                                p,
                                userdata);
-                free(c);
-
                 if (r < 0) {
-                        if (warn)
-                                log_warning_errno(r, "Failed to parse file '%s': %m",
-                                                  filename);
+                        if (flags & CONFIG_PARSE_WARN)
+                                log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
                         return r;
+
                 }
+
+                continuation = mfree(continuation);
         }
 
         return 0;
@@ -402,20 +418,20 @@ static int config_parse_many_files(
                 const char *sections,
                 ConfigItemLookup lookup,
                 const void *table,
-                bool relaxed,
+                ConfigParseFlags flags,
                 void *userdata) {
 
         char **fn;
         int r;
 
         if (conf_file) {
-                r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
+                r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
                 if (r < 0)
                         return r;
         }
 
         STRV_FOREACH(fn, files) {
-                r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata);
+                r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
                 if (r < 0)
                         return r;
         }
@@ -430,18 +446,17 @@ int config_parse_many_nulstr(
                 const char *sections,
                 ConfigItemLookup lookup,
                 const void *table,
-                bool relaxed,
+                ConfigParseFlags flags,
                 void *userdata) {
 
         _cleanup_strv_free_ char **files = NULL;
         int r;
 
-        r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
+        r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
         if (r < 0)
                 return r;
 
-        return config_parse_many_files(conf_file, files,
-                                       sections, lookup, table, relaxed, userdata);
+        return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
 }
 
 /* Parse each config file in the directories specified as strv. */
@@ -452,7 +467,7 @@ int config_parse_many(
                 const char *sections,
                 ConfigItemLookup lookup,
                 const void *table,
-                bool relaxed,
+                ConfigParseFlags flags,
                 void *userdata) {
 
         _cleanup_strv_free_ char **dropin_dirs = NULL;
@@ -465,12 +480,11 @@ int config_parse_many(
         if (r < 0)
                 return r;
 
-        r = conf_files_list_strv(&files, ".conf", NULL, (const char* const*) dropin_dirs);
+        r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
         if (r < 0)
                 return r;
 
-        return config_parse_many_files(conf_file, files,
-                                       sections, lookup, table, relaxed, userdata);
+        return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
 }
 
 #define DEFINE_PARSER(type, vartype, conv_func)                         \
@@ -546,16 +560,17 @@ int config_parse_iec_size(const char* unit,
         return 0;
 }
 
-int config_parse_si_size(const char* unit,
-                            const char *filename,
-                            unsigned line,
-                            const char *section,
-                            unsigned section_line,
-                            const char *lvalue,
-                            int ltype,
-                            const char *rvalue,
-                            void *data,
-                            void *userdata) {
+int config_parse_si_size(
+                const char* unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         size_t *sz = data;
         uint64_t v;
@@ -576,16 +591,17 @@ int config_parse_si_size(const char* unit,
         return 0;
 }
 
-int config_parse_iec_uint64(const char* unit,
-                           const char *filename,
-                           unsigned line,
-                           const char *section,
-                           unsigned section_line,
-                           const char *lvalue,
-                           int ltype,
-                           const char *rvalue,
-                           void *data,
-                           void *userdata) {
+int config_parse_iec_uint64(
+                const char* unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         uint64_t *bytes = data;
         int r;
@@ -615,6 +631,7 @@ int config_parse_bool(const char* unit,
 
         int k;
         bool *b = data;
+        bool fatal = ltype;
 
         assert(filename);
         assert(lvalue);
@@ -623,8 +640,10 @@ int config_parse_bool(const char* unit,
 
         k = parse_boolean(rvalue);
         if (k < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
-                return 0;
+                log_syntax(unit, LOG_ERR, filename, line, k,
+                           "Failed to parse boolean value%s: %s",
+                           fatal ? "" : ", ignoring", rvalue);
+                return fatal ? -ENOEXEC : 0;
         }
 
         *b = !!k;
@@ -715,20 +734,28 @@ int config_parse_path(
                 void *userdata) {
 
         char **s = data, *n;
+        bool fatal = ltype;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
+        if (isempty(rvalue)) {
+                n = NULL;
+                goto finalize;
+        }
+
         if (!utf8_is_valid(rvalue)) {
                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
-                return 0;
+                return fatal ? -ENOEXEC : 0;
         }
 
         if (!path_is_absolute(rvalue)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", rvalue);
-                return 0;
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "Not an absolute path%s: %s",
+                           fatal ? "" : ", ignoring", rvalue);
+                return fatal ? -ENOEXEC : 0;
         }
 
         n = strdup(rvalue);
@@ -737,22 +764,24 @@ int config_parse_path(
 
         path_kill_slashes(n);
 
+finalize:
         free(*s);
         *s = n;
 
         return 0;
 }
 
-int config_parse_strv(const char *unit,
-                      const char *filename,
-                      unsigned line,
-                      const char *section,
-                      unsigned section_line,
-                      const char *lvalue,
-                      int ltype,
-                      const char *rvalue,
-                      void *data,
-                      void *userdata) {
+int config_parse_strv(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         char ***sv = data;
         int r;
@@ -763,19 +792,7 @@ int config_parse_strv(const char *unit,
         assert(data);
 
         if (isempty(rvalue)) {
-                char **empty;
-
-                /* Empty assignment resets the list. As a special rule
-                 * we actually fill in a real empty array here rather
-                 * than NULL, since some code wants to know if
-                 * something was set at all... */
-                empty = new0(char*, 1);
-                if (!empty)
-                        return log_oom();
-
-                strv_free(*sv);
-                *sv = empty;
-
+                *sv = strv_free(*sv);
                 return 0;
         }
 
@@ -797,6 +814,7 @@ int config_parse_strv(const char *unit,
                         free(word);
                         continue;
                 }
+
                 r = strv_consume(sv, word);
                 if (r < 0)
                         return log_oom();
@@ -817,7 +835,6 @@ int config_parse_log_facility(
                 void *data,
                 void *userdata) {
 
-
         int *o = data, x;
 
         assert(filename);
@@ -848,7 +865,6 @@ int config_parse_log_level(
                 void *data,
                 void *userdata) {
 
-
         int *o = data, x;
 
         assert(filename);
@@ -862,7 +878,11 @@ int config_parse_log_level(
                 return 0;
         }
 
-        *o = (*o & LOG_FACMASK) | x;
+        if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
+                *o = x;
+        else
+                *o = (*o & LOG_FACMASK) | x;
+
         return 0;
 }
 
@@ -914,10 +934,14 @@ int config_parse_personality(
         assert(rvalue);
         assert(personality);
 
-        p = personality_from_string(rvalue);
-        if (p == PERSONALITY_INVALID) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
-                return 0;
+        if (isempty(rvalue))
+                p = PERSONALITY_INVALID;
+        else {
+                p = personality_from_string(rvalue);
+                if (p == PERSONALITY_INVALID) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
+                        return 0;
+                }
         }
 
         *personality = p;