chiark / gitweb /
Prep v239: Fix conf-parser.[hc], it got a bit mangled by migration.
[elogind.git] / src / shared / conf-parser.c
index 6bd9d9ebf74476572b6cb378ac57f1c2915b9e4a..472c996423be58577a478487b86f725d9cc655b1 100644 (file)
@@ -1,86 +1,38 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+/* SPDX-License-Identifier: LGPL-2.1+ */
 
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <string.h>
-#include <stdio.h>
 #include <errno.h>
-#include <assert.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
 #include <stdlib.h>
-#include <netinet/ether.h>
+#include <string.h>
+#include <sys/types.h>
 
-#include "conf-parser.h"
+#include "alloc-util.h"
 #include "conf-files.h"
-#include "util.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"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+//#include "rlimit-util.h"
+#include "signal-util.h"
+#include "socket-util.h"
+#include "string-util.h"
 #include "strv.h"
-#include "log.h"
+#include "syslog-util.h"
+#include "time-util.h"
 #include "utf8.h"
-#include "path-util.h"
-#include "set.h"
-#include "exit-status.h"
-#include "sd-messages.h"
 
-int log_syntax_internal(
-                const char *unit,
-                int level,
-                const char *file,
-                int line,
-                const char *func,
-                const char *config_file,
-                unsigned config_line,
-                int error,
-                const char *format, ...) {
-
-        _cleanup_free_ char *msg = NULL;
-        int r;
-        va_list ap;
-
-        va_start(ap, format);
-        r = vasprintf(&msg, format, ap);
-        va_end(ap);
-        if (r < 0)
-                return log_oom();
-
-        if (unit)
-                r = log_struct_internal(level,
-                                        error > 0 ? error : EINVAL,
-                                        file, line, func,
-                                        getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
-                                        MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
-                                        "CONFIG_FILE=%s", config_file,
-                                        "CONFIG_LINE=%u", config_line,
-                                        "MESSAGE=[%s:%u] %s", config_file, config_line, msg,
-                                        NULL);
-        else
-                r = log_struct_internal(level,
-                                        error > 0 ? error : EINVAL,
-                                        file, line, func,
-                                        MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
-                                        "CONFIG_FILE=%s", config_file,
-                                        "CONFIG_LINE=%u", config_line,
-                                        "MESSAGE=[%s:%u] %s", config_file, config_line, msg,
-                                        NULL);
-
-        return r;
-}
+/// Additional includes needed by elogind
+#include "def.h"
+#include "fileio.h"
 
 int config_item_table_lookup(
                 const void *table,
@@ -139,7 +91,7 @@ int config_item_perf_lookup(
         else {
                 char *key;
 
-                key = strjoin(section, ".", lvalue, NULL);
+                key = strjoin(section, ".", lvalue);
                 if (!key)
                         return -ENOMEM;
 
@@ -157,17 +109,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;
@@ -193,29 +146,28 @@ static int next_assignment(const char *unit,
         }
 
         /* Warn about unknown non-extension fields. */
-        if (!relaxed && !startswith(lvalue, "X-"))
-                log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
-                           "Unknown lvalue '%s' in section '%s'", lvalue, section);
+        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) {
+/* Parse a single logical line */
+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;
+        char *e, *include;
 
         assert(filename);
         assert(line > 0);
@@ -223,14 +175,14 @@ static int parse_line(const char* unit,
         assert(l);
 
         l = strstrip(l);
-
         if (!*l)
                 return 0;
 
         if (strchr(COMMENTS "\n", *l))
                 return 0;
 
-        if (startswith(l, ".include ")) {
+        include = first_word(l, ".include");
+        if (include) {
                 _cleanup_free_ char *fn = NULL;
 
                 /* .includes are a bad idea, we only support them here
@@ -242,19 +194,25 @@ static int parse_line(const char* unit,
                  *
                  * Support for them should be eventually removed. */
 
-                if (!allow_include) {
-                        log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
-                                   ".include not allowed here. Ignoring.");
+                if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
                         return 0;
                 }
 
-                fn = file_in_same_dir(filename, strstrip(l+9));
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           ".include directives are deprecated, and support for them will be removed in a future version of elogind. "
+                           "Please use drop-in files instead.");
+
+                fn = file_in_same_dir(filename, strstrip(include));
                 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 (!utf8_is_valid(l))
+                return log_syntax_invalid_utf8(unit, LOG_WARNING, filename, line, l);
+
         if (*l == '[') {
                 size_t k;
                 char *n;
@@ -263,8 +221,7 @@ static int parse_line(const char* unit,
                 assert(k > 0);
 
                 if (l[k-1] != ']') {
-                        log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
-                                   "Invalid section header '%s'", l);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
                         return -EBADMSG;
                 }
 
@@ -274,18 +231,15 @@ static int parse_line(const char* unit,
 
                 if (sections && !nulstr_contains(sections, n)) {
 
-                        if (!relaxed && !startswith(n, "X-"))
-                                log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
-                                           "Unknown section '%s'. Ignoring.", n);
+                        if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
+                                log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
 
                         free(n);
-                        free(*section);
-                        *section = NULL;
+                        *section = mfree(*section);
                         *section_line = 0;
                         *section_ignored = true;
                 } else {
-                        free(*section);
-                        *section = n;
+                        free_and_replace(*section, n);
                         *section_line = line;
                         *section_ignored = false;
                 }
@@ -295,17 +249,16 @@ static int parse_line(const char* unit,
 
         if (sections && !*section) {
 
-                if (!relaxed && !*section_ignored)
-                        log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
-                                   "Assignment outside of section. Ignoring.");
+                if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
 
                 return 0;
         }
 
         e = strchr(l, '=');
         if (!e) {
-                log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='.");
-                return -EBADMSG;
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
+                return -EINVAL;
         }
 
         *e = 0;
@@ -320,7 +273,7 @@ static int parse_line(const char* unit,
                                *section_line,
                                strstrip(l),
                                strstrip(e),
-                               relaxed,
+                               flags,
                                userdata);
 }
 
@@ -331,9 +284,7 @@ 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;
@@ -350,40 +301,61 @@ 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)
-                                log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
-                                         "Failed to open configuration file '%s': %m", filename);
+                        if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
+                                log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
+                                               "Failed to open configuration file '%s': %m", filename);
                         return errno == ENOENT ? 0 : -errno;
                 }
         }
 
         fd_warn_permissions(filename, fileno(f));
 
