]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/load-fragment.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[thirdparty/systemd.git] / src / core / load-fragment.c
index 00cc6f7373db4eb128b44a02d5c4ae5f720b6a8a..f5c28e514f879c053b1a6bb4202be2968c769c17 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <linux/oom.h>
 #include <errno.h>
-#include <string.h>
 #include <fcntl.h>
-#include <sched.h>
 #include <linux/fs.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-
+#include <linux/oom.h>
 #ifdef HAVE_SECCOMP
 #include <seccomp.h>
 #endif
+#include <sched.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
 
-#include "unit.h"
-#include "strv.h"
+#include "af-list.h"
+#include "bus-error.h"
+#include "bus-internal.h"
+#include "bus-util.h"
+#include "cap-list.h"
+#include "cgroup.h"
 #include "conf-parser.h"
+#include "cpu-set-util.h"
+#include "env-util.h"
+#include "errno-list.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "ioprio.h"
 #include "load-fragment.h"
 #include "log.h"
-#include "ioprio.h"
-#include "securebits.h"
 #include "missing.h"
-#include "unit-name.h"
-#include "unit-printf.h"
-#include "utf8.h"
+#include "parse-util.h"
 #include "path-util.h"
-#include "env-util.h"
-#include "cgroup.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "errno-list.h"
-#include "af-list.h"
-#include "cap-list.h"
-#include "signal-util.h"
-#include "bus-internal.h"
-
 #ifdef HAVE_SECCOMP
 #include "seccomp-util.h"
 #endif
