X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fload-fragment.c;h=5628d8c910df94530471c462a33568feb08d5144;hp=c3f4f926114cbf58451f5ebc4b7b8469fb296ce4;hb=b5d742138f71e87312541a89aac5657015f50f48;hpb=7fd1b19bc9e9f5574f2877936b8ac267c7706947 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index c3f4f9261..5628d8c91 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -34,8 +34,11 @@ #include #include -#include +#ifdef HAVE_SECCOMP +#include +#endif +#include "sd-messages.h" #include "unit.h" #include "strv.h" #include "conf-parser.h" @@ -46,22 +49,31 @@ #include "missing.h" #include "unit-name.h" #include "unit-printf.h" -#include "dbus-common.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 -int config_parse_warn_compat(const char *unit, - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !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_syntax(unit, LOG_DEBUG, filename, line, EINVAL, "Support for option %s= has been disabled at compile time and is ignored", @@ -74,6 +86,7 @@ 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, @@ -98,9 +111,12 @@ int config_parse_unit_deps(const char* unit, if (!t) return log_oom(); - k = unit_name_printf(u, t); - if (!k) - return log_oom(); + r = unit_name_printf(u, t, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve specifiers, ignoring: %s", strerror(-r)); + continue; + } r = unit_add_dependency_by_name(u, d, k, NULL, true); if (r < 0) @@ -115,6 +131,7 @@ 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, @@ -123,18 +140,19 @@ int config_parse_unit_string_printf(const char *unit, Unit *u = userdata; _cleanup_free_ char *k = NULL; + int r; assert(filename); assert(lvalue); assert(rvalue); assert(u); - k = unit_full_printf(u, rvalue); - if (!k) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + r = unit_full_printf(u, rvalue, &k); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r)); - return config_parse_string(unit, filename, line, section, lvalue, ltype, + return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k ? k : rvalue, data, userdata); } @@ -142,6 +160,7 @@ 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, @@ -150,18 +169,19 @@ int config_parse_unit_strv_printf(const char *unit, Unit *u = userdata; _cleanup_free_ char *k = NULL; + int r; assert(filename); assert(lvalue); assert(rvalue); assert(u); - k = unit_full_printf(u, rvalue); - if (!k) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + r = unit_full_printf(u, rvalue, &k); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r)); - return config_parse_strv(unit, filename, line, section, lvalue, ltype, + return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, k ? k : rvalue, data, userdata); } @@ -169,6 +189,7 @@ 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, @@ -177,18 +198,19 @@ int config_parse_unit_path_printf(const char *unit, Unit *u = userdata; _cleanup_free_ char *k = NULL; + int r; assert(filename); assert(lvalue); assert(rvalue); assert(u); - k = unit_full_printf(u, rvalue); - if (!k) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + r = unit_full_printf(u, rvalue, &k); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r)); - return config_parse_path(unit, filename, line, section, lvalue, ltype, + return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k ? k : rvalue, data, userdata); } @@ -196,6 +218,7 @@ 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, @@ -204,6 +227,7 @@ int config_parse_socket_listen(const char *unit, SocketPort *p, *tail; Socket *s; + int r; assert(filename); assert(lvalue); @@ -225,32 +249,31 @@ int config_parse_socket_listen(const char *unit, if (ltype != SOCKET_SOCKET) { p->type = ltype; - p->path = unit_full_printf(UNIT(s), rvalue); - if (!p->path) { + r = unit_full_printf(UNIT(s), rvalue, &p->path); + if (r < 0) { p->path = strdup(rvalue); if (!p->path) { free(p); return log_oom(); } else - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r)); } path_kill_slashes(p->path); } else if (streq(lvalue, "ListenNetlink")) { _cleanup_free_ char *k = NULL; - int r; p->type = SOCKET_SOCKET; - k = unit_full_printf(UNIT(s), rvalue); - if (!k) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + 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 ? k : rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, + log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse address value, ignoring: %s", rvalue); free(p); return 0; @@ -258,17 +281,16 @@ int config_parse_socket_listen(const char *unit, } else { _cleanup_free_ char *k = NULL; - int r; p->type = SOCKET_SOCKET; - k = unit_full_printf(UNIT(s), rvalue); - if (!k) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + 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_syntax(unit, LOG_ERR, filename, line, EINVAL, + log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse address value, ignoring: %s", rvalue); free(p); return 0; @@ -292,12 +314,13 @@ int config_parse_socket_listen(const char *unit, } 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); return 0; } @@ -306,6 +329,7 @@ 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, @@ -344,6 +368,7 @@ 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, @@ -381,6 +406,7 @@ 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, @@ -418,6 +444,7 @@ 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, @@ -509,9 +536,7 @@ int config_parse_exec(const char *unit, } if (!utf8_is_valid(path)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Path is not UTF-8 clean, ignoring assignment: %s", - rvalue); + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); r = 0; goto fail; } @@ -526,9 +551,7 @@ int config_parse_exec(const char *unit, } if (!utf8_is_valid(c)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Path is not UTF-8 clean, ignoring assignment: %s", - rvalue); + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); r = 0; goto fail; } @@ -589,6 +612,7 @@ 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, @@ -623,6 +647,7 @@ 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, @@ -654,6 +679,7 @@ 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, @@ -685,6 +711,7 @@ 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, @@ -719,6 +746,7 @@ 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, @@ -760,6 +788,7 @@ 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, @@ -817,6 +846,7 @@ 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, @@ -849,6 +879,7 @@ 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, @@ -898,6 +929,7 @@ int config_parse_bounding_set(const char *unit, const char *filename, unsigned line, const char *section, + unsigned section_line, const char *lvalue, int ltype, const char *rvalue, @@ -957,6 +989,7 @@ int config_parse_limit(const char *unit, const char *filename, unsigned line, const char *section, + unsigned section_line, const char *lvalue, int ltype, const char *rvalue, @@ -996,63 +1029,12 @@ int config_parse_limit(const char *unit, return 0; } -int config_parse_unit_cgroup(const char *unit, - 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; - - if (isempty(rvalue)) { - /* An empty assignment resets the list */ - cgroup_bonding_free_list(u->cgroup_bondings, false); - u->cgroup_bondings = NULL; - return 0; - } - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - _cleanup_free_ char *t = NULL, *k = NULL, *ku = NULL; - int r; - - t = strndup(w, l); - if (!t) - return log_oom(); - - k = unit_full_printf(u, t); - if (!k) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to resolve unit specifiers on %s. Ignoring.", - t); - - ku = cunescape(k ? k : t); - if (!ku) - return log_oom(); - - r = unit_add_cgroup_from_text(u, ku, true, NULL); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse cgroup value %s, ignoring: %s", - k, rvalue); - return 0; - } - } - - return 0; -} - #ifdef HAVE_SYSV_COMPAT 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, @@ -1079,41 +1061,13 @@ int config_parse_sysv_priority(const char *unit, } #endif -int config_parse_fsck_passno(const char *unit, - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - int *passno = data; - int i, r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atoi(rvalue, &i); - if (r || i < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse fsck pass number, ignoring: %s", rvalue); - return 0; - } - - *passno = (int) i; - return 0; -} - DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); 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, @@ -1143,6 +1097,7 @@ 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, @@ -1185,10 +1140,109 @@ int config_parse_exec_mount_flags(const char *unit, 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) { + + ExecContext *c = data; + Unit *u = userdata; + bool ignore; + char *k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + free(c->selinux_context); + c->selinux_context = NULL; + c->selinux_context_ignore = false; + return 0; + } + + if (rvalue[0] == '-') { + ignore = true; + rvalue++; + } else + ignore = false; + + r = unit_name_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r)); + return 0; + } + + free(c->selinux_context); + c->selinux_context = k; + c->selinux_context_ignore = ignore; + + return 0; +} + +int config_parse_exec_apparmor_profile( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + ExecContext *c = data; + Unit *u = userdata; + bool ignore; + char *k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + free(c->apparmor_profile); + c->apparmor_profile = NULL; + c->apparmor_profile_ignore = false; + return 0; + } + + if (rvalue[0] == '-') { + ignore = true; + rvalue++; + } else + ignore = false; + + r = unit_name_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r)); + return 0; + } + + free(c->apparmor_profile); + c->apparmor_profile = k; + c->apparmor_profile_ignore = ignore; + + return 0; +} + 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, @@ -1249,55 +1303,64 @@ int config_parse_timer(const char *unit, 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(const char *unit, - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_trigger_unit( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { - Timer *t = data; - int r; - DBusError error; - Unit *u; _cleanup_free_ char *p = NULL; + Unit *u = data; + UnitType type; + int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); - dbus_error_init(&error); + if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Multiple units to trigger specified, ignoring: %s", rvalue); + return 0; + } - p = unit_name_printf(UNIT(t), rvalue); - if (!p) - return log_oom(); + r = unit_name_printf(u, rvalue, &p); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve specifiers, ignoring: %s", strerror(-r)); + + 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(p, ".timer")) { + if (type == u->type) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Unit cannot be of type timer, ignoring: %s", rvalue); + "Trigger cannot be of same type, ignoring: %s", rvalue); return 0; } - r = manager_load_unit(UNIT(t)->manager, p, NULL, NULL, &u); + r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to load unit %s, ignoring: %s", - rvalue, bus_error(&error, r)); - dbus_error_free(&error); + "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r)); return 0; } - unit_ref_set(&t->unit, u); - return 0; } @@ -1305,6 +1368,7 @@ 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, @@ -1315,6 +1379,7 @@ int config_parse_path_spec(const char *unit, PathSpec *s; PathType b; _cleanup_free_ char *k = NULL; + int r; assert(filename); assert(lvalue); @@ -1334,13 +1399,13 @@ int config_parse_path_spec(const char *unit, return 0; } - k = unit_full_printf(UNIT(p), rvalue); - if (!k) { + 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, EINVAL, + log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); } @@ -1355,59 +1420,13 @@ int config_parse_path_spec(const char *unit, 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 *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; - _cleanup_free_ char *p = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - dbus_error_init(&error); - - p = unit_name_printf(UNIT(t), rvalue); - if (!p) - return log_oom(); - - if (endswith(p, ".path")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Unit cannot be of type path, ignoring: %s", p); - return 0; - } - - r = manager_load_unit(UNIT(t)->manager, p, NULL, &error, &u); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to load unit %s, ignoring: %s", - p, bus_error(&error, r)); - dbus_error_free(&error); - return 0; - } - - unit_ref_set(&t->unit, u); + LIST_PREPEND(spec, p->specs, s); return 0; } @@ -1416,15 +1435,16 @@ 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; @@ -1433,24 +1453,20 @@ int config_parse_socket_service(const char *unit, assert(rvalue); assert(data); - dbus_error_init(&error); - - p = unit_name_printf(UNIT(s), rvalue); - if (!p) - return log_oom(); + 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(p, ".service")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Unit must be of type service, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue); return 0; } r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to load unit %s, ignoring: %s", - 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; } @@ -1463,6 +1479,7 @@ 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, @@ -1486,23 +1503,24 @@ int config_parse_service_sockets(const char *unit, if (!t) return log_oom(); - k = unit_name_printf(UNIT(s), t); - if (!k) - return log_oom(); + r = unit_name_printf(UNIT(s), t, &k); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve specifiers, ignoring: %s", strerror(-r)); - if (!endswith(k, ".socket")) { + if (!endswith(k ?: t, ".socket")) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Unit must be of type socket, ignoring: %s", k); + "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_syntax(unit, LOG_ERR, filename, line, -r, "Failed to add dependency on %s, ignoring: %s", - k, strerror(-r)); + 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; } @@ -1514,6 +1532,7 @@ 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, @@ -1528,7 +1547,7 @@ int config_parse_service_timeout(const char *unit, assert(rvalue); assert(s); - r = config_parse_sec(unit, filename, line, section, lvalue, ltype, + r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata); if (r < 0) return r; @@ -1542,37 +1561,86 @@ int config_parse_service_timeout(const char *unit, return 0; } -int config_parse_unit_env_file(const char *unit, - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - char ***env = data; - Unit *u = userdata; - _cleanup_free_ char *s = NULL; +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) { + + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + BusName *n = data; int r; + Unit *x; + _cleanup_free_ char *p = NULL; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (isempty(rvalue)) { - /* Empty assignment frees the list */ - strv_free(*env); - *env = NULL; + r = unit_name_printf(UNIT(n), rvalue, &p); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue); return 0; } - s = unit_full_printf(u, rvalue); - if (!s) - return log_oom(); + if (!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(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; + } + + unit_ref_set(&n->service, x); + + return 0; +} + +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); @@ -1590,6 +1658,7 @@ 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, @@ -1600,11 +1669,12 @@ int config_parse_environ(const char *unit, char*** env = data, *w, *state; size_t l; _cleanup_free_ char *k = NULL; + int r; assert(filename); assert(lvalue); assert(rvalue); - assert(u); + assert(data); if (isempty(rvalue)) { /* Empty assignment resets the list */ @@ -1613,7 +1683,15 @@ int config_parse_environ(const char *unit, return 0; } - k = unit_full_printf(u, rvalue); + 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(); @@ -1646,6 +1724,7 @@ 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, @@ -1674,6 +1753,7 @@ 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, @@ -1685,6 +1765,7 @@ int config_parse_unit_condition_path(const char *unit, bool trigger, negate; Condition *c; _cleanup_free_ char *p = NULL; + int r; assert(filename); assert(lvalue); @@ -1706,9 +1787,15 @@ int config_parse_unit_condition_path(const char *unit, if (negate) rvalue++; - p = unit_full_printf(u, rvalue); - if (!p) - return log_oom(); + r = unit_full_printf(u, rvalue, &p); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve specifiers, ignoring: %s", rvalue); + if (!p) { + p = strdup(rvalue); + if (!p) + return log_oom(); + } if (!path_is_absolute(p)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, @@ -1720,7 +1807,7 @@ int config_parse_unit_condition_path(const char *unit, if (!c) return log_oom(); - LIST_PREPEND(Condition, conditions, u->conditions, c); + LIST_PREPEND(conditions, u->conditions, c); return 0; } @@ -1728,6 +1815,7 @@ 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, @@ -1739,6 +1827,7 @@ int config_parse_unit_condition_string(const char *unit, bool trigger, negate; Condition *c; _cleanup_free_ char *s = NULL; + int r; assert(filename); assert(lvalue); @@ -1760,340 +1849,1055 @@ int config_parse_unit_condition_string(const char *unit, if (negate) rvalue++; - s = unit_full_printf(u, rvalue); - if (!s) - return log_oom(); + r = unit_full_printf(u, rvalue, &s); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve specifiers, ignoring: %s", rvalue); + if (!s) { + s = strdup(rvalue); + if (!s) + return log_oom(); + } + + c = condition_new(cond, s, trigger, negate); + if (!c) + return log_oom(); + + LIST_PREPEND(conditions, u->conditions, c); + return 0; +} + +int config_parse_unit_condition_null(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Unit *u = data; + Condition *c; + bool trigger, negate; + int b; + + 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++; + + negate = rvalue[0] == '!'; + if (negate) + 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; + + c = condition_new(CONDITION_NULL, NULL, trigger, negate); + if (!c) + return log_oom(); + + 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"); + +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 = userdata; + char *state; + size_t l; + char *w; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + FOREACH_WORD_QUOTED(w, l, rvalue, state) { + int r; + _cleanup_free_ char *n; + + n = strndup(w, l); + if (!n) + return log_oom(); + + if (!utf8_is_valid(n)) { + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); + continue; + } + + r = unit_require_mounts_for(u, n); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to add required mount for, ignoring: %s", rvalue); + continue; + } + } + + return 0; +} + +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; + char **a, **b; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + strv_free(u->documentation); + u->documentation = NULL; + return 0; + } + + r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype, + rvalue, data, userdata); + if (r < 0) + return r; + + for (a = b = u->documentation; a && *a; a++) { + + if (is_valid_documentation_url(*a)) + *(b++) = *a; + else { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid URL, ignoring: %s", *a); + free(*a); + } + } + if (b) + *b = NULL; + + return r; +} + +#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; + char *w, *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(trivial_hash_func, trivial_compare_func); + 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(w, l, rvalue, state) { + _cleanup_free_ char *t = NULL; + int id; + + t = strndup(w, 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)); + } + + 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; + char *w, *state; + size_t l; + int r; + + if (isempty(rvalue)) { + set_free(*archs); + *archs = NULL; + return 0; + } + + r = set_ensure_allocated(archs, trivial_hash_func, trivial_compare_func); + if (r < 0) + return log_oom(); + + FOREACH_WORD_QUOTED(w, l, rvalue, state) { + _cleanup_free_ char *t = NULL; + uint32_t a; + + t = strndup(w, 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(); + } + + 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; + char *w, *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(trivial_hash_func, trivial_compare_func); + if (!c->address_families) + return log_oom(); + + c->address_families_whitelist = !invert; + } + + FOREACH_WORD_QUOTED(w, l, rvalue, state) { + _cleanup_free_ char *t = NULL; + int af; + + t = strndup(w, 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)); + } + + 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) { + + CGroupContext *c = data; + unsigned long lu; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + c->cpu_shares = 1024; + 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; + } + + c->cpu_shares = lu; + 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) { + + CGroupContext *c = data; + unsigned long lu; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + c->blockio_weight = 1000; + 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; + } - c = condition_new(cond, s, trigger, negate); - if (!c) - return log_oom(); + c->blockio_weight = lu; - LIST_PREPEND(Condition, conditions, u->conditions, c); return 0; } -int config_parse_unit_condition_null(const char *unit, - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = data; - Condition *c; - bool trigger, negate; - int b; +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); - assert(data); if (isempty(rvalue)) { - /* Empty assignment resets the list */ - condition_free_list(u->conditions); - u->conditions = NULL; + while (c->blockio_device_weights) + cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); + return 0; } - trigger = rvalue[0] == '|'; - if (trigger) - rvalue++; + 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; + } - negate = rvalue[0] == '!'; - if (negate) - rvalue++; + path = strndup(rvalue, n); + if (!path) + return log_oom(); - 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); + if (!path_startswith(path, "/dev")) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid device node path '%s'. Ignoring.", path); return 0; } - if (!b) - negate = !negate; + 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; + } - c = condition_new(CONDITION_NULL, NULL, trigger, negate); - if (!c) + + w = new0(CGroupBlockIODeviceWeight, 1); + if (!w) return log_oom(); - LIST_PREPEND(Condition, conditions, u->conditions, c); - return 0; -} + w->path = path; + path = NULL; -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"); + w->weight = lu; -int config_parse_unit_cgroup_attr(const char *unit, - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { + LIST_PREPEND(device_weights, c->blockio_device_weights, w); + return 0; +} - Unit *u = data; - size_t a, b; - _cleanup_free_ char *n = NULL, *v = NULL; - const CGroupSemantics *s; +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; assert(filename); assert(lvalue); assert(rvalue); - assert(data); + + read = streq("BlockIOReadBandwidth", lvalue); if (isempty(rvalue)) { - /* Empty assignment clears the list */ - cgroup_attribute_free_list(u->cgroup_attributes); - u->cgroup_attributes = NULL; + CGroupBlockIODeviceBandwidth *next; + + LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths) + if (b->read == read) + cgroup_context_free_blockio_device_bandwidth(c, b); + return 0; } - a = strcspn(rvalue, WHITESPACE); - b = strspn(rvalue + a, WHITESPACE); - if (a <= 0 || b <= 0) { + n = strcspn(rvalue, WHITESPACE); + bandwidth = rvalue + n; + bandwidth += strspn(bandwidth, WHITESPACE); + + if (!*bandwidth) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse cgroup attribute value, ignoring: %s", - rvalue); + "Expected space separated pair of device node and bandwidth. Ignoring."); return 0; } - n = strndup(rvalue, a); - if (!n) + path = strndup(rvalue, n); + if (!path) return log_oom(); - r = cgroup_semantics_find(NULL, n, rvalue + a + b, &v, &s); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse cgroup attribute value, ignoring: %s", - rvalue); + if (!path_startswith(path, "/dev")) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid device node path '%s'. Ignoring.", path); return 0; } - r = unit_add_cgroup_attribute(u, s, NULL, n, v ? v : rvalue + a + b, NULL); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to add cgroup attribute value, ignoring: %s", rvalue); + 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; } + b = new0(CGroupBlockIODeviceBandwidth, 1); + if (!b) + return log_oom(); + + b->path = path; + path = NULL; + b->bandwidth = (uint64_t) bytes; + b->read = read; + + LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b); + return 0; } -int config_parse_unit_cgroup_attr_pretty(const char *unit, - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = data; - _cleanup_free_ char *v = NULL; - const CGroupSemantics *s; +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; assert(filename); assert(lvalue); assert(rvalue); - assert(data); - - r = cgroup_semantics_find(NULL, lvalue, rvalue, &v, &s); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse cgroup attribute value, ignoring: %s", - rvalue); - return 0; - } else if (r == 0) { - log_syntax(unit, LOG_ERR, filename, line, ENOTSUP, - "Unknown or unsupported cgroup attribute %s, ignoring: %s", - lvalue, rvalue); - return 0; - } - r = unit_add_cgroup_attribute(u, s, NULL, NULL, v, NULL); + r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to add cgroup attribute value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse boolean, ignoring: %s", rvalue); return 0; } + *m = r ? JOB_ISOLATE : JOB_REPLACE; return 0; } -int config_parse_unit_requires_mounts_for(const char *unit, - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +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) { - Unit *u = userdata; - int r; - bool empty_before; + unsigned long *personality = data, p; assert(filename); assert(lvalue); assert(rvalue); - assert(data); - - empty_before = !u->requires_mounts_for; + assert(personality); - r = config_parse_path_strv(unit, filename, line, section, lvalue, ltype, - rvalue, data, userdata); - - /* 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); + 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; + } - return r; + *personality = p; + return 0; } -int config_parse_documentation(const char *unit, - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = userdata; +int config_parse_runtime_directory( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char***rt = data, *w, *state; + size_t l; int r; - char **a, **b; assert(filename); assert(lvalue); assert(rvalue); - assert(u); + assert(data); if (isempty(rvalue)) { /* Empty assignment resets the list */ - strv_free(u->documentation); - u->documentation = NULL; + strv_free(*rt); + *rt = NULL; return 0; } - r = config_parse_unit_strv_printf(unit, filename, line, section, lvalue, ltype, - rvalue, data, userdata); - if (r < 0) - return r; + FOREACH_WORD_QUOTED(w, l, rvalue, state) { + _cleanup_free_ char *n; - for (a = b = u->documentation; a && *a; a++) { + n = strndup(w, l); + if (!n) + return log_oom(); - 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 (!filename_is_safe(n)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Runtime directory is not valid, ignoring assignment: %s", rvalue); + continue; } - } - *b = NULL; - return r; -} + r = strv_push(rt, n); + if (r < 0) + return log_oom(); -static void syscall_set(uint32_t *p, int nr) { - nr = SYSCALL_TO_INDEX(nr); - p[nr >> 4] |= 1 << (nr & 31); -} + n = NULL; + } -static void syscall_unset(uint32_t *p, int nr) { - nr = SYSCALL_TO_INDEX(nr); - p[nr >> 4] &= ~(1 << (nr & 31)); + return 0; } -int config_parse_syscall_filter(const char *unit, - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +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) { - ExecContext *c = data; - Unit *u = userdata; - bool invert = false; char *w; size_t l; char *state; + int r; + ExitStatusSet *status_set = data; assert(filename); assert(lvalue); assert(rvalue); - assert(u); + assert(data); if (isempty(rvalue)) { /* Empty assignment resets the list */ - free(c->syscall_filter); - c->syscall_filter = NULL; + + set_free(status_set->signal); + set_free(status_set->code); + + status_set->signal = status_set->code = NULL; return 0; } - if (rvalue[0] == '~') { - invert = true; - rvalue++; + FOREACH_WORD(w, l, rvalue, state) { + _cleanup_free_ char *temp; + int val; + + temp = strndup(w, l); + if (!temp) + return log_oom(); + + r = safe_atoi(temp, &val); + if (r < 0) { + val = signal_from_string_try_harder(temp); + + if (val > 0) { + r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func); + if (r < 0) + return log_oom(); + + r = set_put(status_set->signal, INT_TO_PTR(val)); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, "Unable to store: %s", w); + return r; + } + } else { + log_syntax(unit, LOG_ERR, filename, line, -val, "Failed to parse value, ignoring: %s", w); + 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); + else { + r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func); + if (r < 0) + return log_oom(); + + r = set_put(status_set->code, INT_TO_PTR(val)); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, "Unable to store: %s", w); + return r; + } + } + } } - if (!c->syscall_filter) { - size_t n; + return 0; +} - n = (syscall_max() + 31) >> 4; - c->syscall_filter = new(uint32_t, n); - if (!c->syscall_filter) - return log_oom(); +int config_parse_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, *w, *state; + size_t l; + int r; - memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t)); + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); - /* 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); + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + strv_free(*sv); + *sv = NULL; + return 0; } FOREACH_WORD_QUOTED(w, l, rvalue, state) { - int id; - _cleanup_free_ char *t = NULL; + _cleanup_free_ char *n; + int offset; - t = strndup(w, l); - if (!t) + n = strndup(w, l); + if (!n) return log_oom(); - id = syscall_from_name(t); - if (id < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse syscall, ignoring: %s", t); + if (!utf8_is_valid(n)) { + log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); continue; } - if (invert) - syscall_unset(c->syscall_filter, id); - else - syscall_set(c->syscall_filter, id); - } + 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; + } - c->no_new_privileges = true; + path_kill_slashes(n); + + r = strv_push(sv, n); + if (r < 0) + return log_oom(); + + n = NULL; + } return 0; } @@ -2125,9 +2929,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) { @@ -2135,11 +2939,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; - } } } @@ -2218,9 +3020,10 @@ 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; @@ -2234,10 +3037,8 @@ static int load_from_path(Unit *u, const char *path) { 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) { @@ -2245,7 +3046,7 @@ static int load_from_path(Unit *u, const char *path) { filename = NULL; if (r != -ENOENT) - goto finish; + return r; } } else { @@ -2257,10 +3058,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)) @@ -2273,7 +3072,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); @@ -2284,39 +3083,34 @@ 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(u->id, filename, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, - (void*) load_fragment_gperf_lookup, false, u); + (void*) load_fragment_gperf_lookup, false, true, u); if (r < 0) - goto finish; - - u->load_state = UNIT_LOADED; + return r; } free(u->fragment_path); @@ -2332,16 +3126,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) { @@ -2393,31 +3178,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; @@ -2434,9 +3216,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_LIBWRAP) || !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" }, @@ -2453,21 +3240,18 @@ 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" }, @@ -2476,19 +3260,46 @@ void unit_dump_config_items(FILE *f) { { config_parse_socket_bindtodevice, "NETWORKINTERFACE" }, { 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_start_limit_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;