-        while (!feof(f)) {
-                char l[LINE_MAX], *p, *c = NULL, *e;
+        for (;;) {
+                _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(l, sizeof(l), 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);
 
-                        log_error("Failed to read configuration file '%s': %m", filename);
-                        return -errno;
+                        return r;
                 }
 
-                truncate_nl(l);
+                l = buf;
+                if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
+                        char *q;
+
+                        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;
                         }
 
-                        free(continuation);
-                        continuation = NULL;
-                        p = c;
+                        p = continuation;
                 } else
                         p = l;
 
@@ -397,12 +369,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;
                                 }
@@ -417,19 +387,37 @@ 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 (flags & CONFIG_PARSE_WARN)
+                                log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
+                        return r;
+                }
+
+                continuation = mfree(continuation);
+        }
 
+        if (continuation) {
+                r = parse_line(unit,
+                               filename,
+                               ++line,
+                               sections,
+                               lookup,
+                               table,
+                               flags,
+                               &section,
+                               &section_line,
+                               &section_ignored,
+                               continuation,
+                               userdata);
                 if (r < 0) {
-                        if (warn)
-                                log_warning("Failed to parse file '%s': %s",
-                                            filename, strerror(-r));
+                        if (flags & CONFIG_PARSE_WARN)
+                                log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
                         return r;
                 }
         }
@@ -437,30 +425,26 @@ int config_parse(const char *unit,
         return 0;
 }
 