+#include "securebits.h"
+#include "signal-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "unit-printf.h"
+#include "unit.h"
+#include "utf8.h"
 
 int config_parse_warn_compat(
                 const char *unit,
@@ -74,15 +77,15 @@ int config_parse_warn_compat(
 
         switch(reason) {
         case DISABLED_CONFIGURATION:
-                log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
+                log_syntax(unit, LOG_DEBUG, filename, line, 0,
                            "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
                 break;
         case DISABLED_LEGACY:
-                log_syntax(unit, LOG_INFO, filename, line, EINVAL,
+                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, EINVAL,
+                log_syntax(unit, LOG_INFO, filename, line, 0,
                            "Support for option %s= has not yet been enabled and it is ignored", lvalue);
                 break;
         };
@@ -120,18 +123,16 @@ int config_parse_unit_deps(const char *unit,
 
                 r = unit_name_printf(u, t, &k);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
-                                   "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
                         continue;
                 }
 
                 r = unit_add_dependency_by_name(u, d, k, NULL, true);
                 if (r < 0)
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
-                                   "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring.");
 
         return 0;
 }
@@ -166,16 +167,17 @@ int config_parse_unit_string_printf(
         return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
 }
 
-int config_parse_unit_strv_printf(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_unit_strv_printf(
+                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) {
 
         Unit *u = userdata;
         _cleanup_free_ char *k = NULL;
@@ -187,24 +189,25 @@ int config_parse_unit_strv_printf(const char *unit,
         assert(u);
 
         r = unit_full_printf(u, rvalue, &k);
-        if (r < 0)
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+                return 0;
+        }
 
-        return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
-                                 k ? k : rvalue, data, userdata);
+        return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
 }
 
-int config_parse_unit_path_printf(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_unit_path_printf(
+                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) {
 
         _cleanup_free_ char *k = NULL;
         Unit *u = userdata;
@@ -217,7 +220,7 @@ int config_parse_unit_path_printf(const char *unit,
 
         r = unit_full_printf(u, rvalue, &k);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
                 return 0;
         }
 
@@ -256,17 +259,17 @@ int config_parse_unit_path_strv_printf(
 
                 r = unit_full_printf(u, t, &k);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", t, strerror(-r));
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", t);
                         return 0;
                 }
 
                 if (!utf8_is_valid(k)) {
-                        log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+                        log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
                         return 0;
                 }
 
                 if (!path_is_absolute(k)) {
-                        log_syntax(unit, LOG_ERR, filename, line, -r, "Symlink path %s is not absolute, ignoring: %s", k, strerror(-r));
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Symlink path %s is not absolute, ignoring: %m", k);
                         return 0;
                 }
 
@@ -279,7 +282,7 @@ int config_parse_unit_path_strv_printf(
                 k = NULL;
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring.");
 
         return 0;
 }
@@ -322,12 +325,8 @@ int config_parse_socket_listen(const char *unit,
                 p->type = ltype;
                 r = unit_full_printf(UNIT(s), rvalue, &p->path);
                 if (r < 0) {
-                        p->path = strdup(rvalue);
-                        if (!p->path)
-                                return log_oom();
-                        else
-                                log_syntax(unit, LOG_ERR, filename, line, -r,
-                                           "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+                        return 0;
                 }
 
                 path_kill_slashes(p->path);
@@ -337,14 +336,14 @@ int config_parse_socket_listen(const char *unit,
 
                 p->type = SOCKET_SOCKET;
                 r = unit_full_printf(UNIT(s), rvalue, &k);
-                if (r < 0)
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
-                                   "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+                        return 0;
+                }
 
-                r = socket_address_parse_netlink(&p->address, k ?: rvalue);
+                r = socket_address_parse_netlink(&p->address, k);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
-                                   "Failed to parse address value, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue);
                         return 0;
                 }
 
@@ -353,14 +352,14 @@ int config_parse_socket_listen(const char *unit,
 
                 p->type = SOCKET_SOCKET;
                 r = unit_full_printf(UNIT(s), rvalue, &k);
-                if (r < 0)
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
-                                   "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+                        return 0;
+                }
 
-                r = socket_address_parse_and_warn(&p->address, k ? k : rvalue);
+                r = socket_address_parse_and_warn(&p->address, k);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
-                                   "Failed to parse address value, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue);
                         return 0;
                 }
 
@@ -374,13 +373,14 @@ int config_parse_socket_listen(const char *unit,
                 }
 
                 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
-                        log_syntax(unit, LOG_ERR, filename, line, EOPNOTSUPP,
-                                   "Address family not supported, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Address family not supported, ignoring: %s", rvalue);
                         return 0;
                 }
         }
 
         p->fd = -1;
+        p->auxiliary_fds = NULL;
+        p->n_auxiliary_fds = 0;
         p->socket = s;
 
         if (s->ports) {
@@ -420,8 +420,7 @@ int config_parse_socket_bind(const char *unit,
 
                 r = parse_boolean(rvalue);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
                         return 0;
                 }
 
@@ -453,14 +452,12 @@ int config_parse_exec_nice(const char *unit,
 
         r = safe_atoi(rvalue, &priority);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to parse nice priority, ignoring: %s. ", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue);
                 return 0;
         }
 
         if (priority < PRIO_MIN || priority >= PRIO_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, ERANGE,
-                           "Nice priority out of range, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Nice priority out of range, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -491,14 +488,12 @@ int config_parse_exec_oom_score_adjust(const char* unit,
 
         r = safe_atoi(rvalue, &oa);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
                 return 0;
         }
 
         if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, ERANGE,
-                           "OOM score adjust value out of range, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "OOM score adjust value out of range, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -531,9 +526,7 @@ int config_parse_exec(
         assert(e);
 
         e += ltype;
-
         rvalue += strspn(rvalue, WHITESPACE);
-        p = rvalue;
 
         if (isempty(rvalue)) {
                 /* An empty assignment resets the list */
@@ -541,14 +534,15 @@ int config_parse_exec(
                 return 0;
         }
 
+        p = rvalue;
         do {
-                int i;
+                _cleanup_free_ char *path = NULL, *firstword = NULL;
+                bool separate_argv0 = false, ignore = false;
+                _cleanup_free_ ExecCommand *nce = NULL;
                 _cleanup_strv_free_ char **n = NULL;
                 size_t nlen = 0, nbufsize = 0;
-                _cleanup_free_ ExecCommand *nce = NULL;
-                _cleanup_free_ char *path = NULL, *firstword = NULL;
                 char *f;
-                bool separate_argv0 = false, ignore = false;
+                int i;
 
                 semicolon = false;
 
@@ -572,24 +566,19 @@ int config_parse_exec(
 
                 if (isempty(f)) {
                         /* First word is either "-" or "@" with no command. */
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Empty path in command line, ignoring: \"%s\"", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Empty path in command line, ignoring: \"%s\"", rvalue);
                         return 0;
                 }
-
                 if (!string_is_safe(f)) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                        "Executable path contains special characters, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path contains special characters, ignoring: %s", rvalue);
                         return 0;
                 }
                 if (!path_is_absolute(f)) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                        "Executable path is not absolute, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path is not absolute, ignoring: %s", rvalue);
                         return 0;
                 }
                 if (endswith(f, "/")) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                        "Executable path specifies a directory, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path specifies a directory, ignoring: %s", rvalue);
                         return 0;
                 }
 
@@ -656,8 +645,7 @@ int config_parse_exec(
                 }
 
                 if (!n || !n[0]) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                        "Empty executable name or zeroeth argument, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue);
                         return 0;
                 }
 
