X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fload-fragment.c;h=6b6f9908b58ddd8294a68fa796ec024eb228d302;hp=e35fdbc5ecc3809676eddad82469fbc3eb5d1020;hb=b1389b0d0805392570085acc7cb10eafcf885405;hpb=f274ece0f76b5709408821e317e87aef76123db6 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e35fdbc5e..6b6f9908b 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,80 +51,98 @@ #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 -ENOMEM; + return log_oom(); - k = unit_name_printf(u, t); - if (!k) - return -ENOMEM; + 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; - char *k; + _cleanup_free_ char *k = NULL; int r; assert(filename); @@ -125,28 +150,28 @@ int config_parse_unit_string_printf( assert(rvalue); assert(u); - k = unit_full_printf(u, rvalue); - if (!k) - return -ENOMEM; - - r = config_parse_string(filename, line, section, lvalue, ltype, k, data, userdata); - free (k); + 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 r; + 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; - char *k; + _cleanup_free_ char *k = NULL; int r; assert(filename); @@ -154,28 +179,28 @@ int config_parse_unit_strv_printf( assert(rvalue); assert(u); - k = unit_full_printf(u, rvalue); - if (!k) - return -ENOMEM; - - r = config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata); - free(k); + 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 r; + 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) { + + _cleanup_free_ char *k = NULL; Unit *u = userdata; - char *k; int r; assert(filename); @@ -183,98 +208,152 @@ int config_parse_unit_path_printf( assert(rvalue); assert(u); - k = unit_full_printf(u, rvalue); - if (!k) - return log_oom(); - - r = config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata); - free(k); + 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 r; + 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; - Socket *s; + char ***x = data; + const char *word, *state; + Unit *u = userdata; + size_t l; + int r; assert(filename); assert(lvalue); assert(rvalue); - assert(data); + assert(u); - s = SOCKET(data); + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *k = NULL; + char t[l+1]; - p = new0(SocketPort, 1); - if (!p) - return -ENOMEM; + memcpy(t, word, l); + t[l] = 0; - if (streq(lvalue, "ListenFIFO")) { - p->type = SOCKET_FIFO; + 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 (!(p->path = unit_full_printf(UNIT(s), rvalue))) { - free(p); - return -ENOMEM; + if (!utf8_is_valid(k)) { + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); + return 0; } - path_kill_slashes(p->path); + 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; + } - } else if (streq(lvalue, "ListenSpecial")) { - p->type = SOCKET_SPECIAL; + path_kill_slashes(k); - if (!(p->path = unit_full_printf(UNIT(s), rvalue))) { - free(p); - return -ENOMEM; - } + r = strv_push(x, k); + if (r < 0) + return log_oom(); - path_kill_slashes(p->path); + k = NULL; + } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring."); + + return 0; +} - } else if (streq(lvalue, "ListenMessageQueue")) { +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; - p->type = SOCKET_MQUEUE; + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); - if (!(p->path = unit_full_printf(UNIT(s), rvalue))) { - free(p); - return -ENOMEM; + s = SOCKET(data); + + if (isempty(rvalue)) { + /* An empty assignment removes all ports */ + socket_free_ports(s); + return 0; + } + + p = new0(SocketPort, 1); + if (!p) + return log_oom(); + + if (ltype != SOCKET_SOCKET) { + + p->type = ltype; + r = unit_full_printf(UNIT(s), rvalue, &p->path); + if (r < 0) { + p->path = strdup(rvalue); + if (!p->path) + return log_oom(); + else + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r)); } path_kill_slashes(p->path); } else if (streq(lvalue, "ListenNetlink")) { - char *k; - int r; + _cleanup_free_ char *k = NULL; p->type = SOCKET_SOCKET; - k = unit_full_printf(UNIT(s), rvalue); - r = socket_address_parse_netlink(&p->address, k); - free(k); + 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 ?: 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 { - char *k; - int r; + _cleanup_free_ char *k = NULL; p->type = SOCKET_SOCKET; - k = unit_full_printf(UNIT(s), rvalue); - r = socket_address_parse(&p->address, k); - free(k); + 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 ? 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; } @@ -288,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; @@ -331,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; } @@ -342,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; } @@ -376,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; } @@ -410,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; @@ -430,17 +522,22 @@ int config_parse_exec( assert(rvalue); assert(e); + e += ltype; + + if (isempty(rvalue)) { + /* An empty assignment resets the list */ + exec_command_free_list(*e); + *e = NULL; + return 0; + } + /* We accept an absolute path as first argument, or * alternatively an absolute prefixed with @ to allow * overriding of argv[0]. */ - - e += ltype; - for (;;) { int i; - char *w; + const char *word, *state; size_t l; - char *state; bool honour_argv0 = false, ignore = false; path = NULL; @@ -465,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 -ENOMEM; + 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 = -ENOMEM; + 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; } @@ -507,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 = -ENOMEM; + 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; } @@ -524,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; } @@ -532,7 +636,7 @@ int config_parse_exec( if (!path) { path = strdup(n[0]); if (!path) { - r = -ENOMEM; + r = log_oom(); goto fail; } } @@ -541,7 +645,7 @@ int config_parse_exec( nce = new0(ExecCommand, 1); if (!nce) { - r = -ENOMEM; + r = log_oom(); goto fail; } @@ -570,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; @@ -589,8 +694,9 @@ int config_parse_socket_bindtodevice( assert(data); if (rvalue[0] && !streq(rvalue, "*")) { - if (!(n = strdup(rvalue))) - return -ENOMEM; + n = strdup(rvalue); + if (!n) + return log_oom(); } else n = NULL; @@ -603,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; @@ -623,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; } @@ -633,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; } @@ -662,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; @@ -683,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; } @@ -695,36 +808,39 @@ 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; } - /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */ min = sched_get_priority_min(c->cpu_sched_policy); 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; } @@ -734,64 +850,76 @@ 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); assert(rvalue); assert(data); - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char _cleanup_free_ *t = NULL; + if (isempty(rvalue)) { + /* An empty assignment resets the CPU list */ + if (c->cpuset) + CPU_FREE(c->cpuset); + c->cpuset = NULL; + return 0; + } + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL; int r; unsigned cpu; - t = strndup(w, l); + t = strndup(word, l); if (!t) - return -ENOMEM; + return log_oom(); r = safe_atou(t, &cpu); if (!c->cpuset) { c->cpuset = cpu_set_malloc(&c->cpuset_ncpus); if (!c->cpuset) - return -ENOMEM; + return log_oom(); } 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; @@ -801,11 +929,10 @@ int config_parse_exec_capabilities( assert(rvalue); assert(data); - if (!(cap = cap_from_text(rvalue))) { - if (errno == ENOMEM) - return -ENOMEM; - - log_error("[%s:%u] Failed to parse capabilities, ignoring: %s", filename, line, rvalue); + cap = cap_from_text(rvalue); + if (!cap) { + log_syntax(unit, LOG_ERR, filename, line, errno, + "Failed to parse capabilities, ignoring: %s", rvalue); return 0; } @@ -816,63 +943,72 @@ 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); assert(rvalue); assert(data); - 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; + if (isempty(rvalue)) { + /* An empty assignment resets the field */ + c->secure_bits = 0; + return 0; + } + + 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<rlim_cur = (*rl)->rlim_max = (rlim_t) u; - return 0; -} - -int config_parse_unit_cgroup( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = userdata; - char *w; - size_t l; - char *state; - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char _cleanup_free_ *t = NULL, *k = NULL, *ku = NULL; + else { int r; - t = strndup(w, l); - if (!t) - return -ENOMEM; - - k = unit_full_printf(u, t); - if (!k) - return -ENOMEM; - - ku = cunescape(k); - if (!ku) - return -ENOMEM; - - r = unit_add_cgroup_from_text(u, ku); + r = safe_atollu(rvalue, &u); if (r < 0) { - log_error("[%s:%u] Failed to parse cgroup value %s, ignoring: %s", - filename, line, k, rvalue); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to parse resource value, ignoring: %s", rvalue); return 0; } } + if (!*rl) { + *rl = new(struct rlimit, 1); + if (!*rl) + return log_oom(); + } + + (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u; 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_sysv_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) { int *priority = data; - int i; + int i, r; 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); + 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; } @@ -1024,66 +1133,142 @@ int config_parse_sysv_priority( } #endif -int config_parse_fsck_passno( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); - int *passno = data; - int i; +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) { + + int *sig = data; + int r; assert(filename); assert(lvalue); assert(rvalue); - assert(data); + assert(sig); - if (safe_atoi(rvalue, &i) || i < 0) { - log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue); + 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; } - *passno = (int) i; + *sig = r; return 0; } -DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); +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) { + + ExecContext *c = data; + const char *word, *state; + size_t l; + unsigned long flags = 0; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); -int config_parse_kill_signal( + 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."); + + c->mount_flags = flags; + return 0; +} + +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 *sig = data; + ExecContext *c = data; + Unit *u = userdata; + bool ignore; + char *k; int r; assert(filename); assert(lvalue); assert(rvalue); - assert(sig); + assert(data); - if ((r = signal_from_string_try_harder(rvalue)) <= 0) { - log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue); + if (isempty(rvalue)) { + free(c->selinux_context); + c->selinux_context = NULL; + c->selinux_context_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->selinux_context); + c->selinux_context = k; + c->selinux_context_ignore = ignore; + return 0; } -int config_parse_exec_mount_flags( +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, @@ -1091,257 +1276,272 @@ 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->apparmor_profile); + c->apparmor_profile = NULL; + c->apparmor_profile_ignore = false; + return 0; + } - t = strndup(w, l); - if (!t) - return -ENOMEM; + 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->apparmor_profile); + c->apparmor_profile = k; + c->apparmor_profile_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); assert(rvalue); assert(data); + if (isempty(rvalue)) { + /* Empty assignment resets list */ + timer_free_values(t); + return 0; + } + 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) - return -ENOMEM; + if (!v) { + if (c) + 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; + _cleanup_free_ char *p = NULL; + Unit *u = data; + UnitType type; int r; - DBusError error; - Unit *u; 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; + } + + 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)); + + 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; + } - if (endswith(rvalue, ".timer")) { - log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue); + if (type == u->type) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trigger cannot be of same type, ignoring: %s", rvalue); return 0; } - r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, NULL, &u); + r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true); 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 add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r)); return 0; } - unit_ref_set(&t->unit, u); - 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); assert(rvalue); assert(data); + if (isempty(rvalue)) { + /* Empty assignment clears list */ + path_free_specs(p); + return 0; + } + 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); - - 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 r; - DBusError error; - Unit *u; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - dbus_error_init(&error); - - if (endswith(rvalue, ".path")) { - log_error("[%s:%u] Unit cannot be of type path, ignoring: %s", filename, line, rvalue); - return 0; - } - - if ((r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, &error, &u)) < 0) { - log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); - dbus_error_free(&error); - return 0; - } - - unit_ref_set(&t->unit, u); + LIST_PREPEND(spec, p->specs, s); 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) { - +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 *x; + _cleanup_free_ char *p = NULL; assert(filename); assert(lvalue); assert(rvalue); assert(data); - dbus_error_init(&error); + r = unit_name_printf(UNIT(s), rvalue, &p); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve specifiers, ignoring: %s", rvalue); + return 0; + } - if (!endswith(rvalue, ".service")) { - log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue); + if (!endswith(p, ".service")) { + 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, rvalue, NULL, &error, &x); + 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; } @@ -1350,19 +1550,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); @@ -1370,45 +1571,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 -ENOMEM; + return log_oom(); - k = unit_name_printf(UNIT(s), t); - if (!k) - return -ENOMEM; + 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; @@ -1418,9 +1625,9 @@ int config_parse_service_timeout( assert(rvalue); assert(s); - r = config_parse_usec(filename, line, section, lvalue, ltype, rvalue, data, userdata); - - if (r) + r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype, + rvalue, data, userdata); + if (r < 0) return r; if (streq(lvalue, "TimeoutSec")) { @@ -1432,94 +1639,353 @@ 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); - s = unit_full_printf(u, rvalue); - if (!s) - return -ENOMEM; + 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; + } - 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 -ENOMEM; + 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; - return 0; -} + *access_str = '\0'; + access_str++; + access_str += strspn(access_str, WHITESPACE); -int config_parse_unit_condition_path( - const char *filename, + 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_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) { + _cleanup_free_ char *name = NULL; + BusPolicyAccess access; + ExecContext *c = data; + char *access_str; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + 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) { + ConditionType cond = ltype; Unit *u = data; bool trigger, negate; Condition *c; _cleanup_free_ char *p = NULL; + 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; + } + trigger = rvalue[0] == '|'; if (trigger) rvalue++; @@ -1528,44 +1994,60 @@ int config_parse_unit_condition_path( if (negate) rvalue++; - p = unit_full_printf(u, rvalue); - if (!p) - return -ENOMEM; + 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); + if (!p) { + p = strdup(rvalue); + if (!p) + return log_oom(); + } 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); if (!c) - return -ENOMEM; + return log_oom(); - LIST_PREPEND(Condition, conditions, u->conditions, c); + LIST_PREPEND(conditions, u->conditions, c); return 0; } -int config_parse_unit_condition_string( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +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; + 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; + } + trigger = rvalue[0] == '|'; if (trigger) rvalue++; @@ -1574,27 +2056,34 @@ int config_parse_unit_condition_string( if (negate) rvalue++; - s = unit_full_printf(u, rvalue); - if (!s) - return -ENOMEM; + 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); + if (!s) { + s = strdup(rvalue); + if (!s) + return log_oom(); + } c = condition_new(cond, s, trigger, negate); if (!c) return log_oom(); - LIST_PREPEND(Condition, conditions, u->conditions, c); + LIST_PREPEND(conditions, u->conditions, c); return 0; } -int config_parse_unit_condition_null( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +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; @@ -1606,477 +2095,1166 @@ int config_parse_unit_condition_null( assert(rvalue); assert(data); - if ((trigger = rvalue[0] == '|')) + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + condition_free_list(u->conditions); + u->conditions = NULL; + return 0; + } + + trigger = rvalue[0] == '|'; + if (trigger) rvalue++; - if ((negate = rvalue[0] == '!')) + negate = rvalue[0] == '!'; + if (negate) rvalue++; - if ((b = parse_boolean(rvalue)) < 0) { - log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue); + b = parse_boolean(rvalue); + if (b < 0) { + log_syntax(unit, LOG_ERR, filename, line, -b, + "Failed to parse boolean value in condition, ignoring: %s", + rvalue); return 0; } if (!b) negate = !negate; - if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate))) - return -ENOMEM; + c = condition_new(CONDITION_NULL, NULL, trigger, negate); + if (!c) + return log_oom(); - LIST_PREPEND(Condition, conditions, u->conditions, c); + LIST_PREPEND(conditions, u->conditions, 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; - char **l; - int r; + Unit *u = userdata; + const char *word, *state; + size_t l; assert(filename); assert(lvalue); assert(rvalue); assert(data); - l = strv_split_quoted(rvalue); - if (!l) - return -ENOMEM; + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + int r; + _cleanup_free_ char *n; - if (strv_length(l) != 2) { - log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } + n = strndup(word, l); + if (!n) + return log_oom(); - r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL); - strv_free(l); + if (!utf8_is_valid(n)) { + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); + continue; + } - 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; - char *t; + 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 -ENOMEM; + 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); - free(t); + for (a = b = u->documentation; a && *a; a++) { - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; + 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; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + set_free(c->syscall_filter); + c->syscall_filter = NULL; + c->syscall_whitelist = false; + return 0; + } + + if (rvalue[0] == '~') { + invert = true; + rvalue++; + } + + if (!c->syscall_filter) { + c->syscall_filter = set_new(NULL); + if (!c->syscall_filter) + return log_oom(); + + if (invert) + /* Allow everything but the ones listed */ + c->syscall_whitelist = false; + else { + const char *i; + + /* Allow nothing but the ones listed */ + c->syscall_whitelist = true; + + /* Accept default syscalls if we are on a whitelist */ + NULSTR_FOREACH(i, default_syscalls) { + int id; + + id = seccomp_syscall_resolve_name(i); + if (id < 0) + continue; + + r = set_put(c->syscall_filter, INT_TO_PTR(id + 1)); + if (r == -EEXIST) + continue; + if (r < 0) + return log_oom(); + } + } + } + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL; + int id; + + t = strndup(word, l); + if (!t) + return log_oom(); + + 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 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_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; - off_t sz; - char *t; assert(filename); assert(lvalue); assert(rvalue); - assert(data); - if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) { - log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue); - return 0; - } + read = streq("BlockIOReadBandwidth", lvalue); - if (asprintf(&t, "%llu", (unsigned long long) sz) < 0) - return -ENOMEM; + if (isempty(rvalue)) { + CGroupBlockIODeviceBandwidth *next; - r = unit_add_cgroup_attribute(u, - "memory", - streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes", - t, NULL); - free(t); + LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths) + if (b->read == read) + cgroup_context_free_blockio_device_bandwidth(c, b); - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); return 0; } - return 0; -} - -static int device_map(const char *controller, const char *name, const char *value, char **ret) { - char **l; - - assert(controller); - assert(name); - assert(value); - assert(ret); + n = strcspn(rvalue, WHITESPACE); + bandwidth = rvalue + n; + bandwidth += strspn(bandwidth, WHITESPACE); - l = strv_split_quoted(value); - if (!l) - return -ENOMEM; - - assert(strv_length(l) >= 1); - - if (streq(l[0], "*")) { + if (!*bandwidth) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Expected space separated pair of device node and bandwidth. Ignoring."); + return 0; + } - if (asprintf(ret, "a *:*%s%s", - isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) { - strv_free(l); - return -ENOMEM; - } + path = strndup(rvalue, n); + if (!path) + return log_oom(); - } else { - struct stat st; + 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]); - strv_free(l); - 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_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) { - log_warning("%s is not a device.", l[0]); - strv_free(l); - return -ENODEV; - } + b = new0(CGroupBlockIODeviceBandwidth, 1); + if (!b) + return log_oom(); - 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) { + b->path = path; + path = NULL; + b->bandwidth = (uint64_t) bytes; + b->read = read; - strv_free(l); - return -ENOMEM; - } - } + LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b); - strv_free(l); 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; - char **l; +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 k; assert(filename); assert(lvalue); assert(rvalue); - assert(data); - - l = strv_split_quoted(rvalue); - if (!l) - return -ENOMEM; - k = strv_length(l); - if (k < 1 || k > 2) { - log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue); - strv_free(l); + 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 (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) { - log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } + *m = r ? JOB_ISOLATE : JOB_REPLACE; + return 0; +} - if (!isempty(l[1]) && !in_charset(l[1], "rwm")) { - log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } - strv_free(l); +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) { - r = unit_add_cgroup_attribute(u, "devices", - streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny", - rvalue, device_map); + unsigned long *personality = data, p; - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + assert(filename); + assert(lvalue); + assert(rvalue); + assert(personality); + + 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; } -static int blkio_map(const char *controller, const char *name, const char *value, char **ret) { - struct stat st; - char **l; - dev_t d; - - assert(controller); - assert(name); - assert(value); - assert(ret); +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) { - l = strv_split_quoted(value); - if (!l) - return -ENOMEM; + char***rt = data; + const char *word, *state; + size_t l; + int r; - assert(strv_length(l) == 2); + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); - if (stat(l[0], &st) < 0) { - log_warning("Couldn't stat device %s", l[0]); - strv_free(l); - return -errno; + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + strv_free(*rt); + *rt = NULL; + 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; + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *n; - /* 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]); - strv_free(l); - return -ENODEV; - } + n = strndup(word, l); + if (!n) + return log_oom(); - if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) { - strv_free(l); - return -ENOMEM; + if (!filename_is_safe(n)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Runtime directory is not valid, ignoring assignment: %s", rvalue); + continue; + } + + 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."); - strv_free(l); 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; +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) { + + size_t l; + const char *word, *state; int r; - unsigned long ul; - const char *device = NULL, *weight; - unsigned k; - char *t, **l; + ExitStatusSet *status_set = data; assert(filename); assert(lvalue); assert(rvalue); assert(data); - l = strv_split_quoted(rvalue); - if (!l) - return -ENOMEM; - - k = strv_length(l); - if (k < 1 || k > 2) { - log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue); - strv_free(l); + /* Empty assignment resets the list */ + if (isempty(rvalue)) { + exit_status_set_free(status_set); return 0; } - if (k == 1) - weight = l[0]; - else { - device = l[0]; - weight = l[1]; - } - - if (device && !path_is_absolute(device)) { - log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } + FOREACH_WORD(word, l, rvalue, state) { + _cleanup_free_ char *temp; + int val; - 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); - strv_free(l); - return 0; - } + temp = strndup(word, l); + if (!temp) + return log_oom(); - if (device) - r = asprintf(&t, "%s %lu", device, ul); - else - r = asprintf(&t, "%lu", ul); - strv_free(l); + r = safe_atoi(temp, &val); + if (r < 0) { + val = signal_from_string_try_harder(temp); - if (r < 0) - return -ENOMEM; + 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; + } + } - if (device) - r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map); - else - r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL); - free(t); + r = set_ensure_allocated(&status_set->status, NULL); + if (r < 0) + return log_oom(); - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; + 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_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_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) { + + char*** sv = data; + const char *word, *state; + size_t l; int r; - off_t bytes; - unsigned k; - char *t, **l; assert(filename); assert(lvalue); assert(rvalue); assert(data); - l = strv_split_quoted(rvalue); - if (!l) - return -ENOMEM; - - k = strv_length(l); - if (k != 2) { - log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue); - strv_free(l); + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + strv_free(*sv); + *sv = 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); - strv_free(l); - return 0; - } + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *n; + int offset; - 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); - strv_free(l); - return 0; - } + n = strndup(word, l); + if (!n) + return log_oom(); - r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes); - strv_free(l); + if (!utf8_is_valid(n)) { + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); + continue; + } - if (r < 0) - return -ENOMEM; + 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; + } - r = unit_add_cgroup_attribute(u, "blkio", - streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device", - t, blkio_map); - free(t); + path_kill_slashes(n); - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; + 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."); return 0; } -int config_parse_unit_requires_mounts_for( +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, void *data, void *userdata) { - Unit *u = userdata; - int r; - bool empty_before; + ExecContext *c = data; + int k; assert(filename); assert(lvalue); assert(rvalue); assert(data); - empty_before = !u->requires_mounts_for; - - r = config_parse_path_strv(filename, line, section, lvalue, ltype, rvalue, data, userdata); + 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; + } - /* 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); + c->no_new_privileges = !!k; + c->no_new_privileges_set = true; - return r; + return 0; } -int config_parse_documentation( +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) { - Unit *u = userdata; - int r; - char **a, **b; + ExecContext *c = data; + int k; assert(filename); assert(lvalue); assert(rvalue); - assert(u); + assert(data); - r = config_parse_unit_strv_printf(filename, line, section, lvalue, ltype, rvalue, data, userdata); - if (r < 0) - return r; + /* Our enum shall be a superset of booleans, hence first try + * to parse as as boolean, and then as enum */ - for (a = b = u->documentation; a && *a; a++) { + 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; - if (is_valid_documentation_url(*a)) - *(b++) = *a; - else { - log_error("[%s:%u] Invalid URL, ignoring: %s", filename, line, *a); - free(*a); + 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; } - } - *b = NULL; - - return r; -} -static void syscall_set(uint32_t *p, int nr) { - p[nr >> 4] |= 1 << (nr & 31); -} + c->protect_home = h; + } -static void syscall_unset(uint32_t *p, int nr) { - p[nr >> 4] &= ~(1 << (nr & 31)); + return 0; } -int config_parse_syscall_filter( +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, @@ -2084,66 +3262,34 @@ 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); - - if (rvalue[0] == '~') { - invert = true; - rvalue++; - } - - if (!c->syscall_filter) { - size_t n; - - n = (syscall_max() + 31) >> 4; - c->syscall_filter = new(uint32_t, n); - if (!c->syscall_filter) - return -ENOMEM; - - memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t)); - - /* 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); - } - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - int id; - char _cleanup_free_ *t = NULL; + assert(data); - t = strndup(w, l); - if (!t) - return -ENOMEM; + /* Our enum shall be a superset of booleans, hence first try + * to parse as as boolean, and then as enum */ - id = syscall_from_name(t); + 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; - if (id < 0) { - log_error("[%s:%u] Failed to parse syscall, ignoring: %s", - filename, line, t); - continue; + 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; } - if (invert) - syscall_unset(c->syscall_filter, id); - else - syscall_set(c->syscall_filter, id); + c->protect_system = s; } - c->no_new_privileges = true; - return 0; } @@ -2174,9 +3320,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) { @@ -2184,11 +3330,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; - } } } @@ -2212,7 +3356,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; } @@ -2267,26 +3411,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) { @@ -2294,7 +3437,7 @@ static int load_from_path(Unit *u, const char *path) { filename = NULL; if (r != -ENOENT) - goto finish; + return r; } } else { @@ -2306,10 +3449,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)) @@ -2322,7 +3463,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); @@ -2333,37 +3474,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); @@ -2379,16 +3518,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) { @@ -2440,31 +3570,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; @@ -2481,9 +3608,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" }, @@ -2500,42 +3632,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; @@ -2557,7 +3713,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);