-/* Parse each config file in the specified directories. */
-int config_parse_many(const char *conf_file,
-                      const char *conf_file_dirs,
-                      const char *sections,
-                      ConfigItemLookup lookup,
-                      const void *table,
-                      bool relaxed,
-                      void *userdata) {
-        _cleanup_strv_free_ char **files = NULL;
+static int config_parse_many_files(
+                const char *conf_file,
+                char **files,
+                const char *sections,
+                ConfigItemLookup lookup,
+                const void *table,
+                ConfigParseFlags flags,
+                void *userdata) {
+
         char **fn;
         int r;
 
-        r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
-        if (r < 0)
-                return 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;
         }
@@ -468,43 +452,76 @@ int config_parse_many(const char *conf_file,
         return 0;
 }
 
-#define DEFINE_PARSER(type, vartype, conv_func)                         \
-        int config_parse_##type(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) {                       \
-                                                                        \
-                vartype *i = data;                                      \
-                int r;                                                  \
-                                                                        \
-                assert(filename);                                       \
-                assert(lvalue);                                         \
-                assert(rvalue);                                         \
-                assert(data);                                           \
-                                                                        \
-                r = conv_func(rvalue, i);                               \
-                if (r < 0)                                              \
-                        log_syntax(unit, LOG_ERR, filename, line, -r,   \
-                                   "Failed to parse %s value, ignoring: %s", \
-                                   #vartype, rvalue);                   \
-                                                                        \
-                return 0;                                               \
-        }
-
-DEFINE_PARSER(int, int, safe_atoi)
-DEFINE_PARSER(long, long, safe_atoli)
-DEFINE_PARSER(uint64, uint64_t, safe_atou64)
-DEFINE_PARSER(unsigned, unsigned, safe_atou)
-DEFINE_PARSER(double, double, safe_atod)
-DEFINE_PARSER(nsec, nsec_t, parse_nsec)
-DEFINE_PARSER(sec, usec_t, parse_sec)
+/* Parse each config file in the directories specified as nulstr. */
+int config_parse_many_nulstr(
+                const char *conf_file,
+                const char *conf_file_dirs,
+                const char *sections,
+                ConfigItemLookup lookup,
+                const void *table,
+                ConfigParseFlags flags,
+                void *userdata) {
+
+        _cleanup_strv_free_ char **files = NULL;
+        int r;
+
+        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, flags, userdata);
+}
+
+#if 0 /// UNNEEDED by elogind
+/* Parse each config file in the directories specified as strv. */
+int config_parse_many(
+                const char *conf_file,
+                const char* const* conf_file_dirs,
+                const char *dropin_dirname,
+                const char *sections,
+                ConfigItemLookup lookup,
+                const void *table,
+                ConfigParseFlags flags,
+                void *userdata) {
+
+        _cleanup_strv_free_ char **dropin_dirs = NULL;
+        _cleanup_strv_free_ char **files = NULL;
+        const char *suffix;
+        int r;
+
+        suffix = strjoina("/", dropin_dirname);
+        r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
+        if (r < 0)
+                return r;
+
+        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, flags, userdata);
+}
+#endif // 0
 