@@ -741,8 +729,7 @@ int config_parse_exec_io_class(const char *unit,
 
         x = ioprio_class_from_string(rvalue);
         if (x < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Failed to parse IO scheduling class, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -773,8 +760,7 @@ int config_parse_exec_io_priority(const char *unit,
 
         r = safe_atoi(rvalue, &i);
         if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to parse IO priority, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -806,8 +792,7 @@ int config_parse_exec_cpu_sched_policy(const char *unit,
 
         x = sched_policy_from_string(rvalue);
         if (x < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -x,
-                           "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -840,8 +825,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit,
 
         r = safe_atoi(rvalue, &i);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -850,8 +834,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit,
         max = sched_get_priority_max(c->cpu_sched_policy);
 
         if (i < min || i > max) {
-                log_syntax(unit, LOG_ERR, filename, line, ERANGE,
-                           "CPU scheduling priority is out of range, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -873,50 +856,29 @@ int config_parse_exec_cpu_affinity(const char *unit,
                                    void *userdata) {
 
         ExecContext *c = data;
-        const char *word, *state;
-        size_t l;
+        _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
+        int ncpus;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        if (isempty(rvalue)) {
-                /* An empty assignment resets the CPU list */
-                if (c->cpuset)
-                        CPU_FREE(c->cpuset);
-                c->cpuset = NULL;
-                return 0;
-        }
-
-        FOREACH_WORD_QUOTED(word, l, rvalue, state) {
-                _cleanup_free_ char *t = NULL;
-                int r;
-                unsigned cpu;
-
-                t = strndup(word, l);
-                if (!t)
-                        return log_oom();
-
-                r = safe_atou(t, &cpu);
+        ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
+        if (ncpus < 0)
+                return ncpus;
 
-                if (!c->cpuset) {
-                        c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
-                        if (!c->cpuset)
-                                return log_oom();
-                }
-
-                if (r < 0 || cpu >= c->cpuset_ncpus) {
-                        log_syntax(unit, LOG_ERR, filename, line, ERANGE,
-                                   "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
-                        return 0;
-                }
+        if (c->cpuset)
+                CPU_FREE(c->cpuset);
 
-                CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
+        if (ncpus == 0)
+                /* An empty assignment resets the CPU list */
+                c->cpuset = NULL;
+        else {
+                c->cpuset = cpuset;
+                cpuset = NULL;
         }
-        if (!isempty(state))
-                log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
+        c->cpuset_ncpus = ncpus;
 
         return 0;
 }
@@ -942,8 +904,7 @@ int config_parse_exec_capabilities(const char *unit,
 
         cap = cap_from_text(rvalue);
         if (!cap) {
-                log_syntax(unit, LOG_ERR, filename, line, errno,
-                           "Failed to parse capabilities, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse capabilities, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -994,14 +955,12 @@ int config_parse_exec_secure_bits(const char *unit,
                 else if (first_word(word, "noroot-locked"))
                         c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
                 else {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Failed to parse secure bits, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse secure bits, ignoring: %s", rvalue);
                         return 0;
                 }
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Invalid syntax, garbage at the end, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, garbage at the end, ignoring.");
 
         return 0;
 }
@@ -1048,15 +1007,14 @@ int config_parse_bounding_set(const char *unit,
 
                 cap = capability_from_name(t);
                 if (cap < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse capability in bounding set, ignoring: %s", t);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding set, ignoring: %s", t);
                         continue;
                 }
 
                 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         if (invert)
                 *capability_bounding_set_drop |= sum;
@@ -1094,8 +1052,7 @@ int config_parse_limit(const char *unit,
 
                 r = safe_atollu(rvalue, &u);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
-                                   "Failed to parse resource value, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
                         return 0;
                 }
         }
@@ -1132,8 +1089,7 @@ int config_parse_sysv_priority(const char *unit,
 
         r = safe_atoi(rvalue, &i);
         if (r < 0 || i < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to parse SysV start priority, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SysV start priority, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -1180,12 +1136,12 @@ int config_parse_exec_mount_flags(const char *unit,
                 else if (streq(t, "private"))
                         flags = MS_PRIVATE;
                 else {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse mount flag %s, ignoring: %s", t, rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring: %s", t, rvalue);
                         return 0;
                 }
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         c->mount_flags = flags;
         return 0;
@@ -1228,8 +1184,7 @@ int config_parse_exec_selinux_context(
 
         r = unit_name_printf(u, rvalue, &k);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
                 return 0;
         }
 
@@ -1277,8 +1232,7 @@ int config_parse_exec_apparmor_profile(
 
         r = unit_name_printf(u, rvalue, &k);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
                 return 0;
         }
 
@@ -1326,8 +1280,7 @@ int config_parse_exec_smack_process_label(
 
         r = unit_name_printf(u, rvalue, &k);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
                 return 0;
         }
 
@@ -1368,23 +1321,18 @@ int config_parse_timer(const char *unit,
 
         b = timer_base_from_string(lvalue);
         if (b < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -b,
-                           "Failed to parse timer base, ignoring: %s", lvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer base, ignoring: %s", lvalue);
                 return 0;
         }
 
         if (b == TIMER_CALENDAR) {
                 if (calendar_spec_from_string(rvalue, &c) < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Failed to parse calendar specification, ignoring: %s",
-                                   rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", rvalue);
                         return 0;
                 }
         } else {
                 if (parse_sec(rvalue, &u) < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Failed to parse timer value, ignoring: %s",
-                                   rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", rvalue);
                         return 0;
                 }
         }
@@ -1427,33 +1375,30 @@ int config_parse_trigger_unit(
         assert(data);
 
         if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Multiple units to trigger specified, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
                 return 0;
         }
 
         r = unit_name_printf(u, rvalue, &p);
-        if (r < 0)
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
+                return 0;
+        }
 
-        type = unit_name_to_type(p ?: rvalue);
+        type = unit_name_to_type(p);
         if (type < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Unit type not valid, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Unit type not valid, ignoring: %s", rvalue);
                 return 0;
         }
 
         if (type == u->type) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trigger cannot be of same type, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trigger cannot be of same type, ignoring: %s", rvalue);
                 return 0;
         }
 
-        r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
+        r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
                 return 0;
         }
 
@@ -1490,25 +1435,18 @@ int config_parse_path_spec(const char *unit,
 
         b = path_type_from_string(lvalue);
         if (b < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Failed to parse path type, ignoring: %s", lvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse path type, ignoring: %s", lvalue);
                 return 0;
         }
 
         r = unit_full_printf(UNIT(p), rvalue, &k);
         if (r < 0) {
-                k = strdup(rvalue);
-                if (!k)
-                        return log_oom();
-                else
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
-                                   "Failed to resolve unit specifiers on %s. Ignoring.",
-                                   rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+                return 0;
         }
 
         if (!path_is_absolute(k)) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Path is not absolute, ignoring: %s", k);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Path is not absolute, ignoring: %s", k);
                 return 0;
         }
 
@@ -1540,10 +1478,10 @@ int config_parse_socket_service(
                 void *userdata) {
 
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_free_ char *p = NULL;
         Socket *s = data;
-        int r;
         Unit *x;
-        _cleanup_free_ char *p = NULL;
+        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1557,13 +1495,13 @@ int config_parse_socket_service(
         }
 
         if (!endswith(p, ".service")) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
                 return 0;
         }
 
         r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
                 return 0;
         }
 
@@ -1572,6 +1510,50 @@ int config_parse_socket_service(
         return 0;
 }
 
+int config_parse_fdname(
+                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) {
+
+        _cleanup_free_ char *p = NULL;
+        Socket *s = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                s->fdname = mfree(s->fdname);
+                return 0;
+        }
+
+        r = unit_name_printf(UNIT(s), rvalue, &p);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (!fdname_is_valid(p)) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
+                return 0;
+        }
+
+        free(s->fdname);
+        s->fdname = p;
+        p = NULL;
+
+        return 0;
+}
+
 int config_parse_service_sockets(
                 const char *unit,
                 const char *filename,
@@ -1608,7 +1590,7 @@ int config_parse_service_sockets(
                 }
 
                 if (!endswith(k, ".socket")) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type socket, ignoring: %s", k);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
                         continue;
                 }
 
@@ -1621,7 +1603,7 @@ int config_parse_service_sockets(
                         log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         return 0;
 }
@@ -1654,7 +1636,7 @@ int config_parse_bus_name(
         }
 
         if (!service_name_is_valid(k)) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bus name %s, ignoring.", k);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k);
                 return 0;
         }
 
@@ -1719,21 +1701,18 @@ int config_parse_busname_service(
 
         r = unit_name_printf(UNIT(n), rvalue, &p);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to resolve specifiers, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
                 return 0;
         }
 
         if (!endswith(p, ".service")) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Unit must be of type service, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
                 return 0;
         }
 
         r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
                 return 0;
         }
 
@@ -1783,8 +1762,7 @@ int config_parse_bus_policy(
 
         access_str = strpbrk(id_str, WHITESPACE);
         if (!access_str) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Invalid busname policy value '%s'", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy value '%s'", rvalue);
                 return 0;
         }
 
