X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fload-fragment.c;h=4309121649f41cf483b4e036a8b5424f35be5e21;hp=8436d4f95ef1e9224ae5b7d22d98b6f2a4e3b8fc;hb=2ca620c4ed28c01f285d869d0b22f22a360957da;hpb=843fc7f7f26a6507fe896a79ed1b474c35c4300f diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 8436d4f95..430912164 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -33,7 +33,14 @@ #include #include #include +#include +#include +#ifdef HAVE_SECCOMP +#include +#endif + +#include "sd-messages.h" #include "unit.h" #include "strv.h" #include "conf-parser.h" @@ -44,155 +51,247 @@ #include "missing.h" #include "unit-name.h" #include "unit-printf.h" -#include "bus-errors.h" #include "utf8.h" #include "path-util.h" -#include "syscall-list.h" +#include "env-util.h" +#include "cgroup.h" +#include "bus-util.h" +#include "bus-error.h" +#include "errno-list.h" +#include "af-list.h" + +#ifdef HAVE_SECCOMP +#include "seccomp-util.h" +#endif -#ifndef HAVE_SYSV_COMPAT +#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR) 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) { - log_debug("[%s:%u] Support for option %s= has been disabled at compile time and is ignored", filename, line, lvalue); + log_syntax(unit, LOG_DEBUG, filename, line, EINVAL, + "Support for option %s= has been disabled at compile time and is ignored", + lvalue); return 0; } #endif -int config_parse_unit_deps( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_unit_deps(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) { UnitDependency d = ltype; Unit *u = userdata; - char *w; + const char *word, *state; size_t l; - char *state; assert(filename); assert(lvalue); assert(rvalue); - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char _cleanup_free_ *t = NULL, *k = NULL; + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL, *k = NULL; int r; - t = strndup(w, l); + t = strndup(word, l); if (!t) return log_oom(); - k = unit_name_printf(u, t); - if (!k) - return log_oom(); + 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)); + continue; + } r = unit_add_dependency_by_name(u, d, k, NULL, true); if (r < 0) - log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", - filename, line, k, strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to add dependency on %s, ignoring: %s", k, strerror(-r)); } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring."); return 0; } -int config_parse_unit_string_printf( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_unit_string_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; + int r; assert(filename); assert(lvalue); assert(rvalue); assert(u); - k = unit_full_printf(u, rvalue); - if (!k) - return log_oom(); + 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)); - return config_parse_string(filename, line, section, lvalue, ltype, k, data, userdata); + return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, + k ? k : rvalue, data, userdata); } -int config_parse_unit_strv_printf( - const char *filename, - unsigned line, - const char *section, - 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; + int r; assert(filename); assert(lvalue); assert(rvalue); assert(u); - k = unit_full_printf(u, rvalue); - if (!k) - return log_oom(); + 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)); - return config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata); + return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, + k ? k : rvalue, data, userdata); } -int config_parse_unit_path_printf( - const char *filename, - unsigned line, - const char *section, - 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) { - Unit *u = userdata; _cleanup_free_ char *k = NULL; + Unit *u = userdata; + int r; assert(filename); assert(lvalue); assert(rvalue); assert(u); - k = unit_full_printf(u, rvalue); - if (!k) - return log_oom(); + 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)); + return 0; + } - return config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata); + return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata); } -int config_parse_socket_listen( +int config_parse_unit_path_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) { - SocketPort *p, *tail; + char ***x = data; + const char *word, *state; + Unit *u = userdata; + size_t l; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *k = NULL; + char t[l+1]; + + memcpy(t, word, l); + t[l] = 0; + + 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)); + return 0; + } + + if (!utf8_is_valid(k)) { + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, 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)); + return 0; + } + + path_kill_slashes(k); + + r = strv_push(x, k); + if (r < 0) + return log_oom(); + + k = NULL; + } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring."); + + return 0; +} + +int config_parse_socket_listen(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_ SocketPort *p = NULL; + SocketPort *tail; Socket *s; + int r; assert(filename); assert(lvalue); @@ -214,47 +313,47 @@ int config_parse_socket_listen( if (ltype != SOCKET_SOCKET) { p->type = ltype; - p->path = unit_full_printf(UNIT(s), rvalue); - if (!p->path) { - free(p); - return log_oom(); + 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)); } path_kill_slashes(p->path); } else if (streq(lvalue, "ListenNetlink")) { _cleanup_free_ char *k = NULL; - int r; p->type = SOCKET_SOCKET; - k = unit_full_printf(UNIT(s), rvalue); - if (!k) { - free(p); - return log_oom(); - } + 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)); - r = socket_address_parse_netlink(&p->address, k); + r = socket_address_parse_netlink(&p->address, k ?: rvalue); if (r < 0) { - log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue); - free(p); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to parse address value, ignoring: %s", rvalue); return 0; } } else { _cleanup_free_ char *k = NULL; - int r; p->type = SOCKET_SOCKET; - k = unit_full_printf(UNIT(s), rvalue); - if (!k) { - free(p); - return log_oom(); - } + 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)); - r = socket_address_parse(&p->address, k); + r = socket_address_parse(&p->address, k ? k : rvalue); if (r < 0) { - log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue); - free(p); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to parse address value, ignoring: %s", rvalue); return 0; } @@ -268,32 +367,35 @@ int config_parse_socket_listen( } if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) { - log_error("[%s:%u] Address family not supported, ignoring: %s", filename, line, rvalue); - free(p); + log_syntax(unit, LOG_ERR, filename, line, ENOTSUP, + "Address family not supported, ignoring: %s", rvalue); return 0; } } p->fd = -1; + p->socket = s; if (s->ports) { - LIST_FIND_TAIL(SocketPort, port, s->ports, tail); - LIST_INSERT_AFTER(SocketPort, port, s->ports, tail, p); + LIST_FIND_TAIL(port, s->ports, tail); + LIST_INSERT_AFTER(port, s->ports, tail, p); } else - LIST_PREPEND(SocketPort, port, s->ports, p); + LIST_PREPEND(port, s->ports, p); + p = NULL; return 0; } -int config_parse_socket_bind( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_socket_bind(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) { Socket *s; SocketAddressBindIPv6Only b; @@ -311,7 +413,8 @@ int config_parse_socket_bind( r = parse_boolean(rvalue); if (r < 0) { - log_error("[%s:%u] Failed to parse bind IPv6 only value, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse bind IPv6 only value, ignoring: %s", rvalue); return 0; } @@ -322,31 +425,35 @@ int config_parse_socket_bind( return 0; } -int config_parse_exec_nice( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_nice(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; - int priority; + int priority, r; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (safe_atoi(rvalue, &priority) < 0) { - log_error("[%s:%u] Failed to parse nice priority, ignoring: %s. ", filename, line, rvalue); + r = safe_atoi(rvalue, &priority); + if (r < 0) { + 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_error("[%s:%u] Nice priority out of range, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, ERANGE, + "Nice priority out of range, ignoring: %s", rvalue); return 0; } @@ -356,31 +463,35 @@ int config_parse_exec_nice( return 0; } -int config_parse_exec_oom_score_adjust( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_oom_score_adjust(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; - int oa; + int oa, r; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (safe_atoi(rvalue, &oa) < 0) { - log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue); + 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); return 0; } if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) { - log_error("[%s:%u] OOM score adjust value out of range, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, ERANGE, + "OOM score adjust value out of range, ignoring: %s", rvalue); return 0; } @@ -390,15 +501,16 @@ int config_parse_exec_oom_score_adjust( return 0; } -int config_parse_exec( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec(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) { ExecCommand **e = data, *nce; char *path, **n; @@ -424,9 +536,8 @@ int config_parse_exec( * overriding of argv[0]. */ for (;;) { int i; - char *w; + const char *word, *state; size_t l; - char *state; bool honour_argv0 = false, ignore = false; path = NULL; @@ -451,41 +562,47 @@ int config_parse_exec( } if (*rvalue != '/') { - log_error("[%s:%u] Executable path is not absolute, ignoring: %s", - filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Executable path is not absolute, ignoring: %s", rvalue); return 0; } k = 0; - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (strncmp(w, ";", MAX(l, 1U)) == 0) - break; + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + if (strneq(word, ";", MAX(l, 1U))) + goto found; k++; } + if (!isempty(state)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trailing garbage, ignoring."); + return 0; + } + found: n = new(char*, k + !honour_argv0); if (!n) return log_oom(); k = 0; - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (strncmp(w, ";", MAX(l, 1U)) == 0) + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + if (strneq(word, ";", MAX(l, 1U))) break; - else if (strncmp(w, "\\;", MAX(l, 1U)) == 0) - w ++; + else if (strneq(word, "\\;", MAX(l, 1U))) + word ++; - if (honour_argv0 && w == rvalue) { + if (honour_argv0 && word == rvalue) { assert(!path); - path = strndup(w, l); + path = strndup(word, l); if (!path) { r = log_oom(); goto fail; } if (!utf8_is_valid(path)) { - log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue); + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); r = 0; goto fail; } @@ -493,14 +610,14 @@ int config_parse_exec( } else { char *c; - c = n[k++] = cunescape_length(w, l); + c = n[k++] = cunescape_length(word, l); if (!c) { r = log_oom(); goto fail; } if (!utf8_is_valid(c)) { - log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue); + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); r = 0; goto fail; } @@ -510,7 +627,8 @@ int config_parse_exec( n[k] = NULL; if (!n[0]) { - log_error("[%s:%u] Invalid command line, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid command line, ignoring: %s", rvalue); r = 0; goto fail; } @@ -556,15 +674,16 @@ fail: DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); -int config_parse_socket_bindtodevice( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_socket_bindtodevice(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) { Socket *s = data; char *n; @@ -590,15 +709,16 @@ int config_parse_socket_bindtodevice( DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier"); DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier"); -int config_parse_exec_io_class( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_io_class(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; int x; @@ -610,7 +730,8 @@ int config_parse_exec_io_class( x = ioprio_class_from_string(rvalue); if (x < 0) { - log_error("[%s:%u] Failed to parse IO scheduling class, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse IO scheduling class, ignoring: %s", rvalue); return 0; } @@ -620,26 +741,29 @@ int config_parse_exec_io_class( return 0; } -int config_parse_exec_io_priority( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_io_priority(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; - int i; + int i, r; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) { - log_error("[%s:%u] Failed to parse io priority, ignoring: %s", filename, line, rvalue); + 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); return 0; } @@ -649,15 +773,16 @@ int config_parse_exec_io_priority( return 0; } -int config_parse_exec_cpu_sched_policy( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_cpu_sched_policy(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; @@ -670,7 +795,8 @@ int config_parse_exec_cpu_sched_policy( x = sched_policy_from_string(rvalue); if (x < 0) { - log_error("[%s:%u] Failed to parse CPU scheduling policy, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, -x, + "Failed to parse CPU scheduling policy, ignoring: %s", rvalue); return 0; } @@ -682,26 +808,29 @@ int config_parse_exec_cpu_sched_policy( return 0; } -int config_parse_exec_cpu_sched_prio( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_cpu_sched_prio(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; - int i, min, max; + int i, min, max, r; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (safe_atoi(rvalue, &i) < 0) { - log_error("[%s:%u] Failed to parse CPU scheduling priority, ignoring: %s", filename, line, rvalue); + 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); return 0; } @@ -710,7 +839,8 @@ int config_parse_exec_cpu_sched_prio( max = sched_get_priority_max(c->cpu_sched_policy); if (i < min || i > max) { - log_error("[%s:%u] CPU scheduling priority is out of range, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, ERANGE, + "CPU scheduling priority is out of range, ignoring: %s", rvalue); return 0; } @@ -720,20 +850,20 @@ int config_parse_exec_cpu_sched_prio( return 0; } -int config_parse_exec_cpu_affinity( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_cpu_affinity(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; - char *w; + const char *word, *state; size_t l; - char *state; assert(filename); assert(lvalue); @@ -748,12 +878,12 @@ int config_parse_exec_cpu_affinity( return 0; } - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char _cleanup_free_ *t = NULL; + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL; int r; unsigned cpu; - t = strndup(w, l); + t = strndup(word, l); if (!t) return log_oom(); @@ -766,26 +896,30 @@ int config_parse_exec_cpu_affinity( } if (r < 0 || cpu >= c->cpuset_ncpus) { - log_error("[%s:%u] Failed to parse CPU affinity %s, ignoring: %s", - filename, line, t, rvalue); + log_syntax(unit, LOG_ERR, filename, line, ERANGE, + "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue); return 0; } CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset); } + if (!isempty(state)) + log_syntax(unit, LOG_WARNING, filename, line, EINVAL, + "Trailing garbage, ignoring."); return 0; } -int config_parse_exec_capabilities( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_capabilities(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; cap_t cap; @@ -797,10 +931,8 @@ int config_parse_exec_capabilities( cap = cap_from_text(rvalue); if (!cap) { - if (errno == ENOMEM) - return log_oom(); - - log_error("[%s:%u] Failed to parse capabilities, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, errno, + "Failed to parse capabilities, ignoring: %s", rvalue); return 0; } @@ -811,20 +943,20 @@ int config_parse_exec_capabilities( return 0; } -int config_parse_exec_secure_bits( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_secure_bits(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; - char *w; size_t l; - char *state; + const char *word, *state; assert(filename); assert(lvalue); @@ -837,43 +969,46 @@ int config_parse_exec_secure_bits( return 0; } - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (first_word(w, "keep-caps")) - c->secure_bits |= SECURE_KEEP_CAPS; - else if (first_word(w, "keep-caps-locked")) - c->secure_bits |= SECURE_KEEP_CAPS_LOCKED; - else if (first_word(w, "no-setuid-fixup")) - c->secure_bits |= SECURE_NO_SETUID_FIXUP; - else if (first_word(w, "no-setuid-fixup-locked")) - c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED; - else if (first_word(w, "noroot")) - c->secure_bits |= SECURE_NOROOT; - else if (first_word(w, "noroot-locked")) - c->secure_bits |= SECURE_NOROOT_LOCKED; + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + if (first_word(word, "keep-caps")) + c->secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<cgroup_bondings, false); - u->cgroup_bondings = NULL; + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + 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); return 0; } - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char _cleanup_free_ *t = NULL, *k = NULL, *ku = NULL; - int r; + *priority = (int) i; + return 0; +} +#endif - t = strndup(w, l); - if (!t) - return log_oom(); +DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); - k = unit_full_printf(u, t); - if (!k) - return log_oom(); +int config_parse_kill_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) { - ku = cunescape(k); - if (!ku) - return log_oom(); + int *sig = data; + int r; - r = unit_add_cgroup_from_text(u, ku, true, NULL); - if (r < 0) { - log_error("[%s:%u] Failed to parse cgroup value %s, ignoring: %s", - filename, line, k, rvalue); - return 0; - } + assert(filename); + assert(lvalue); + assert(rvalue); + assert(sig); + + r = signal_from_string_try_harder(rvalue); + if (r <= 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to parse kill signal, ignoring: %s", rvalue); + return 0; } + *sig = r; return 0; } -#ifdef HAVE_SYSV_COMPAT -int config_parse_sysv_priority( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_mount_flags(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 *priority = data; - int i; + ExecContext *c = data; + const char *word, *state; + size_t l; + unsigned long flags = 0; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (safe_atoi(rvalue, &i) < 0 || i < 0) { - log_error("[%s:%u] Failed to parse SysV start priority, ignoring: %s", filename, line, rvalue); - return 0; + FOREACH_WORD_SEPARATOR(word, l, rvalue, ", ", state) { + _cleanup_free_ char *t; + + t = strndup(word, l); + if (!t) + return log_oom(); + + if (streq(t, "shared")) + flags = MS_SHARED; + else if (streq(t, "slave")) + flags = MS_SLAVE; + else if (streq(word, "private")) + flags = MS_PRIVATE; + else { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "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."); - *priority = (int) i; + c->mount_flags = flags; return 0; } -#endif -int config_parse_fsck_passno( +int config_parse_exec_selinux_context( + 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 *passno = data; - int i; + ExecContext *c = data; + Unit *u = userdata; + bool ignore; + char *k; + int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (safe_atoi(rvalue, &i) || i < 0) { - log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue); + if (isempty(rvalue)) { + free(c->selinux_context); + c->selinux_context = NULL; + c->selinux_context_ignore = false; + return 0; + } + + if (rvalue[0] == '-') { + ignore = true; + rvalue++; + } else + ignore = false; + + 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)); return 0; } - *passno = (int) i; + free(c->selinux_context); + c->selinux_context = k; + c->selinux_context_ignore = ignore; + return 0; } -DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); - -int config_parse_kill_signal( +int config_parse_exec_apparmor_profile( + 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; + ExecContext *c = data; + Unit *u = userdata; + bool ignore; + char *k; int r; assert(filename); assert(lvalue); assert(rvalue); - assert(sig); + assert(data); - r = signal_from_string_try_harder(rvalue); - if (r <= 0) { - log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue); + if (isempty(rvalue)) { + free(c->apparmor_profile); + c->apparmor_profile = NULL; + c->apparmor_profile_ignore = false; return 0; } - *sig = r; + if (rvalue[0] == '-') { + ignore = true; + rvalue++; + } else + ignore = false; + + 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)); + return 0; + } + + free(c->apparmor_profile); + c->apparmor_profile = k; + c->apparmor_profile_ignore = ignore; + return 0; } -int config_parse_exec_mount_flags( +int config_parse_exec_smack_process_label( + const char *unit, const char *filename, unsigned line, const char *section, + unsigned section_line, const char *lvalue, int ltype, const char *rvalue, @@ -1108,56 +1326,59 @@ int config_parse_exec_mount_flags( void *userdata) { ExecContext *c = data; - char *w; - size_t l; - char *state; - unsigned long flags = 0; + Unit *u = userdata; + bool ignore; + char *k; + int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); - FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) { - char _cleanup_free_ *t; + if (isempty(rvalue)) { + free(c->smack_process_label); + c->smack_process_label = NULL; + c->smack_process_label_ignore = false; + return 0; + } - t = strndup(w, l); - if (!t) - return log_oom(); + if (rvalue[0] == '-') { + ignore = true; + rvalue++; + } else + ignore = false; - if (streq(t, "shared")) - flags |= MS_SHARED; - else if (streq(t, "slave")) - flags |= MS_SLAVE; - else if (streq(w, "private")) - flags |= MS_PRIVATE; - else { - log_error("[%s:%u] Failed to parse mount flag %s, ignoring: %s", - filename, line, t, rvalue); - return 0; - } + 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)); + return 0; } - c->mount_flags = flags; + free(c->smack_process_label); + c->smack_process_label = k; + c->smack_process_label_ignore = ignore; + return 0; } -int config_parse_timer( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_timer(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) { Timer *t = data; usec_t u = 0; TimerValue *v; TimerBase b; CalendarSpec *c = NULL; - clockid_t id; assert(filename); assert(lvalue); @@ -1172,98 +1393,114 @@ int config_parse_timer( b = timer_base_from_string(lvalue); if (b < 0) { - log_error("[%s:%u] Failed to parse timer base, ignoring: %s", filename, line, lvalue); + log_syntax(unit, LOG_ERR, filename, line, -b, + "Failed to parse timer base, ignoring: %s", lvalue); return 0; } if (b == TIMER_CALENDAR) { if (calendar_spec_from_string(rvalue, &c) < 0) { - log_error("[%s:%u] Failed to parse calendar specification, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse calendar specification, ignoring: %s", + rvalue); return 0; } - - id = CLOCK_REALTIME; } else { - if (parse_usec(rvalue, &u) < 0) { - log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue); + if (parse_sec(rvalue, &u) < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse timer value, ignoring: %s", + rvalue); return 0; } - - id = CLOCK_MONOTONIC; } v = new0(TimerValue, 1); - if (!v) + if (!v) { + calendar_spec_free(c); return log_oom(); + } v->base = b; - v->clock_id = id; v->value = u; v->calendar_spec = c; - LIST_PREPEND(TimerValue, value, t->values, v); + LIST_PREPEND(value, t->values, v); return 0; } -int config_parse_timer_unit( +int config_parse_trigger_unit( + 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) { - Timer *t = data; - int r; - DBusError error; - Unit *u; _cleanup_free_ char *p = NULL; + Unit *u = data; + UnitType type; + int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); - dbus_error_init(&error); + if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Multiple units to trigger specified, ignoring: %s", rvalue); + return 0; + } - p = unit_name_printf(UNIT(t), rvalue); - if (!p) - return log_oom(); + 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 (endswith(p, ".timer")) { - log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue); + type = unit_name_to_type(p ?: rvalue); + if (type < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Unit type not valid, ignoring: %s", rvalue); return 0; } - r = manager_load_unit(UNIT(t)->manager, p, NULL, NULL, &u); - if (r < 0) { - log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); - dbus_error_free(&error); + if (type == u->type) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trigger cannot be of same type, ignoring: %s", rvalue); return 0; } - unit_ref_set(&t->unit, u); + r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, 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)); + return 0; + } return 0; } -int config_parse_path_spec( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_path_spec(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) { Path *p = data; PathSpec *s; PathType b; - char *k; + _cleanup_free_ char *k = NULL; + int r; assert(filename); assert(lvalue); @@ -1278,49 +1515,58 @@ int config_parse_path_spec( b = path_type_from_string(lvalue); if (b < 0) { - log_error("[%s:%u] Failed to parse path type, ignoring: %s", filename, line, lvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse path type, ignoring: %s", lvalue); return 0; } - k = unit_full_printf(UNIT(p), rvalue); - if (!k) - return log_oom(); + 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); + } if (!path_is_absolute(k)) { - log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, k); - free(k); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Path is not absolute, ignoring: %s", k); return 0; } s = new0(PathSpec, 1); - if (!s) { - free(k); + if (!s) return log_oom(); - } + s->unit = UNIT(p); s->path = path_kill_slashes(k); + k = NULL; s->type = b; s->inotify_fd = -1; - LIST_PREPEND(PathSpec, spec, p->specs, s); + LIST_PREPEND(spec, p->specs, s); return 0; } -int config_parse_path_unit( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Path *t = data; +int config_parse_socket_service(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_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + Socket *s = data; int r; - DBusError error; - Unit *u; + Unit *x; _cleanup_free_ char *p = NULL; assert(filename); @@ -1328,65 +1574,23 @@ int config_parse_path_unit( assert(rvalue); assert(data); - dbus_error_init(&error); - - p = unit_name_printf(UNIT(t), rvalue); - if (!p) - return log_oom(); - - if (endswith(p, ".path")) { - log_error("[%s:%u] Unit cannot be of type path, ignoring: %s", filename, line, p); - return 0; - } - - r = manager_load_unit(UNIT(t)->manager, p, NULL, &error, &u); + r = unit_name_printf(UNIT(s), rvalue, &p); if (r < 0) { - log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, p, bus_error(&error, r)); - dbus_error_free(&error); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve specifiers, ignoring: %s", rvalue); return 0; } - unit_ref_set(&t->unit, u); - - return 0; -} - -int config_parse_socket_service( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Socket *s = data; - int r; - DBusError error; - Unit *x; - _cleanup_free_ char *p = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - dbus_error_init(&error); - - p = unit_name_printf(UNIT(s), rvalue); - if (!p) - return log_oom(); - if (!endswith(p, ".service")) { - log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "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_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); - dbus_error_free(&error); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r)); return 0; } @@ -1395,19 +1599,20 @@ int config_parse_socket_service( return 0; } -int config_parse_service_sockets( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_service_sockets(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) { Service *s = data; int r; - char *state, *w; + const char *word, *state; size_t l; assert(filename); @@ -1415,45 +1620,51 @@ int config_parse_service_sockets( assert(rvalue); assert(data); - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char _cleanup_free_ *t = NULL, *k = NULL; + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL, *k = NULL; - t = strndup(w, l); + t = strndup(word, l); if (!t) return log_oom(); - k = unit_name_printf(UNIT(s), t); - if (!k) - return log_oom(); + r = unit_name_printf(UNIT(s), t, &k); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve specifiers, ignoring: %s", strerror(-r)); - if (!endswith(k, ".socket")) { - log_error("[%s:%u] Unit must be of type socket, ignoring: %s", - filename, line, k); + if (!endswith(k ?: t, ".socket")) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Unit must be of type socket, ignoring: %s", k ?: t); continue; } - r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true); + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true); if (r < 0) - log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", - filename, line, k, strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to add dependency on %s, ignoring: %s", + k ?: t, strerror(-r)); - r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true); + r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true); if (r < 0) return r; } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trailing garbage, ignoring."); return 0; } -int config_parse_service_timeout( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_service_timeout(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) { Service *s = userdata; int r; @@ -1463,7 +1674,8 @@ int config_parse_service_timeout( assert(rvalue); assert(s); - r = config_parse_usec(filename, line, section, lvalue, ltype, rvalue, data, userdata); + r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype, + rvalue, data, userdata); if (r < 0) return r; @@ -1476,107 +1688,352 @@ int config_parse_service_timeout( return 0; } -int config_parse_unit_env_file( +int config_parse_busname_service( + 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 ***env = data, **k; - Unit *u = userdata; - char *s; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + BusName *n = data; + int r; + Unit *x; + _cleanup_free_ char *p = NULL; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (isempty(rvalue)) { - /* Empty assignment frees the list */ - - strv_free(*env); - *env = NULL; + 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); return 0; } - s = unit_full_printf(u, rvalue); - if (!s) - return log_oom(); - - if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) { - log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, s); - free(s); + if (!endswith(p, ".service")) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Unit must be of type service, ignoring: %s", rvalue); return 0; } - k = strv_append(*env, s); - free(s); - if (!k) - return log_oom(); + 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)); + return 0; + } - strv_free(*env); - *env = k; + unit_ref_set(&n->service, x); return 0; } -int config_parse_ip_tos( +DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world, bus_policy_access, BusPolicyAccess, "Failed to parse bus name policy access"); + +int config_parse_bus_policy( + 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 *ip_tos = data, x; + _cleanup_free_ BusNamePolicy *p = NULL; + _cleanup_free_ char *id_str = NULL; + BusName *busname = data; + char *access_str; assert(filename); assert(lvalue); assert(rvalue); assert(data); - x = ip_tos_from_string(rvalue); - if (x < 0) { - log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue); + p = new0(BusNamePolicy, 1); + if (!p) + return log_oom(); + + if (streq(lvalue, "AllowUser")) + p->type = BUSNAME_POLICY_TYPE_USER; + else if (streq(lvalue, "AllowGroup")) + p->type = BUSNAME_POLICY_TYPE_GROUP; + else + assert_not_reached("Unknown lvalue"); + + id_str = strdup(rvalue); + if (!id_str) + return log_oom(); + + access_str = strpbrk(id_str, WHITESPACE); + if (!access_str) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid busname policy value '%s'", rvalue); return 0; } - *ip_tos = x; + *access_str = '\0'; + access_str++; + access_str += strspn(access_str, WHITESPACE); + + 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); + return 0; + } + + p->name = id_str; + id_str = NULL; + + LIST_PREPEND(policy, busname->policy, p); + p = NULL; + return 0; } -int config_parse_unit_condition_path( +int config_parse_bus_endpoint_policy( + 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) { - ConditionType cond = ltype; - Unit *u = data; - bool trigger, negate; - Condition *c; - _cleanup_free_ char *p = NULL; + _cleanup_free_ char *name = NULL; + BusPolicyAccess access; + ExecContext *c = data; + char *access_str; + int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (isempty(rvalue)) { - /* Empty assignment resets the list */ - condition_free_list(u->conditions); - u->conditions = NULL; - return 0; + name = strdup(rvalue); + if (!name) + return log_oom(); + + access_str = strpbrk(name, WHITESPACE); + if (!access_str) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid endpoint policy value '%s'", rvalue); + return 0; + } + + *access_str = '\0'; + access_str++; + access_str += strspn(access_str, WHITESPACE); + + 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); + return 0; + } + + if (!c->bus_endpoint) { + r = bus_endpoint_new(&c->bus_endpoint); + + if (r < 0) + return r; + } + + return bus_endpoint_add_policy(c->bus_endpoint, name, access); +} + +int config_parse_unit_env_file(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 ***env = data; + Unit *u = userdata; + _cleanup_free_ char *n = NULL; + const char *s; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment frees the list */ + strv_free(*env); + *env = NULL; + return 0; + } + + 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); + + 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); + return 0; + } + + r = strv_extend(env, s); + if (r < 0) + return log_oom(); + + return 0; +} + +int config_parse_environ(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; + char*** env = data; + const char *word, *state; + size_t l; + _cleanup_free_ char *k = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + strv_free(*env); + *env = NULL; + return 0; + } + + 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 (!k) + k = strdup(rvalue); + if (!k) + return log_oom(); + + FOREACH_WORD_QUOTED(word, l, k, state) { + _cleanup_free_ char *n; + char **x; + + n = cunescape_length(word, l); + if (!n) + return log_oom(); + + if (!env_assignment_is_valid(n)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid environment assignment, ignoring: %s", rvalue); + continue; + } + + x = strv_env_set(*env, n); + if (!x) + return log_oom(); + + strv_free(*env); + *env = x; + } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trailing garbage, ignoring."); + + return 0; +} + +int config_parse_ip_tos(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 *ip_tos = data, x; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + 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); + return 0; + } + + *ip_tos = x; + return 0; +} + +int config_parse_unit_condition_path( + 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; + Condition **list = data, *c; + ConditionType t = ltype; + bool trigger, negate; + Unit *u = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + condition_free_list(*list); + *list = NULL; + return 0; } trigger = rvalue[0] == '|'; @@ -1587,38 +2044,43 @@ int config_parse_unit_condition_path( if (negate) rvalue++; - p = unit_full_printf(u, rvalue); - if (!p) - return log_oom(); + 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); + return 0; + } if (!path_is_absolute(p)) { - log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, p); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path in condition not absolute, ignoring: %s", p); return 0; } - c = condition_new(cond, p, trigger, negate); + c = condition_new(t, p, trigger, negate); if (!c) return log_oom(); - LIST_PREPEND(Condition, conditions, u->conditions, c); + LIST_PREPEND(conditions, *list, c); return 0; } int config_parse_unit_condition_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) { - ConditionType cond = ltype; - Unit *u = data; - bool trigger, negate; - Condition *c; _cleanup_free_ char *s = NULL; + Condition **list = data, *c; + ConditionType t = ltype; + bool trigger, negate; + Unit *u = userdata; + int r; assert(filename); assert(lvalue); @@ -1627,8 +2089,8 @@ int config_parse_unit_condition_string( if (isempty(rvalue)) { /* Empty assignment resets the list */ - condition_free_list(u->conditions); - u->conditions = NULL; + condition_free_list(*list); + *list = NULL; return 0; } @@ -1640,30 +2102,33 @@ int config_parse_unit_condition_string( if (negate) rvalue++; - s = unit_full_printf(u, rvalue); - if (!s) - return log_oom(); + 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); + return 0; + } - c = condition_new(cond, s, trigger, negate); + c = condition_new(t, s, trigger, negate); if (!c) return log_oom(); - LIST_PREPEND(Condition, conditions, u->conditions, c); + LIST_PREPEND(conditions, *list, c); return 0; } int config_parse_unit_condition_null( + 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 = data; - Condition *c; + Condition **list = data, *c; bool trigger, negate; int b; @@ -1674,8 +2139,8 @@ int config_parse_unit_condition_null( if (isempty(rvalue)) { /* Empty assignment resets the list */ - condition_free_list(u->conditions); - u->conditions = NULL; + condition_free_list(*list); + *list = NULL; return 0; } @@ -1689,7 +2154,7 @@ int config_parse_unit_condition_null( b = parse_boolean(rvalue); if (b < 0) { - log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, -b, "Failed to parse boolean value in condition, ignoring: %s", rvalue); return 0; } @@ -1700,439 +2165,1059 @@ int config_parse_unit_condition_null( if (!c) return log_oom(); - LIST_PREPEND(Condition, conditions, u->conditions, c); + LIST_PREPEND(conditions, *list, c); return 0; } DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action, failure_action, FailureAction, "Failed to parse failure action specifier"); -int config_parse_unit_cgroup_attr( +int config_parse_unit_requires_mounts_for( + 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 = data; - _cleanup_strv_free_ char **l = NULL; - int r; + Unit *u = userdata; + const char *word, *state; + size_t l; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (isempty(rvalue)) { - /* Empty assignment clears the list */ - cgroup_attribute_free_list(u->cgroup_attributes); - u->cgroup_attributes = NULL; - return 0; - } + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + int r; + _cleanup_free_ char *n; - l = strv_split_quoted(rvalue); - if (!l) - return log_oom(); + n = strndup(word, l); + if (!n) + return log_oom(); - if (strv_length(l) != 2) { - log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; - } + if (!utf8_is_valid(n)) { + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); + continue; + } - r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL, NULL); - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; + 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); + continue; + } } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trailing garbage, ignoring."); return 0; } -int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { - Unit *u = data; +int config_parse_documentation(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; int r; - unsigned long ul; - _cleanup_free_ char *t = NULL; + char **a, **b; assert(filename); assert(lvalue); assert(rvalue); - assert(data); + assert(u); - if (safe_atolu(rvalue, &ul) < 0 || ul < 1) { - log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue); + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + strv_free(u->documentation); + u->documentation = NULL; return 0; } - if (asprintf(&t, "%lu", ul) < 0) - return log_oom(); + r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype, + rvalue, data, userdata); + if (r < 0) + return r; - r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL, NULL); - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; + for (a = b = u->documentation; a && *a; a++) { + + if (is_valid_documentation_url(*a)) + *(b++) = *a; + else { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid URL, ignoring: %s", *a); + free(*a); + } } + if (b) + *b = NULL; - return 0; + return r; } -int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { - Unit *u = data; +#ifdef HAVE_SECCOMP +int config_parse_syscall_filter( + 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) { + + static const char default_syscalls[] = + "execve\0" + "exit\0" + "exit_group\0" + "rt_sigreturn\0" + "sigreturn\0"; + + ExecContext *c = data; + Unit *u = userdata; + bool invert = false; + const char *word, *state; + size_t l; int r; - off_t sz; - _cleanup_free_ char *t = NULL; assert(filename); assert(lvalue); assert(rvalue); - assert(data); + assert(u); - if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) { - log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue); + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + set_free(c->syscall_filter); + c->syscall_filter = NULL; + c->syscall_whitelist = false; return 0; } - if (asprintf(&t, "%llu", (unsigned long long) sz) < 0) - return log_oom(); - - r = unit_add_cgroup_attribute(u, - "memory", - streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes", - t, NULL, NULL); - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; + if (rvalue[0] == '~') { + invert = true; + rvalue++; } - return 0; -} + if (!c->syscall_filter) { + c->syscall_filter = set_new(NULL); + if (!c->syscall_filter) + return log_oom(); -static int device_map(const char *controller, const char *name, const char *value, char **ret) { - _cleanup_strv_free_ char **l = NULL; + if (invert) + /* Allow everything but the ones listed */ + c->syscall_whitelist = false; + else { + const char *i; - assert(controller); - assert(name); - assert(value); - assert(ret); + /* Allow nothing but the ones listed */ + c->syscall_whitelist = true; - l = strv_split_quoted(value); - if (!l) - return -ENOMEM; + /* Accept default syscalls if we are on a whitelist */ + NULSTR_FOREACH(i, default_syscalls) { + int id; - assert(strv_length(l) >= 1); + id = seccomp_syscall_resolve_name(i); + if (id < 0) + continue; - if (streq(l[0], "*")) { + r = set_put(c->syscall_filter, INT_TO_PTR(id + 1)); + if (r == -EEXIST) + continue; + if (r < 0) + return log_oom(); + } + } + } - if (asprintf(ret, "a *:*%s%s", - isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) - return -ENOMEM; - } else { - struct stat st; + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL; + int id; - if (stat(l[0], &st) < 0) { - log_warning("Couldn't stat device %s", l[0]); - return -errno; - } + t = strndup(word, l); + if (!t) + return log_oom(); - if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) { - log_warning("%s is not a device.", l[0]); - return -ENODEV; + 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); + continue; } - if (asprintf(ret, "%c %u:%u%s%s", - S_ISCHR(st.st_mode) ? 'c' : 'b', - major(st.st_rdev), minor(st.st_rdev), - isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) - return -ENOMEM; + /* If we previously wanted to forbid a syscall and now + * we want to allow it, then remove it from the list + */ + if (!invert == c->syscall_whitelist) { + r = set_put(c->syscall_filter, INT_TO_PTR(id + 1)); + if (r == -EEXIST) + continue; + if (r < 0) + return log_oom(); + } else + set_remove(c->syscall_filter, INT_TO_PTR(id + 1)); } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trailing garbage, ignoring."); + + /* Turn on NNP, but only if it wasn't configured explicitly + * before, and only if we are in user mode. */ + if (!c->no_new_privileges_set && u->manager->running_as == SYSTEMD_USER) + c->no_new_privileges = true; return 0; } -int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { - Unit *u = data; - _cleanup_strv_free_ char **l = NULL; +int config_parse_syscall_archs( + 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) { + + Set **archs = data; + const char *word, *state; + size_t l; + int r; + + if (isempty(rvalue)) { + set_free(*archs); + *archs = NULL; + return 0; + } + + r = set_ensure_allocated(archs, NULL); + if (r < 0) + return log_oom(); + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL; + uint32_t a; + + t = strndup(word, l); + if (!t) + return log_oom(); + + 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); + continue; + } + + r = set_put(*archs, UINT32_TO_PTR(a + 1)); + if (r == -EEXIST) + continue; + 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_syscall_errno( + 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; + int e; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + /* Empty assignment resets to KILL */ + c->syscall_errno = 0; + return 0; + } + + e = errno_from_name(rvalue); + if (e < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse error number, ignoring: %s", rvalue); + return 0; + } + + c->syscall_errno = e; + return 0; +} + +int config_parse_address_families( + 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 invert = false; + const char *word, *state; + size_t l; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + set_free(c->address_families); + c->address_families = NULL; + c->address_families_whitelist = false; + return 0; + } + + if (rvalue[0] == '~') { + invert = true; + rvalue++; + } + + if (!c->address_families) { + c->address_families = set_new(NULL); + if (!c->address_families) + return log_oom(); + + c->address_families_whitelist = !invert; + } + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL; + int af; + + t = strndup(word, l); + if (!t) + return log_oom(); + + af = af_from_name(t); + if (af <= 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse address family, ignoring: %s", t); + continue; + } + + /* If we previously wanted to forbid an address family and now + * we want to allow it, then remove it from the list + */ + if (!invert == c->address_families_whitelist) { + r = set_put(c->address_families, INT_TO_PTR(af)); + if (r == -EEXIST) + continue; + if (r < 0) + return log_oom(); + } else + set_remove(c->address_families, INT_TO_PTR(af)); + } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trailing garbage, ignoring."); + + return 0; +} +#endif + +int config_parse_unit_slice( + 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, *slice; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + r = unit_name_printf(u, rvalue, &k); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + if (!k) { + k = strdup(rvalue); + if (!k) + return log_oom(); + } + + r = manager_load_unit(u->manager, k, NULL, NULL, &slice); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to load slice unit %s. Ignoring.", k); + return 0; + } + + if (slice->type != UNIT_SLICE) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Slice unit %s is not a slice. Ignoring.", k); + return 0; + } + + unit_ref_set(&u->slice, slice); + return 0; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy"); + +int config_parse_cpu_shares( + 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 *shares = data, lu; + 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); + return 0; + } + + *shares = lu; + return 0; +} + +int config_parse_cpu_quota( + 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; + double percent; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + c->cpu_quota_per_sec_usec = USEC_INFINITY; + return 0; + } + + if (!endswith(rvalue, "%")) { + + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "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); + return 0; + } + + c->cpu_quota_per_sec_usec = (usec_t) (percent * USEC_PER_SEC / 100); + + return 0; +} + +int config_parse_memory_limit( + 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; + off_t bytes; + int r; + + if (isempty(rvalue)) { + c->memory_limit = (uint64_t) -1; + return 0; + } + + assert_cc(sizeof(uint64_t) == sizeof(off_t)); + + r = parse_size(rvalue, 1024, &bytes); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Memory limit '%s' invalid. Ignoring.", rvalue); + return 0; + } + + c->memory_limit = (uint64_t) bytes; + return 0; +} + +int config_parse_device_allow( + 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 *path = NULL; + CGroupContext *c = data; + CGroupDeviceAllow *a; + const char *m; + size_t n; + + if (isempty(rvalue)) { + while (c->device_allow) + cgroup_context_free_device_allow(c, c->device_allow); + + return 0; + } + + n = strcspn(rvalue, WHITESPACE); + path = strndup(rvalue, n); + if (!path) + return log_oom(); + + 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); + return 0; + } + + m = rvalue + n + strspn(rvalue + n, WHITESPACE); + if (isempty(m)) + m = "rwm"; + + if (!in_charset(m, "rwm")) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid device rights '%s'. Ignoring.", m); + return 0; + } + + a = new0(CGroupDeviceAllow, 1); + if (!a) + return log_oom(); + + a->path = path; + path = NULL; + a->r = !!strchr(m, 'r'); + a->w = !!strchr(m, 'w'); + a->m = !!strchr(m, 'm'); + + LIST_PREPEND(device_allow, c->device_allow, a); + return 0; +} + +int config_parse_blockio_weight( + 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 *weight = data, lu; + 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); + return 0; + } + + *weight = lu; + return 0; +} + +int config_parse_blockio_device_weight( + 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 *path = NULL; + CGroupBlockIODeviceWeight *w; + CGroupContext *c = data; + unsigned long lu; + const char *weight; + size_t n; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + while (c->blockio_device_weights) + cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); + + return 0; + } + + n = strcspn(rvalue, WHITESPACE); + weight = rvalue + n; + if (!*weight) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Expected block device and device weight. Ignoring."); + return 0; + } + + path = strndup(rvalue, n); + if (!path) + return log_oom(); + + if (!path_startswith(path, "/dev")) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "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); + return 0; + } + + w = new0(CGroupBlockIODeviceWeight, 1); + if (!w) + return log_oom(); + + w->path = path; + path = NULL; + + w->weight = lu; + + LIST_PREPEND(device_weights, c->blockio_device_weights, w); + return 0; +} + +int config_parse_blockio_bandwidth( + 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 *path = NULL; + CGroupBlockIODeviceBandwidth *b; + CGroupContext *c = data; + const char *bandwidth; + off_t bytes; + bool read; + size_t n; int r; - unsigned k; assert(filename); assert(lvalue); assert(rvalue); - assert(data); - l = strv_split_quoted(rvalue); - if (!l) - return log_oom(); + read = streq("BlockIOReadBandwidth", lvalue); - k = strv_length(l); - if (k < 1 || k > 2) { - log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue); - return 0; - } + if (isempty(rvalue)) { + CGroupBlockIODeviceBandwidth *next; - if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) { - log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue); - return 0; - } + LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths) + if (b->read == read) + cgroup_context_free_blockio_device_bandwidth(c, b); - if (!isempty(l[1]) && !in_charset(l[1], "rwm")) { - log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue); return 0; } - r = unit_add_cgroup_attribute(u, "devices", - streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny", - rvalue, device_map, NULL); + n = strcspn(rvalue, WHITESPACE); + bandwidth = rvalue + n; + bandwidth += strspn(bandwidth, WHITESPACE); - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + if (!*bandwidth) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Expected space separated pair of device node and bandwidth. Ignoring."); return 0; } - return 0; -} - -static int blkio_map(const char *controller, const char *name, const char *value, char **ret) { - struct stat st; - _cleanup_strv_free_ char **l = NULL; - dev_t d; - - assert(controller); - assert(name); - assert(value); - assert(ret); - - l = strv_split_quoted(value); - if (!l) + path = strndup(rvalue, n); + if (!path) return log_oom(); - assert(strv_length(l) == 2); + if (!path_startswith(path, "/dev")) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid device node path '%s'. Ignoring.", path); + return 0; + } - if (stat(l[0], &st) < 0) { - log_warning("Couldn't stat device %s", l[0]); - return -errno; + 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); + return 0; } - if (S_ISBLK(st.st_mode)) - d = st.st_rdev; - else if (major(st.st_dev) != 0) { - /* If this is not a device node then find the block - * device this file is stored on */ - d = st.st_dev; + b = new0(CGroupBlockIODeviceBandwidth, 1); + if (!b) + return log_oom(); - /* If this is a partition, try to get the originating - * block device */ - block_get_whole_disk(d, &d); - } else { - log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]); - return -ENODEV; - } + b->path = path; + path = NULL; + b->bandwidth = (uint64_t) bytes; + b->read = read; - if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) - return -ENOMEM; + LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b); return 0; } -int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { - Unit *u = data; +DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode"); + +int config_parse_job_mode_isolate( + 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) { + + JobMode *m = data; int r; - unsigned long ul; - const char *device = NULL, *weight; - unsigned k; - _cleanup_free_ char *t = NULL; - _cleanup_strv_free_ char **l = NULL; assert(filename); assert(lvalue); assert(rvalue); - assert(data); - - l = strv_split_quoted(rvalue); - if (!l) - return log_oom(); - k = strv_length(l); - if (k < 1 || k > 2) { - log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue); + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse boolean, ignoring: %s", rvalue); return 0; } - if (k == 1) - weight = l[0]; - else { - device = l[0]; - weight = l[1]; - } + *m = r ? JOB_ISOLATE : JOB_REPLACE; + return 0; +} - if (device && !path_is_absolute(device)) { - log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue); - 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) { - if (safe_atolu(weight, &ul) < 0 || ul < 10 || ul > 1000) { - log_error("[%s:%u] Failed to parse block IO weight value, ignoring: %s", filename, line, rvalue); - return 0; - } + unsigned long *personality = data, p; - if (device) - r = asprintf(&t, "%s %lu", device, ul); - else - r = asprintf(&t, "%lu", ul); - if (r < 0) - return log_oom(); + assert(filename); + assert(lvalue); + assert(rvalue); + assert(personality); - if (device) - r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map, NULL); - else - r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL, NULL); - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + p = personality_from_string(rvalue); + if (p == 0xffffffffUL) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse personality, ignoring: %s", rvalue); return 0; } + *personality = p; return 0; } -int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { - Unit *u = data; +int config_parse_runtime_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) { + + char***rt = data; + const char *word, *state; + size_t l; int r; - off_t bytes; - unsigned k; - _cleanup_free_ char *t = NULL; - _cleanup_strv_free_ char **l = NULL; assert(filename); assert(lvalue); assert(rvalue); assert(data); - l = strv_split_quoted(rvalue); - if (!l) - return log_oom(); - - k = strv_length(l); - if (k != 2) { - log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue); + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + strv_free(*rt); + *rt = NULL; return 0; } - if (!path_is_absolute(l[0])) { - log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue); - return 0; - } + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *n; - if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) { - log_error("[%s:%u] Failed to parse block IO bandwidth value, ignoring: %s", filename, line, rvalue); - return 0; - } + n = strndup(word, l); + if (!n) + return log_oom(); - r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes); - if (r < 0) - return log_oom(); + if (!filename_is_safe(n)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Runtime directory is not valid, ignoring assignment: %s", rvalue); + continue; + } - r = unit_add_cgroup_attribute(u, "blkio", - streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device", - t, blkio_map, NULL); - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; + r = strv_push(rt, n); + if (r < 0) + return log_oom(); + + n = NULL; } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trailing garbage, ignoring."); return 0; } -int config_parse_unit_requires_mounts_for( +int config_parse_set_status( + 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; + size_t l; + const char *word, *state; int r; - bool empty_before; + ExitStatusSet *status_set = data; assert(filename); assert(lvalue); assert(rvalue); assert(data); - empty_before = !u->requires_mounts_for; + /* Empty assignment resets the list */ + if (isempty(rvalue)) { + exit_status_set_free(status_set); + return 0; + } + + FOREACH_WORD(word, l, rvalue, state) { + _cleanup_free_ char *temp; + int val; - r = config_parse_path_strv(filename, line, section, lvalue, ltype, rvalue, data, userdata); + temp = strndup(word, l); + if (!temp) + return log_oom(); - /* Make it easy to find units with requires_mounts set */ - if (empty_before && u->requires_mounts_for) - LIST_PREPEND(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u); + r = safe_atoi(temp, &val); + if (r < 0) { + val = signal_from_string_try_harder(temp); - return r; + if (val <= 0) { + log_syntax(unit, LOG_ERR, filename, line, -val, + "Failed to parse value, ignoring: %s", word); + return 0; + } + } else { + if (val < 0 || val > 255) { + log_syntax(unit, LOG_ERR, filename, line, ERANGE, + "Value %d is outside range 0-255, ignoring", val); + continue; + } + } + + r = set_ensure_allocated(&status_set->status, NULL); + if (r < 0) + return log_oom(); + + r = set_put(status_set->status, INT_TO_PTR(val)); + if (r < 0) { + 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."); + + return 0; } -int config_parse_documentation( +int config_parse_namespace_path_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) { - Unit *u = userdata; + char*** sv = data; + const char *word, *state; + size_t l; int r; - char **a, **b; assert(filename); assert(lvalue); assert(rvalue); - assert(u); + assert(data); if (isempty(rvalue)) { /* Empty assignment resets the list */ - strv_free(u->documentation); - u->documentation = NULL; + strv_free(*sv); + *sv = NULL; return 0; } - r = config_parse_unit_strv_printf(filename, line, section, lvalue, ltype, rvalue, data, userdata); - if (r < 0) - return r; + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *n; + int offset; - for (a = b = u->documentation; a && *a; a++) { + n = strndup(word, l); + if (!n) + return log_oom(); - if (is_valid_documentation_url(*a)) - *(b++) = *a; - else { - log_error("[%s:%u] Invalid URL, ignoring: %s", filename, line, *a); - free(*a); + if (!utf8_is_valid(n)) { + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); + continue; } - } - *b = NULL; - return r; -} + offset = n[0] == '-'; + if (!path_is_absolute(n + offset)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Not an absolute path, ignoring: %s", rvalue); + continue; + } -static void syscall_set(uint32_t *p, int nr) { - nr = SYSCALL_TO_INDEX(nr); - p[nr >> 4] |= 1 << (nr & 31); -} + path_kill_slashes(n); + + r = strv_push(sv, n); + if (r < 0) + return log_oom(); + + n = NULL; + } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trailing garbage, ignoring."); -static void syscall_unset(uint32_t *p, int nr) { - nr = SYSCALL_TO_INDEX(nr); - p[nr >> 4] &= ~(1 << (nr & 31)); + return 0; } -int config_parse_syscall_filter( +int config_parse_no_new_privileges( + const char* unit, const char *filename, unsigned line, const char *section, + unsigned section_line, const char *lvalue, int ltype, const char *rvalue, @@ -2140,71 +3225,110 @@ int config_parse_syscall_filter( void *userdata) { ExecContext *c = data; - Unit *u = userdata; - bool invert = false; - char *w; - size_t l; - char *state; + int k; assert(filename); assert(lvalue); assert(rvalue); - assert(u); + assert(data); - if (isempty(rvalue)) { - /* Empty assignment resets the list */ - free(c->syscall_filter); - c->syscall_filter = NULL; + 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 (rvalue[0] == '~') { - invert = true; - rvalue++; - } + c->no_new_privileges = !!k; + c->no_new_privileges_set = true; - if (!c->syscall_filter) { - size_t n; + return 0; +} - n = (syscall_max() + 31) >> 4; - c->syscall_filter = new(uint32_t, n); - if (!c->syscall_filter) - return log_oom(); +int config_parse_protect_home( + 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) { - memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t)); + ExecContext *c = data; + int k; - /* Add these by default */ - syscall_set(c->syscall_filter, __NR_execve); - syscall_set(c->syscall_filter, __NR_rt_sigreturn); -#ifdef __NR_sigreturn - syscall_set(c->syscall_filter, __NR_sigreturn); -#endif - syscall_set(c->syscall_filter, __NR_exit_group); - syscall_set(c->syscall_filter, __NR_exit); - } + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - int id; - char _cleanup_free_ *t = NULL; + /* Our enum shall be a superset of booleans, hence first try + * to parse as as boolean, and then as enum */ - t = strndup(w, l); - if (!t) - return log_oom(); + k = parse_boolean(rvalue); + if (k > 0) + c->protect_home = PROTECT_HOME_YES; + else if (k == 0) + c->protect_home = PROTECT_HOME_NO; + else { + ProtectHome h; - id = syscall_from_name(t); - if (id < 0) { - log_error("[%s:%u] Failed to parse syscall, ignoring: %s", - filename, line, t); - continue; + 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); + return 0; } - if (invert) - syscall_unset(c->syscall_filter, id); - else - syscall_set(c->syscall_filter, id); + c->protect_home = h; } - c->no_new_privileges = true; + return 0; +} + +int config_parse_protect_system( + 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; + int k; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* Our enum shall be a superset of booleans, hence first try + * to parse as as boolean, and then as enum */ + + k = parse_boolean(rvalue); + if (k > 0) + c->protect_system = PROTECT_SYSTEM_YES; + else if (k == 0) + c->protect_system = PROTECT_SYSTEM_NO; + else { + ProtectSystem s; + + 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); + return 0; + } + + c->protect_system = s; + } return 0; } @@ -2236,9 +3360,9 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { /* Add the file name we are currently looking at to * the names of this unit, but only if it is a valid * unit name. */ - name = path_get_file_name(*filename); + name = basename(*filename); - if (unit_name_is_valid(name, true)) { + if (unit_name_is_valid(name, TEMPLATE_VALID)) { id = set_get(names, name); if (!id) { @@ -2246,11 +3370,9 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { if (!id) return -ENOMEM; - r = set_put(names, id); - if (r < 0) { - free(id); + r = set_consume(names, id); + if (r < 0) return r; - } } } @@ -2274,7 +3396,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { f = fdopen(fd, "re"); if (!f) { r = -errno; - close_nointr_nofail(fd); + safe_close(fd); return r; } @@ -2329,26 +3451,25 @@ static int merge_by_names(Unit **u, Set *names, const char *id) { static int load_from_path(Unit *u, const char *path) { int r; - Set *symlink_names; - FILE *f = NULL; - char *filename = NULL, *id = NULL; + _cleanup_set_free_free_ Set *symlink_names = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *filename = NULL; + char *id = NULL; Unit *merged; struct stat st; assert(u); assert(path); - symlink_names = set_new(string_hash_func, string_compare_func); + symlink_names = set_new(&string_hash_ops); if (!symlink_names) return -ENOMEM; if (path_is_absolute(path)) { filename = strdup(path); - if (!filename) { - r = -ENOMEM; - goto finish; - } + if (!filename) + return -ENOMEM; r = open_follow(&filename, &f, symlink_names, &id); if (r < 0) { @@ -2356,7 +3477,7 @@ static int load_from_path(Unit *u, const char *path) { filename = NULL; if (r != -ENOENT) - goto finish; + return r; } } else { @@ -2368,10 +3489,8 @@ static int load_from_path(Unit *u, const char *path) { * follow all symlinks and add their name to our unit * name set while doing so */ filename = path_make_absolute(path, *p); - if (!filename) { - r = -ENOMEM; - goto finish; - } + if (!filename) + return -ENOMEM; if (u->manager->unit_path_cache && !set_get(u->manager->unit_path_cache, filename)) @@ -2384,7 +3503,7 @@ static int load_from_path(Unit *u, const char *path) { filename = NULL; if (r != -ENOENT) - goto finish; + return r; /* Empty the symlink names for the next run */ set_clear_free(symlink_names); @@ -2395,37 +3514,35 @@ static int load_from_path(Unit *u, const char *path) { } } - if (!filename) { + if (!filename) /* Hmm, no suitable file found? */ - r = 0; - goto finish; - } + return 0; merged = u; r = merge_by_names(&merged, symlink_names, id); if (r < 0) - goto finish; + return r; if (merged != u) { u->load_state = UNIT_MERGED; - r = 0; - goto finish; + return 0; } - if (fstat(fileno(f), &st) < 0) { - r = -errno; - goto finish; - } + if (fstat(fileno(f), &st) < 0) + return -errno; if (null_or_empty(&st)) u->load_state = UNIT_MASKED; else { + u->load_state = UNIT_LOADED; + /* Now, parse the file contents */ - r = config_parse(filename, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, (void*) load_fragment_gperf_lookup, false, u); + r = config_parse(u->id, filename, f, + UNIT_VTABLE(u)->sections, + config_item_perf_lookup, load_fragment_gperf_lookup, + false, true, false, u); if (r < 0) - goto finish; - - u->load_state = UNIT_LOADED; + return r; } free(u->fragment_path); @@ -2441,16 +3558,7 @@ static int load_from_path(Unit *u, const char *path) { u->source_mtime = 0; } - r = 0; - -finish: - set_free_free(symlink_names); - free(filename); - - if (f) - fclose(f); - - return r; + return 0; } int unit_load_fragment(Unit *u) { @@ -2502,31 +3610,28 @@ int unit_load_fragment(Unit *u) { /* Look for a template */ if (u->load_state == UNIT_STUB && u->instance) { - char *k; + _cleanup_free_ char *k; k = unit_name_template(u->id); if (!k) return -ENOMEM; r = load_from_path(u, k); - free(k); - if (r < 0) return r; if (u->load_state == UNIT_STUB) SET_FOREACH(t, u->names, i) { + _cleanup_free_ char *z = NULL; if (t == u->id) continue; - k = unit_name_template(t); - if (!k) + z = unit_name_template(t); + if (!z) return -ENOMEM; - r = load_from_path(u, k); - free(k); - + r = load_from_path(u, z); if (r < 0) return r; @@ -2543,9 +3648,14 @@ void unit_dump_config_items(FILE *f) { const ConfigParserCallback callback; const char *rvalue; } table[] = { +#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR) + { config_parse_warn_compat, "NOTSUPPORTED" }, +#endif { config_parse_int, "INTEGER" }, { config_parse_unsigned, "UNSIGNED" }, - { config_parse_bytes_size, "SIZE" }, + { config_parse_iec_size, "SIZE" }, + { config_parse_iec_off, "SIZE" }, + { config_parse_si_size, "SIZE" }, { config_parse_bool, "BOOLEAN" }, { config_parse_string, "STRING" }, { config_parse_path, "PATH" }, @@ -2562,42 +3672,66 @@ void unit_dump_config_items(FILE *f) { { config_parse_unit_env_file, "FILE" }, { config_parse_output, "OUTPUT" }, { config_parse_input, "INPUT" }, - { config_parse_facility, "FACILITY" }, - { config_parse_level, "LEVEL" }, + { config_parse_log_facility, "FACILITY" }, + { config_parse_log_level, "LEVEL" }, { config_parse_exec_capabilities, "CAPABILITIES" }, { config_parse_exec_secure_bits, "SECUREBITS" }, { config_parse_bounding_set, "BOUNDINGSET" }, { config_parse_limit, "LIMIT" }, - { config_parse_unit_cgroup, "CGROUP [...]" }, { config_parse_unit_deps, "UNIT [...]" }, { config_parse_exec, "PATH [ARGUMENT [...]]" }, { config_parse_service_type, "SERVICETYPE" }, { config_parse_service_restart, "SERVICERESTART" }, #ifdef HAVE_SYSV_COMPAT { config_parse_sysv_priority, "SYSVPRIORITY" }, -#else - { config_parse_warn_compat, "NOTSUPPORTED" }, #endif { config_parse_kill_mode, "KILLMODE" }, { config_parse_kill_signal, "SIGNAL" }, { config_parse_socket_listen, "SOCKET [...]" }, { config_parse_socket_bind, "SOCKETBIND" }, { config_parse_socket_bindtodevice, "NETWORKINTERFACE" }, - { config_parse_usec, "SECONDS" }, + { config_parse_sec, "SECONDS" }, { config_parse_nsec, "NANOSECONDS" }, - { config_parse_path_strv, "PATH [...]" }, + { config_parse_namespace_path_strv, "PATH [...]" }, { config_parse_unit_requires_mounts_for, "PATH [...]" }, { config_parse_exec_mount_flags, "MOUNTFLAG [...]" }, { config_parse_unit_string_printf, "STRING" }, + { config_parse_trigger_unit, "UNIT" }, { config_parse_timer, "TIMER" }, - { config_parse_timer_unit, "NAME" }, { config_parse_path_spec, "PATH" }, - { config_parse_path_unit, "UNIT" }, { config_parse_notify_access, "ACCESS" }, { config_parse_ip_tos, "TOS" }, { config_parse_unit_condition_path, "CONDITION" }, { config_parse_unit_condition_string, "CONDITION" }, { config_parse_unit_condition_null, "CONDITION" }, + { config_parse_unit_slice, "SLICE" }, + { config_parse_documentation, "URL" }, + { config_parse_service_timeout, "SECONDS" }, + { config_parse_failure_action, "ACTION" }, + { config_parse_set_status, "STATUS" }, + { config_parse_service_sockets, "SOCKETS" }, + { config_parse_environ, "ENVIRON" }, +#ifdef HAVE_SECCOMP + { config_parse_syscall_filter, "SYSCALLS" }, + { config_parse_syscall_archs, "ARCHS" }, + { config_parse_syscall_errno, "ERRNO" }, + { config_parse_address_families, "FAMILIES" }, +#endif + { config_parse_cpu_shares, "SHARES" }, + { config_parse_memory_limit, "LIMIT" }, + { config_parse_device_allow, "DEVICE" }, + { config_parse_device_policy, "POLICY" }, + { config_parse_blockio_bandwidth, "BANDWIDTH" }, + { config_parse_blockio_weight, "WEIGHT" }, + { config_parse_blockio_device_weight, "DEVICEWEIGHT" }, + { config_parse_long, "LONG" }, + { config_parse_socket_service, "SERVICE" }, +#ifdef HAVE_SELINUX + { config_parse_exec_selinux_context, "LABEL" }, +#endif + { config_parse_job_mode, "MODE" }, + { config_parse_job_mode_isolate, "BOOLEAN" }, + { config_parse_personality, "PERSONALITY" }, }; const char *prev = NULL; @@ -2619,7 +3753,7 @@ void unit_dump_config_items(FILE *f) { prefix_len = dot-i; if (dot) - if (!prev || strncmp(prev, i, prefix_len+1) != 0) { + if (!prev || !strneq(prev, i, prefix_len+1)) { if (prev) fputc('\n', f);