+#define DEFINE_PARSER(type, vartype, conv_func)                         \
+        DEFINE_CONFIG_PARSE_PTR(config_parse_##type, conv_func, vartype, "Failed to parse " #type " value")
+
+DEFINE_PARSER(int, int, safe_atoi);
+DEFINE_PARSER(long, long, safe_atoli);
+#if 0 /// UNNEEDED by elogind
+DEFINE_PARSER(uint8, uint8_t, safe_atou8);
+DEFINE_PARSER(uint16, uint16_t, safe_atou16);
+DEFINE_PARSER(uint32, uint32_t, safe_atou32);
+#endif // 0
+DEFINE_PARSER(uint64, uint64_t, safe_atou64);
+DEFINE_PARSER(unsigned, unsigned, safe_atou);
+DEFINE_PARSER(double, double, safe_atod);
+#if 0 /// UNNEEDED by elogind
+DEFINE_PARSER(nsec, nsec_t, parse_nsec);
+#endif // 0
+DEFINE_PARSER(sec, usec_t, parse_sec);
+DEFINE_PARSER(mode, mode_t, parse_mode);
+
+#if 0 /// UNNEEDED by elogind
 int config_parse_iec_size(const char* unit,
                             const char *filename,
                             unsigned line,
@@ -517,7 +534,7 @@ int config_parse_iec_size(const char* unit,
                             void *userdata) {
 
         size_t *sz = data;
-        off_t o;
+        uint64_t v;
         int r;
 
         assert(filename);
@@ -525,29 +542,30 @@ int config_parse_iec_size(const char* unit,
         assert(rvalue);
         assert(data);
 
-        r = parse_size(rvalue, 1024, &o);
-        if (r < 0 || (off_t) (size_t) o != o) {
-                log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
+        r = parse_size(rvalue, 1024, &v);
+        if (r < 0 || (uint64_t) (size_t) v != v) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
                 return 0;
         }
 
-        *sz = (size_t) o;
+        *sz = (size_t) v;
         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;
-        off_t o;
+        uint64_t v;
         int r;
 
         assert(filename);
@@ -555,28 +573,29 @@ int config_parse_si_size(const char* unit,
         assert(rvalue);
         assert(data);
 
-        r = parse_size(rvalue, 1000, &o);
-        if (r < 0 || (off_t) (size_t) o != o) {
-                log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
+        r = parse_size(rvalue, 1000, &v);
+        if (r < 0 || (uint64_t) (size_t) v != v) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
                 return 0;
         }
 
-        *sz = (size_t) o;
+        *sz = (size_t) v;
         return 0;
 }
 
-int config_parse_iec_off(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) {
-
-        off_t *bytes = data;
+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;
 
         assert(filename);
@@ -584,14 +603,13 @@ int config_parse_iec_off(const char* unit,
         assert(rvalue);
         assert(data);
 
-        assert_cc(sizeof(off_t) == sizeof(uint64_t));
-
         r = parse_size(rvalue, 1024, bytes);
         if (r < 0)
-                log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
 
         return 0;
 }
+#endif // 0
 
 int config_parse_bool(const char* unit,
                       const char *filename,
@@ -606,6 +624,7 @@ int config_parse_bool(const char* unit,
 
         int k;
         bool *b = data;
+        bool fatal = ltype;
 
         assert(filename);
         assert(lvalue);
@@ -614,17 +633,19 @@ 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;
+        *b = k;
         return 0;
 }
 
-int config_parse_string(
-                const char *unit,
+#if 0 /// UNNEEDED by elogind
+int config_parse_tristate(
+                const char* unit,
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -635,32 +656,54 @@ int config_parse_string(
                 void *data,
                 void *userdata) {
 
-        char **s = data, *n;
+        int k, *t = data;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if (!utf8_is_valid(rvalue)) {
-                log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+        /* A tristate is pretty much a boolean, except that it can
+         * also take the special value -1, indicating "uninitialized",
+         * much like NULL is for a pointer type. */
+
+        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;
         }
 
-        if (isempty(rvalue))
-                n = NULL;
-        else {
-                n = strdup(rvalue);
-                if (!n)
-                        return log_oom();
-        }
+        *t = !!k;
+        return 0;
+}
+#endif // 0
+
+int config_parse_string(
+                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) {
 
-        free(*s);
-        *s = n;
+        char **s = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (free_and_strdup(s, empty_to_null(rvalue)) < 0)
+                return log_oom();
 
         return 0;
 }
 
+#if 0 /// UNNEEDED by elogind
 int config_parse_path(
                 const char *unit,
                 const char *filename,
@@ -673,49 +716,45 @@ int config_parse_path(
                 void *data,
                 void *userdata) {
 
-        char **s = data, *n;
+        _cleanup_free_ char *n = NULL;
+        bool fatal = ltype;
+        char **s = data;
+        int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if (!utf8_is_valid(rvalue)) {
-                log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
-                return 0;
-        }
-
-        if (!path_is_absolute(rvalue)) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
-                return 0;
-        }
+        if (isempty(rvalue))
+                goto finalize;
 
         n = strdup(rvalue);
         if (!n)
                 return log_oom();
 
-        path_kill_slashes(n);
-
-        free(*s);
-        *s = n;
+        r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE | (fatal ? PATH_CHECK_FATAL : 0), unit, filename, line, lvalue);
+        if (r < 0)
+                return fatal ? -ENOEXEC : 0;
 
-        return 0;
+finalize:
+        return free_and_replace(*s, n);
 }
+#endif // 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;
-        const char *word, *state;
-        size_t l;
         int r;
 
         assert(filename);
@@ -724,80 +763,64 @@ 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 = strv_new(NULL, NULL);
-                if (!empty)
-                        return log_oom();
-
-                strv_free(*sv);
-                *sv = empty;
+                *sv = strv_free(*sv);
                 return 0;
         }
 
-        FOREACH_WORD_QUOTED(word, l, rvalue, state) {
-                char *n;
+        for (;;) {
+                char *word = NULL;
 
-                n = strndup(word, l);
-                if (!n)
+                r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
+                if (r == 0)
+                        break;
+                if (r == -ENOMEM)
                         return log_oom();
-
-                if (!utf8_is_valid(n)) {
-                        log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
-                        free(n);
-                        continue;
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+                        break;
                 }
 
-                r = strv_consume(sv, n);
+                r = strv_consume(sv, word);
                 if (r < 0)
                         return log_oom();
         }
-        if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
 
         return 0;
 }
 
-int config_parse_mode(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) {
+#if 0 /// UNNEEDED by elogind
+int config_parse_warn_compat(
+                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) {
 
-        mode_t *m = data;
-        long l;
-        char *x = NULL;
+        Disabled reason = ltype;
 
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
+        switch(reason) {
 
-        errno = 0;
-        l = strtol(rvalue, &x, 8);
-        if (!x || x == rvalue || *x || errno) {
-                log_syntax(unit, LOG_ERR, filename, line, errno,
-                           "Failed to parse mode value, ignoring: %s", rvalue);
-                return 0;
-        }
+        case DISABLED_CONFIGURATION:
+                log_syntax(unit, LOG_DEBUG, filename, line, 0,
+                           "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
+                break;
 
-        if (l < 0000 || l > 07777) {
-                log_syntax(unit, LOG_ERR, filename, line, ERANGE,
-                           "Mode value out of range, ignoring: %s", rvalue);
-                return 0;
+        case DISABLED_LEGACY:
+                log_syntax(unit, LOG_INFO, filename, line, 0,
+                           "Support for option %s= has been removed and it is ignored", lvalue);
+                break;
+
+        case DISABLED_EXPERIMENTAL:
+                log_syntax(unit, LOG_INFO, filename, line, 0,
+                           "Support for option %s= has not yet been enabled and it is ignored", lvalue);
+                break;
         }
 
-        *m = (mode_t) l;
         return 0;
 }
 
@@ -813,7 +836,6 @@ int config_parse_log_facility(
                 void *data,
                 void *userdata) {
 
-
         int *o = data, x;
 
         assert(filename);
@@ -823,8 +845,7 @@ int config_parse_log_facility(
 
         x = log_facility_unshifted_from_string(rvalue);
         if (x < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Failed to parse log facility, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -845,7 +866,6 @@ int config_parse_log_level(
                 void *data,
                 void *userdata) {
 
-
         int *o = data, x;
 
         assert(filename);
@@ -855,11 +875,371 @@ int config_parse_log_level(
 
         x = log_level_from_string(rvalue);
         if (x < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Failed to parse log level, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
+                *o = x;
+        else
+                *o = (*o & LOG_FACMASK) | x;
+
+        return 0;
+}
+
+int config_parse_signal(
+                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 *sig = data, r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(sig);
+
+        r = signal_from_string(rvalue);
+        if (r <= 0) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        *sig = r;
+        return 0;
+}
+
+int config_parse_personality(
+                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) {
+
+        unsigned long *personality = data, p;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(personality);
+
+        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;
+        return 0;
+}
+
+int config_parse_ifname(
+                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 **s = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                *s = mfree(*s);
+                return 0;
+        }
+
+        if (!ifname_valid(rvalue)) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        r = free_and_strdup(s, rvalue);
+        if (r < 0)
+                return log_oom();
+
+        return 0;
+}
+
+int config_parse_ip_port(
+                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) {
+
+        uint16_t *s = data;
+        uint16_t port;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                *s = 0;
+                return 0;
+        }
+
+        r = parse_ip_port(rvalue, &port);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
+                return 0;
+        }
+
+        *s = port;
+
+        return 0;
+}
+
+int config_parse_join_controllers(
+                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 ****ret = data;
+        const char *whole_rvalue = rvalue;
+        unsigned n = 0;
+        _cleanup_(strv_free_freep) char ***controllers = NULL;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(ret);
+
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
+                char **l;
+                int r;
+
+                r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
+                        return r;
+                }
+                if (r == 0)
+                        break;
+
+                l = strv_split(word, ",");
+                if (!l)
+                        return log_oom();
+                strv_uniq(l);
+
+                if (strv_length(l) <= 1) {
+                        strv_free(l);
+                        continue;
+                }
+
+                if (!controllers) {
+                        controllers = new(char**, 2);
+                        if (!controllers) {
+                                strv_free(l);
+                                return log_oom();
+                        }
+
+                        controllers[0] = l;
+                        controllers[1] = NULL;
+
+                        n = 1;
+                } else {
+                        char ***a;
+                        char ***t;
+
+                        t = new0(char**, n+2);
+                        if (!t) {
+                                strv_free(l);
+                                return log_oom();
+                        }
+
+                        n = 0;
+
+                        for (a = controllers; *a; a++)
+                                if (strv_overlap(*a, l)) {
+                                        if (strv_extend_strv(&l, *a, false) < 0) {
+                                                strv_free(l);
+                                                strv_free_free(t);
+                                                return log_oom();
+                                        }
+
+                                } else {
+                                        char **c;
+
+                                        c = strv_copy(*a);
+                                        if (!c) {
+                                                strv_free(l);
+                                                strv_free_free(t);
+                                                return log_oom();
+                                        }
+
+                                        t[n++] = c;
+                                }
+
+                        t[n++] = strv_uniq(l);
+
+                        strv_free_free(controllers);
+                        controllers = t;
+                }
+        }
+        if (!isempty(rvalue))
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
+
+        /* As a special case, return a single empty strv, to override the default */
+        if (!controllers) {
+                controllers = new(char**, 2);
+                if (!controllers)
+                        return log_oom();
+                controllers[0] = strv_new(NULL, NULL);
+                if (!controllers[0])
+                        return log_oom();
+                controllers[1] = NULL;
+        }
+
+        strv_free_free(*ret);
+        *ret = TAKE_PTR(controllers);
+
+        return 0;
+}
+x
+int config_parse_mtu(
+                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) {
+
+        uint32_t *mtu = data;
+        int r;
+
+        assert(rvalue);
+        assert(mtu);
+
+        r = parse_mtu(ltype, rvalue, mtu);
+        if (r == -ERANGE) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Maximum transfer unit (MTU) value out of range. Permitted range is %" PRIu32 "…%" PRIu32 ", ignoring: %s",
+                           (uint32_t) (ltype == AF_INET6 ? IPV6_MIN_MTU : IPV4_MIN_MTU), (uint32_t) UINT32_MAX,
+                           rvalue);
+                return 0;
+        }
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse MTU value '%s', ignoring: %m", rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
+int config_parse_rlimit(
+                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) {
+
+        struct rlimit **rl = data, d = {};
+        int r;
+
+        assert(rvalue);
+        assert(rl);
+
+        r = rlimit_parse(ltype, rvalue, &d);
+        if (r == -EILSEQ) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue);
+                return 0;
+        }
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (rl[ltype])
+                *rl[ltype] = d;
+        else {
+                rl[ltype] = newdup(struct rlimit, &d, 1);
+                if (!rl[ltype])
+                        return log_oom();
+        }
+
+        return 0;
+}
+
+int config_parse_permille(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) {
+
+        unsigned *permille = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(permille);
+
+        r = parse_permille(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse permille value, ignoring: %s", rvalue);
                 return 0;
         }
 
-        *o = (*o & LOG_FACMASK) | x;
+        *permille = (unsigned) r;
+
         return 0;
 }
+#endif // 0