@@ -1794,8 +1772,7 @@ int config_parse_bus_policy(
 
         p->access = bus_policy_access_from_string(access_str);
         if (p->access < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Invalid busname policy access type '%s'", access_str);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy access type '%s'", access_str);
                 return 0;
         }
 
@@ -1837,8 +1814,7 @@ int config_parse_bus_endpoint_policy(
 
         access_str = strpbrk(name, WHITESPACE);
         if (!access_str) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Invalid endpoint policy value '%s'", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid endpoint policy value '%s'", rvalue);
                 return 0;
         }
 
@@ -1849,21 +1825,83 @@ int config_parse_bus_endpoint_policy(
         access = bus_policy_access_from_string(access_str);
         if (access <= _BUS_POLICY_ACCESS_INVALID ||
             access >= _BUS_POLICY_ACCESS_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Invalid endpoint policy access type '%s'", access_str);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid endpoint policy access type '%s'", access_str);
                 return 0;
         }
 
         if (!c->bus_endpoint) {
                 r = bus_endpoint_new(&c->bus_endpoint);
-
                 if (r < 0)
-                        return r;
+                        return log_error_errno(r, "Failed to create bus endpoint object: %m");
         }
 
         return bus_endpoint_add_policy(c->bus_endpoint, name, access);
 }
 
+int config_parse_working_directory(
+                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) {
+
+        ExecContext *c = data;
+        Unit *u = userdata;
+        bool missing_ok;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(c);
+        assert(u);
+
+        if (rvalue[0] == '-') {
+                missing_ok = true;
+                rvalue++;
+        } else
+                missing_ok = false;
+
+        if (streq(rvalue, "~")) {
+                c->working_directory_home = true;
+                c->working_directory = mfree(c->working_directory);
+        } else {
+                _cleanup_free_ char *k = NULL;
+
+                r = unit_full_printf(u, rvalue, &k);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue);
+                        return 0;
+                }
+
+                path_kill_slashes(k);
+
+                if (!utf8_is_valid(k)) {
+                        log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
+                        return 0;
+                }
+
+                if (!path_is_absolute(k)) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue);
+                        return 0;
+                }
+
+                free(c->working_directory);
+                c->working_directory = k;
+                k = NULL;
+
+                c->working_directory_home = false;
+        }
+
+        c->working_directory_missing_ok = missing_ok;
+        return 0;
+}
+
 int config_parse_unit_env_file(const char *unit,
                                const char *filename,
                                unsigned line,
@@ -1878,7 +1916,6 @@ int config_parse_unit_env_file(const char *unit,
         char ***env = data;
         Unit *u = userdata;
         _cleanup_free_ char *n = NULL;
-        const char *s;
         int r;
 
         assert(filename);
@@ -1893,18 +1930,17 @@ int config_parse_unit_env_file(const char *unit,
         }
 
         r = unit_full_printf(u, rvalue, &n);
-        if (r < 0)
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to resolve specifiers, ignoring: %s", rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+                return 0;
+        }
 
-        s = n ?: rvalue;
-        if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Path '%s' is not absolute, ignoring.", s);
+        if (!path_is_absolute(n[0] == '-' ? n + 1 : n)) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Path '%s' is not absolute, ignoring.", n);
                 return 0;
         }
 
-        r = strv_extend(env, s);
+        r = strv_extend(env, n);
         if (r < 0)
                 return log_oom();
 
@@ -1942,14 +1978,17 @@ int config_parse_environ(const char *unit,
 
         if (u) {
                 r = unit_full_printf(u, rvalue, &k);
-                if (r < 0)
-                        log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+                        return 0;
+                }
         }
 
-        if (!k)
+        if (!k) {
                 k = strdup(rvalue);
-        if (!k)
-                return log_oom();
+                if (!k)
+                        return log_oom();
+        }
 
         FOREACH_WORD_QUOTED(word, l, k, state) {
                 _cleanup_free_ char *n = NULL;
@@ -1962,7 +2001,7 @@ int config_parse_environ(const char *unit,
                 }
 
                 if (!env_assignment_is_valid(n)) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid environment assignment, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid environment assignment, ignoring: %s", rvalue);
                         continue;
                 }
 
@@ -1974,8 +2013,7 @@ int config_parse_environ(const char *unit,
                 *env = x;
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         return 0;
 }
@@ -2000,8 +2038,7 @@ int config_parse_ip_tos(const char *unit,
 
         x = ip_tos_from_string(rvalue);
         if (x < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Failed to parse IP TOS value, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -2049,12 +2086,12 @@ int config_parse_unit_condition_path(
 
         r = unit_full_printf(u, rvalue, &p);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
                 return 0;
         }
 
         if (!path_is_absolute(p)) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path in condition not absolute, ignoring: %s", p);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p);
                 return 0;
         }
 
@@ -2106,7 +2143,7 @@ int config_parse_unit_condition_string(
 
         r = unit_full_printf(u, rvalue, &s);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -2155,7 +2192,7 @@ int config_parse_unit_condition_null(
 
         b = parse_boolean(rvalue);
         if (b < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -2203,20 +2240,18 @@ int config_parse_unit_requires_mounts_for(
                         return log_oom();
 
                 if (!utf8_is_valid(n)) {
-                        log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+                        log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
                         continue;
                 }
 
                 r = unit_require_mounts_for(u, n);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
-                                   "Failed to add required mount for, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount for, ignoring: %s", rvalue);
                         continue;
                 }
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         return 0;
 }
@@ -2257,8 +2292,7 @@ int config_parse_documentation(const char *unit,
                 if (documentation_url_is_valid(*a))
                         *(b++) = *a;
                 else {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Invalid URL, ignoring: %s", *a);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a);
                         free(*a);
                 }
         }
@@ -2353,8 +2387,7 @@ int config_parse_syscall_filter(
 
                 id = seccomp_syscall_resolve_name(t);
                 if (id < 0)  {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Failed to parse system call, ignoring: %s", t);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call, ignoring: %s", t);
                         continue;
                 }
 
@@ -2371,8 +2404,7 @@ int config_parse_syscall_filter(
                         set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         /* Turn on NNP, but only if it wasn't configured explicitly
          * before, and only if we are in user mode. */
@@ -2418,8 +2450,7 @@ int config_parse_syscall_archs(
 
                 r = seccomp_arch_from_string(t, &a);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Failed to parse system call architecture, ignoring: %s", t);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call architecture, ignoring: %s", t);
                         continue;
                 }
 
@@ -2430,8 +2461,7 @@ int config_parse_syscall_archs(
                         return log_oom();
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         return 0;
 }
@@ -2463,8 +2493,7 @@ int config_parse_syscall_errno(
 
         e = errno_from_name(rvalue);
         if (e < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Failed to parse error number, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -2524,8 +2553,7 @@ int config_parse_address_families(
 
                 af = af_from_name(t);
                 if (af <= 0)  {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Failed to parse address family, ignoring: %s", t);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse address family, ignoring: %s", t);
                         continue;
                 }
 
@@ -2542,8 +2570,7 @@ int config_parse_address_families(
                         set_remove(c->address_families, INT_TO_PTR(af));
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         return 0;
 }
@@ -2605,26 +2632,19 @@ int config_parse_cpu_shares(
                 void *data,
                 void *userdata) {
 
-        unsigned long *shares = data, lu;
+        uint64_t *shares = data;
         int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
 
-        if (isempty(rvalue)) {
-                *shares = (unsigned long) -1;
-                return 0;
-        }
-
-        r = safe_atolu(rvalue, &lu);
-        if (r < 0 || lu <= 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "CPU shares '%s' invalid. Ignoring.", rvalue);
+        r = cg_cpu_shares_parse(rvalue, shares);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue);
                 return 0;
         }
 
-        *shares = lu;
         return 0;
 }
 
@@ -2653,15 +2673,12 @@ int config_parse_cpu_quota(
         }
 
         if (!endswith(rvalue, "%")) {
-
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue);
                 return 0;
         }
 
         if (sscanf(rvalue, "%lf%%", &percent) != 1 || percent <= 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "CPU quota '%s' invalid. Ignoring.", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' invalid. Ignoring.", rvalue);
                 return 0;
         }
 
@@ -2686,15 +2703,14 @@ int config_parse_memory_limit(
         uint64_t bytes;
         int r;
 
-        if (isempty(rvalue)) {
+        if (isempty(rvalue) || streq(rvalue, "infinity")) {
                 c->memory_limit = (uint64_t) -1;
                 return 0;
         }
 
         r = parse_size(rvalue, 1024, &bytes);
-        if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Memory limit '%s' invalid. Ignoring.", rvalue);
+        if (r < 0 || bytes < 1) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
                 return 0;
         }
 
@@ -2702,6 +2718,37 @@ int config_parse_memory_limit(
         return 0;
 }
 
+int config_parse_tasks_max(
+                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) {
+
+        CGroupContext *c = data;
+        uint64_t u;
+        int r;
+
+        if (isempty(rvalue) || streq(rvalue, "infinity")) {
+                c->tasks_max = (uint64_t) -1;
+                return 0;
+        }
+
+        r = safe_atou64(rvalue, &u);
+        if (r < 0 || u < 1) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
+                return 0;
+        }
+
+        c->tasks_max = u;
+        return 0;
+}
+
 int config_parse_device_allow(
                 const char *unit,
                 const char *filename,
@@ -2735,8 +2782,7 @@ int config_parse_device_allow(
         if (!startswith(path, "/dev/") &&
             !startswith(path, "block-") &&
             !startswith(path, "char-")) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Invalid device node path '%s'. Ignoring.", path);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
                 return 0;
         }
 
@@ -2745,8 +2791,7 @@ int config_parse_device_allow(
                 m = "rwm";
 
         if (!in_charset(m, "rwm")) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Invalid device rights '%s'. Ignoring.", m);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m);
                 return 0;
         }
 
@@ -2776,26 +2821,19 @@ int config_parse_blockio_weight(
                 void *data,
                 void *userdata) {
 
-        unsigned long *weight = data, lu;
+        uint64_t *weight = data;
         int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
 
-        if (isempty(rvalue)) {
-                *weight = (unsigned long) -1;
-                return 0;
-        }
-
-        r = safe_atolu(rvalue, &lu);
-        if (r < 0 || lu < 10 || lu > 1000) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Block IO weight '%s' invalid. Ignoring.", rvalue);
+        r = cg_blkio_weight_parse(rvalue, weight);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue);
                 return 0;
         }
 
-        *weight = lu;
         return 0;
 }
 
@@ -2814,8 +2852,8 @@ int config_parse_blockio_device_weight(
         _cleanup_free_ char *path = NULL;
         CGroupBlockIODeviceWeight *w;
         CGroupContext *c = data;
-        unsigned long lu;
         const char *weight;
+        uint64_t u;
         size_t n;
         int r;
 
@@ -2832,9 +2870,10 @@ int config_parse_blockio_device_weight(
 
         n = strcspn(rvalue, WHITESPACE);
         weight = rvalue + n;
-        if (!*weight) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Expected block device and device weight. Ignoring.");
+        weight += strspn(weight, WHITESPACE);
+
+        if (isempty(weight)) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
                 return 0;
         }
 
@@ -2843,19 +2882,18 @@ int config_parse_blockio_device_weight(
                 return log_oom();
 
         if (!path_startswith(path, "/dev")) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Invalid device node path '%s'. Ignoring.", path);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
                 return 0;
         }
 
-        weight += strspn(weight, WHITESPACE);
-        r = safe_atolu(weight, &lu);
-        if (r < 0 || lu < 10 || lu > 1000) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Block IO weight '%s' invalid. Ignoring.", rvalue);
+        r = cg_blkio_weight_parse(weight, &u);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight);
                 return 0;
         }
 
+        assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
+
         w = new0(CGroupBlockIODeviceWeight, 1);
         if (!w)
                 return log_oom();
@@ -2863,7 +2901,7 @@ int config_parse_blockio_device_weight(
         w->path = path;
         path = NULL;
 
-        w->weight = lu;
+        w->weight = u;
 
         LIST_PREPEND(device_weights, c->blockio_device_weights, w);
         return 0;
@@ -2911,8 +2949,7 @@ int config_parse_blockio_bandwidth(
         bandwidth += strspn(bandwidth, WHITESPACE);
 
         if (!*bandwidth) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Expected space separated pair of device node and bandwidth. Ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
                 return 0;
         }
 
@@ -2921,15 +2958,13 @@ int config_parse_blockio_bandwidth(
                 return log_oom();
 
         if (!path_startswith(path, "/dev")) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Invalid device node path '%s'. Ignoring.", path);
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
                 return 0;
         }
 
         r = parse_size(bandwidth, 1000, &bytes);
         if (r < 0 || bytes <= 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
                 return 0;
         }
 
@@ -2947,6 +2982,47 @@ int config_parse_blockio_bandwidth(
         return 0;
 }
 
+int config_parse_netclass(
+                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) {
+
+        CGroupContext *c = data;
+        unsigned v;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (streq(rvalue, "auto")) {
+                c->netclass_type = CGROUP_NETCLASS_TYPE_AUTO;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &v);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Netclass '%s' invalid. Ignoring.", rvalue);
+                return 0;
+        }
+
+        if (v > CGROUP_NETCLASS_FIXED_MAX)
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "Fixed netclass %" PRIu32 " out of allowed range (0-%d). Applying anyway.", v, (uint32_t) CGROUP_NETCLASS_FIXED_MAX);
+
+        c->netclass_id = v;
+        c->netclass_type = CGROUP_NETCLASS_TYPE_FIXED;
+
+        return 0;
+}
+
 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
 
 int config_parse_job_mode_isolate(
@@ -2970,8 +3046,7 @@ int config_parse_job_mode_isolate(
 
         r = parse_boolean(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Failed to parse boolean, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -2992,6 +3067,7 @@ int config_parse_runtime_directory(
                 void *userdata) {
 
         char***rt = data;
+        Unit *u = userdata;
         const char *word, *state;
         size_t l;
         int r;
@@ -3008,15 +3084,20 @@ int config_parse_runtime_directory(
         }
 
         FOREACH_WORD_QUOTED(word, l, rvalue, state) {
-                _cleanup_free_ char *n;
+                _cleanup_free_ char *t = NULL, *n = NULL;
 
-                n = strndup(word, l);
-                if (!n)
+                t = strndup(word, l);
+                if (!t)
                         return log_oom();
 
+                r = unit_name_printf(u, t, &n);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
+                        continue;
+                }
+
                 if (!filename_is_valid(n)) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Runtime directory is not valid, ignoring assignment: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
                         continue;
                 }
 
@@ -3027,8 +3108,7 @@ int config_parse_runtime_directory(
                 n = NULL;
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         return 0;
 }
@@ -3075,15 +3155,13 @@ int config_parse_set_status(
                         val = signal_from_string_try_harder(temp);
 
                         if (val <= 0) {
-                                log_syntax(unit, LOG_ERR, filename, line, -val,
-                                           "Failed to parse value, ignoring: %s", word);
+                                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
                                 continue;
                         }
                         set = &status_set->signal;
                 } else {
                         if (val < 0 || val > 255) {
-                                log_syntax(unit, LOG_ERR, filename, line, ERANGE,
-                                           "Value %d is outside range 0-255, ignoring", val);
+                                log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
                                 continue;
                         }
                         set = &status_set->status;
@@ -3095,14 +3173,12 @@ int config_parse_set_status(
 
                 r = set_put(*set, INT_TO_PTR(val));
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
-                                   "Unable to store: %s", word);
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
                         return r;
                 }
         }
         if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
 
         return 0;
 }
@@ -3120,8 +3196,8 @@ int config_parse_namespace_path_strv(
                 void *userdata) {
 
         char*** sv = data;
-        const char *word, *state;
-        size_t l;
+        const char *prev;
+        const char *cur;
         int r;
 
         assert(filename);
@@ -3135,37 +3211,41 @@ int config_parse_namespace_path_strv(
                 return 0;
         }
 
-        FOREACH_WORD_QUOTED(word, l, rvalue, state) {
-                _cleanup_free_ char *n;
+        prev = cur = rvalue;
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
                 int offset;
 
-                n = strndup(word, l);
-                if (!n)
-                        return log_oom();
+                r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring: %s", prev);
+                        return 0;
+                }
+                if (r == 0)
+                        break;
 
-                if (!utf8_is_valid(n)) {
-                        log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+                if (!utf8_is_valid(word)) {
+                        log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
+                        prev = cur;
                         continue;
                 }
 
-                offset = n[0] == '-';
-                if (!path_is_absolute(n + offset)) {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Not an absolute path, ignoring: %s", rvalue);
+                offset = word[0] == '-';
+                if (!path_is_absolute(word + offset)) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", word);
+                        prev = cur;
                         continue;
                 }
 
-                path_kill_slashes(n);
+                path_kill_slashes(word + offset);
 
-                r = strv_push(sv, n);
+                r = strv_push(sv, word);
                 if (r < 0)
                         return log_oom();
 
-                n = NULL;
+                prev = cur;
+                word = NULL;
         }
-        if (!isempty(state))
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Trailing garbage, ignoring.");
 
         return 0;
 }
@@ -3192,8 +3272,7 @@ int config_parse_no_new_privileges(
 
         k = parse_boolean(rvalue);
         if (k < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -k,
-                           "Failed to parse boolean value, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -3236,8 +3315,7 @@ int config_parse_protect_home(
 
                 h = protect_home_from_string(rvalue);
                 if (h < 0){
-                        log_syntax(unit, LOG_ERR, filename, line, -h,
-                                   "Failed to parse protect home value, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
                         return 0;
                 }
 
@@ -3280,8 +3358,7 @@ int config_parse_protect_system(
 
                 s = protect_system_from_string(rvalue);
                 if (s < 0){
-                        log_syntax(unit, LOG_ERR, filename, line, -s,
-                                   "Failed to parse protect system value, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
                         return 0;
                 }