diff options
Diffstat (limited to 'src/core')
85 files changed, 5493 insertions, 3359 deletions
diff --git a/src/core/automount.c b/src/core/automount.c index 4a98540d82..a20d5340f2 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -109,7 +109,6 @@ static void automount_done(Unit *u) { assert(a); unmount_autofs(a); - unit_ref_unset(&a->mount); free(a->where); a->where = NULL; @@ -200,8 +199,8 @@ static int automount_verify(Automount *a) { } static int automount_load(Unit *u) { - int r; Automount *a = AUTOMOUNT(u); + int r; assert(u); assert(u->load_state == UNIT_STUB); @@ -222,17 +221,15 @@ static int automount_load(Unit *u) { path_kill_slashes(a->where); - r = automount_add_mount_links(a); + r = unit_load_related_unit(u, ".mount", &x); if (r < 0) return r; - r = unit_load_related_unit(u, ".mount", &x); + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true); if (r < 0) return r; - unit_ref_set(&a->mount, x); - - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(a->mount), true); + r = automount_add_mount_links(a); if (r < 0) return r; @@ -586,18 +583,17 @@ fail: } static void automount_enter_runnning(Automount *a) { - int r; - struct stat st; _cleanup_dbus_error_free_ DBusError error; + struct stat st; + int r; assert(a); - assert(UNIT_DEREF(a->mount)); dbus_error_init(&error); /* We don't take mount requests anymore if we are supposed to * shut down anyway */ - if (unit_pending_inactive(UNIT(a))) { + if (unit_stop_pending(UNIT(a))) { log_debug_unit(UNIT(a)->id, "Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id); automount_send_ready(a, -EHOSTDOWN); @@ -616,11 +612,15 @@ static void automount_enter_runnning(Automount *a) { if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) log_info_unit(UNIT(a)->id, "%s's automount point already active?", UNIT(a)->id); - else if ((r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_DEREF(a->mount), JOB_REPLACE, true, &error, NULL)) < 0) { - log_warning_unit(UNIT(a)->id, - "%s failed to queue mount startup job: %s", - UNIT(a)->id, bus_error(&error, r)); - goto fail; + else { + r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)), + JOB_REPLACE, true, &error, NULL); + if (r < 0) { + log_warning_unit(UNIT(a)->id, + "%s failed to queue mount startup job: %s", + UNIT(a)->id, bus_error(&error, r)); + goto fail; + } } automount_set_state(a, AUTOMOUNT_RUNNING); @@ -643,7 +643,7 @@ static int automount_start(Unit *u) { return -EEXIST; } - if (UNIT_DEREF(a->mount)->load_state != UNIT_LOADED) + if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED) return -ENOENT; a->result = AUTOMOUNT_SUCCESS; @@ -765,14 +765,12 @@ static const char *automount_sub_state_to_string(Unit *u) { } static bool automount_check_gc(Unit *u) { - Automount *a = AUTOMOUNT(u); - - assert(a); + assert(u); - if (!UNIT_DEREF(a->mount)) + if (!UNIT_TRIGGER(u)) return false; - return UNIT_VTABLE(UNIT_DEREF(a->mount))->check_gc(UNIT_DEREF(a->mount)); + return UNIT_VTABLE(UNIT_TRIGGER(u))->check_gc(UNIT_TRIGGER(u)); } static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { diff --git a/src/core/automount.h b/src/core/automount.h index 3d5736d1cb..0c6b8a72e9 100644 --- a/src/core/automount.h +++ b/src/core/automount.h @@ -48,8 +48,6 @@ struct Automount { char *where; - UnitRef mount; - int pipe_fd; mode_t directory_mode; Watch pipe_watch; @@ -66,8 +64,8 @@ int automount_send_ready(Automount *a, int status); int automount_add_one_mount_link(Automount *a, Mount *m); -const char* automount_state_to_string(AutomountState i); -AutomountState automount_state_from_string(const char *s); +const char* automount_state_to_string(AutomountState i) _const_; +AutomountState automount_state_from_string(const char *s) _pure_; -const char* automount_result_to_string(AutomountResult i); -AutomountResult automount_result_from_string(const char *s); +const char* automount_result_to_string(AutomountResult i) _const_; +AutomountResult automount_result_from_string(const char *s) _pure_; diff --git a/src/core/bus-errors.h b/src/core/bus-errors.h index 04c1b2849d..7a4084ea15 100644 --- a/src/core/bus-errors.h +++ b/src/core/bus-errors.h @@ -21,9 +21,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <string.h> -#include <dbus/dbus.h> - #define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit" #define BUS_ERROR_NO_SUCH_JOB "org.freedesktop.systemd1.NoSuchJob" #define BUS_ERROR_NOT_SUBSCRIBED "org.freedesktop.systemd1.NotSubscribed" @@ -43,13 +40,3 @@ #define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic" #define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown" #define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess" - -static inline const char *bus_error(const DBusError *e, int r) { - if (e && e->message) - return e->message; - - if (r >= 0) - return strerror(r); - - return strerror(-r); -} diff --git a/src/core/cgroup-attr.c b/src/core/cgroup-attr.c index 71af09cf87..7e3e08eabb 100644 --- a/src/core/cgroup-attr.c +++ b/src/core/cgroup-attr.c @@ -22,11 +22,11 @@ #include "cgroup-attr.h" #include "cgroup-util.h" #include "list.h" +#include "fileio.h" int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) { + _cleanup_free_ char *path = NULL, *v = NULL; int r; - char *path = NULL; - char *v = NULL; assert(a); @@ -34,25 +34,20 @@ int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) { if (!b) return 0; - if (a->map_callback) { - r = a->map_callback(a->controller, a->name, a->value, &v); + if (a->semantics && a->semantics->map_write) { + r = a->semantics->map_write(a->semantics, a->value, &v); if (r < 0) return r; } r = cg_get_path(a->controller, b->path, a->name, &path); - if (r < 0) { - free(v); + if (r < 0) return r; - } - r = write_one_line_file(path, v ? v : a->value); + r = write_string_file(path, v ? v : a->value); if (r < 0) log_warning("Failed to write '%s' to %s: %s", v ? v : a->value, path, strerror(-r)); - free(path); - free(v); - return r; } @@ -71,23 +66,50 @@ int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) { return r; } -CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) { +bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name) { + assert(a); + + if (controller) { + if (streq(a->controller, controller) && (!name || streq(a->name, name))) + return true; + + } else if (!name) + return true; + else if (streq(a->name, name)) { + size_t x, y; + x = strlen(a->controller); + y = strlen(name); + + if (y > x && + memcmp(a->controller, name, x) == 0 && + name[x] == '.') + return true; + } + + return false; +} + +CGroupAttribute *cgroup_attribute_find_list( + CGroupAttribute *first, + const char *controller, + const char *name) { CGroupAttribute *a; - assert(controller); assert(name); LIST_FOREACH(by_unit, a, first) - if (streq(a->controller, controller) && - streq(a->name, name)) + if (cgroup_attribute_matches(a, controller, name)) return a; return NULL; } -static void cgroup_attribute_free(CGroupAttribute *a) { +void cgroup_attribute_free(CGroupAttribute *a) { assert(a); + if (a->unit) + LIST_REMOVE(CGroupAttribute, by_unit, a->unit->cgroup_attributes, a); + free(a->controller); free(a->name); free(a->value); @@ -100,3 +122,11 @@ void cgroup_attribute_free_list(CGroupAttribute *first) { LIST_FOREACH_SAFE(by_unit, a, n, first) cgroup_attribute_free(a); } + +void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name) { + CGroupAttribute *a, *n; + + LIST_FOREACH_SAFE(by_unit, a, n, first) + if (cgroup_attribute_matches(a, controller, name)) + cgroup_attribute_free(a); +} diff --git a/src/core/cgroup-attr.h b/src/core/cgroup-attr.h index 2b754eac40..3a13b7c92d 100644 --- a/src/core/cgroup-attr.h +++ b/src/core/cgroup-attr.h @@ -23,17 +23,18 @@ typedef struct CGroupAttribute CGroupAttribute; -typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret); - #include "unit.h" #include "cgroup.h" +#include "cgroup-semantics.h" struct CGroupAttribute { char *controller; char *name; char *value; - CGroupAttributeMapCallback map_callback; + Unit *unit; + + const CGroupSemantics *semantics; LIST_FIELDS(CGroupAttribute, by_unit); }; @@ -41,6 +42,9 @@ struct CGroupAttribute { int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b); int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b); -CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name); +bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name) _pure_; +CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) _pure_; +void cgroup_attribute_free(CGroupAttribute *a); void cgroup_attribute_free_list(CGroupAttribute *first); +void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name); diff --git a/src/core/cgroup-semantics.c b/src/core/cgroup-semantics.c new file mode 100644 index 0000000000..82b02bbd78 --- /dev/null +++ b/src/core/cgroup-semantics.c @@ -0,0 +1,333 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "util.h" +#include "strv.h" +#include "path-util.h" +#include "cgroup-util.h" + +#include "cgroup-semantics.h" + +static int parse_cpu_shares(const CGroupSemantics *s, const char *value, char **ret) { + unsigned long ul; + + assert(s); + assert(value); + assert(ret); + + if (safe_atolu(value, &ul) < 0 || ul < 1) + return -EINVAL; + + if (asprintf(ret, "%lu", ul) < 0) + return -ENOMEM; + + return 1; +} + +static int parse_memory_limit(const CGroupSemantics *s, const char *value, char **ret) { + off_t sz; + + assert(s); + assert(value); + assert(ret); + + if (parse_bytes(value, &sz) < 0 || sz <= 0) + return -EINVAL; + + if (asprintf(ret, "%llu", (unsigned long long) sz) < 0) + return -ENOMEM; + + return 1; +} + +static int parse_device(const CGroupSemantics *s, const char *value, char **ret) { + _cleanup_strv_free_ char **l = NULL; + char *x; + unsigned k; + + assert(s); + assert(value); + assert(ret); + + l = strv_split_quoted(value); + if (!l) + return -ENOMEM; + + k = strv_length(l); + if (k < 1 || k > 2) + return -EINVAL; + + if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) + return -EINVAL; + + if (!isempty(l[1]) && !in_charset(l[1], "rwm")) + return -EINVAL; + + x = strdup(value); + if (!x) + return -ENOMEM; + + *ret = x; + return 1; +} + +static int parse_blkio_weight(const CGroupSemantics *s, const char *value, char **ret) { + _cleanup_strv_free_ char **l = NULL; + unsigned long ul; + + assert(s); + assert(value); + assert(ret); + + l = strv_split_quoted(value); + if (!l) + return -ENOMEM; + + if (strv_length(l) != 1) + return 0; /* Returning 0 will cause parse_blkio_weight_device() be tried instead */ + + if (safe_atolu(l[0], &ul) < 0 || ul < 10 || ul > 1000) + return -EINVAL; + + if (asprintf(ret, "%lu", ul) < 0) + return -ENOMEM; + + return 1; +} + +static int parse_blkio_weight_device(const CGroupSemantics *s, const char *value, char **ret) { + _cleanup_strv_free_ char **l = NULL; + unsigned long ul; + + assert(s); + assert(value); + assert(ret); + + l = strv_split_quoted(value); + if (!l) + return -ENOMEM; + + if (strv_length(l) != 2) + return -EINVAL; + + if (!path_startswith(l[0], "/dev")) + return -EINVAL; + + if (safe_atolu(l[1], &ul) < 0 || ul < 10 || ul > 1000) + return -EINVAL; + + if (asprintf(ret, "%s %lu", l[0], ul) < 0) + return -ENOMEM; + + return 1; +} + +static int parse_blkio_bandwidth(const CGroupSemantics *s, const char *value, char **ret) { + _cleanup_strv_free_ char **l = NULL; + off_t bytes; + + assert(s); + assert(value); + assert(ret); + + l = strv_split_quoted(value); + if (!l) + return -ENOMEM; + + if (strv_length(l) != 2) + return -EINVAL; + + if (!path_startswith(l[0], "/dev")) { + return -EINVAL; + } + + if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) + return -EINVAL; + + if (asprintf(ret, "%s %llu", l[0], (unsigned long long) bytes) < 0) + return -ENOMEM; + + return 0; +} + +static int map_device(const CGroupSemantics *s, const char *value, char **ret) { + _cleanup_strv_free_ char **l = NULL; + unsigned k; + + assert(s); + assert(value); + assert(ret); + + l = strv_split_quoted(value); + if (!l) + return -ENOMEM; + + k = strv_length(l); + if (k < 1 || k > 2) + return -EINVAL; + + if (streq(l[0], "*")) { + + if (asprintf(ret, "a *:*%s%s", + isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) + return -ENOMEM; + } else { + struct stat st; + + if (stat(l[0], &st) < 0) { + log_warning("Couldn't stat device %s", l[0]); + return -errno; + } + + if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) { + log_warning("%s is not a device.", l[0]); + return -ENODEV; + } + + if (asprintf(ret, "%c %u:%u%s%s", + S_ISCHR(st.st_mode) ? 'c' : 'b', + major(st.st_rdev), minor(st.st_rdev), + isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) + return -ENOMEM; + } + + return 0; +} + +static int map_blkio(const CGroupSemantics *s, const char *value, char **ret) { + _cleanup_strv_free_ char **l = NULL; + struct stat st; + dev_t d; + + assert(s); + assert(value); + assert(ret); + + l = strv_split_quoted(value); + if (!l) + return log_oom(); + + if (strv_length(l) != 2) + return -EINVAL; + + if (stat(l[0], &st) < 0) { + log_warning("Couldn't stat device %s", l[0]); + return -errno; + } + + 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; + + /* If this is a partition, try to get the originating + * block device */ + block_get_whole_disk(d, &d); + } else { + log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]); + return -ENODEV; + } + + if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) + return -ENOMEM; + + return 0; +} + +static const CGroupSemantics semantics[] = { + { "cpu", "cpu.shares", "CPUShare", false, parse_cpu_shares, NULL, NULL }, + { "memory", "memory.soft_limit_in_bytes", "MemorySoftLimit", false, parse_memory_limit, NULL, NULL }, + { "memory", "memory.limit_in_bytes", "MemoryLimit", false, parse_memory_limit, NULL, NULL }, + { "devices", "devices.allow", "DeviceAllow", true, parse_device, map_device, NULL }, + { "devices", "devices.deny", "DeviceDeny", true, parse_device, map_device, NULL }, + { "blkio", "blkio.weight", "BlockIOWeight", false, parse_blkio_weight, NULL, NULL }, + { "blkio", "blkio.weight_device", "BlockIOWeight", true, parse_blkio_weight_device, map_blkio, NULL }, + { "blkio", "blkio.read_bps_device", "BlockIOReadBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL }, + { "blkio", "blkio.write_bps_device", "BlockIOWriteBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL } +}; + +int cgroup_semantics_find( + const char *controller, + const char *name, + const char *value, + char **ret, + const CGroupSemantics **_s) { + + _cleanup_free_ char *c = NULL; + unsigned i; + int r; + + assert(name); + assert(_s); + assert(!value == !ret); + + if (!controller) { + r = cg_controller_from_attr(name, &c); + if (r < 0) + return r; + + controller = c; + } + + for (i = 0; i < ELEMENTSOF(semantics); i++) { + const CGroupSemantics *s = semantics + i; + bool matches_name, matches_pretty; + + if (controller && s->controller && !streq(s->controller, controller)) + continue; + + matches_name = s->name && streq(s->name, name); + matches_pretty = s->pretty && streq(s->pretty, name); + + if (!matches_name && !matches_pretty) + continue; + + if (value) { + if (matches_pretty && s->map_pretty) { + + r = s->map_pretty(s, value, ret); + if (r < 0) + return r; + + if (r == 0) + continue; + + } else { + char *x; + + x = strdup(value); + if (!x) + return -ENOMEM; + + *ret = x; + } + } + + *_s = s; + return 1; + } + + *ret = NULL; + *_s = NULL; + return 0; +} diff --git a/src/core/cgroup-semantics.h b/src/core/cgroup-semantics.h new file mode 100644 index 0000000000..4f848f4bb7 --- /dev/null +++ b/src/core/cgroup-semantics.h @@ -0,0 +1,43 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +typedef struct CGroupSemantics CGroupSemantics; + +struct CGroupSemantics { + const char *controller; + const char *name; + const char *pretty; + + bool multiple; + + /* This call is used for parsing the pretty value to the actual attribute value */ + int (*map_pretty)(const CGroupSemantics *semantics, const char *value, char **ret); + + /* Right before writing this attribute the attribute value is converted to a low-level value */ + int (*map_write)(const CGroupSemantics *semantics, const char *value, char **ret); + + /* If this attribute takes a list, this call can be used to reset the list to empty */ + int (*reset)(const CGroupSemantics *semantics, const char *group); +}; + +int cgroup_semantics_find(const char *controller, const char *name, const char *value, char **ret, const CGroupSemantics **semantics); diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 8fc1731485..83df0f3c9a 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -40,7 +40,7 @@ int cgroup_bonding_realize(CGroupBonding *b) { assert(b->path); assert(b->controller); - r = cg_create(b->controller, b->path); + r = cg_create(b->controller, b->path, NULL); if (r < 0) { log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r)); return r; @@ -110,9 +110,8 @@ void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) { cgroup_bonding_trim(b, delete_root); } - int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffix) { - char *p = NULL; + _cleanup_free_ char *p = NULL; const char *path; int r; @@ -129,8 +128,6 @@ int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffi path = b->path; r = cg_create_and_attach(b->controller, path, pid); - free(p); - if (r < 0) return r; @@ -151,6 +148,34 @@ int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgr return 0; } +int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list) { + CGroupBonding *q; + int ret = 0; + + LIST_FOREACH(by_unit, q, list) { + int r; + + if (q == b) + continue; + + if (!q->ours) + continue; + + r = cg_migrate_recursive(q->controller, q->path, b->controller, b->path, true, false); + if (r < 0 && ret == 0) + ret = r; + } + + return ret; +} + +int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem) { + assert(b); + assert(target); + + return cg_migrate_recursive(b->controller, b->path, b->controller, target, true, rem); +} + int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) { assert(b); @@ -294,9 +319,10 @@ int cgroup_bonding_is_empty_list(CGroupBonding *first) { } int manager_setup_cgroup(Manager *m) { - char *current = NULL, *path = NULL; + _cleanup_free_ char *current = NULL, *path = NULL; + char suffix_buffer[sizeof("/systemd-") + DECIMAL_STR_MAX(pid_t)]; + const char *suffix; int r; - char suffix[32]; assert(m); @@ -307,17 +333,17 @@ int manager_setup_cgroup(Manager *m) { } /* 1. Determine hierarchy */ - r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, ¤t); + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, ¤t); if (r < 0) { log_error("Cannot determine cgroup we are running in: %s", strerror(-r)); - goto finish; + return r; } if (m->running_as == SYSTEMD_SYSTEM) - strcpy(suffix, "/system"); + suffix = "/system"; else { - snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid()); - char_array_0(suffix); + sprintf(suffix_buffer, "/systemd-%lu", (unsigned long) getpid()); + suffix = suffix_buffer; } free(m->cgroup_hierarchy); @@ -325,39 +351,42 @@ int manager_setup_cgroup(Manager *m) { /* We probably got reexecuted and can continue to use our root cgroup */ m->cgroup_hierarchy = current; current = NULL; - } else { /* We need a new root cgroup */ - m->cgroup_hierarchy = NULL; - if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0) { - r = log_oom(); - goto finish; - } + if (streq(current, "/")) + m->cgroup_hierarchy = strdup(suffix); + else + m->cgroup_hierarchy = strappend(current, suffix); + + if (!m->cgroup_hierarchy) + return log_oom(); } /* 2. Show data */ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path); if (r < 0) { log_error("Cannot find cgroup mount point: %s", strerror(-r)); - goto finish; + return r; } log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path); /* 3. Install agent */ - r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH); - if (r < 0) - log_warning("Failed to install release agent, ignoring: %s", strerror(-r)); - else if (r > 0) - log_debug("Installed release agent."); - else - log_debug("Release agent already installed."); + if (m->running_as == SYSTEMD_SYSTEM) { + r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH); + if (r < 0) + log_warning("Failed to install release agent, ignoring: %s", strerror(-r)); + else if (r > 0) + log_debug("Installed release agent."); + else + log_debug("Release agent already installed."); + } /* 4. Realize the group */ r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0); if (r < 0) { log_error("Failed to create root cgroup hierarchy: %s", strerror(-r)); - goto finish; + return r; } /* 5. And pin it, so that it cannot be unmounted */ @@ -367,19 +396,21 @@ int manager_setup_cgroup(Manager *m) { m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK); if (r < 0) { log_error("Failed to open pin file: %m"); - r = -errno; - goto finish; + return -errno; } - log_debug("Created root group."); - + /* 6. Remove non-existing controllers from the default controllers list */ cg_shorten_controllers(m->default_controllers); -finish: - free(current); - free(path); + /* 7. Let's create the user and machine hierarchies + * right-away, so that people can inotify on them, if they + * wish, without this being racy. */ + if (m->running_as == SYSTEMD_SYSTEM) { + cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../user"); + cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../machine"); + } - return r; + return 0; } void manager_shutdown_cgroup(Manager *m, bool delete) { @@ -411,7 +442,7 @@ int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) return 1; } - p = strdup(cgroup); + p = strdupa(cgroup); if (!p) return -ENOMEM; @@ -419,8 +450,7 @@ int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) char *e; e = strrchr(p, '/'); - if (!e || e == p) { - free(p); + if (e == p || !e) { *bonding = NULL; return 0; } @@ -429,7 +459,6 @@ int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) b = hashmap_get(m->cgroup_bondings, p); if (b) { - free(p); *bonding = b; return 1; } @@ -484,7 +513,7 @@ Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) { if (pid <= 1) return NULL; - if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0) + if (cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0) return NULL; l = hashmap_get(m->cgroup_bondings, group); @@ -520,7 +549,8 @@ Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) { CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) { CGroupBonding *b; - assert(controller); + if (!controller) + controller = SYSTEMD_CGROUP_CONTROLLER; LIST_FOREACH(by_unit, b, first) if (streq(b->controller, controller)) diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 229da52ba4..6555d89e37 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -58,6 +58,9 @@ void cgroup_bonding_free_list(CGroupBonding *first, bool trim); int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *suffix); int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *suffix); +int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list); +int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem); + int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid); int cgroup_bonding_set_group_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid); @@ -73,7 +76,7 @@ void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root); int cgroup_bonding_is_empty(CGroupBonding *b); int cgroup_bonding_is_empty_list(CGroupBonding *first); -CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller); +CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) _pure_; char *cgroup_bonding_to_string(CGroupBonding *b); diff --git a/src/core/condition.c b/src/core/condition.c index b3184922b8..16cae6d23b 100644 --- a/src/core/condition.c +++ b/src/core/condition.c @@ -36,6 +36,7 @@ #include "condition.h" #include "virt.h" #include "path-util.h" +#include "fileio.h" Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { Condition *c; @@ -161,6 +162,10 @@ static bool test_security(const char *parameter) { if (streq(parameter, "selinux")) return is_selinux_enabled() > 0; #endif + if (streq(parameter, "apparmor")) + return access("/sys/kernel/security/apparmor/", F_OK) == 0; + if (streq(parameter, "smack")) + return access("/sys/fs/smackfs", F_OK) == 0; return false; } diff --git a/src/core/condition.h b/src/core/condition.h index 1797385d26..50ed955af9 100644 --- a/src/core/condition.h +++ b/src/core/condition.h @@ -66,5 +66,5 @@ bool condition_test_list(Condition *c); void condition_dump(Condition *c, FILE *f, const char *prefix); void condition_dump_list(Condition *c, FILE *f, const char *prefix); -const char* condition_type_to_string(ConditionType t); -int condition_type_from_string(const char *s); +const char* condition_type_to_string(ConditionType t) _const_; +int condition_type_from_string(const char *s) _pure_; diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index e815cb58e4..2a8a0e1ac5 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -29,6 +29,7 @@ #include "strv.h" #include "dbus-common.h" #include "syscall-list.h" +#include "fileio.h" DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_input, exec_input, ExecInput); DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_output, exec_output, ExecOutput); @@ -159,12 +160,12 @@ int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *proper if (c->cpu_sched_set) n = c->cpu_sched_priority; else { - struct sched_param p; - n = 0; + struct sched_param p = {}; - zero(p); if (sched_getparam(0, &p) >= 0) n = p.sched_priority; + else + n = 0; } if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n)) @@ -278,9 +279,8 @@ int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *d if (c->rlimit[r]) u = (uint64_t) c->rlimit[r]->rlim_max; else { - struct rlimit rl; + struct rlimit rl = {}; - zero(rl); getrlimit(r, &rl); u = (uint64_t) rl.rlim_max; diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h index eaa1b73e69..91d70e535f 100644 --- a/src/core/dbus-execute.h +++ b/src/core/dbus-execute.h @@ -37,6 +37,7 @@ #define BUS_EXEC_CONTEXT_INTERFACE \ " <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"EnvironmentFiles\" type=\"a(sb)\" access=\"read\"/>\n" \ " <property name=\"UMask\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"LimitCPU\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"LimitFSIZE\" type=\"t\" access=\"read\"/>\n" \ @@ -88,13 +89,11 @@ " <property name=\"InaccessibleDirectories\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"MountFlags\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"PrivateTmp\" type=\"b\" access=\"read\"/>\n" \ + " <property name=\"PrivateNetwork\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"SameProcessGroup\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n" \ " <property name=\"UtmpIdentifier\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"ControlGroupModify\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"ControlGroupPersistent\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"PrivateNetwork\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"IgnoreSIGPIPE\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"NoNewPrivileges\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"SystemCallFilter\" type=\"au\" access=\"read\"/>\n" diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c index fdc1dce177..98ccfa62ec 100644 --- a/src/core/dbus-job.c +++ b/src/core/dbus-job.c @@ -101,12 +101,11 @@ static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connec if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) { SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop"); + job_finish_and_invalidate(j, JOB_CANCELED, true); reply = dbus_message_new_method_return(message); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - - job_finish_and_invalidate(j, JOB_CANCELED, true); } else { const BusBoundProperties bps[] = { { "org.freedesktop.systemd1.Job", bus_job_properties, j }, @@ -114,11 +113,10 @@ static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connec }; SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status"); - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); } - if (!dbus_connection_send(connection, reply, NULL)) + if (!bus_maybe_send_reply(connection, message, reply)) return DBUS_HANDLER_RESULT_NEED_MEMORY; return DBUS_HANDLER_RESULT_HANDLED; @@ -187,7 +185,7 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu free(introspection); - if (!dbus_connection_send(connection, reply, NULL)) + if (!bus_maybe_send_reply(connection, message, reply)) goto oom; return DBUS_HANDLER_RESULT_HANDLED; @@ -220,7 +218,7 @@ const DBusObjectPathVTable bus_job_vtable = { }; static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) { - DBusMessage *m = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL; int r; assert(j); @@ -229,9 +227,9 @@ static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) { if (bus_has_subscriber(j->manager) || j->forgot_bus_clients) { m = new_message(j); if (!m) - goto oom; + return -ENOMEM; + r = bus_broadcast(j->manager, m); - dbus_message_unref(m); if (r < 0) return r; @@ -240,18 +238,19 @@ static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) { * to the client(s) which created the job */ JobBusClient *cl; assert(j->bus_client_list); + LIST_FOREACH(client, cl, j->bus_client_list) { assert(cl->bus); m = new_message(j); if (!m) - goto oom; + return -ENOMEM; if (!dbus_message_set_destination(m, cl->name)) - goto oom; + return -ENOMEM; if (!dbus_connection_send(cl->bus, m, NULL)) - goto oom; + return -ENOMEM; dbus_message_unref(m); m = NULL; @@ -259,10 +258,6 @@ static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) { } return 0; -oom: - if (m) - dbus_message_unref(m); - return -ENOMEM; } static DBusMessage* new_change_signal_message(Job *j) { diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 859fa1a906..56b02a1cf5 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -36,6 +36,7 @@ #include "path-util.h" #include "dbus-unit.h" #include "virt.h" +#include "env-util.h" #define BUS_MANAGER_INTERFACE_BEGIN \ " <interface name=\"org.freedesktop.systemd1.Manager\">\n" @@ -102,10 +103,39 @@ " <method name=\"ResetFailedUnit\">\n" \ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ " </method>\n" \ + " <method name=\"SetUnitControlGroup\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"UnsetUnitControlGroup\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \ + " </method>\n" \ + " <method name=\"GetUnitControlGroupAttribute\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \ + " </method>\n" \ + " <method name=\"SetUnitControlGroupAttribute\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \ + " </method>\n" \ + " <method name=\"UnsetUnitControlGroupAttributes\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ " <method name=\"GetJob\">\n" \ " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ " </method>\n" \ + " <method name=\"CancelJob\">\n" \ + " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \ + " </method>\n" \ " <method name=\"ClearJobs\"/>\n" \ " <method name=\"ResetFailed\"/>\n" \ " <method name=\"ListUnits\">\n" \ @@ -124,6 +154,9 @@ " <arg name=\"cleanup\" type=\"b\" direction=\"in\"/>\n" \ " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \ " </method>\n" \ + " <method name=\"RemoveSnapshot\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ " <method name=\"Reload\"/>\n" \ " <method name=\"Reexecute\"/>\n" \ " <method name=\"Exit\"/>\n" \ @@ -257,8 +290,8 @@ " <property name=\"DefaultControllers\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"DefaultStandardOutput\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"DefaultStandardError\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"RuntimeWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \ - " <property name=\"ShutdownWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \ + " <property name=\"RuntimeWatchdogUSec\" type=\"t\" access=\"readwrite\"/>\n" \ + " <property name=\"ShutdownWatchdogUSec\" type=\"t\" access=\"readwrite\"/>\n" \ " <property name=\"Virtualization\" type=\"s\" access=\"read\"/>\n" #define BUS_MANAGER_INTERFACE_END \ @@ -774,10 +807,33 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBUS_TYPE_INVALID)) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CancelJob")) { + uint32_t id; + Job *j; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_UINT32, &id, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + j = manager_get_job(m, id); + if (!j) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop"); + job_finish_and_invalidate(j, JOB_CANCELED, true); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) { SELINUX_ACCESS_CHECK(connection, message, "reboot"); - manager_clear_jobs(m); reply = dbus_message_new_method_return(message); @@ -819,6 +875,151 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, if (!reply) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroup")) { + const char *name; + Unit *u; + DBusMessageIter iter; + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + u = manager_get_unit(m, name); + if (!u) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + + r = bus_unit_cgroup_set(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroup")) { + const char *name; + Unit *u; + DBusMessageIter iter; + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + u = manager_get_unit(m, name); + if (!u) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + + r = bus_unit_cgroup_unset(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroupAttribute")) { + const char *name; + Unit *u; + DBusMessageIter iter; + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + u = manager_get_unit(m, name); + if (!u) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + + r = bus_unit_cgroup_attribute_set(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroupAttribute")) { + const char *name; + Unit *u; + DBusMessageIter iter; + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + u = manager_get_unit(m, name); + if (!u) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + + r = bus_unit_cgroup_attribute_unset(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitControlGroupAttribute")) { + const char *name; + Unit *u; + DBusMessageIter iter; + _cleanup_strv_free_ char **list = NULL; + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + u = manager_get_unit(m, name); + if (!u) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status"); + + r = bus_unit_cgroup_attribute_get(u, &iter, &list); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + dbus_message_iter_init_append(reply, &iter); + if (bus_append_strv_iter(&iter, list) < 0) + goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) { DBusMessageIter iter, sub; Iterator i; @@ -985,11 +1186,9 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, if (!client) goto oom; - r = set_put(s, client); - if (r < 0) { - free(client); + r = set_consume(s, client); + if (r < 0) return bus_send_error_reply(connection, message, NULL, r); - } reply = dbus_message_new_method_return(message); if (!reply) @@ -1080,6 +1279,35 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBUS_TYPE_INVALID)) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RemoveSnapshot")) { + const char *name; + Unit *u; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + u = manager_get_unit(m, name); + if (!u) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s does not exist.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + if (u->type != UNIT_SNAPSHOT) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not a snapshot.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + snapshot_remove(SNAPSHOT(u)); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { char *introspection = NULL; FILE *f; @@ -1251,7 +1479,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SwitchRoot")) { const char *switch_root, *switch_root_init; char *u, *v; - int k; + bool good; SELINUX_ACCESS_CHECK(connection, message, "reboot"); @@ -1275,20 +1503,24 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, } /* Safety check */ - if (isempty(switch_root_init)) - k = access(switch_root, F_OK); + if (isempty(switch_root_init)) { + good = path_is_os_tree(switch_root); + if (!good) + log_error("Not switching root: %s does not seem to be an OS tree. /etc/os-release is missing.", switch_root); + } else { - char *p; + _cleanup_free_ char *p = NULL; p = strjoin(switch_root, "/", switch_root_init, NULL); if (!p) goto oom; - k = access(p, X_OK); - free(p); + good = access(p, X_OK) >= 0; + if (!good) + log_error("Not switching root: cannot execute new init %s", p); } - if (k < 0) - return bus_send_error_reply(connection, message, NULL, -errno); + if (!good) + return bus_send_error_reply(connection, message, NULL, -EINVAL); u = strdup(switch_root); if (!u) @@ -1315,7 +1547,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, m->exit_code = MANAGER_SWITCH_ROOT; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) { - char **l = NULL, **e = NULL; + _cleanup_strv_free_ char **l = NULL; + char **e = NULL; SELINUX_ACCESS_CHECK(connection, message, "reboot"); @@ -1324,9 +1557,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, goto oom; if (r < 0) return bus_send_error_reply(connection, message, NULL, r); + if (!strv_env_is_valid(l)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); e = strv_env_merge(2, m->environment, l); - strv_free(l); if (!e) goto oom; @@ -1340,7 +1574,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, m->environment = e; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) { - char **l = NULL, **e = NULL; + _cleanup_strv_free_ char **l = NULL; + char **e = NULL; SELINUX_ACCESS_CHECK(connection, message, "reboot"); @@ -1349,10 +1584,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, goto oom; if (r < 0) return bus_send_error_reply(connection, message, NULL, r); + if (!strv_env_name_or_assignment_is_valid(l)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); e = strv_env_delete(m->environment, 1, l); - strv_free(l); - if (!e) goto oom; @@ -1366,7 +1601,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, m->environment = e; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) { - char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL; + _cleanup_strv_free_ char **l_set = NULL, **l_unset = NULL, **e = NULL; + char **f = NULL; DBusMessageIter iter; SELINUX_ACCESS_CHECK(connection, message, "reboot"); @@ -1379,33 +1615,25 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, goto oom; if (r < 0) return bus_send_error_reply(connection, message, NULL, r); + if (!strv_env_name_or_assignment_is_valid(l_unset)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); - if (!dbus_message_iter_next(&iter)) { - strv_free(l_unset); + if (!dbus_message_iter_next(&iter)) return bus_send_error_reply(connection, message, NULL, -EINVAL); - } r = bus_parse_strv_iter(&iter, &l_set); - if (r < 0) { - strv_free(l_unset); - if (r == -ENOMEM) - goto oom; - + if (r == -ENOMEM) + goto oom; + if (r < 0) return bus_send_error_reply(connection, message, NULL, r); - } + if (!strv_env_is_valid(l_set)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); e = strv_env_delete(m->environment, 1, l_unset); - strv_free(l_unset); - - if (!e) { - strv_free(l_set); + if (!e) goto oom; - } f = strv_env_merge(2, e, l_set); - strv_free(l_set); - strv_free(e); - if (!f) goto oom; @@ -1668,7 +1896,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, } if (reply) - if (!dbus_connection_send(connection, reply, NULL)) + if (!bus_maybe_send_reply(connection, message, reply)) goto oom; return DBUS_HANDLER_RESULT_HANDLED; diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index d81edeb807..0fcceb500d 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -40,6 +40,7 @@ BUS_EXEC_COMMAND_INTERFACE("ExecRemount") \ BUS_EXEC_CONTEXT_INTERFACE \ BUS_KILL_CONTEXT_INTERFACE \ + BUS_UNIT_CGROUP_INTERFACE \ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ @@ -159,6 +160,7 @@ DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMess { "org.freedesktop.systemd1.Mount", bus_mount_properties, m }, { "org.freedesktop.systemd1.Mount", bus_exec_context_properties, &m->exec_context }, { "org.freedesktop.systemd1.Mount", bus_kill_context_properties, &m->kill_context }, + { "org.freedesktop.systemd1.Mount", bus_unit_cgroup_properties, u }, { NULL, } }; diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c index f7fed1754d..1e62083d9b 100644 --- a/src/core/dbus-path.c +++ b/src/core/dbus-path.c @@ -84,15 +84,15 @@ static int bus_path_append_paths(DBusMessageIter *i, const char *property, void } static int bus_path_append_unit(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - Path *p = PATH(u); + Unit *u = data, *trigger; const char *t; assert(i); assert(property); assert(u); - t = UNIT_DEREF(p->unit) ? UNIT_DEREF(p->unit)->id : ""; + trigger = UNIT_TRIGGER(u); + t = trigger ? trigger->id : ""; return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM; } diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index d99058dd46..e06a5dce97 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -50,6 +50,7 @@ BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \ BUS_EXEC_CONTEXT_INTERFACE \ BUS_KILL_CONTEXT_INTERFACE \ + BUS_UNIT_CGROUP_INTERFACE \ " <property name=\"PermissionsStartOnly\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"RootDirectoryStartOnly\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"RemainAfterExit\" type=\"b\" access=\"read\"/>\n" \ @@ -152,6 +153,7 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connectio { "org.freedesktop.systemd1.Service", bus_exec_context_properties, &s->exec_context }, { "org.freedesktop.systemd1.Service", bus_kill_context_properties, &s->kill_context }, { "org.freedesktop.systemd1.Service", bus_exec_main_status_properties, &s->main_exec_status }, + { "org.freedesktop.systemd1.Service", bus_unit_cgroup_properties, u }, { NULL, } }; diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c index 435c6df39c..2ae8574f59 100644 --- a/src/core/dbus-snapshot.c +++ b/src/core/dbus-snapshot.c @@ -54,19 +54,16 @@ static const BusProperty bus_snapshot_properties[] = { DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { Snapshot *s = SNAPSHOT(u); _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - DBusError error; - - dbus_error_init(&error); if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Snapshot", "Remove")) { SELINUX_UNIT_ACCESS_CHECK(u, c, message, "stop"); - snapshot_remove(SNAPSHOT(u)); - reply = dbus_message_new_method_return(message); if (!reply) - goto oom; + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + snapshot_remove(SNAPSHOT(u)); } else { const BusBoundProperties bps[] = { @@ -80,15 +77,8 @@ DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusM return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } - if (reply) { - if (!dbus_connection_send(c, reply, NULL)) - goto oom; - } + if (!bus_maybe_send_reply(c, message, reply)) + return DBUS_HANDLER_RESULT_NEED_MEMORY; return DBUS_HANDLER_RESULT_HANDLED; - -oom: - dbus_error_free(&error); - - return DBUS_HANDLER_RESULT_NEED_MEMORY; } diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 095a031612..77d98ea0fd 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -39,6 +39,7 @@ BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \ BUS_EXEC_CONTEXT_INTERFACE \ BUS_KILL_CONTEXT_INTERFACE \ + BUS_UNIT_CGROUP_INTERFACE \ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"BindToDevice\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \ @@ -62,6 +63,7 @@ " <property name=\"NConnections\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"MessageQueueMaxMessages\" type=\"x\" access=\"read\"/>\n" \ " <property name=\"MessageQueueMessageSize\" type=\"x\" access=\"read\"/>\n" \ + " <property name=\"Listen\" type=\"a(ss)\" access=\"read\"/>\n" \ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"SmackLabel\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"SmackLabelIPIn\" type=\"s\" access=\"read\"/>\n" \ @@ -97,6 +99,66 @@ const char bus_socket_invalidating_properties[] = static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_socket_result, socket_result, SocketResult); +static int bus_socket_append_listen(DBusMessageIter *i, const char *property, void *data) { + + Socket *s = SOCKET(data); + SocketPort *p; + DBusMessageIter array, stru; + + assert(data); + assert(property); + assert(s); + + if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(ss)", &array)) + return log_oom(); + + LIST_FOREACH(port, p, s->ports) { + const char *type = socket_port_type_to_string(p); + _cleanup_free_ char *address = NULL; + const char *a; + + if (!dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT, NULL, &stru)) + return log_oom(); + + if (!dbus_message_iter_append_basic(&stru, DBUS_TYPE_STRING, &type)) + return log_oom(); + + switch (p->type) { + case SOCKET_SOCKET: { + int r; + + r = socket_address_print(&p->address, &address); + if (r) { + log_error("socket_address_print failed: %s", strerror(-r)); + return r; + } + a = address; + break; + } + + case SOCKET_SPECIAL: + case SOCKET_MQUEUE: + case SOCKET_FIFO: + a = p->path; + break; + + default: + a = type; + } + + if (!dbus_message_iter_append_basic(&stru, DBUS_TYPE_STRING, &a)) + return -ENOMEM; + + if (!dbus_message_iter_close_container(&array, &stru)) + return -ENOMEM; + } + + if (!dbus_message_iter_close_container(i, &array)) + return -ENOMEM; + + return 0; +} + static const BusProperty bus_socket_properties[] = { { "BindIPv6Only", bus_socket_append_bind_ipv6_only, "s", offsetof(Socket, bind_ipv6_only) }, { "Backlog", bus_property_append_unsigned, "u", offsetof(Socket, backlog) }, @@ -122,6 +184,7 @@ static const BusProperty bus_socket_properties[] = { { "Broadcast", bus_property_append_bool, "b", offsetof(Socket, broadcast) }, { "PassCredentials",bus_property_append_bool, "b", offsetof(Socket, pass_cred) }, { "PassSecurity", bus_property_append_bool, "b", offsetof(Socket, pass_sec) }, + { "Listen", bus_socket_append_listen, "a(ss)", 0, }, { "Mark", bus_property_append_int, "i", offsetof(Socket, mark) }, { "MaxConnections", bus_property_append_unsigned, "u", offsetof(Socket, max_connections) }, { "NConnections", bus_property_append_unsigned, "u", offsetof(Socket, n_connections) }, @@ -142,6 +205,7 @@ DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMes { "org.freedesktop.systemd1.Socket", bus_socket_properties, s }, { "org.freedesktop.systemd1.Socket", bus_exec_context_properties, &s->exec_context }, { "org.freedesktop.systemd1.Socket", bus_kill_context_properties, &s->kill_context }, + { "org.freedesktop.systemd1.Socket", bus_unit_properties, u }, { NULL, } }; diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c index 67ea0f24fe..2e99fba7db 100644 --- a/src/core/dbus-swap.c +++ b/src/core/dbus-swap.c @@ -38,6 +38,7 @@ BUS_EXEC_COMMAND_INTERFACE("ExecDeactivate") \ BUS_EXEC_CONTEXT_INTERFACE \ BUS_KILL_CONTEXT_INTERFACE \ + BUS_UNIT_CGROUP_INTERFACE \ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ " </interface>\n" @@ -106,6 +107,7 @@ DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessa { "org.freedesktop.systemd1.Swap", bus_swap_properties, s }, { "org.freedesktop.systemd1.Swap", bus_exec_context_properties, &s->exec_context }, { "org.freedesktop.systemd1.Swap", bus_kill_context_properties, &s->kill_context }, + { "org.freedesktop.systemd1.Swap", bus_unit_cgroup_properties, u }, { NULL, } }; diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index 11d18cbd83..4082f7f9b9 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -30,8 +30,10 @@ #define BUS_TIMER_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Timer\">\n" \ " <property name=\"Unit\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Timers\" type=\"a(stt)\" access=\"read\"/>\n" \ - " <property name=\"NextElapseUSec\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"TimersMonotonic\" type=\"a(stt)\" access=\"read\"/>\n" \ + " <property name=\"TimersCalendar\" type=\"a(sst)\" access=\"read\"/>\n" \ + " <property name=\"NextElapseUSecRealtime\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"NextElapseUSecMonotonic\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ " </interface>\n" @@ -52,11 +54,13 @@ const char bus_timer_interface[] _introspect_("Timer") = BUS_TIMER_INTERFACE; const char bus_timer_invalidating_properties[] = - "Timers\0" - "NextElapseUSec\0" + "TimersMonotonic\0" + "TimersRealtime\0" + "NextElapseUSecRealtime\0" + "NextElapseUSecMonotonic\0" "Result\0"; -static int bus_timer_append_timers(DBusMessageIter *i, const char *property, void *data) { +static int bus_timer_append_monotonic_timers(DBusMessageIter *i, const char *property, void *data) { Timer *p = data; DBusMessageIter sub, sub2; TimerValue *k; @@ -69,11 +73,14 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi return -ENOMEM; LIST_FOREACH(value, k, p->values) { - char *buf; + _cleanup_free_ char *buf = NULL; const char *t; size_t l; bool b; + if (k->base == TIMER_CALENDAR) + continue; + t = timer_base_to_string(k->base); assert(endswith(t, "Sec")); @@ -92,7 +99,48 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->next_elapse) && dbus_message_iter_close_container(&sub, &sub2); - free(buf); + if (!b) + return -ENOMEM; + } + + if (!dbus_message_iter_close_container(i, &sub)) + return -ENOMEM; + + return 0; +} + +static int bus_timer_append_calendar_timers(DBusMessageIter *i, const char *property, void *data) { + Timer *p = data; + DBusMessageIter sub, sub2; + TimerValue *k; + + assert(i); + assert(property); + assert(p); + + if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sst)", &sub)) + return -ENOMEM; + + LIST_FOREACH(value, k, p->values) { + _cleanup_free_ char *buf = NULL; + const char *t; + bool b; + int j; + + if (k->base != TIMER_CALENDAR) + continue; + + t = timer_base_to_string(k->base); + j = calendar_spec_to_string(k->calendar_spec, &buf); + if (j < 0) + return j; + + b = dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) && + dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &t) && + dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &buf) && + dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->next_elapse) && + dbus_message_iter_close_container(&sub, &sub2); + if (!b) return -ENOMEM; } @@ -104,15 +152,15 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi } static int bus_timer_append_unit(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - Timer *timer = TIMER(u); + Unit *u = data, *trigger; const char *t; assert(i); assert(property); assert(u); - t = UNIT_DEREF(timer->unit) ? UNIT_DEREF(timer->unit)->id : ""; + trigger = UNIT_TRIGGER(u); + t = trigger ? trigger->id : ""; return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM; } @@ -120,11 +168,12 @@ static int bus_timer_append_unit(DBusMessageIter *i, const char *property, void static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_timer_append_timer_result, timer_result, TimerResult); static const BusProperty bus_timer_properties[] = { - { "Unit", bus_timer_append_unit, "s", 0 }, - { "Timers", bus_timer_append_timers, "a(stt)", 0 }, - { "NextElapseUSec", bus_property_append_usec, "t", offsetof(Timer, next_elapse_monotonic) }, - { "NextElapseUSecRealtime", bus_property_append_usec, "t", offsetof(Timer, next_elapse_realtime) }, - { "Result", bus_timer_append_timer_result,"s", offsetof(Timer, result) }, + { "Unit", bus_timer_append_unit, "s", 0 }, + { "TimersMonotonic", bus_timer_append_monotonic_timers, "a(stt)", 0 }, + { "TimersCalendar", bus_timer_append_calendar_timers, "a(sst)", 0 }, + { "NextElapseUSecMonotonic", bus_property_append_usec, "t", offsetof(Timer, next_elapse_monotonic) }, + { "NextElapseUSecRealtime", bus_property_append_usec, "t", offsetof(Timer, next_elapse_realtime) }, + { "Result", bus_timer_append_timer_result, "s", offsetof(Timer, result) }, { NULL, } }; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 8433a720b2..575f8eb36a 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -27,6 +27,10 @@ #include "bus-errors.h" #include "dbus-common.h" #include "selinux-access.h" +#include "cgroup-util.h" +#include "strv.h" +#include "path-util.h" +#include "fileio.h" const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE; @@ -310,7 +314,7 @@ static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, voi return -ENOMEM; LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) { - char _cleanup_free_ *t = NULL; + _cleanup_free_ char *t = NULL; bool success; t = cgroup_bonding_to_string(cgb); @@ -337,11 +341,11 @@ static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property return -ENOMEM; LIST_FOREACH(by_unit, a, u->cgroup_attributes) { - char _cleanup_free_ *v = NULL; + _cleanup_free_ char *v = NULL; bool success; - if (a->map_callback) - a->map_callback(a->controller, a->name, a->value, &v); + if (a->semantics && a->semantics->map_write) + a->semantics->map_write(a->semantics, a->value, &v); success = dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) && @@ -468,6 +472,90 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn if (!reply) goto oom; + } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_set(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroup")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_unset(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttribute")) { + DBusMessageIter iter; + _cleanup_strv_free_ char **list = NULL; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_attribute_get(u, &iter, &list); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + dbus_message_iter_init_append(reply, &iter); + if (bus_append_strv_iter(&iter, list) < 0) + goto oom; + + } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_attribute_set(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttribute")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_attribute_unset(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else if (UNIT_VTABLE(u)->bus_message_handler) return UNIT_VTABLE(u)->bus_message_handler(u, connection, message); else @@ -494,7 +582,7 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn } if (reply) - if (!dbus_connection_send(connection, reply, NULL)) + if (!bus_maybe_send_reply(connection, message, reply)) goto oom; return DBUS_HANDLER_RESULT_HANDLED; @@ -585,7 +673,7 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB free(introspection); - if (!dbus_connection_send(connection, reply, NULL)) + if (!bus_maybe_send_reply(connection, message, reply)) goto oom; return DBUS_HANDLER_RESULT_HANDLED; @@ -798,7 +886,7 @@ DBusHandlerResult bus_unit_queue_job( DBUS_TYPE_INVALID)) goto oom; - if (!dbus_connection_send(connection, reply, NULL)) + if (!bus_maybe_send_reply(connection, message, reply)) goto oom; return DBUS_HANDLER_RESULT_HANDLED; @@ -809,6 +897,360 @@ oom: return DBUS_HANDLER_RESULT_NEED_MEMORY; } +static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) { + const char *mode; + int r; + + assert(iter); + assert(runtime); + + r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next); + if (r < 0) + return r; + + if (streq(mode, "runtime")) + *runtime = true; + else if (streq(mode, "persistent")) + *runtime = false; + else + return -EINVAL; + + return 0; +} + +int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) { + _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL; + const char *name; + CGroupBonding *b; + bool runtime; + int r; + + assert(u); + assert(iter); + + if (!unit_get_exec_context(u)) + return -EINVAL; + + r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return r; + + r = parse_mode(iter, &runtime, false); + if (r < 0) + return r; + + r = cg_split_spec(name, &controller, &new_path); + if (r < 0) + return r; + + if (!new_path) { + new_path = unit_default_cgroup_path(u); + if (!new_path) + return -ENOMEM; + } + + if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER)) + return -EINVAL; + + b = cgroup_bonding_find_list(u->cgroup_bondings, controller); + if (b) { + if (streq(b->path, new_path)) + return 0; + + if (b->essential) + return -EINVAL; + + old_path = strdup(b->path); + if (!old_path) + return -ENOMEM; + } + + r = unit_add_cgroup_from_text(u, name, true, &b); + if (r < 0) + return r; + if (r > 0) { + CGroupAttribute *a; + + /* Try to move things to the new place, and clean up the old place */ + cgroup_bonding_realize(b); + cgroup_bonding_migrate(b, u->cgroup_bondings); + + if (old_path) + cg_trim(controller, old_path, true); + + /* Apply the attributes to the new group */ + LIST_FOREACH(by_unit, a, u->cgroup_attributes) + if (streq(a->controller, controller)) + cgroup_attribute_apply(a, b); + } + + contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n" + "ControlGroup=", name, "\n", NULL); + if (!contents) + return -ENOMEM; + + return unit_write_drop_in(u, runtime, controller, contents); +} + +int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) { + _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL; + const char *name; + CGroupAttribute *a, *n; + CGroupBonding *b; + bool runtime; + int r; + + assert(u); + assert(iter); + + if (!unit_get_exec_context(u)) + return -EINVAL; + + r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return r; + + r = parse_mode(iter, &runtime, false); + if (r < 0) + return r; + + r = cg_split_spec(name, &controller, &path); + if (r < 0) + return r; + + if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER)) + return -EINVAL; + + b = cgroup_bonding_find_list(u->cgroup_bondings, controller); + if (!b) + return -ENOENT; + + if (path && !path_equal(path, b->path)) + return -ENOENT; + + if (b->essential) + return -EINVAL; + + unit_remove_drop_in(u, runtime, controller); + + /* Try to migrate the old group away */ + if (cg_pid_get_path(controller, 0, &target) >= 0) + cgroup_bonding_migrate_to(u->cgroup_bondings, target, false); + + cgroup_bonding_free(b, true); + + /* Drop all attributes of this controller */ + LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) { + if (!streq(a->controller, controller)) + continue; + + unit_remove_drop_in(u, runtime, a->name); + cgroup_attribute_free(a); + } + + return 0; +} + +int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) { + _cleanup_free_ char *controller = NULL; + CGroupAttribute *a; + CGroupBonding *b; + const char *name; + char **l = NULL; + int r; + + assert(u); + assert(iter); + assert(_result); + + if (!unit_get_exec_context(u)) + return -EINVAL; + + r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false); + if (r < 0) + return r; + + r = cg_controller_from_attr(name, &controller); + if (r < 0) + return r; + + /* First attempt, read the value from the kernel */ + b = cgroup_bonding_find_list(u->cgroup_bondings, controller); + if (b) { + _cleanup_free_ char *p = NULL, *v = NULL; + + r = cg_get_path(b->controller, b->path, name, &p); + if (r < 0) + return r; + + r = read_full_file(p, &v, NULL); + if (r >= 0) { + /* Split on new lines */ + l = strv_split_newlines(v); + if (!l) + return -ENOMEM; + + *_result = l; + return 0; + + } + } + + /* If that didn't work, read our cached value */ + LIST_FOREACH(by_unit, a, u->cgroup_attributes) { + + if (!cgroup_attribute_matches(a, controller, name)) + continue; + + r = strv_extend(&l, a->value); + if (r < 0) { + strv_free(l); + return r; + } + } + + if (!l) + return -ENOENT; + + *_result = l; + return 0; +} + +static int update_attribute_drop_in(Unit *u, bool runtime, const char *name) { + _cleanup_free_ char *buf = NULL; + CGroupAttribute *a; + + assert(u); + assert(name); + + LIST_FOREACH(by_unit, a, u->cgroup_attributes) { + if (!cgroup_attribute_matches(a, NULL, name)) + continue; + + if (!buf) { + buf = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n" + "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL); + + if (!buf) + return -ENOMEM; + } else { + char *b; + + b = strjoin(buf, + "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL); + + if (!b) + return -ENOMEM; + + free(buf); + buf = b; + } + } + + if (buf) + return unit_write_drop_in(u, runtime, name, buf); + else + return unit_remove_drop_in(u, runtime, name); +} + +int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) { + _cleanup_strv_free_ char **l = NULL; + int r; + bool runtime = false; + char **value; + const char *name; + + assert(u); + assert(iter); + + if (!unit_get_exec_context(u)) + return -EINVAL; + + r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return r; + + r = bus_parse_strv_iter(iter, &l); + if (r < 0) + return r; + + if (!dbus_message_iter_next(iter)) + return -EINVAL; + + r = parse_mode(iter, &runtime, false); + if (r < 0) + return r; + + STRV_FOREACH(value, l) { + _cleanup_free_ char *v = NULL; + CGroupAttribute *a; + const CGroupSemantics *s; + + r = cgroup_semantics_find(NULL, name, *value, &v, &s); + if (r < 0) + return r; + + if (s && !s->multiple && l[1]) + return -EINVAL; + + r = unit_add_cgroup_attribute(u, s, NULL, name, v ? v : *value, &a); + if (r < 0) + return r; + + if (r > 0) { + CGroupBonding *b; + + b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller); + if (!b) { + /* Doesn't exist yet? Then let's add it */ + r = unit_add_cgroup_from_text(u, a->controller, false, &b); + if (r < 0) + return r; + + if (r > 0) { + cgroup_bonding_realize(b); + cgroup_bonding_migrate(b, u->cgroup_bondings); + } + } + + /* Make it count */ + cgroup_attribute_apply(a, u->cgroup_bondings); + } + + } + + r = update_attribute_drop_in(u, runtime, name); + if (r < 0) + return r; + + return 0; +} + +int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) { + const char *name; + bool runtime; + int r; + + assert(u); + assert(iter); + + if (!unit_get_exec_context(u)) + return -EINVAL; + + r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return r; + + r = parse_mode(iter, &runtime, false); + if (r < 0) + return r; + + cgroup_attribute_free_some(u->cgroup_attributes, NULL, name); + update_attribute_drop_in(u, runtime, name); + + return 0; +} + const BusProperty bus_unit_properties[] = { { "Id", bus_property_append_string, "s", offsetof(Unit, id), true }, { "Names", bus_unit_append_names, "as", 0 }, @@ -842,6 +1284,7 @@ const BusProperty bus_unit_properties[] = { { "SubState", bus_unit_append_sub_state, "s", 0 }, { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true }, { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true }, + { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true }, { "UnitFileState", bus_unit_append_file_state, "s", 0 }, { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) }, { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) }, @@ -864,9 +1307,6 @@ const BusProperty bus_unit_properties[] = { { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) }, { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) }, { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) }, - { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 }, - { "ControlGroup", bus_unit_append_cgroups, "as", 0 }, - { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 }, { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 }, { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) }, { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) }, @@ -875,3 +1315,10 @@ const BusProperty bus_unit_properties[] = { { "LoadError", bus_unit_append_load_error, "(ss)", 0 }, { NULL, } }; + +const BusProperty bus_unit_cgroup_properties[] = { + { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 }, + { "ControlGroups", bus_unit_append_cgroups, "as", 0 }, + { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 }, + { NULL, } +}; diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index ac6785a949..acd1ddbe78 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -69,11 +69,13 @@ " <property name=\"Requisite\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"RequisiteOverridable\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"Wants\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"BindsTo\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"BindsTo\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"PartOf\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"RequiredBy\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"RequiredByOverridable\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"WantedBy\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"BoundBy\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"ConsistsOf\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"Conflicts\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"ConflictedBy\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"Before\" type=\"as\" access=\"read\"/>\n" \ @@ -86,6 +88,7 @@ " <property name=\"RequiresMountsFor\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"Description\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"SourcePath\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"DropInPaths\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"Documentation\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"LoadState\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"ActiveState\" type=\"s\" access=\"read\"/>\n" \ @@ -113,9 +116,6 @@ " <property name=\"OnFailureIsolate\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"IgnoreOnIsolate\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"IgnoreOnSnapshot\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"ControlGroup\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \ " <property name=\"NeedDaemonReload\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"JobTimeoutUSec\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"ConditionTimestamp\" type=\"t\" access=\"read\"/>\n" \ @@ -124,16 +124,42 @@ " <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \ " </interface>\n" +#define BUS_UNIT_CGROUP_INTERFACE \ + " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"ControlGroups\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \ + " <method name=\"SetControlGroup\">\n" \ + " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"UnsetControlGroup\">\n" \ + " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"GetControlGroupAttribute\">\n" \ + " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \ + " </method>\n" \ + " <method name=\"SetControlGroupAttribute\">\n" \ + " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"UnsetControlGroupAttribute\">\n" \ + " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" + #define BUS_UNIT_INTERFACES_LIST \ BUS_GENERIC_INTERFACES_LIST \ "org.freedesktop.systemd1.Unit\0" extern const BusProperty bus_unit_properties[]; +extern const BusProperty bus_unit_cgroup_properties[]; void bus_unit_send_change_signal(Unit *u); void bus_unit_send_removed_signal(Unit *u); - DBusHandlerResult bus_unit_queue_job( DBusConnection *connection, DBusMessage *message, @@ -142,6 +168,12 @@ DBusHandlerResult bus_unit_queue_job( JobMode mode, bool reload_if_possible); +int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter); +int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter); +int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result); +int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter); +int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter); + extern const DBusObjectPathVTable bus_unit_vtable; extern const char bus_unit_interface[]; diff --git a/src/core/dbus.c b/src/core/dbus.c index 2a1c66054a..1272c938cf 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -48,7 +48,7 @@ #include "special.h" #include "dbus-common.h" -#define CONNECTIONS_MAX 52 +#define CONNECTIONS_MAX 512 /* Well-known address (http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-types) */ #define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" @@ -203,13 +203,11 @@ static void bus_toggle_watch(DBusWatch *bus_watch, void *data) { } static int bus_timeout_arm(Manager *m, Watch *w) { - struct itimerspec its; + struct itimerspec its = {}; assert(m); assert(w); - zero(its); - if (dbus_timeout_get_enabled(w->data.bus_timeout)) { timespec_store(&its.it_value, dbus_timeout_get_interval(w->data.bus_timeout) * USEC_PER_MSEC); its.it_interval = its.it_value; @@ -365,8 +363,8 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBus log_debug("Got D-Bus activation request for %s", name); - if (manager_unit_pending_inactive(m, SPECIAL_DBUS_SERVICE) || - manager_unit_pending_inactive(m, SPECIAL_DBUS_SOCKET)) { + if (manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SERVICE) || + manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SOCKET)) { r = -EADDRNOTAVAIL; dbus_set_error(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down."); } else { @@ -406,7 +404,7 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBus dbus_error_free(&error); if (reply) { - if (!dbus_connection_send(connection, reply, NULL)) + if (!bus_maybe_send_reply(connection, message, reply)) goto oom; dbus_message_unref(reply); @@ -978,9 +976,8 @@ static DBusConnection* manager_bus_connect_private(Manager *m, DBusBusType type) } return connection; + fail: - if (connection) - dbus_connection_close(connection); dbus_error_free(&error); return NULL; } @@ -1054,7 +1051,7 @@ fail: static int bus_init_private(Manager *m) { DBusError error; int r; - const char *const external_only[] = { + static const char *const external_only[] = { "EXTERNAL", NULL }; @@ -1077,18 +1074,33 @@ static int bus_init_private(Manager *m) { } else { const char *e; char *p; + char *escaped; e = secure_getenv("XDG_RUNTIME_DIR"); if (!e) return 0; - if (asprintf(&p, "unix:path=%s/systemd/private", e) < 0) { + if (asprintf(&p, "%s/systemd/private", e) < 0) { + r = log_oom(); + goto fail; + } + + mkdir_parents_label(p, 0755); + unlink(p); + free(p); + + escaped = dbus_address_escape_value(e); + if (!escaped) { + r = log_oom(); + goto fail; + } + if (asprintf(&p, "unix:path=%s/systemd/private", escaped) < 0) { + dbus_free(escaped); r = log_oom(); goto fail; } + dbus_free(escaped); - mkdir_parents_label(p+10, 0755); - unlink(p+10); m->private_bus = dbus_server_listen(p, &error); free(p); } diff --git a/src/core/device.c b/src/core/device.c index 0b01718ad4..9fca82ab16 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -124,13 +124,13 @@ static void device_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(d->sysfs)); } -static UnitActiveState device_active_state(Unit *u) { +_pure_ static UnitActiveState device_active_state(Unit *u) { assert(u); return state_translation_table[DEVICE(u)->state]; } -static const char *device_sub_state_to_string(Unit *u) { +_pure_ static const char *device_sub_state_to_string(Unit *u) { assert(u); return device_state_to_string(DEVICE(u)->state); @@ -281,16 +281,22 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p size_t l; FOREACH_WORD_QUOTED(w, l, wants, state) { - char *e; + char *e, *n; e = strndup(w, l); if (!e) { r = -ENOMEM; goto fail; } - - r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true); + n = unit_name_mangle(e); + if (!n) { + r = -ENOMEM; + goto fail; + } free(e); + + r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true); + free(n); if (r < 0) goto fail; } @@ -312,6 +318,7 @@ fail: static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) { const char *sysfs, *dn; struct udev_list_entry *item = NULL, *first = NULL; + int r; assert(m); @@ -319,7 +326,9 @@ static int device_process_new_device(Manager *m, struct udev_device *dev, bool u return -ENOMEM; /* Add the main unit named after the sysfs path */ - device_update_unit(m, dev, sysfs, true); + r = device_update_unit(m, dev, sysfs, true); + if (r < 0) + return r; /* Add an additional unit for the device node */ if ((dn = udev_device_get_devnode(dev))) @@ -477,7 +486,6 @@ static void device_shutdown(Manager *m) { } static int device_enumerate(Manager *m) { - struct epoll_event ev; int r; struct udev_enumerate *e = NULL; struct udev_list_entry *item = NULL, *first = NULL; @@ -485,6 +493,8 @@ static int device_enumerate(Manager *m) { assert(m); if (!m->udev) { + struct epoll_event ev; + if (!(m->udev = udev_new())) return -ENOMEM; diff --git a/src/core/device.h b/src/core/device.h index 3c4604f60e..41e8de9703 100644 --- a/src/core/device.h +++ b/src/core/device.h @@ -52,5 +52,5 @@ extern const UnitVTable device_vtable; void device_fd_event(Manager *m, int events); -const char* device_state_to_string(DeviceState i); -DeviceState device_state_from_string(const char *s); +const char* device_state_to_string(DeviceState i) _const_; +DeviceState device_state_from_string(const char *s) _pure_; diff --git a/src/core/execute.c b/src/core/execute.c index 7dc15044b4..3959ef9623 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -40,6 +40,7 @@ #include <sys/poll.h> #include <linux/seccomp-bpf.h> #include <glob.h> +#include <libgen.h> #ifdef HAVE_PAM #include <security/pam_appl.h> @@ -64,6 +65,8 @@ #include "loopback-setup.h" #include "path-util.h" #include "syscall-list.h" +#include "env-util.h" +#include "fileio.h" #define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC) @@ -141,7 +144,7 @@ static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) { return 0; } -static const char *tty_path(const ExecContext *context) { +_pure_ static const char *tty_path(const ExecContext *context) { assert(context); if (context->tty_path) @@ -163,6 +166,26 @@ void exec_context_tty_reset(const ExecContext *context) { vt_disallocate(context->tty_path); } +static bool is_terminal_output(ExecOutput o) { + return + o == EXEC_OUTPUT_TTY || + o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || + o == EXEC_OUTPUT_KMSG_AND_CONSOLE || + o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE; +} + +void exec_context_serialize(const ExecContext *context, Unit *u, FILE *f) { + assert(context); + assert(u); + assert(f); + + if (context->tmp_dir) + unit_serialize_item(u, f, "tmp-dir", context->tmp_dir); + + if (context->var_tmp_dir) + unit_serialize_item(u, f, "var-tmp-dir", context->var_tmp_dir); +} + static int open_null_as(int flags, int nfd) { int fd, r; @@ -182,7 +205,10 @@ static int open_null_as(int flags, int nfd) { static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd) { int fd, r; - union sockaddr_union sa; + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/stdout", + }; assert(context); assert(output < _EXEC_OUTPUT_MAX); @@ -193,10 +219,6 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons if (fd < 0) return -errno; - zero(sa); - sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path)); - r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); if (r < 0) { close_nointr_nofail(fd); @@ -222,7 +244,7 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons !!context->syslog_level_prefix, output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE, output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE, - output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || output == EXEC_OUTPUT_KMSG_AND_CONSOLE || output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE); + is_terminal_output(output)); if (fd != nfd) { r = dup2(fd, nfd) < 0 ? -errno : nfd; @@ -318,9 +340,10 @@ static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty } } -static int setup_output(const ExecContext *context, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) { +static int setup_output(const ExecContext *context, int fileno, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) { ExecOutput o; ExecInput i; + int r; assert(context); assert(ident); @@ -328,91 +351,55 @@ static int setup_output(const ExecContext *context, int socket_fd, const char *i i = fixup_input(context->std_input, socket_fd, apply_tty_stdin); o = fixup_output(context->std_output, socket_fd); - /* This expects the input is already set up */ + if (fileno == STDERR_FILENO) { + ExecOutput e; + e = fixup_output(context->std_error, socket_fd); - switch (o) { + /* This expects the input and output are already set up */ + + /* Don't change the stderr file descriptor if we inherit all + * the way and are not on a tty */ + if (e == EXEC_OUTPUT_INHERIT && + o == EXEC_OUTPUT_INHERIT && + i == EXEC_INPUT_NULL && + !is_terminal_input(context->std_input) && + getppid () != 1) + return fileno; - case EXEC_OUTPUT_INHERIT: + /* Duplicate from stdout if possible */ + if (e == o || e == EXEC_OUTPUT_INHERIT) + return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno; + o = e; + + } else if (o == EXEC_OUTPUT_INHERIT) { /* If input got downgraded, inherit the original value */ if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input)) - return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO); + return open_terminal_as(tty_path(context), O_WRONLY, fileno); /* If the input is connected to anything that's not a /dev/null, inherit that... */ if (i != EXEC_INPUT_NULL) - return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO; + return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno; /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */ if (getppid() != 1) - return STDOUT_FILENO; - - /* We need to open /dev/null here anew, to get the - * right access mode. So we fall through */ - - case EXEC_OUTPUT_NULL: - return open_null_as(O_WRONLY, STDOUT_FILENO); - - case EXEC_OUTPUT_TTY: - if (is_terminal_input(i)) - return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO; + return fileno; - /* We don't reset the terminal if this is just about output */ - return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO); - - case EXEC_OUTPUT_SYSLOG: - case EXEC_OUTPUT_SYSLOG_AND_CONSOLE: - case EXEC_OUTPUT_KMSG: - case EXEC_OUTPUT_KMSG_AND_CONSOLE: - case EXEC_OUTPUT_JOURNAL: - case EXEC_OUTPUT_JOURNAL_AND_CONSOLE: - return connect_logger_as(context, o, ident, unit_id, STDOUT_FILENO); - - case EXEC_OUTPUT_SOCKET: - assert(socket_fd >= 0); - return dup2(socket_fd, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO; - - default: - assert_not_reached("Unknown output type"); + /* We need to open /dev/null here anew, to get the right access mode. */ + return open_null_as(O_WRONLY, fileno); } -} - -static int setup_error(const ExecContext *context, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) { - ExecOutput o, e; - ExecInput i; - - assert(context); - assert(ident); - - i = fixup_input(context->std_input, socket_fd, apply_tty_stdin); - o = fixup_output(context->std_output, socket_fd); - e = fixup_output(context->std_error, socket_fd); - - /* This expects the input and output are already set up */ - /* Don't change the stderr file descriptor if we inherit all - * the way and are not on a tty */ - if (e == EXEC_OUTPUT_INHERIT && - o == EXEC_OUTPUT_INHERIT && - i == EXEC_INPUT_NULL && - !is_terminal_input(context->std_input) && - getppid () != 1) - return STDERR_FILENO; - - /* Duplicate from stdout if possible */ - if (e == o || e == EXEC_OUTPUT_INHERIT) - return dup2(STDOUT_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO; - - switch (e) { + switch (o) { case EXEC_OUTPUT_NULL: - return open_null_as(O_WRONLY, STDERR_FILENO); + return open_null_as(O_WRONLY, fileno); case EXEC_OUTPUT_TTY: if (is_terminal_input(i)) - return dup2(STDIN_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO; + return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno; /* We don't reset the terminal if this is just about output */ - return open_terminal_as(tty_path(context), O_WRONLY, STDERR_FILENO); + return open_terminal_as(tty_path(context), O_WRONLY, fileno); case EXEC_OUTPUT_SYSLOG: case EXEC_OUTPUT_SYSLOG_AND_CONSOLE: @@ -420,11 +407,21 @@ static int setup_error(const ExecContext *context, int socket_fd, const char *id case EXEC_OUTPUT_KMSG_AND_CONSOLE: case EXEC_OUTPUT_JOURNAL: case EXEC_OUTPUT_JOURNAL_AND_CONSOLE: - return connect_logger_as(context, e, ident, unit_id, STDERR_FILENO); + r = connect_logger_as(context, o, ident, unit_id, fileno); + if (r < 0) { + log_struct_unit(LOG_CRIT, unit_id, + "MESSAGE=Failed to connect std%s of %s to the journal socket: %s", + fileno == STDOUT_FILENO ? "out" : "err", + unit_id, strerror(-r), + "ERRNO=%d", -r, + NULL); + r = open_null_as(O_WRONLY, fileno); + } + return r; case EXEC_OUTPUT_SOCKET: assert(socket_fd >= 0); - return dup2(socket_fd, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO; + return dup2(socket_fd, fileno) < 0 ? -errno : fileno; default: assert_not_reached("Unknown error type"); @@ -512,7 +509,7 @@ fail: return r; } -static int write_confirm_message(const char *format, ...) { +_printf_attr_(1, 2) static int write_confirm_message(const char *format, ...) { int fd; va_list ap; @@ -673,9 +670,9 @@ static int enforce_user(const ExecContext *context, uid_t uid) { /* First step: If we need to keep capabilities but * drop privileges we need to make sure we keep our - * caps, whiel we drop privileges. */ + * caps, while we drop privileges. */ if (uid != 0) { - int sb = context->secure_bits|SECURE_KEEP_CAPS; + int sb = context->secure_bits | 1<<SECURE_KEEP_CAPS; if (prctl(PR_GET_SECUREBITS) != sb) if (prctl(PR_SET_SECUREBITS, sb) < 0) @@ -940,7 +937,7 @@ static int apply_seccomp(uint32_t *syscall_filter) { int i; unsigned n; struct sock_filter *f; - struct sock_fprog prog; + struct sock_fprog prog = {}; assert(syscall_filter); @@ -957,7 +954,7 @@ static int apply_seccomp(uint32_t *syscall_filter) { for (i = 0, n = 0; i < syscall_max(); i++) if (syscall_filter[i >> 4] & (1 << (i & 31))) { struct sock_filter item[] = { - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, i, 0, 1), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, INDEX_TO_SYSCALL(i), 0, 1), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) }; @@ -972,7 +969,6 @@ static int apply_seccomp(uint32_t *syscall_filter) { memcpy(f + (ELEMENTSOF(header) + 2*n), footer, sizeof(footer)); /* Third: install the filter */ - zero(prog); prog.len = ELEMENTSOF(header) + ELEMENTSOF(footer) + 2*n; prog.filter = f; if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0) @@ -983,7 +979,7 @@ static int apply_seccomp(uint32_t *syscall_filter) { int exec_spawn(ExecCommand *command, char **argv, - const ExecContext *context, + ExecContext *context, int fds[], unsigned n_fds, char **environment, bool apply_permissions, @@ -1001,7 +997,7 @@ int exec_spawn(ExecCommand *command, int r; char *line; int socket_fd; - char _cleanup_strv_free_ **files_env = NULL; + _cleanup_strv_free_ char **files_env = NULL; assert(command); assert(context); @@ -1024,8 +1020,8 @@ int exec_spawn(ExecCommand *command, r = exec_context_load_environment(context, &files_env); if (r < 0) { - log_struct(LOG_ERR, - "UNIT=%s", unit_id, + log_struct_unit(LOG_ERR, + unit_id, "MESSAGE=Failed to load environment files: %s", strerror(-r), "ERRNO=%d", -r, NULL); @@ -1039,18 +1035,30 @@ int exec_spawn(ExecCommand *command, if (!line) return log_oom(); - log_struct(LOG_DEBUG, - "UNIT=%s", unit_id, - "MESSAGE=About to execute %s", line, - NULL); + log_struct_unit(LOG_DEBUG, + unit_id, + "EXECUTABLE=%s", command->path, + "MESSAGE=About to execute: %s", line, + NULL); free(line); r = cgroup_bonding_realize_list(cgroup_bondings); if (r < 0) return r; + /* We must initialize the attributes in the parent, before we + fork, because we really need them initialized before making + the process a member of the group (which we do in both the + child and the parent), and we cannot really apply them twice + (due to 'append' style attributes) */ cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings); + if (context->private_tmp && !context->tmp_dir && !context->var_tmp_dir) { + r = setup_tmpdirs(&context->tmp_dir, &context->var_tmp_dir); + if (r < 0) + return r; + } + pid = fork(); if (pid < 0) return -errno; @@ -1061,7 +1069,7 @@ int exec_spawn(ExecCommand *command, const char *username = NULL, *home = NULL; uid_t uid = (uid_t) -1; gid_t gid = (gid_t) -1; - char _cleanup_strv_free_ **our_env = NULL, **pam_env = NULL, + _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; unsigned n_env = 0; bool set_access = false; @@ -1165,13 +1173,13 @@ int exec_spawn(ExecCommand *command, goto fail_child; } - err = setup_output(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin); + err = setup_output(context, STDOUT_FILENO, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin); if (err < 0) { r = EXIT_STDOUT; goto fail_child; } - err = setup_error(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin); + err = setup_output(context, STDERR_FILENO, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin); if (err < 0) { r = EXIT_STDERR; goto fail_child; @@ -1191,7 +1199,7 @@ int exec_spawn(ExecCommand *command, snprintf(t, sizeof(t), "%i", context->oom_score_adjust); char_array_0(t); - if (write_one_line_file("/proc/self/oom_score_adj", t) < 0) { + if (write_string_file("/proc/self/oom_score_adj", t) < 0) { err = -errno; r = EXIT_OOM_ADJUST; goto fail_child; @@ -1206,13 +1214,16 @@ int exec_spawn(ExecCommand *command, } if (context->cpu_sched_set) { - struct sched_param param; - - zero(param); - param.sched_priority = context->cpu_sched_priority; + struct sched_param param = { + .sched_priority = context->cpu_sched_priority, + }; - if (sched_setscheduler(0, context->cpu_sched_policy | - (context->cpu_sched_reset_on_fork ? SCHED_RESET_ON_FORK : 0), ¶m) < 0) { + r = sched_setscheduler(0, + context->cpu_sched_policy | + (context->cpu_sched_reset_on_fork ? + SCHED_RESET_ON_FORK : 0), + ¶m); + if (r < 0) { err = -errno; r = EXIT_SETSCHEDULER; goto fail_child; @@ -1262,7 +1273,12 @@ int exec_spawn(ExecCommand *command, if (cgroup_bondings && context->control_group_modify) { err = cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid); if (err >= 0) - err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid, context->control_group_persistent); + err = cgroup_bonding_set_task_access_list( + cgroup_bondings, + 0644, + uid, + gid, + context->control_group_persistent); if (err < 0) { r = EXIT_CGROUP; goto fail_child; @@ -1273,7 +1289,12 @@ int exec_spawn(ExecCommand *command, } if (cgroup_bondings && !set_access && context->control_group_persistent >= 0) { - err = cgroup_bonding_set_task_access_list(cgroup_bondings, (mode_t) -1, (uid_t) -1, (uid_t) -1, context->control_group_persistent); + err = cgroup_bonding_set_task_access_list( + cgroup_bondings, + (mode_t) -1, + (uid_t) -1, + (uid_t) -1, + context->control_group_persistent); if (err < 0) { r = EXIT_CGROUP; goto fail_child; @@ -1317,6 +1338,8 @@ int exec_spawn(ExecCommand *command, err = setup_namespace(context->read_write_dirs, context->read_only_dirs, context->inaccessible_dirs, + context->tmp_dir, + context->var_tmp_dir, context->private_tmp, context->mount_flags); if (err < 0) { @@ -1339,7 +1362,7 @@ int exec_spawn(ExecCommand *command, goto fail_child; } } else { - char _cleanup_free_ *d = NULL; + _cleanup_free_ char *d = NULL; if (asprintf(&d, "%s/%s", context->root_directory ? context->root_directory : "", @@ -1431,7 +1454,8 @@ int exec_spawn(ExecCommand *command, } } - if (!(our_env = new0(char*, 7))) { + our_env = new0(char*, 7); + if (!our_env) { err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; @@ -1471,20 +1495,21 @@ int exec_spawn(ExecCommand *command, assert(n_env <= 7); - if (!(final_env = strv_env_merge( - 5, - environment, - our_env, - context->environment, - files_env, - pam_env, - NULL))) { + final_env = strv_env_merge(5, + environment, + our_env, + context->environment, + files_env, + pam_env, + NULL); + if (!final_env) { err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; } - if (!(final_argv = replace_env_argv(argv, final_env))) { + final_argv = replace_env_argv(argv, final_env); + if (!final_argv) { err = -ENOMEM; r = EXIT_MEMORY; goto fail_child; @@ -1492,6 +1517,20 @@ int exec_spawn(ExecCommand *command, final_env = strv_env_clean(final_env); + if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) { + line = exec_command_line(final_argv); + if (line) { + log_open(); + log_struct_unit(LOG_DEBUG, + unit_id, + "EXECUTABLE=%s", command->path, + "MESSAGE=Executing: %s", line, + NULL); + log_close(); + free(line); + line = NULL; + } + } execve(command->path, final_argv, final_env); err = -errno; r = EXIT_EXEC; @@ -1512,19 +1551,18 @@ int exec_spawn(ExecCommand *command, _exit(r); } - log_struct(LOG_DEBUG, - "UNIT=%s", unit_id, - "MESSAGE=Forked %s as %lu", - command->path, (unsigned long) pid, - NULL); + log_struct_unit(LOG_DEBUG, + unit_id, + "MESSAGE=Forked %s as %lu", + command->path, (unsigned long) pid, + NULL); /* We add the new process to the cgroup both in the child (so * that we can be sure that no user code is ever executed * outside of the cgroup) and in the parent (so that we can be * sure that when we kill the cgroup the process will be * killed too). */ - if (cgroup_bondings) - cgroup_bonding_install_list(cgroup_bondings, pid, cgroup_suffix); + cgroup_bonding_install_list(cgroup_bondings, pid, cgroup_suffix); exec_status_start(&command->exec_status, pid); @@ -1545,7 +1583,35 @@ void exec_context_init(ExecContext *c) { c->timer_slack_nsec = (nsec_t) -1; } -void exec_context_done(ExecContext *c) { +void exec_context_tmp_dirs_done(ExecContext *c) { + char* dirs[] = {c->tmp_dir ? c->tmp_dir : c->var_tmp_dir, + c->tmp_dir ? c->var_tmp_dir : NULL, + NULL}; + char **dirp; + + for(dirp = dirs; *dirp; dirp++) { + char *dir; + int r; + + r = rm_rf_dangerous(*dirp, false, true, false); + dir = dirname(*dirp); + if (r < 0) + log_warning("Failed to remove content of temporary directory %s: %s", + dir, strerror(-r)); + else { + r = rmdir(dir); + if (r < 0) + log_warning("Failed to remove temporary directory %s: %s", + dir, strerror(-r)); + } + + free(*dirp); + } + + c->tmp_dir = c->var_tmp_dir = NULL; +} + +void exec_context_done(ExecContext *c, bool reloading_or_reexecuting) { unsigned l; assert(c); @@ -1609,6 +1675,9 @@ void exec_context_done(ExecContext *c) { free(c->syscall_filter); c->syscall_filter = NULL; + + if (!reloading_or_reexecuting) + exec_context_tmp_dirs_done(c); } void exec_command_done(ExecCommand *c) { @@ -1658,7 +1727,7 @@ int exec_context_load_environment(const ExecContext *c, char ***l) { int k; bool ignore = false; char **p; - glob_t pglob; + _cleanup_globfree_ glob_t pglob = {}; int count, n; fn = *i; @@ -1669,7 +1738,6 @@ int exec_context_load_environment(const ExecContext *c, char ***l) { } if (!path_is_absolute(fn)) { - if (ignore) continue; @@ -1678,10 +1746,8 @@ int exec_context_load_environment(const ExecContext *c, char ***l) { } /* Filename supports globbing, take all matching files */ - zero(pglob); errno = 0; if (glob(fn, 0, NULL, &pglob) != 0) { - globfree(&pglob); if (ignore) continue; @@ -1690,7 +1756,6 @@ int exec_context_load_environment(const ExecContext *c, char ***l) { } count = pglob.gl_pathc; if (count == 0) { - globfree(&pglob); if (ignore) continue; @@ -1698,15 +1763,17 @@ int exec_context_load_environment(const ExecContext *c, char ***l) { return -EINVAL; } for (n = 0; n < count; n++) { - k = load_env_file(pglob.gl_pathv[n], &p); + k = load_env_file(pglob.gl_pathv[n], NULL, &p); if (k < 0) { if (ignore) continue; strv_free(r); - globfree(&pglob); return k; } + /* Log invalid environment variables with filename */ + if (p) + p = strv_env_clean_log(p, pglob.gl_pathv[n]); if (r == NULL) r = p; @@ -1716,16 +1783,12 @@ int exec_context_load_environment(const ExecContext *c, char ***l) { m = strv_env_merge(2, r, p); strv_free(r); strv_free(p); - - if (!m) { - globfree(&pglob); + if (!m) return -ENOMEM; - } r = m; } } - globfree(&pglob); } *l = r; @@ -1733,6 +1796,37 @@ int exec_context_load_environment(const ExecContext *c, char ***l) { return 0; } +static bool tty_may_match_dev_console(const char *tty) { + char *active = NULL, *console; + bool b; + + if (startswith(tty, "/dev/")) + tty += 5; + + /* trivial identity? */ + if (streq(tty, "console")) + return true; + + console = resolve_dev_console(&active); + /* if we could not resolve, assume it may */ + if (!console) + return true; + + /* "tty0" means the active VC, so it may be the same sometimes */ + b = streq(console, tty) || (streq(console, "tty0") && tty_is_vc(tty)); + free(active); + + return b; +} + +bool exec_context_may_touch_console(ExecContext *ec) { + return (ec->tty_reset || ec->tty_vhangup || ec->tty_vt_disallocate || + is_terminal_input(ec->std_input) || + is_terminal_output(ec->std_output) || + is_terminal_output(ec->std_error)) && + tty_may_match_dev_console(tty_path(ec)); +} + static void strv_fprintf(FILE *f, char **l) { char **g; @@ -1827,7 +1921,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { prefix, c->cpu_sched_priority, prefix, yes_no(c->cpu_sched_reset_on_fork)); free(policy_str); - } + } if (c->cpuset) { fprintf(f, "%sCPUAffinity:", prefix); @@ -1895,12 +1989,12 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { if (c->secure_bits) fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n", prefix, - (c->secure_bits & SECURE_KEEP_CAPS) ? " keep-caps" : "", - (c->secure_bits & SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "", - (c->secure_bits & SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "", - (c->secure_bits & SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "", - (c->secure_bits & SECURE_NOROOT) ? " noroot" : "", - (c->secure_bits & SECURE_NOROOT_LOCKED) ? "noroot-locked" : ""); + (c->secure_bits & 1<<SECURE_KEEP_CAPS) ? " keep-caps" : "", + (c->secure_bits & 1<<SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "", + (c->secure_bits & 1<<SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "", + (c->secure_bits & 1<<SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "", + (c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "", + (c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : ""); if (c->capability_bounding_set_drop) { unsigned long l; diff --git a/src/core/execute.h b/src/core/execute.h index 2bcd2e1e6c..15574dc97e 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -36,6 +36,8 @@ typedef struct ExecContext ExecContext; struct CGroupBonding; struct CGroupAttribute; +typedef struct Unit Unit; + #include "list.h" #include "util.h" @@ -141,6 +143,8 @@ struct ExecContext { bool non_blocking; bool private_tmp; bool private_network; + char *tmp_dir; + char *var_tmp_dir; bool no_new_privileges; @@ -164,7 +168,7 @@ struct ExecContext { int exec_spawn(ExecCommand *command, char **argv, - const ExecContext *context, + ExecContext *context, int fds[], unsigned n_fds, char **environment, bool apply_permissions, @@ -192,18 +196,22 @@ void exec_command_append_list(ExecCommand **l, ExecCommand *e); int exec_command_set(ExecCommand *c, const char *path, ...); void exec_context_init(ExecContext *c); -void exec_context_done(ExecContext *c); +void exec_context_done(ExecContext *c, bool reloading_or_reexecuting); +void exec_context_tmp_dirs_done(ExecContext *c); void exec_context_dump(ExecContext *c, FILE* f, const char *prefix); void exec_context_tty_reset(const ExecContext *context); int exec_context_load_environment(const ExecContext *c, char ***l); +bool exec_context_may_touch_console(ExecContext *c); +void exec_context_serialize(const ExecContext *c, Unit *u, FILE *f); + void exec_status_start(ExecStatus *s, pid_t pid); void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status); void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix); -const char* exec_output_to_string(ExecOutput i); -ExecOutput exec_output_from_string(const char *s); +const char* exec_output_to_string(ExecOutput i) _const_; +ExecOutput exec_output_from_string(const char *s) _pure_; -const char* exec_input_to_string(ExecInput i); -ExecInput exec_input_from_string(const char *s); +const char* exec_input_to_string(ExecInput i) _const_; +ExecInput exec_input_from_string(const char *s) _pure_; diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c index 7894f8a5f2..8aa1cff1d3 100644 --- a/src/core/hostname-setup.c +++ b/src/core/hostname-setup.c @@ -29,6 +29,7 @@ #include "macro.h" #include "util.h" #include "log.h" +#include "fileio.h" static int read_and_strip_hostname(const char *path, char **hn) { char *s; @@ -41,7 +42,7 @@ static int read_and_strip_hostname(const char *path, char **hn) { if (r < 0) return r; - hostname_cleanup(s); + hostname_cleanup(s, false); if (isempty(s)) { free(s); diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c index e8cc1ba8b6..7f8ec23d58 100644 --- a/src/core/ima-setup.c +++ b/src/core/ima-setup.c @@ -50,11 +50,6 @@ int ima_setup(void) { int policyfd = -1, imafd = -1; int result = 0; -#ifndef HAVE_SELINUX - /* Mount the securityfs filesystem */ - mount_setup_early(); -#endif - if (stat(IMA_POLICY_PATH, &st) < 0) return 0; diff --git a/src/core/job.c b/src/core/job.c index 5ff95f5c90..d304a16d06 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -34,6 +34,9 @@ #include "load-dropin.h" #include "log.h" #include "dbus-job.h" +#include "special.h" +#include "sync.h" +#include "virt.h" JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name) { JobBusClient *cl; @@ -166,6 +169,8 @@ static void job_merge_into_installed(Job *j, Job *other) { assert(other->type == JOB_NOP); j->override = j->override || other->override; + j->irreversible = j->irreversible || other->irreversible; + j->ignore_order = j->ignore_order || other->ignore_order; } Job* job_install(Job *j) { @@ -201,6 +206,7 @@ Job* job_install(Job *j) { "Merged into running job, re-running: %s/%s as %u", uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); uj->state = JOB_WAITING; + uj->manager->n_running_jobs--; return uj; } } @@ -290,11 +296,13 @@ void job_dump(Job *j, FILE*f, const char *prefix) { "%s-> Job %u:\n" "%s\tAction: %s -> %s\n" "%s\tState: %s\n" - "%s\tForced: %s\n", + "%s\tForced: %s\n" + "%s\tIrreversible: %s\n", prefix, j->id, prefix, j->unit->id, job_type_to_string(j->type), prefix, job_state_to_string(j->state), - prefix, yes_no(j->override)); + prefix, yes_no(j->override), + prefix, yes_no(j->irreversible)); } /* @@ -476,7 +484,7 @@ static void job_change_type(Job *j, JobType newtype) { int job_run_and_invalidate(Job *j) { int r; uint32_t id; - Manager *m; + Manager *m = j->manager; assert(j); assert(j->installed); @@ -493,6 +501,7 @@ int job_run_and_invalidate(Job *j) { return -EAGAIN; j->state = JOB_RUNNING; + m->n_running_jobs++; job_add_to_dbus_queue(j); /* While we execute this operation the job might go away (for @@ -501,7 +510,6 @@ int job_run_and_invalidate(Job *j) { * store the id here, so that we can verify the job is still * valid. */ id = j->id; - m = j->manager; switch (j->type) { @@ -551,16 +559,17 @@ int job_run_and_invalidate(Job *j) { r = job_finish_and_invalidate(j, JOB_DONE, true); else if (r == -ENOEXEC) r = job_finish_and_invalidate(j, JOB_SKIPPED, true); - else if (r == -EAGAIN) + else if (r == -EAGAIN) { j->state = JOB_WAITING; - else if (r < 0) + m->n_running_jobs--; + } else if (r < 0) r = job_finish_and_invalidate(j, JOB_FAILED, true); } return r; } -static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) { +_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) { const UnitStatusMessageFormats *format_table; assert(u); @@ -579,7 +588,7 @@ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult r return NULL; } -static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) { +_pure_ static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) { const char *format; assert(u); @@ -635,20 +644,20 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { case JOB_DONE: if (u->condition_result) - unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format, unit_description(u)); + unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format); break; case JOB_FAILED: - unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format, unit_description(u)); - unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id); + unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format); + manager_status_printf(u->manager, false, NULL, "See 'systemctl status %s' for details.", u->id); break; case JOB_DEPENDENCY: - unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format, unit_description(u)); + unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format); break; case JOB_TIMEOUT: - unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format, unit_description(u)); + unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format); break; default: @@ -664,12 +673,12 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { switch (result) { case JOB_TIMEOUT: - unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format, unit_description(u)); + unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format); break; case JOB_DONE: case JOB_FAILED: - unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format, unit_description(u)); + unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format); break; default: @@ -682,7 +691,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { * Most likely a DEPEND warning from a requisiting unit will * occur next and it's nice to see what was requisited. */ if (result == JOB_SKIPPED) - unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.", unit_description(u)); + unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active."); } } @@ -713,25 +722,25 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { sd_id128_t mid; mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED; - log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR, + log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR, + u->id, MESSAGE_ID(mid), - "UNIT=%s", u->id, "RESULT=%s", job_result_to_string(result), "MESSAGE=%s", buf, NULL); } else if (t == JOB_STOP) - log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR, + log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR, + u->id, MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED), - "UNIT=%s", u->id, "RESULT=%s", job_result_to_string(result), "MESSAGE=%s", buf, NULL); else if (t == JOB_RELOAD) - log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR, + log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR, + u->id, MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED), - "UNIT=%s", u->id, "RESULT=%s", job_result_to_string(result), "MESSAGE=%s", buf, NULL); @@ -753,6 +762,9 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { j->result = result; + if (j->state == JOB_RUNNING) + j->manager->n_running_jobs--; + log_debug_unit(u->id, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result)); @@ -818,19 +830,21 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { * this context. And JOB_FAILURE is already handled by the * unit itself. */ if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) { - log_struct(LOG_NOTICE, - "UNIT=%s", u->id, + log_struct_unit(LOG_NOTICE, + u->id, "JOB_TYPE=%s", job_type_to_string(t), - "JOB_RESULT=%s", job_result_to_string(t), + "JOB_RESULT=%s", job_result_to_string(result), "Job %s/%s failed with result '%s'.", u->id, job_type_to_string(t), job_result_to_string(result), NULL); - unit_trigger_on_failure(u); + unit_start_on_failure(u); } + unit_trigger_notify(u); + finish: /* Try to start the next jobs that can be started */ SET_FOREACH(other, u->dependencies[UNIT_AFTER], i) @@ -846,10 +860,12 @@ finish: } int job_start_timer(Job *j) { - struct itimerspec its; - struct epoll_event ev; + struct itimerspec its = {}; + struct epoll_event ev = { + .data.ptr = &j->timer_watch, + .events = EPOLLIN, + }; int fd, r; - assert(j); if (j->unit->job_timeout <= 0 || j->timer_watch.type == WATCH_JOB_TIMER) @@ -862,7 +878,6 @@ int job_start_timer(Job *j) { goto fail; } - zero(its); timespec_store(&its.it_value, j->unit->job_timeout); if (timerfd_settime(fd, 0, &its, NULL) < 0) { @@ -870,10 +885,6 @@ int job_start_timer(Job *j) { goto fail; } - zero(ev); - ev.data.ptr = &j->timer_watch; - ev.events = EPOLLIN; - if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { r = -errno; goto fail; @@ -943,6 +954,7 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) { fprintf(f, "job-type=%s\n", job_type_to_string(j->type)); fprintf(f, "job-state=%s\n", job_state_to_string(j->state)); fprintf(f, "job-override=%s\n", yes_no(j->override)); + fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible)); fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal)); fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order)); /* Cannot save bus clients. Just note the fact that we're losing @@ -1010,6 +1022,12 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) { log_debug("Failed to parse job override flag %s", v); else j->override = j->override || b; + } else if (streq(l, "job-irreversible")) { + int b = parse_boolean(v); + if (b < 0) + log_debug("Failed to parse job irreversible flag %s", v); + else + j->irreversible = j->irreversible || b; } else if (streq(l, "job-sent-dbus-new-signal")) { int b = parse_boolean(v); if (b < 0) @@ -1045,21 +1063,43 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) { } int job_coldplug(Job *j) { - struct epoll_event ev; + struct epoll_event ev = { + .data.ptr = &j->timer_watch, + .events = EPOLLIN, + }; if (j->timer_watch.type != WATCH_JOB_TIMER) return 0; - zero(ev); - ev.data.ptr = &j->timer_watch; - ev.events = EPOLLIN; - if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, j->timer_watch.fd, &ev) < 0) return -errno; return 0; } +void job_shutdown_magic(Job *j) { + assert(j); + + /* The shutdown target gets some special treatment here: we + * tell the kernel to begin with flushing its disk caches, to + * optimize shutdown time a bit. Ideally we wouldn't hardcode + * this magic into PID 1. However all other processes aren't + * options either since they'd exit much sooner than PID 1 and + * asynchronous sync() would cause their exit to be + * delayed. */ + + if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET)) + return; + + if (j->type != JOB_START) + return; + + if (detect_container(NULL) > 0) + return; + + asynchronous_sync(); +} + static const char* const job_state_table[_JOB_STATE_MAX] = { [JOB_WAITING] = "waiting", [JOB_RUNNING] = "running" @@ -1083,6 +1123,7 @@ DEFINE_STRING_TABLE_LOOKUP(job_type, JobType); static const char* const job_mode_table[_JOB_MODE_MAX] = { [JOB_FAIL] = "fail", [JOB_REPLACE] = "replace", + [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly", [JOB_ISOLATE] = "isolate", [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies", [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements" diff --git a/src/core/job.h b/src/core/job.h index 3aa49d4b46..d90bc96b76 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -83,6 +83,7 @@ enum JobState { enum JobMode { JOB_FAIL, /* Fail if a conflicting job is already queued */ JOB_REPLACE, /* Replace an existing conflicting job */ + JOB_REPLACE_IRREVERSIBLY, /* Like JOB_REPLACE + produce irreversible jobs */ JOB_ISOLATE, /* Start a unit, and stop all others */ JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */ JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */ @@ -161,6 +162,7 @@ struct Job { bool sent_dbus_new_signal:1; bool ignore_order:1; bool forgot_bus_clients:1; + bool irreversible:1; }; JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name); @@ -181,22 +183,22 @@ void job_dependency_free(JobDependency *l); int job_merge(Job *j, Job *other); -JobType job_type_lookup_merge(JobType a, JobType b); +JobType job_type_lookup_merge(JobType a, JobType b) _pure_; -static inline bool job_type_is_mergeable(JobType a, JobType b) { +_pure_ static inline bool job_type_is_mergeable(JobType a, JobType b) { return job_type_lookup_merge(a, b) >= 0; } -static inline bool job_type_is_conflicting(JobType a, JobType b) { +_pure_ static inline bool job_type_is_conflicting(JobType a, JobType b) { return !job_type_is_mergeable(a, b); } -static inline bool job_type_is_superset(JobType a, JobType b) { +_pure_ static inline bool job_type_is_superset(JobType a, JobType b) { /* Checks whether operation a is a "superset" of b in its actions */ return a == job_type_lookup_merge(a, b); } -bool job_type_is_redundant(JobType a, UnitActiveState b); +bool job_type_is_redundant(JobType a, UnitActiveState b) _pure_; /* Collapses a state-dependent job type into a simpler type by observing * the state of the unit which it is going to be applied to. */ @@ -217,14 +219,16 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive); char *job_dbus_path(Job *j); -const char* job_type_to_string(JobType t); -JobType job_type_from_string(const char *s); +void job_shutdown_magic(Job *j); -const char* job_state_to_string(JobState t); -JobState job_state_from_string(const char *s); +const char* job_type_to_string(JobType t) _const_; +JobType job_type_from_string(const char *s) _pure_; -const char* job_mode_to_string(JobMode t); -JobMode job_mode_from_string(const char *s); +const char* job_state_to_string(JobState t) _const_; +JobState job_state_from_string(const char *s) _pure_; -const char* job_result_to_string(JobResult t); -JobResult job_result_from_string(const char *s); +const char* job_mode_to_string(JobMode t) _const_; +JobMode job_mode_from_string(const char *s) _pure_; + +const char* job_result_to_string(JobResult t) _const_; +JobResult job_result_from_string(const char *s) _pure_; diff --git a/src/core/kill.h b/src/core/kill.h index 3c9b0ab8db..71a0513e84 100644 --- a/src/core/kill.h +++ b/src/core/kill.h @@ -26,6 +26,8 @@ typedef struct KillContext KillContext; #include <stdbool.h> #include <stdio.h> +#include "macro.h" + typedef enum KillMode { /* The kill mode is a property of a unit. */ KILL_CONTROL_GROUP = 0, @@ -53,8 +55,8 @@ typedef enum KillWho { void kill_context_init(KillContext *c); void kill_context_dump(KillContext *c, FILE *f, const char *prefix); -const char *kill_mode_to_string(KillMode k); -KillMode kill_mode_from_string(const char *s); +const char *kill_mode_to_string(KillMode k) _const_; +KillMode kill_mode_from_string(const char *s) _pure_; -const char *kill_who_to_string(KillWho k); -KillWho kill_who_from_string(const char *s); +const char *kill_who_to_string(KillWho k) _const_; +KillWho kill_who_from_string(const char *s) _pure_; diff --git a/src/core/killall.c b/src/core/killall.c index 55200ffa48..a0f57455fb 100644 --- a/src/core/killall.c +++ b/src/core/killall.c @@ -22,16 +22,17 @@ #include <sys/wait.h> #include <signal.h> #include <errno.h> +#include <unistd.h> #include "util.h" #include "def.h" #include "killall.h" +#include "set.h" -#define TIMEOUT_USEC (5 * USEC_PER_SEC) +#define TIMEOUT_USEC (10 * USEC_PER_SEC) static bool ignore_proc(pid_t pid) { - char buf[PATH_MAX]; - FILE *f; + _cleanup_fclose_ FILE *f = NULL; char c; size_t count; uid_t uid; @@ -49,15 +50,11 @@ static bool ignore_proc(pid_t pid) { if (uid != 0) return false; - snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long) pid); - char_array_0(buf); - - f = fopen(buf, "re"); + f = fopen(procfs_file_alloca(pid, "cmdline"), "re"); if (!f) return true; /* not really, but has the desired effect */ count = fread(&c, 1, 1, f); - fclose(f); /* Kernel threads have an empty cmdline */ if (count <= 0) @@ -73,38 +70,68 @@ static bool ignore_proc(pid_t pid) { return false; } -static void wait_for_children(int n_processes, sigset_t *mask) { +static void wait_for_children(Set *pids, sigset_t *mask) { usec_t until; assert(mask); + if (set_isempty(pids)) + return; + until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC; for (;;) { struct timespec ts; int k; usec_t n; + void *p; + Iterator i; + /* First, let the kernel inform us about killed + * children. Most processes will probably be our + * children, but some are not (might be our + * grandchildren instead...). */ for (;;) { - pid_t pid = waitpid(-1, NULL, WNOHANG); + pid_t pid; + pid = waitpid(-1, NULL, WNOHANG); if (pid == 0) break; + if (pid < 0) { + if (errno == ECHILD) + break; - if (pid < 0 && errno == ECHILD) + log_error("waitpid() failed: %m"); return; + } + + set_remove(pids, ULONG_TO_PTR(pid)); + } - if (n_processes > 0) - if (--n_processes == 0) - return; + /* Now explicitly check who might be remaining, who + * might not be our child. */ + SET_FOREACH(p, pids, i) { + + /* We misuse getpgid as a check whether a + * process still exists. */ + if (getpgid((pid_t) PTR_TO_ULONG(p)) >= 0) + continue; + + if (errno != ESRCH) + continue; + + set_remove(pids, p); } + if (set_isempty(pids)) + return; + n = now(CLOCK_MONOTONIC); if (n >= until) return; timespec_store(&ts, until - n); - - if ((k = sigtimedwait(mask, NULL, &ts)) != SIGCHLD) { + k = sigtimedwait(mask, NULL, &ts); + if (k != SIGCHLD) { if (k < 0 && errno != EAGAIN) { log_error("sigtimedwait() failed: %m"); @@ -117,10 +144,9 @@ static void wait_for_children(int n_processes, sigset_t *mask) { } } -static int killall(int sig) { - DIR *dir; +static int killall(int sig, Set *pids) { + _cleanup_closedir_ DIR *dir = NULL; struct dirent *d; - unsigned int n_processes = 0; dir = opendir("/proc"); if (!dir) @@ -139,20 +165,29 @@ static int killall(int sig) { if (ignore_proc(pid)) continue; - if (kill(pid, sig) >= 0) - n_processes++; - else if (errno != ENOENT) + if (sig == SIGKILL) { + _cleanup_free_ char *s; + + get_process_comm(pid, &s); + log_notice("Sending SIGKILL to PID %lu (%s).", (unsigned long) pid, strna(s)); + } + + if (kill(pid, sig) >= 0) { + if (pids) + set_put(pids, ULONG_TO_PTR((unsigned long) pid)); + } else if (errno != ENOENT) log_warning("Could not kill %d: %m", pid); } - closedir(dir); - - return n_processes; + return set_size(pids); } void broadcast_signal(int sig, bool wait_for_exit) { sigset_t mask, oldmask; - int n_processes; + Set *pids = NULL; + + if (wait_for_exit) + pids = set_new(trivial_hash_func, trivial_compare_func); assert_se(sigemptyset(&mask) == 0); assert_se(sigaddset(&mask, SIGCHLD) == 0); @@ -161,17 +196,15 @@ void broadcast_signal(int sig, bool wait_for_exit) { if (kill(-1, SIGSTOP) < 0 && errno != ESRCH) log_warning("kill(-1, SIGSTOP) failed: %m"); - n_processes = killall(sig); + killall(sig, pids); if (kill(-1, SIGCONT) < 0 && errno != ESRCH) log_warning("kill(-1, SIGCONT) failed: %m"); - if (n_processes <= 0) - goto finish; - if (wait_for_exit) - wait_for_children(n_processes, &mask); + wait_for_children(pids, &mask); + + assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0); -finish: - sigprocmask(SIG_SETMASK, &oldmask, NULL); + set_free(pids); } diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c index 20ab232374..e4885fb212 100644 --- a/src/core/kmod-setup.c +++ b/src/core/kmod-setup.c @@ -30,66 +30,72 @@ #include "kmod-setup.h" -typedef struct Kmodule { - const char *name; - const char *directory; - bool (*condition_fn)(void); -} KModule; - -static const KModule kmod_table[] = { - { "autofs4", "/sys/class/misc/autofs", NULL } , - { "ipv6", "/sys/module/ipv6", NULL }, - { "unix", "/proc/net/unix", NULL } , -}; - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" -static void systemd_kmod_log(void *data, int priority, const char *file, int line, - const char *fn, const char *format, va_list args) -{ + +static void systemd_kmod_log( + void *data, + int priority, + const char *file, int line, + const char *fn, + const char *format, + va_list args) { + /* library logging is enabled at debug only */ log_metav(LOG_DEBUG, file, line, fn, format, args); } + #pragma GCC diagnostic pop int kmod_setup(void) { - unsigned i; + + static const char kmod_table[] = + /* This one we need to load explicitly, since + * auto-loading on use doesn't work before udev + * created the ghost device nodes, and we need it + * earlier than that. */ + "autofs4\0" "/sys/class/misc/autofs\0" + + /* This one we need to load explicitly, since + * auto-loading of IPv6 is not done when we try to + * configure ::1 on the loopback device. */ + "ipv6\0" "/sys/module/ipv6\0" + + "unix\0" "/proc/net/unix\0"; + struct kmod_ctx *ctx = NULL; - struct kmod_module *mod; - int err; + const char *name, *path; + int r; - for (i = 0; i < ELEMENTSOF(kmod_table); i += 2) { - if (kmod_table[i].condition_fn && !kmod_table[i].condition_fn()) - continue; + NULSTR_FOREACH_PAIR(name, path, kmod_table) { + struct kmod_module *mod; - if (access(kmod_table[i].directory, F_OK) >= 0) + if (access(path, F_OK) >= 0) continue; log_debug("Your kernel apparently lacks built-in %s support. Might be a good idea to compile it in. " "We'll now try to work around this by loading the module...", - kmod_table[i].name); + name); if (!ctx) { ctx = kmod_new(NULL, NULL); - if (!ctx) { - log_error("Failed to allocate memory for kmod"); - return -ENOMEM; - } + if (!ctx) + return log_oom(); kmod_set_log_fn(ctx, systemd_kmod_log, NULL); kmod_load_resources(ctx); } - err = kmod_module_new_from_name(ctx, kmod_table[i].name, &mod); - if (err < 0) { - log_error("Failed to lookup module '%s'", kmod_table[i].name); + r = kmod_module_new_from_name(ctx, name, &mod); + if (r < 0) { + log_error("Failed to lookup module '%s'", name); continue; } - err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); - if (err == 0) + r = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); + if (r == 0) log_info("Inserted module '%s'", kmod_module_get_name(mod)); - else if (err == KMOD_PROBE_APPLY_BLACKLIST) + else if (r == KMOD_PROBE_APPLY_BLACKLIST) log_info("Module '%s' is blacklisted", kmod_module_get_name(mod)); else log_error("Failed to insert module '%s'", kmod_module_get_name(mod)); diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index 86f81c7484..a877e66098 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -27,51 +27,78 @@ #include "log.h" #include "strv.h" #include "unit-name.h" +#include "conf-parser.h" +#include "load-fragment.h" +#include "conf-files.h" -static int iterate_dir(Unit *u, const char *path, UnitDependency dependency) { - DIR *d; - struct dirent *de; +static int iterate_dir( + Unit *u, + const char *path, + UnitDependency dependency, + char ***strv) { + + _cleanup_closedir_ DIR *d = NULL; int r; assert(u); assert(path); + /* The config directories are special, since the order of the + * drop-ins matters */ + if (dependency < 0) { + r = strv_extend(strv, path); + if (r < 0) + return log_oom(); + + return 0; + } + d = opendir(path); if (!d) { - if (errno == ENOENT) return 0; return -errno; } - while ((de = readdir(d))) { - char *f; + for (;;) { + struct dirent *de; + union dirent_storage buf; + _cleanup_free_ char *f = NULL; + int k; + + k = readdir_r(d, &buf.de, &de); + if (k != 0) { + log_error("Failed to read directory %s: %s", path, strerror(k)); + return -k; + } + + if (!de) + break; if (ignore_file(de->d_name)) continue; f = strjoin(path, "/", de->d_name, NULL); - if (!f) { - r = -ENOMEM; - goto finish; - } + if (!f) + return log_oom(); r = unit_add_dependency_by_name(u, dependency, de->d_name, f, true); - free(f); - if (r < 0) log_error("Cannot add dependency %s to %s, ignoring: %s", de->d_name, u->id, strerror(-r)); } - r = 0; - -finish: - closedir(d); - return r; + return 0; } -static int process_dir(Unit *u, const char *unit_path, const char *name, const char *suffix, UnitDependency dependency) { +static int process_dir( + Unit *u, + const char *unit_path, + const char *name, + const char *suffix, + UnitDependency dependency, + char ***strv) { + int r; char *path; @@ -82,13 +109,13 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c path = strjoin(unit_path, "/", name, suffix, NULL); if (!path) - return -ENOMEM; + return log_oom(); if (u->manager->unit_path_cache && !set_get(u->manager->unit_path_cache, path)) r = 0; else - r = iterate_dir(u, path, dependency); + r = iterate_dir(u, path, dependency, strv); free(path); if (r < 0) @@ -100,19 +127,19 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c template = unit_name_template(name); if (!template) - return -ENOMEM; + return log_oom(); path = strjoin(unit_path, "/", template, suffix, NULL); free(template); if (!path) - return -ENOMEM; + return log_oom(); if (u->manager->unit_path_cache && !set_get(u->manager->unit_path_cache, path)) r = 0; else - r = iterate_dir(u, path, dependency); + r = iterate_dir(u, path, dependency, strv); free(path); if (r < 0) @@ -122,9 +149,43 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c return 0; } -int unit_load_dropin(Unit *u) { +char **unit_find_dropin_paths(Unit *u) { + _cleanup_strv_free_ char **strv = NULL; + char **configs = NULL; Iterator i; char *t; + int r; + + assert(u); + + SET_FOREACH(t, u->names, i) { + char **p; + + STRV_FOREACH(p, u->manager->lookup_paths.unit_path) { + /* This loads the drop-in config snippets */ + r = process_dir(u, *p, t, ".d", _UNIT_DEPENDENCY_INVALID, &strv); + if (r < 0) + return NULL; + } + } + + if (strv_isempty(strv)) + return NULL; + + r = conf_files_list_strv(&configs, ".conf", NULL, (const char**) strv); + if (r < 0) { + log_error("Failed to get list of configuration files: %s", strerror(-r)); + strv_free(configs); + return NULL; + } + + return configs; +} + +int unit_load_dropin(Unit *u) { + Iterator i; + char *t, **f; + int r; assert(u); @@ -134,17 +195,29 @@ int unit_load_dropin(Unit *u) { char **p; STRV_FOREACH(p, u->manager->lookup_paths.unit_path) { - int r; - - r = process_dir(u, *p, t, ".wants", UNIT_WANTS); + r = process_dir(u, *p, t, ".wants", UNIT_WANTS, NULL); if (r < 0) return r; - r = process_dir(u, *p, t, ".requires", UNIT_REQUIRES); + r = process_dir(u, *p, t, ".requires", UNIT_REQUIRES, NULL); if (r < 0) return r; } } + u->dropin_paths = unit_find_dropin_paths(u); + if (! u->dropin_paths) + return 0; + + STRV_FOREACH(f, u->dropin_paths) { + r = config_parse(u->id, *f, NULL, + UNIT_VTABLE(u)->sections, config_item_perf_lookup, + (void*) load_fragment_gperf_lookup, false, false, u); + if (r < 0) + return r; + } + + u->dropin_mtime = now(CLOCK_REALTIME); + return 0; } diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index 1d2fafeee6..fd551179e2 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -25,4 +25,5 @@ /* Read service data supplementary drop-in directories */ +char **unit_find_dropin_paths(Unit *u); int unit_load_dropin(Unit *u); diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 7fba0cfb77..4e1454ee6c 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -31,7 +31,7 @@ $1.CPUSchedulingPriority, config_parse_exec_cpu_sched_prio, 0, $1.CPUSchedulingResetOnFork, config_parse_bool, 0, offsetof($1, exec_context.cpu_sched_reset_on_fork) $1.CPUAffinity, config_parse_exec_cpu_affinity, 0, offsetof($1, exec_context) $1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask) -$1.Environment, config_parse_unit_strv_printf, 0, offsetof($1, exec_context.environment) +$1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment) $1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files) $1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input) $1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output) @@ -48,7 +48,7 @@ $1.Capabilities, config_parse_exec_capabilities, 0, $1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context) $1.CapabilityBoundingSet, config_parse_bounding_set, 0, offsetof($1, exec_context.capability_bounding_set_drop) $1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec) -$1.NoNewPrivileges config_parse_bool, 0, offsetof($1, exec_context.no_new_privileges) +$1.NoNewPrivileges, config_parse_bool, 0, offsetof($1, exec_context.no_new_privileges) $1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context) $1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) $1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit) @@ -68,14 +68,14 @@ $1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPR $1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit) $1.ControlGroup, config_parse_unit_cgroup, 0, 0 $1.ControlGroupAttribute, config_parse_unit_cgroup_attr, 0, 0 -$1.CPUShares, config_parse_unit_cpu_shares, 0, 0 -$1.MemoryLimit, config_parse_unit_memory_limit, 0, 0 -$1.MemorySoftLimit, config_parse_unit_memory_limit, 0, 0 -$1.DeviceAllow, config_parse_unit_device_allow, 0, 0 -$1.DeviceDeny, config_parse_unit_device_allow, 0, 0 -$1.BlockIOWeight, config_parse_unit_blkio_weight, 0, 0 -$1.BlockIOReadBandwidth, config_parse_unit_blkio_bandwidth, 0, 0 -$1.BlockIOWriteBandwidth, config_parse_unit_blkio_bandwidth, 0, 0 +$1.CPUShares, config_parse_unit_cgroup_attr_pretty, 0, 0 +$1.MemoryLimit, config_parse_unit_cgroup_attr_pretty, 0, 0 +$1.MemorySoftLimit, config_parse_unit_cgroup_attr_pretty, 0, 0 +$1.DeviceAllow, config_parse_unit_cgroup_attr_pretty, 0, 0 +$1.DeviceDeny, config_parse_unit_cgroup_attr_pretty, 0, 0 +$1.BlockIOWeight, config_parse_unit_cgroup_attr_pretty, 0, 0 +$1.BlockIOReadBandwidth, config_parse_unit_cgroup_attr_pretty, 0, 0 +$1.BlockIOWriteBandwidth, config_parse_unit_cgroup_attr_pretty, 0, 0 $1.ReadWriteDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_write_dirs) $1.ReadOnlyDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_only_dirs) $1.InaccessibleDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs) @@ -90,7 +90,7 @@ $1.ControlGroupModify, config_parse_bool, 0, $1.ControlGroupPersistent, config_parse_tristate, 0, offsetof($1, exec_context.control_group_persistent)' )m4_dnl m4_define(`KILL_CONTEXT_CONFIG_ITEMS', -`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill) +`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill) $1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode) $1.KillSignal, config_parse_kill_signal, 0, offsetof($1, kill_context.kill_signal)' )m4_dnl @@ -122,7 +122,7 @@ Unit.DefaultDependencies, config_parse_bool, 0, Unit.OnFailureIsolate, config_parse_bool, 0, offsetof(Unit, on_failure_isolate) Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate) Unit.IgnoreOnSnapshot, config_parse_bool, 0, offsetof(Unit, ignore_on_snapshot) -Unit.JobTimeoutSec, config_parse_usec, 0, offsetof(Unit, job_timeout) +Unit.JobTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_timeout) Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, 0 Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, 0 Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, 0 @@ -147,12 +147,12 @@ Service.ExecStartPost, config_parse_exec, SERVICE_EXE Service.ExecReload, config_parse_exec, SERVICE_EXEC_RELOAD, offsetof(Service, exec_command) Service.ExecStop, config_parse_exec, SERVICE_EXEC_STOP, offsetof(Service, exec_command) Service.ExecStopPost, config_parse_exec, SERVICE_EXEC_STOP_POST, offsetof(Service, exec_command) -Service.RestartSec, config_parse_usec, 0, offsetof(Service, restart_usec) +Service.RestartSec, config_parse_sec, 0, offsetof(Service, restart_usec) Service.TimeoutSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec) Service.TimeoutStartSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec) Service.TimeoutStopSec, config_parse_service_timeout, 0, offsetof(Service, timeout_stop_usec) -Service.WatchdogSec, config_parse_usec, 0, offsetof(Service, watchdog_usec) -Service.StartLimitInterval, config_parse_usec, 0, offsetof(Service, start_limit.interval) +Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec) +Service.StartLimitInterval, config_parse_sec, 0, offsetof(Service, start_limit.interval) Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Service, start_limit.burst) Service.StartLimitAction, config_parse_start_limit_action, 0, offsetof(Service, start_limit_action) Service.Type, config_parse_service_type, 0, offsetof(Service, type) @@ -174,13 +174,13 @@ Service.FsckPassNo, config_parse_fsck_passno, 0, EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl m4_dnl -Socket.ListenStream, config_parse_socket_listen, 0, 0 -Socket.ListenDatagram, config_parse_socket_listen, 0, 0 -Socket.ListenSequentialPacket, config_parse_socket_listen, 0, 0 -Socket.ListenFIFO, config_parse_socket_listen, 0, 0 -Socket.ListenNetlink, config_parse_socket_listen, 0, 0 -Socket.ListenSpecial, config_parse_socket_listen, 0, 0 -Socket.ListenMessageQueue, config_parse_socket_listen, 0, 0 +Socket.ListenStream, config_parse_socket_listen, SOCKET_SOCKET, 0 +Socket.ListenDatagram, config_parse_socket_listen, SOCKET_SOCKET, 0 +Socket.ListenSequentialPacket, config_parse_socket_listen, SOCKET_SOCKET, 0 +Socket.ListenFIFO, config_parse_socket_listen, SOCKET_FIFO, 0 +Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCKET, 0 +Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0 +Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0 Socket.BindIPv6Only, config_parse_socket_bind, 0, 0, Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog) Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0 @@ -188,7 +188,7 @@ Socket.ExecStartPre, config_parse_exec, SOCKET_EXEC Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC_START_POST, offsetof(Socket, exec_command) Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command) Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command) -Socket.TimeoutSec, config_parse_usec, 0, offsetof(Socket, timeout_usec) +Socket.TimeoutSec, config_parse_sec, 0, offsetof(Socket, timeout_usec) Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode) Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) @@ -221,7 +221,7 @@ Mount.Where, config_parse_path, 0, Mount.Options, config_parse_string, 0, offsetof(Mount, parameters_fragment.options) Mount.Type, config_parse_string, 0, offsetof(Mount, parameters_fragment.fstype) Mount.FsckPassNo, config_parse_fsck_passno, 0, offsetof(Mount, parameters_fragment.passno) -Mount.TimeoutSec, config_parse_usec, 0, offsetof(Mount, timeout_usec) +Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec) Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode) EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl @@ -231,7 +231,7 @@ Automount.DirectoryMode, config_parse_mode, 0, m4_dnl Swap.What, config_parse_path, 0, offsetof(Swap, parameters_fragment.what) Swap.Priority, config_parse_int, 0, offsetof(Swap, parameters_fragment.priority) -Swap.TimeoutSec, config_parse_usec, 0, offsetof(Swap, timeout_usec) +Swap.TimeoutSec, config_parse_sec, 0, offsetof(Swap, timeout_usec) EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl m4_dnl @@ -241,14 +241,14 @@ Timer.OnBootSec, config_parse_timer, 0, Timer.OnStartupSec, config_parse_timer, 0, 0 Timer.OnUnitActiveSec, config_parse_timer, 0, 0 Timer.OnUnitInactiveSec, config_parse_timer, 0, 0 -Timer.Unit, config_parse_timer_unit, 0, 0 +Timer.Unit, config_parse_trigger_unit, 0, 0 m4_dnl Path.PathExists, config_parse_path_spec, 0, 0 Path.PathExistsGlob, config_parse_path_spec, 0, 0 Path.PathChanged, config_parse_path_spec, 0, 0 Path.PathModified, config_parse_path_spec, 0, 0 Path.DirectoryNotEmpty, config_parse_path_spec, 0, 0 -Path.Unit, config_parse_path_unit, 0, 0 +Path.Unit, config_parse_trigger_unit, 0, 0 Path.MakeDirectory, config_parse_bool, 0, offsetof(Path, make_directory) Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode) m4_dnl The [Install] section is ignored here. diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e35fdbc5ec..e2015ed58f 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -34,6 +34,8 @@ #include <sys/time.h> #include <sys/resource.h> +#include <systemd/sd-messages.h> + #include "unit.h" #include "strv.h" #include "conf-parser.h" @@ -44,36 +46,39 @@ #include "missing.h" #include "unit-name.h" #include "unit-printf.h" -#include "bus-errors.h" +#include "dbus-common.h" #include "utf8.h" #include "path-util.h" #include "syscall-list.h" +#include "env-util.h" #ifndef HAVE_SYSV_COMPAT -int config_parse_warn_compat( - const char *filename, - unsigned line, - const char *section, - 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); +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) { + + 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { UnitDependency d = ltype; Unit *u = userdata; @@ -86,39 +91,38 @@ int config_parse_unit_deps( assert(rvalue); FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char _cleanup_free_ *t = NULL, *k = NULL; + _cleanup_free_ char *t = NULL, *k = NULL; int r; t = strndup(w, l); if (!t) - return -ENOMEM; + return log_oom(); k = unit_name_printf(u, t); if (!k) - return -ENOMEM; + return log_oom(); 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)); } 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Unit *u = userdata; - char *k; - int r; + _cleanup_free_ char *k = NULL; assert(filename); assert(lvalue); @@ -127,27 +131,25 @@ int config_parse_unit_string_printf( k = unit_full_printf(u, rvalue); if (!k) - return -ENOMEM; + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); - r = config_parse_string(filename, line, section, lvalue, ltype, k, data, userdata); - free (k); - - return r; + return config_parse_string(unit, filename, line, section, 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Unit *u = userdata; - char *k; - int r; + _cleanup_free_ char *k = NULL; assert(filename); assert(lvalue); @@ -156,27 +158,25 @@ int config_parse_unit_strv_printf( k = unit_full_printf(u, rvalue); if (!k) - return -ENOMEM; + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); - r = config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata); - free(k); - - return r; + return config_parse_strv(unit, filename, line, section, 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Unit *u = userdata; - char *k; - int r; + _cleanup_free_ char *k = NULL; assert(filename); assert(lvalue); @@ -185,23 +185,22 @@ int config_parse_unit_path_printf( 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); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); - return r; + return config_parse_path(unit, filename, line, section, lvalue, ltype, + k ? k : rvalue, data, userdata); } -int config_parse_socket_listen( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_socket_listen(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { SocketPort *p, *tail; Socket *s; @@ -213,67 +212,64 @@ int config_parse_socket_listen( 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 -ENOMEM; - - if (streq(lvalue, "ListenFIFO")) { - p->type = SOCKET_FIFO; - - if (!(p->path = unit_full_printf(UNIT(s), rvalue))) { - free(p); - return -ENOMEM; - } - - path_kill_slashes(p->path); - - } else if (streq(lvalue, "ListenSpecial")) { - p->type = SOCKET_SPECIAL; + return log_oom(); - if (!(p->path = unit_full_printf(UNIT(s), rvalue))) { - free(p); - return -ENOMEM; - } - - path_kill_slashes(p->path); - - } else if (streq(lvalue, "ListenMessageQueue")) { - - p->type = SOCKET_MQUEUE; - - if (!(p->path = unit_full_printf(UNIT(s), rvalue))) { - free(p); - return -ENOMEM; + if (ltype != SOCKET_SOCKET) { + + p->type = ltype; + p->path = unit_full_printf(UNIT(s), rvalue); + if (!p->path) { + 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); } path_kill_slashes(p->path); } else if (streq(lvalue, "ListenNetlink")) { - char *k; + _cleanup_free_ char *k = NULL; int r; p->type = SOCKET_SOCKET; k = unit_full_printf(UNIT(s), rvalue); - r = socket_address_parse_netlink(&p->address, k); - free(k); + if (!k) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + r = socket_address_parse_netlink(&p->address, k ? k : rvalue); if (r < 0) { - log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse address value, ignoring: %s", rvalue); free(p); return 0; } } else { - char *k; + _cleanup_free_ char *k = NULL; int r; p->type = SOCKET_SOCKET; k = unit_full_printf(UNIT(s), rvalue); - r = socket_address_parse(&p->address, k); - free(k); + if (!k) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + 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); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse address value, ignoring: %s", rvalue); free(p); return 0; } @@ -288,7 +284,8 @@ 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); + log_syntax(unit, LOG_ERR, filename, line, ENOTSUP, + "Address family not supported, ignoring: %s", rvalue); free(p); return 0; } @@ -305,15 +302,15 @@ int config_parse_socket_listen( 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Socket *s; SocketAddressBindIPv6Only b; @@ -331,7 +328,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 +340,34 @@ 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, + 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 +377,34 @@ 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, + 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 +414,15 @@ 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { ExecCommand **e = data, *nce; char *path, **n; @@ -430,12 +434,18 @@ 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; @@ -465,14 +475,14 @@ 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) + if (strneq(w, ";", MAX(l, 1U))) break; k++; @@ -480,13 +490,13 @@ int config_parse_exec( 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) + if (strneq(w, ";", MAX(l, 1U))) break; - else if (strncmp(w, "\\;", MAX(l, 1U)) == 0) + else if (strneq(w, "\\;", MAX(l, 1U))) w ++; if (honour_argv0 && w == rvalue) { @@ -494,12 +504,14 @@ int config_parse_exec( path = strndup(w, 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_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Path is not UTF-8 clean, ignoring assignment: %s", + rvalue); r = 0; goto fail; } @@ -509,12 +521,14 @@ int config_parse_exec( c = n[k++] = cunescape_length(w, 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_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Path is not UTF-8 clean, ignoring assignment: %s", + rvalue); r = 0; goto fail; } @@ -524,7 +538,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 +547,7 @@ int config_parse_exec( if (!path) { path = strdup(n[0]); if (!path) { - r = -ENOMEM; + r = log_oom(); goto fail; } } @@ -541,7 +556,7 @@ int config_parse_exec( nce = new0(ExecCommand, 1); if (!nce) { - r = -ENOMEM; + r = log_oom(); goto fail; } @@ -570,15 +585,15 @@ 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Socket *s = data; char *n; @@ -589,8 +604,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 +619,15 @@ 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { ExecContext *c = data; int x; @@ -623,7 +639,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 +650,28 @@ 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, + 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 +681,15 @@ 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { ExecContext *c = data; @@ -683,7 +702,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 +715,38 @@ 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, + 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,15 +756,15 @@ 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { ExecContext *c = data; char *w; @@ -754,26 +776,34 @@ int config_parse_exec_cpu_affinity( assert(rvalue); assert(data); + if (isempty(rvalue)) { + /* An empty assignment resets the CPU list */ + if (c->cpuset) + CPU_FREE(c->cpuset); + c->cpuset = NULL; + return 0; + } + FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char _cleanup_free_ *t = NULL; + _cleanup_free_ char *t = NULL; int r; unsigned cpu; t = strndup(w, 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; } @@ -783,15 +813,15 @@ int config_parse_exec_cpu_affinity( 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { ExecContext *c = data; cap_t cap; @@ -801,11 +831,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,15 +845,15 @@ 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { ExecContext *c = data; char *w; @@ -836,22 +865,28 @@ int config_parse_exec_secure_bits( assert(rvalue); assert(data); + if (isempty(rvalue)) { + /* An empty assignment resets the field */ + c->secure_bits = 0; + return 0; + } + FOREACH_WORD_QUOTED(w, l, rvalue, state) { if (first_word(w, "keep-caps")) - c->secure_bits |= SECURE_KEEP_CAPS; + c->secure_bits |= 1<<SECURE_KEEP_CAPS; else if (first_word(w, "keep-caps-locked")) - c->secure_bits |= SECURE_KEEP_CAPS_LOCKED; + c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED; else if (first_word(w, "no-setuid-fixup")) - c->secure_bits |= SECURE_NO_SETUID_FIXUP; + c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP; else if (first_word(w, "no-setuid-fixup-locked")) - c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED; + c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED; else if (first_word(w, "noroot")) - c->secure_bits |= SECURE_NOROOT; + c->secure_bits |= 1<<SECURE_NOROOT; else if (first_word(w, "noroot-locked")) - c->secure_bits |= SECURE_NOROOT_LOCKED; + c->secure_bits |= 1<<SECURE_NOROOT_LOCKED; else { - log_error("[%s:%u] Failed to parse secure bits, ignoring: %s", - filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse secure bits, ignoring: %s", rvalue); return 0; } } @@ -859,15 +894,15 @@ int config_parse_exec_secure_bits( return 0; } -int config_parse_bounding_set( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_bounding_set(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { uint64_t *capability_bounding_set_drop = data; char *w; @@ -892,18 +927,18 @@ int config_parse_bounding_set( * interface. */ FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char _cleanup_free_ *t = NULL; + _cleanup_free_ char *t = NULL; int r; cap_value_t cap; t = strndup(w, l); if (!t) - return -ENOMEM; + return log_oom(); r = cap_from_name(t, &cap); if (r < 0) { - log_error("[%s:%u] Failed to parse capability in bounding set, ignoring: %s", - filename, line, t); + log_syntax(unit, LOG_ERR, filename, line, errno, + "Failed to parse capability in bounding set, ignoring: %s", t); continue; } @@ -918,15 +953,15 @@ int config_parse_bounding_set( return 0; } -int config_parse_limit( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_limit(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { struct rlimit **rl = data; unsigned long long u; @@ -940,54 +975,72 @@ int config_parse_limit( if (streq(rvalue, "infinity")) u = (unsigned long long) RLIM_INFINITY; - else if (safe_atollu(rvalue, &u) < 0) { - log_error("[%s:%u] Failed to parse resource value, ignoring: %s", filename, line, rvalue); - return 0; + else { + int r; + + r = safe_atollu(rvalue, &u); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to parse resource value, ignoring: %s", rvalue); + return 0; + } } - if (!*rl) - if (!(*rl = new(struct rlimit, 1))) - return -ENOMEM; + if (!*rl) { + *rl = new(struct rlimit, 1); + if (!*rl) + return log_oom(); + } (*rl)->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) { +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) { - char _cleanup_free_ *t = NULL, *k = NULL, *ku = NULL; + _cleanup_free_ char *t = NULL, *k = NULL, *ku = NULL; int r; t = strndup(w, l); if (!t) - return -ENOMEM; + return log_oom(); k = unit_full_printf(u, t); if (!k) - return -ENOMEM; + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to resolve unit specifiers on %s. Ignoring.", + t); - ku = cunescape(k); + ku = cunescape(k ? k : t); if (!ku) - return -ENOMEM; + return log_oom(); - r = unit_add_cgroup_from_text(u, ku); + r = unit_add_cgroup_from_text(u, ku, true, NULL); if (r < 0) { - log_error("[%s:%u] Failed to parse cgroup value %s, ignoring: %s", - filename, line, k, rvalue); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to parse cgroup value %s, ignoring: %s", + k, rvalue); return 0; } } @@ -996,26 +1049,28 @@ int config_parse_unit_cgroup( } #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, + 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,26 +1079,28 @@ 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) { +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; + int i, r; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (safe_atoi(rvalue, &i) || i < 0) { - log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue); + 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; } @@ -1053,15 +1110,15 @@ int config_parse_fsck_passno( DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); -int config_parse_kill_signal( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_kill_signal(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { int *sig = data; int r; @@ -1071,8 +1128,10 @@ int config_parse_kill_signal( assert(rvalue); assert(sig); - if ((r = signal_from_string_try_harder(rvalue)) <= 0) { - log_error("[%s:%u] Failed to parse kill signal, 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; } @@ -1080,15 +1139,15 @@ int config_parse_kill_signal( return 0; } -int config_parse_exec_mount_flags( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_mount_flags(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { ExecContext *c = data; char *w; @@ -1102,11 +1161,11 @@ int config_parse_exec_mount_flags( assert(data); FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) { - char _cleanup_free_ *t; + _cleanup_free_ char *t; t = strndup(w, l); if (!t) - return -ENOMEM; + return log_oom(); if (streq(t, "shared")) flags |= MS_SHARED; @@ -1115,8 +1174,9 @@ int config_parse_exec_mount_flags( 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); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse mount flag %s, ignoring: %s", + t, rvalue); return 0; } } @@ -1125,15 +1185,15 @@ int config_parse_exec_mount_flags( 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Timer *t = data; usec_t u = 0; @@ -1147,22 +1207,33 @@ int config_parse_timer( 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; } @@ -1171,7 +1242,7 @@ int config_parse_timer( v = new0(TimerValue, 1); if (!v) - return -ENOMEM; + return log_oom(); v->base = b; v->clock_id = id; @@ -1183,7 +1254,8 @@ int config_parse_timer( return 0; } -int config_parse_timer_unit( +int config_parse_trigger_unit( + const char *unit, const char *filename, unsigned line, const char *section, @@ -1193,78 +1265,105 @@ int config_parse_timer_unit( 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; + } + + p = unit_name_printf(u, rvalue); + if (!p) + return log_oom(); - if (endswith(rvalue, ".timer")) { - log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue); + type = unit_name_to_type(p); + if (type < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Unit type not valid, ignoring: %s", rvalue); return 0; } - r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, NULL, &u); - if (r < 0) { - log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); - dbus_error_free(&error); + if (type == u->type) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Trigger cannot be of same type, ignoring: %s", rvalue); return 0; } - unit_ref_set(&t->unit, u); + r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to add trigger on %s, ignoring: %s", p, strerror(-r)); + return 0; + } return 0; } -int config_parse_path_spec( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_path_spec(const char *unit, + const char *filename, + unsigned line, + const char *section, + 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; 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(); + if (!k) { + k = strdup(rvalue); + if (!k) + return log_oom(); + else + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "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->path = path_kill_slashes(k); + k = NULL; s->type = b; s->inotify_fd = -1; @@ -1273,58 +1372,21 @@ int config_parse_path_spec( 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); - - 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Socket *s = data; int r; DBusError error; Unit *x; + _cleanup_free_ char *p = NULL; assert(filename); assert(lvalue); @@ -1333,14 +1395,21 @@ int config_parse_socket_service( dbus_error_init(&error); - if (!endswith(rvalue, ".service")) { - log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue); + p = unit_name_printf(UNIT(s), rvalue); + if (!p) + 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(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)); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to load unit %s, ignoring: %s", + rvalue, bus_error(&error, r)); dbus_error_free(&error); return 0; } @@ -1350,15 +1419,15 @@ 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Service *s = data; int r; @@ -1371,26 +1440,27 @@ int config_parse_service_sockets( assert(data); FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char _cleanup_free_ *t = NULL, *k = NULL; + _cleanup_free_ char *t = NULL, *k = NULL; t = strndup(w, l); if (!t) - return -ENOMEM; + return log_oom(); k = unit_name_printf(UNIT(s), t); if (!k) - return -ENOMEM; + return log_oom(); if (!endswith(k, ".socket")) { - log_error("[%s:%u] Unit must be of type socket, ignoring: %s", - filename, line, k); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Unit must be of type socket, ignoring: %s", k); continue; } r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, 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)); r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true); if (r < 0) @@ -1400,15 +1470,15 @@ int config_parse_service_sockets( 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Service *s = userdata; int r; @@ -1418,9 +1488,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, lvalue, ltype, + rvalue, data, userdata); + if (r < 0) return r; if (streq(lvalue, "TimeoutSec")) { @@ -1432,55 +1502,115 @@ int config_parse_service_timeout( return 0; } -int config_parse_unit_env_file( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - char ***env = data, **k; +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; - char *s; + _cleanup_free_ char *s = NULL; + 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; + } + s = unit_full_printf(u, rvalue); if (!s) - return -ENOMEM; + return log_oom(); if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) { - log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, s); - free(s); + 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Unit *u = userdata; + char*** env = data, *w, *state; + size_t l; + _cleanup_free_ char *k = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + strv_free(*env); + *env = NULL; return 0; } - k = strv_append(*env, s); - free(s); + k = unit_full_printf(u, rvalue); if (!k) - return -ENOMEM; + return log_oom(); - strv_free(*env); - *env = k; + FOREACH_WORD_QUOTED(w, l, k, state) { + _cleanup_free_ char *n; + char **x; + + n = cunescape_length(w, 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; + } return 0; } -int config_parse_ip_tos( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_ip_tos(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { int *ip_tos = data, x; @@ -1491,7 +1621,8 @@ int config_parse_ip_tos( x = ip_tos_from_string(rvalue); if (x < 0) { - log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse IP TOS value, ignoring: %s", rvalue); return 0; } @@ -1499,15 +1630,15 @@ int config_parse_ip_tos( return 0; } -int config_parse_unit_condition_path( - 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_path(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { ConditionType cond = ltype; Unit *u = data; @@ -1520,6 +1651,13 @@ int config_parse_unit_condition_path( 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++; @@ -1530,30 +1668,31 @@ int config_parse_unit_condition_path( p = unit_full_printf(u, rvalue); if (!p) - return -ENOMEM; + 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); 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { ConditionType cond = ltype; Unit *u = data; @@ -1566,6 +1705,13 @@ int config_parse_unit_condition_string( 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++; @@ -1576,7 +1722,7 @@ int config_parse_unit_condition_string( s = unit_full_printf(u, rvalue); if (!s) - return -ENOMEM; + return log_oom(); c = condition_new(cond, s, trigger, negate); if (!c) @@ -1586,15 +1732,15 @@ int config_parse_unit_condition_string( 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, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Unit *u = data; Condition *c; @@ -1606,22 +1752,35 @@ 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); return 0; @@ -1630,383 +1789,117 @@ int config_parse_unit_condition_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"); -int config_parse_unit_cgroup_attr( - 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; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - l = strv_split_quoted(rvalue); - if (!l) - return -ENOMEM; - - 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; - } - - r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL); - strv_free(l); - - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; - } - - return 0; -} +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) { -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; + size_t a, b; + _cleanup_free_ char *n = NULL, *v = NULL; + const CGroupSemantics *s; int r; - unsigned long ul; - char *t; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (safe_atolu(rvalue, &ul) < 0 || ul < 1) { - log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue); - return 0; - } - - if (asprintf(&t, "%lu", ul) < 0) - return -ENOMEM; - - r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL); - free(t); - - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + if (isempty(rvalue)) { + /* Empty assignment clears the list */ + cgroup_attribute_free_list(u->cgroup_attributes); + u->cgroup_attributes = NULL; return 0; } - return 0; -} - -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; - 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); + a = strcspn(rvalue, WHITESPACE); + b = strspn(rvalue + a, WHITESPACE); + if (a <= 0 || b <= 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse cgroup attribute value, ignoring: %s", + rvalue); return 0; } - if (asprintf(&t, "%llu", (unsigned long long) sz) < 0) - return -ENOMEM; - - r = unit_add_cgroup_attribute(u, - "memory", - streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes", - t, NULL); - free(t); + n = strndup(rvalue, a); + if (!n) + return log_oom(); + r = cgroup_semantics_find(NULL, n, rvalue + a + b, &v, &s); 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); - - l = strv_split_quoted(value); - if (!l) - return -ENOMEM; - - assert(strv_length(l) >= 1); - - if (streq(l[0], "*")) { - - if (asprintf(ret, "a *:*%s%s", - isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) { - strv_free(l); - return -ENOMEM; - } - - } else { - struct stat st; - - if (stat(l[0], &st) < 0) { - log_warning("Couldn't stat device %s", l[0]); - strv_free(l); - return -errno; - } - - 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; - } - - 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) { - - strv_free(l); - return -ENOMEM; - } - } - - 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; - 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); - 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); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to parse cgroup attribute value, ignoring: %s", + rvalue); 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); - - r = unit_add_cgroup_attribute(u, "devices", - streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny", - rvalue, device_map); - + r = unit_add_cgroup_attribute(u, s, NULL, n, v ? v : rvalue + a + b, NULL); if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to add cgroup attribute value, ignoring: %s", rvalue); return 0; } 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); - - l = strv_split_quoted(value); - if (!l) - return -ENOMEM; - - assert(strv_length(l) == 2); - - if (stat(l[0], &st) < 0) { - log_warning("Couldn't stat device %s", l[0]); - strv_free(l); - return -errno; - } - - 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; - - /* 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; - } - - if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) { - strv_free(l); - return -ENOMEM; - } +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) { - 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; + _cleanup_free_ char *v = NULL; + const CGroupSemantics *s; int r; - unsigned long ul; - const char *device = NULL, *weight; - 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 < 1 || k > 2) { - log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue); - strv_free(l); - 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; - } - - 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; - } - - if (device) - r = asprintf(&t, "%s %lu", device, ul); - else - r = asprintf(&t, "%lu", ul); - strv_free(l); - - if (r < 0) - return -ENOMEM; - - 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 = cgroup_semantics_find(NULL, lvalue, rvalue, &v, &s); if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; - } - - 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 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); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to parse cgroup attribute value, ignoring: %s", + rvalue); 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); + } else if (r == 0) { + log_syntax(unit, LOG_ERR, filename, line, ENOTSUP, + "Unknown or unsupported cgroup attribute %s, ignoring: %s", + lvalue, rvalue); return 0; } - 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; - } - - r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes); - strv_free(l); - - if (r < 0) - return -ENOMEM; - - r = unit_add_cgroup_attribute(u, "blkio", - streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device", - t, blkio_map); - free(t); - + r = unit_add_cgroup_attribute(u, s, NULL, NULL, v, NULL); if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to add cgroup attribute value, ignoring: %s", rvalue); return 0; } return 0; } -int config_parse_unit_requires_mounts_for( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +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) { Unit *u = userdata; int r; @@ -2019,7 +1912,8 @@ int config_parse_unit_requires_mounts_for( empty_before = !u->requires_mounts_for; - r = config_parse_path_strv(filename, line, section, lvalue, ltype, rvalue, data, userdata); + 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) @@ -2028,15 +1922,15 @@ int config_parse_unit_requires_mounts_for( return r; } -int config_parse_documentation( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +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 r; @@ -2047,7 +1941,15 @@ int config_parse_documentation( assert(rvalue); assert(u); - r = config_parse_unit_strv_printf(filename, line, section, lvalue, ltype, rvalue, data, userdata); + 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, lvalue, ltype, + rvalue, data, userdata); if (r < 0) return r; @@ -2056,7 +1958,8 @@ int config_parse_documentation( if (is_valid_documentation_url(*a)) *(b++) = *a; else { - log_error("[%s:%u] Invalid URL, ignoring: %s", filename, line, *a); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid URL, ignoring: %s", *a); free(*a); } } @@ -2066,22 +1969,24 @@ int config_parse_documentation( } static void syscall_set(uint32_t *p, int nr) { + nr = SYSCALL_TO_INDEX(nr); p[nr >> 4] |= 1 << (nr & 31); } static void syscall_unset(uint32_t *p, int nr) { + nr = SYSCALL_TO_INDEX(nr); p[nr >> 4] &= ~(1 << (nr & 31)); } -int config_parse_syscall_filter( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +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) { ExecContext *c = data; Unit *u = userdata; @@ -2095,6 +2000,13 @@ int config_parse_syscall_filter( assert(rvalue); assert(u); + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + free(c->syscall_filter); + c->syscall_filter = NULL; + return 0; + } + if (rvalue[0] == '~') { invert = true; rvalue++; @@ -2106,7 +2018,7 @@ int config_parse_syscall_filter( n = (syscall_max() + 31) >> 4; c->syscall_filter = new(uint32_t, n); if (!c->syscall_filter) - return -ENOMEM; + return log_oom(); memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t)); @@ -2122,17 +2034,16 @@ int config_parse_syscall_filter( FOREACH_WORD_QUOTED(w, l, rvalue, state) { int id; - char _cleanup_free_ *t = NULL; + _cleanup_free_ char *t = NULL; t = strndup(w, l); if (!t) - return -ENOMEM; + return log_oom(); id = syscall_from_name(t); - if (id < 0) { - log_error("[%s:%u] Failed to parse syscall, ignoring: %s", - filename, line, t); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Failed to parse syscall, ignoring: %s", t); continue; } @@ -2184,11 +2095,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; - } } } @@ -2359,7 +2268,9 @@ static int load_from_path(Unit *u, const char *path) { u->load_state = UNIT_MASKED; else { /* 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, + (void*) load_fragment_gperf_lookup, false, true, u); if (r < 0) goto finish; @@ -2521,16 +2432,15 @@ void unit_dump_config_items(FILE *f) { { 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_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" }, @@ -2557,7 +2467,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); diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 24f738464c..ff7f22a6f0 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -29,59 +29,55 @@ int unit_load_fragment(Unit *u); void unit_dump_config_items(FILE *f); -int config_parse_warn_compat(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 *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 *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 *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 *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_documentation(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_socket_listen(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 *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 *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 *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -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_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_type(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_service_restart(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 *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_output(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_input(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 *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 *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 *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 *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 *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 *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 *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bounding_set(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -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); -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_fsck_passno(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_kill_signal(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_mount_flags(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -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_unit(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 *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -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); -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_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_unit_env_file(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_ip_tos(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_path(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 *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 *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_start_limit_action(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -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); -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); -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); -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); -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); -int config_parse_unit_requires_mounts_for(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_syscall_filter(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +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); +int config_parse_unit_deps(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_unit_string_printf(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_unit_strv_printf(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_unit_path_printf(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_documentation(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_socket_listen(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_socket_bind(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_exec_nice(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_exec_oom_score_adjust(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_exec(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_service_timeout(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_service_type(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_service_restart(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_socket_bindtodevice(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_output(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_input(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_exec_io_class(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_exec_io_priority(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_exec_cpu_sched_policy(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_exec_cpu_sched_prio(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_exec_cpu_affinity(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_exec_capabilities(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_exec_secure_bits(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_bounding_set(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_limit(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_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); +int config_parse_sysv_priority(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_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 config_parse_kill_signal(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_exec_mount_flags(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_timer(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, 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, 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, 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, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +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); +int config_parse_ip_tos(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_unit_condition_path(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_unit_condition_string(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_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); +int config_parse_kill_mode(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_notify_access(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_start_limit_action(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_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); +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); +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_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_environ(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c index 48b59bf448..d7113b9795 100644 --- a/src/core/locale-setup.c +++ b/src/core/locale-setup.c @@ -27,6 +27,7 @@ #include "util.h" #include "macro.h" #include "virt.h" +#include "fileio.h" enum { /* We don't list LC_ALL here on purpose. People should be @@ -67,11 +68,9 @@ static const char * const variable_names[_VARIABLE_MAX] = { }; int locale_setup(void) { - char *variables[_VARIABLE_MAX]; + char *variables[_VARIABLE_MAX] = {}; int r = 0, i; - zero(variables); - if (detect_container(NULL) <= 0) { r = parse_env_file("/proc/cmdline", WHITESPACE, "locale.LANG", &variables[VARIABLE_LANG], diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c index 065b75a6e3..aff24fa642 100644 --- a/src/core/loopback-setup.c +++ b/src/core/loopback-setup.c @@ -88,25 +88,26 @@ static int add_adresses(int fd, int if_loopback, unsigned *requests) { union { struct sockaddr sa; struct sockaddr_nl nl; - } sa; + } sa = { + .nl.nl_family = AF_NETLINK, + }; + union { struct nlmsghdr header; uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + RTA_LENGTH(sizeof(struct in6_addr))]; - } request; + } request = { + .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), + .header.nlmsg_type = RTM_NEWADDR, + .header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK, + .header.nlmsg_seq = *requests + 1, + }; struct ifaddrmsg *ifaddrmsg; uint32_t ipv4_address = htonl(INADDR_LOOPBACK); int r; - zero(request); - - request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - request.header.nlmsg_type = RTM_NEWADDR; - request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK; - request.header.nlmsg_seq = *requests + 1; - ifaddrmsg = NLMSG_DATA(&request.header); ifaddrmsg->ifa_family = AF_INET; ifaddrmsg->ifa_prefixlen = 8; @@ -114,13 +115,11 @@ static int add_adresses(int fd, int if_loopback, unsigned *requests) { ifaddrmsg->ifa_scope = RT_SCOPE_HOST; ifaddrmsg->ifa_index = if_loopback; - r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address)); + r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, + &ipv4_address, sizeof(ipv4_address)); if (r < 0) return r; - zero(sa); - sa.nl.nl_family = AF_NETLINK; - if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) return -errno; (*requests)++; @@ -134,7 +133,8 @@ static int add_adresses(int fd, int if_loopback, unsigned *requests) { ifaddrmsg->ifa_family = AF_INET6; ifaddrmsg->ifa_prefixlen = 128; - r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback)); + r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, + &in6addr_loopback, sizeof(in6addr_loopback)); if (r < 0) return r; @@ -149,31 +149,29 @@ static int start_interface(int fd, int if_loopback, unsigned *requests) { union { struct sockaddr sa; struct sockaddr_nl nl; - } sa; + } sa = { + .nl.nl_family = AF_NETLINK, + }; + union { struct nlmsghdr header; uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))]; - } request; + } request = { + .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .header.nlmsg_type = RTM_NEWLINK, + .header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK, + .header.nlmsg_seq = *requests + 1, + }; struct ifinfomsg *ifinfomsg; - zero(request); - - request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - request.header.nlmsg_type = RTM_NEWLINK; - request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; - request.header.nlmsg_seq = *requests + 1; - ifinfomsg = NLMSG_DATA(&request.header); ifinfomsg->ifi_family = AF_UNSPEC; ifinfomsg->ifi_index = if_loopback; ifinfomsg->ifi_flags = IFF_UP; ifinfomsg->ifi_change = IFF_UP; - zero(sa); - sa.nl.nl_family = AF_NETLINK; - if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) return -errno; @@ -229,11 +227,15 @@ static int read_response(int fd, unsigned requests_max) { } static int check_loopback(void) { - int r, fd; + int r; + _cleanup_close_ int fd; union { struct sockaddr sa; struct sockaddr_in in; - } sa; + } sa = { + .in.sin_family = AF_INET, + .in.sin_addr.s_addr = INADDR_LOOPBACK, + }; /* If we failed to set up the loop back device, check whether * it might already be set up */ @@ -242,17 +244,11 @@ static int check_loopback(void) { if (fd < 0) return -errno; - zero(sa); - sa.in.sin_family = AF_INET; - sa.in.sin_addr.s_addr = INADDR_LOOPBACK; - if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0) r = 1; else r = errno == EADDRNOTAVAIL ? 0 : -errno; - close_nointr_nofail(fd); - return r; } @@ -261,9 +257,11 @@ int loopback_setup(void) { union { struct sockaddr sa; struct sockaddr_nl nl; - } sa; + } sa = { + .nl.nl_family = AF_NETLINK, + }; unsigned requests = 0, i; - int fd; + _cleanup_close_ int fd = -1; bool eperm = false; errno = 0; @@ -275,20 +273,18 @@ int loopback_setup(void) { if (fd < 0) return -errno; - zero(sa); - sa.nl.nl_family = AF_NETLINK; if (bind(fd, &sa.sa, sizeof(sa)) < 0) { r = -errno; - goto finish; + goto error; } r = add_adresses(fd, if_loopback, &requests); if (r < 0) - goto finish; + goto error; r = start_interface(fd, if_loopback, &requests); if (r < 0) - goto finish; + goto error; for (i = 0; i < requests; i++) { r = read_response(fd, requests); @@ -296,22 +292,17 @@ int loopback_setup(void) { if (r == -EPERM) eperm = true; else if (r < 0) - goto finish; + goto error; } if (eperm && check_loopback() < 0) { r = -EPERM; - goto finish; + goto error; } - r = 0; - -finish: - if (r < 0) - log_warning("Failed to configure loopback device: %s", strerror(-r)); - - if (fd >= 0) - close_nointr_nofail(fd); + return 0; +error: + log_warning("Failed to configure loopback device: %s", strerror(-r)); return r; } diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index 7f4c23b130..18e015fe7f 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -35,6 +35,7 @@ #include "mkdir.h" #include "log.h" #include "virt.h" +#include "fileio.h" static int shorten_uuid(char destination[36], const char *source) { unsigned i, j; @@ -71,16 +72,19 @@ static int generate(char id[34]) { /* First, try reading the D-Bus machine id, unless it is a symlink */ fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); if (fd >= 0) { - - k = loop_read(fd, id, 32, false); + k = loop_read(fd, id, 33, false); close_nointr_nofail(fd); - if (k >= 32) { - id[32] = '\n'; - id[33] = 0; + if (k == 33 && id[32] == '\n') { - log_info("Initializing machine ID from D-Bus machine ID."); - return 0; + id[32] = 0; + if (id128_is_valid(id)) { + id[32] = '\n'; + id[33] = 0; + + log_info("Initializing machine ID from D-Bus machine ID."); + return 0; + } } } @@ -112,7 +116,7 @@ static int generate(char id[34]) { * $container_uuid the way libvirt/LXC does it */ r = detect_container(NULL); if (r > 0) { - char *e; + _cleanup_free_ char *e = NULL; r = getenv_for_pid(1, "container_uuid", &e); if (r > 0) { @@ -120,12 +124,9 @@ static int generate(char id[34]) { r = shorten_uuid(id, e); if (r >= 0) { log_info("Initializing machine ID from container UUID."); - free(e); return 0; } } - - free(e); } } @@ -150,62 +151,57 @@ static int generate(char id[34]) { } int machine_id_setup(void) { - int fd, r; + _cleanup_close_ int fd = -1; + int r; bool writable; struct stat st; char id[34]; /* 32 + \n + \0 */ - mode_t m; - - m = umask(0000); - - /* We create this 0444, to indicate that this isn't really - * something you should ever modify. Of course, since the file - * will be owned by root it doesn't matter much, but maybe - * people look. */ - - fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444); - if (fd >= 0) - writable = true; - else { - fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - umask(m); - log_error("Cannot open /etc/machine-id: %m"); - return -errno; - } - writable = false; - } + RUN_WITH_UMASK(0000) { + /* We create this 0444, to indicate that this isn't really + * something you should ever modify. Of course, since the file + * will be owned by root it doesn't matter much, but maybe + * people look. */ + + fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444); + if (fd >= 0) + writable = true; + else { + fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + log_error("Cannot open /etc/machine-id: %m"); + return -errno; + } - umask(m); + writable = false; + } + } if (fstat(fd, &st) < 0) { log_error("fstat() failed: %m"); - r = -errno; - goto finish; + return -errno; } - if (S_ISREG(st.st_mode)) { - if (loop_read(fd, id, 32, false) >= 32) { - r = 0; - goto finish; + if (S_ISREG(st.st_mode)) + if (loop_read(fd, id, 33, false) == 33 && id[32] == '\n') { + id[32] = 0; + + if (id128_is_valid(id)) + return 0; } - } /* Hmm, so, the id currently stored is not useful, then let's * generate one */ r = generate(id); if (r < 0) - goto finish; + return r; if (S_ISREG(st.st_mode) && writable) { lseek(fd, 0, SEEK_SET); - if (loop_write(fd, id, 33, false) == 33) { - r = 0; - goto finish; - } + if (loop_write(fd, id, 33, false) == 33) + return 0; } close_nointr_nofail(fd); @@ -214,33 +210,28 @@ int machine_id_setup(void) { /* Hmm, we couldn't write it? So let's write it to * /run/machine-id as a replacement */ - m = umask(0022); - r = write_one_line_file("/run/machine-id", id); - umask(m); - + RUN_WITH_UMASK(0022) { + r = write_string_file("/run/machine-id", id); + } if (r < 0) { log_error("Cannot write /run/machine-id: %s", strerror(-r)); - unlink("/run/machine-id"); - goto finish; + return r; } /* And now, let's mount it over */ - r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL) < 0 ? -errno : 0; + r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL); if (r < 0) { - unlink("/run/machine-id"); - log_error("Failed to mount /etc/machine-id: %s", strerror(-r)); - } else { - log_info("Installed transient /etc/machine-id file."); - - /* Mark the mount read-only */ - mount(NULL, "/etc/machine-id", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL); + log_error("Failed to mount /etc/machine-id: %m"); + unlink_noerrno("/run/machine-id"); + return -errno; } -finish: + log_info("Installed transient /etc/machine-id file."); - if (fd >= 0) - close_nointr_nofail(fd); + /* Mark the mount read-only */ + if (mount(NULL, "/etc/machine-id", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0) + log_warning("Failed to make transient /etc/machine-id read-only: %m"); - return r; + return 0; } diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in index 647cce6913..f77082c2db 100644 --- a/src/core/macros.systemd.in +++ b/src/core/macros.systemd.in @@ -61,11 +61,11 @@ fi \ %{nil} %udev_hwdb_update() \ -@bindir@/udevadm hwdb --update >/dev/null 2>&1 || : \ +@rootbindir@/udevadm hwdb --update >/dev/null 2>&1 || : \ %{nil} %udev_rules_update() \ -@bindir@/udevadm control --reload >/dev/null 2>&1 || : \ +@rootbindir@/udevadm control --reload >/dev/null 2>&1 || : \ %{nil} %journal_catalog_update() \ diff --git a/src/core/main.c b/src/core/main.c index 1ee3c9c0e8..7fc06bea05 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -40,7 +40,7 @@ #include "fdset.h" #include "special.h" #include "conf-parser.h" -#include "bus-errors.h" +#include "dbus-common.h" #include "missing.h" #include "label.h" #include "build.h" @@ -52,6 +52,10 @@ #include "switch-root.h" #include "capability.h" #include "killall.h" +#include "env-util.h" +#include "hwclock.h" +#include "sd-daemon.h" +#include "sd-messages.h" #include "mount-setup.h" #include "loopback-setup.h" @@ -61,10 +65,11 @@ #include "hostname-setup.h" #include "machine-id-setup.h" #include "locale-setup.h" -#include "hwclock.h" #include "selinux-setup.h" #include "ima-setup.h" -#include "sd-daemon.h" +#include "fileio.h" +#include "smack-setup.h" +#include "efivars.h" static enum { ACTION_RUN, @@ -104,20 +109,21 @@ _noreturn_ static void crash(int sig) { if (!arg_dump_core) log_error("Caught <%s>, not dumping core.", signal_to_string(sig)); else { - struct sigaction sa; + struct sigaction sa = { + .sa_handler = nop_handler, + .sa_flags = SA_NOCLDSTOP|SA_RESTART, + }; pid_t pid; /* We want to wait for the core process, hence let's enable SIGCHLD */ - zero(sa); - sa.sa_handler = nop_handler; - sa.sa_flags = SA_NOCLDSTOP|SA_RESTART; assert_se(sigaction(SIGCHLD, &sa, NULL) == 0); - if ((pid = fork()) < 0) + pid = fork(); + if (pid < 0) log_error("Caught <%s>, cannot fork for core dump: %s", signal_to_string(sig), strerror(errno)); else if (pid == 0) { - struct rlimit rl; + struct rlimit rl = {}; /* Enable default signal handler for core dump */ zero(sa); @@ -125,7 +131,6 @@ _noreturn_ static void crash(int sig) { assert_se(sigaction(sig, &sa, NULL) == 0); /* Don't limit the core dump size */ - zero(rl); rl.rlim_cur = RLIM_INFINITY; rl.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_CORE, &rl); @@ -144,7 +149,8 @@ _noreturn_ static void crash(int sig) { int r; /* Order things nicely. */ - if ((r = wait_for_terminate(pid, &status)) < 0) + r = wait_for_terminate(pid, &status); + if (r < 0) log_error("Caught <%s>, waitpid() failed: %s", signal_to_string(sig), strerror(-r)); else if (status.si_code != CLD_DUMPED) log_error("Caught <%s>, core dump failed.", signal_to_string(sig)); @@ -157,16 +163,16 @@ _noreturn_ static void crash(int sig) { chvt(arg_crash_chvt); if (arg_crash_shell) { - struct sigaction sa; + struct sigaction sa = { + .sa_handler = SIG_IGN, + .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART, + }; pid_t pid; log_info("Executing crash shell in 10s..."); sleep(10); /* Let the kernel reap children for us */ - zero(sa); - sa.sa_handler = SIG_IGN; - sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART; assert_se(sigaction(SIGCHLD, &sa, NULL) == 0); pid = fork(); @@ -188,12 +194,10 @@ _noreturn_ static void crash(int sig) { } static void install_crash_handler(void) { - struct sigaction sa; - - zero(sa); - - sa.sa_handler = crash; - sa.sa_flags = SA_NODEFER; + struct sigaction sa = { + .sa_handler = crash, + .sa_flags = SA_NODEFER, + }; sigaction_many(&sa, SIGNALS_CRASH_HANDLER, -1); } @@ -342,7 +346,8 @@ static int parse_proc_cmdline_word(const char *word) { else arg_default_std_error = r; } else if (startswith(word, "systemd.setenv=")) { - char *cenv, *eq; + _cleanup_free_ char *cenv = NULL; + char *eq; int r; cenv = strdup(word + 15); @@ -351,40 +356,58 @@ static int parse_proc_cmdline_word(const char *word) { eq = strchr(cenv, '='); if (!eq) { - r = unsetenv(cenv); - if (r < 0) - log_warning("unsetenv failed %m. Ignoring."); + if (!env_name_is_valid(cenv)) + log_warning("Environment variable name '%s' is not valid. Ignoring.", cenv); + else { + r = unsetenv(cenv); + if (r < 0) + log_warning("Unsetting environment variable '%s' failed, ignoring: %m", cenv); + } } else { - *eq = 0; - r = setenv(cenv, eq + 1, 1); - if (r < 0) - log_warning("setenv failed %m. Ignoring."); + if (!env_assignment_is_valid(cenv)) + log_warning("Environment variable assignment '%s' is not valid. Ignoring.", cenv); + else { + *eq = 0; + r = setenv(cenv, eq + 1, 1); + if (r < 0) + log_warning("Setting environment variable '%s=%s' failed, ignoring: %m", cenv, eq + 1); + } } - free(cenv); } else if (startswith(word, "systemd.") || (in_initrd() && startswith(word, "rd.systemd."))) { - log_warning("Unknown kernel switch %s. Ignoring.", word); - - log_info("Supported kernel switches:\n" - "systemd.unit=UNIT Default unit to start\n" - "rd.systemd.unit=UNIT Default unit to start when run in initrd\n" - "systemd.dump_core=0|1 Dump core on crash\n" - "systemd.crash_shell=0|1 Run shell on crash\n" - "systemd.crash_chvt=N Change to VT #N on crash\n" - "systemd.confirm_spawn=0|1 Confirm every process spawn\n" - "systemd.show_status=0|1 Show status updates on the console during bootup\n" - "systemd.log_target=console|kmsg|journal|journal-or-kmsg|syslog|syslog-or-kmsg|null\n" - " Log target\n" - "systemd.log_level=LEVEL Log level\n" - "systemd.log_color=0|1 Highlight important log messages\n" - "systemd.log_location=0|1 Include code location in log messages\n" - "systemd.default_standard_output=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n" - " Set default log output for services\n" - "systemd.default_standard_error=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n" - " Set default log error output for services\n" - "systemd.setenv=ASSIGNMENT Set an environment variable for all spawned processes\n"); + const char *c; + + /* Ignore systemd.journald.xyz and friends */ + c = word; + if (startswith(c, "rd.")) + c += 3; + if (startswith(c, "systemd.")) + c += 8; + if (c[strcspn(c, ".=")] != '.') { + + log_warning("Unknown kernel switch %s. Ignoring.", word); + + log_info("Supported kernel switches:\n" + "systemd.unit=UNIT Default unit to start\n" + "rd.systemd.unit=UNIT Default unit to start when run in initrd\n" + "systemd.dump_core=0|1 Dump core on crash\n" + "systemd.crash_shell=0|1 Run shell on crash\n" + "systemd.crash_chvt=N Change to VT #N on crash\n" + "systemd.confirm_spawn=0|1 Confirm every process spawn\n" + "systemd.show_status=0|1 Show status updates on the console during bootup\n" + "systemd.log_target=console|kmsg|journal|journal-or-kmsg|syslog|syslog-or-kmsg|null\n" + " Log target\n" + "systemd.log_level=LEVEL Log level\n" + "systemd.log_color=0|1 Highlight important log messages\n" + "systemd.log_location=0|1 Include code location in log messages\n" + "systemd.default_standard_output=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n" + " Set default log output for services\n" + "systemd.default_standard_error=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n" + " Set default log error output for services\n" + "systemd.setenv=ASSIGNMENT Set an environment variable for all spawned processes\n"); + } } else if (streq(word, "quiet")) arg_show_status = false; @@ -400,87 +423,47 @@ static int parse_proc_cmdline_word(const char *word) { return 0; } -static int config_parse_level2( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - assert(filename); - assert(lvalue); - assert(rvalue); - - log_set_max_level_from_string(rvalue); - return 0; -} - -static int config_parse_target( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - assert(filename); - assert(lvalue); - assert(rvalue); - - log_set_target_from_string(rvalue); - return 0; -} - -static int config_parse_color( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +#define DEFINE_SETTER(name, func, descr) \ + static int name(const char *unit, \ + const char *filename, \ + unsigned line, \ + const char *section, \ + const char *lvalue, \ + int ltype, \ + const char *rvalue, \ + void *data, \ + void *userdata) { \ + \ + int r; \ + \ + assert(filename); \ + assert(lvalue); \ + assert(rvalue); \ + \ + r = func(rvalue); \ + if (r < 0) \ + log_syntax(unit, LOG_ERR, filename, line, -r, \ + "Invalid " descr "'%s': %s", \ + rvalue, strerror(-r)); \ + \ + return 0; \ + } - assert(filename); - assert(lvalue); - assert(rvalue); +DEFINE_SETTER(config_parse_level2, log_set_max_level_from_string, "log level") +DEFINE_SETTER(config_parse_target, log_set_target_from_string, "target") +DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color" ) +DEFINE_SETTER(config_parse_location, log_show_location_from_string, "location") - log_show_color_from_string(rvalue); - return 0; -} -static int config_parse_location( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - assert(filename); - assert(lvalue); - assert(rvalue); - - log_show_location_from_string(rvalue); - return 0; -} - -static int config_parse_cpu_affinity2( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +static int config_parse_cpu_affinity2(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { char *w; size_t l; @@ -508,7 +491,8 @@ static int config_parse_cpu_affinity2( return log_oom(); if (r < 0 || cpu >= ncpus) { - log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue); + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to parse CPU affinity '%s'", rvalue); CPU_FREE(c); return -EBADMSG; } @@ -518,7 +502,7 @@ static int config_parse_cpu_affinity2( if (c) { if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0) - log_warning("Failed to set CPU affinity: %m"); + log_warning_unit(unit, "Failed to set CPU affinity: %m"); CPU_FREE(c); } @@ -539,22 +523,19 @@ static void strv_free_free(char ***l) { } static void free_join_controllers(void) { - if (!arg_join_controllers) - return; - strv_free_free(arg_join_controllers); arg_join_controllers = NULL; } -static int config_parse_join_controllers( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +static int config_parse_join_controllers(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { unsigned n = 0; char *state, *w; @@ -660,8 +641,8 @@ static int parse_config_file(void) { { "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output }, { "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error }, { "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers }, - { "Manager", "RuntimeWatchdogSec", config_parse_usec, 0, &arg_runtime_watchdog }, - { "Manager", "ShutdownWatchdogSec", config_parse_usec, 0, &arg_shutdown_watchdog }, + { "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog }, + { "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog }, { "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop }, { "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec }, { "Manager", "DefaultLimitCPU", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CPU]}, @@ -683,11 +664,11 @@ static int parse_config_file(void) { { NULL, NULL, NULL, 0, NULL } }; - FILE *f; + _cleanup_fclose_ FILE *f; const char *fn; int r; - fn = arg_running_as == SYSTEMD_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE; + fn = arg_running_as == SYSTEMD_SYSTEM ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf"; f = fopen(fn, "re"); if (!f) { if (errno == ENOENT) @@ -697,17 +678,16 @@ static int parse_config_file(void) { return 0; } - r = config_parse(fn, f, "Manager\0", config_item_table_lookup, (void*) items, false, NULL); + r = config_parse(NULL, fn, f, "Manager\0", config_item_table_lookup, (void*) items, false, false, NULL); if (r < 0) log_warning("Failed to parse configuration file: %s", strerror(-r)); - fclose(f); - return 0; } static int parse_proc_cmdline(void) { - char *line, *w, *state; + _cleanup_free_ char *line = NULL; + char *w, *state; int r; size_t l; @@ -716,34 +696,27 @@ static int parse_proc_cmdline(void) { if (detect_container(NULL) > 0) return 0; - if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) { + r = read_one_line_file("/proc/cmdline", &line); + if (r < 0) { log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); return 0; } FOREACH_WORD_QUOTED(w, l, line, state) { - char *word; + _cleanup_free_ char *word; - if (!(word = strndup(w, l))) { - r = -ENOMEM; - goto finish; - } + word = strndup(w, l); + if (!word) + return log_oom(); r = parse_proc_cmdline_word(word); if (r < 0) { log_error("Failed on cmdline argument %s: %s", word, strerror(-r)); - free(word); - goto finish; + return r; } - - free(word); } - r = 0; - -finish: - free(line); - return r; + return 0; } static int parse_argv(int argc, char *argv[]) { @@ -1069,7 +1042,7 @@ static int version(void) { return 0; } -static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool serialize_jobs) { +static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching_root) { FILE *f = NULL; FDSet *fds = NULL; int r; @@ -1094,7 +1067,7 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool serialize goto fail; } - r = manager_serialize(m, f, fds, serialize_jobs); + r = manager_serialize(m, f, fds, switching_root); if (r < 0) { log_error("Failed to serialize state: %s", strerror(-r)); goto fail; @@ -1244,14 +1217,14 @@ static int initialize_join_controllers(void) { return -ENOMEM; arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL); - if (!arg_join_controllers[0]) - return -ENOMEM; - arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL); - if (!arg_join_controllers[1]) + arg_join_controllers[2] = NULL; + + if (!arg_join_controllers[0] || !arg_join_controllers[1]) { + free_join_controllers(); return -ENOMEM; + } - arg_join_controllers[2] = NULL; return 0; } @@ -1264,6 +1237,10 @@ int main(int argc, char *argv[]) { bool reexecute = false; const char *shutdown_verb = NULL; dual_timestamp initrd_timestamp = { 0ULL, 0ULL }; + dual_timestamp userspace_timestamp = { 0ULL, 0ULL }; + dual_timestamp kernel_timestamp = { 0ULL, 0ULL }; + dual_timestamp firmware_timestamp = { 0ULL, 0ULL }; + dual_timestamp loader_timestamp = { 0ULL, 0ULL }; static char systemd[] = "systemd"; bool skip_setup = false; int j; @@ -1285,22 +1262,19 @@ int main(int argc, char *argv[]) { } #endif + dual_timestamp_from_monotonic(&kernel_timestamp, 0); + dual_timestamp_get(&userspace_timestamp); + /* Determine if this is a reexecution or normal bootup. We do * the full command line parsing much later, so let's just * have a quick peek here. */ - for (j = 1; j < argc; j++) - if (streq(argv[j], "--deserialize")) { - skip_setup = true; - break; - } + if (strv_find(argv+1, "--deserialize")) + skip_setup = true; /* If we have switched root, do all the special setup * things */ - for (j = 1; j < argc; j++) - if (streq(argv[j], "--switched-root")) { - skip_setup = false; - break; - } + if (strv_find(argv+1, "--switched-root")) + skip_setup = false; /* If we get started via the /sbin/init symlink then we are called 'init'. After a subsequent reexecution we are then @@ -1315,7 +1289,9 @@ int main(int argc, char *argv[]) { log_show_color(isatty(STDERR_FILENO) > 0); if (getpid() == 1 && detect_container(NULL) <= 0) { - +#ifdef ENABLE_EFI + efi_get_boot_timestamps(&userspace_timestamp, &firmware_timestamp, &loader_timestamp); +#endif /* Running outside of a container as PID 1 */ arg_running_as = SYSTEMD_SYSTEM; make_null_stdio(); @@ -1325,7 +1301,7 @@ int main(int argc, char *argv[]) { if (in_initrd()) { char *rd_timestamp = NULL; - dual_timestamp_get(&initrd_timestamp); + initrd_timestamp = userspace_timestamp; asprintf(&rd_timestamp, "%llu %llu", (unsigned long long) initrd_timestamp.realtime, (unsigned long long) initrd_timestamp.monotonic); @@ -1336,10 +1312,13 @@ int main(int argc, char *argv[]) { } if (!skip_setup) { + mount_setup_early(); if (selinux_setup(&loaded_policy) < 0) goto finish; if (ima_setup() < 0) goto finish; + if (smack_setup() < 0) + goto finish; } if (label_init(NULL) < 0) @@ -1382,7 +1361,6 @@ int main(int argc, char *argv[]) { log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); } else if (getpid() == 1) { - /* Running inside a container, as PID 1 */ arg_running_as = SYSTEMD_SYSTEM; log_set_target(LOG_TARGET_CONSOLE); @@ -1391,12 +1369,21 @@ int main(int argc, char *argv[]) { /* For the later on, see above... */ log_set_target(LOG_TARGET_JOURNAL); - } else { + /* clear the kernel timestamp, + * because we are in a container */ + kernel_timestamp.monotonic = 0ULL; + kernel_timestamp.realtime = 0ULL; + } else { /* Running as user instance */ arg_running_as = SYSTEMD_USER; log_set_target(LOG_TARGET_AUTO); log_open(); + + /* clear the kernel timestamp, + * because we are not PID 1 */ + kernel_timestamp.monotonic = 0ULL; + kernel_timestamp.realtime = 0ULL; } /* Initialize default unit */ @@ -1412,7 +1399,7 @@ int main(int argc, char *argv[]) { /* Mount /proc, /sys and friends, so that /proc/cmdline and * /proc/$PID/fd is available. */ - if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) { + if (getpid() == 1) { r = mount_setup(loaded_policy); if (r < 0) goto finish; @@ -1554,10 +1541,9 @@ int main(int argc, char *argv[]) { /* Make sure we leave a core dump without panicing the * kernel. */ - if (getpid() == 1) + if (getpid() == 1) { install_crash_handler(); - if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) { r = mount_cgroup_controllers(arg_join_controllers); if (r < 0) goto finish; @@ -1639,12 +1625,14 @@ int main(int argc, char *argv[]) { m->default_std_error = arg_default_std_error; m->runtime_watchdog = arg_runtime_watchdog; m->shutdown_watchdog = arg_shutdown_watchdog; + m->userspace_timestamp = userspace_timestamp; + m->kernel_timestamp = kernel_timestamp; + m->firmware_timestamp = firmware_timestamp; + m->loader_timestamp = loader_timestamp; + m->initrd_timestamp = initrd_timestamp; manager_set_default_rlimits(m, arg_default_rlimit); - if (dual_timestamp_is_set(&initrd_timestamp)) - m->initrd_timestamp = initrd_timestamp; - if (arg_default_controllers) manager_set_default_controllers(m, arg_default_controllers); @@ -1710,18 +1698,29 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); } - r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job); - if (r < 0) { - log_error("Failed to start default target: %s", bus_error(&error, r)); + r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, false, &error, &default_unit_job); + if (r == -EPERM) { + log_debug("Default target could not be isolated, starting instead: %s", bus_error(&error, r)); + dbus_error_free(&error); + + r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job); + if (r < 0) { + log_error("Failed to start default target: %s", bus_error(&error, r)); + dbus_error_free(&error); + goto finish; + } + } else if (r < 0) { + log_error("Failed to isolate default target: %s", bus_error(&error, r)); dbus_error_free(&error); goto finish; } + m->default_unit_job_id = default_unit_job->id; after_startup = now(CLOCK_MONOTONIC); log_full(arg_action == ACTION_TEST ? LOG_INFO : LOG_DEBUG, "Loaded units and determined initial transaction in %s.", - format_timespan(timespan, sizeof(timespan), after_startup - before_startup)); + format_timespan(timespan, sizeof(timespan), after_startup - before_startup, 0)); if (arg_action == ACTION_TEST) { printf("-> By jobs:\n"); @@ -1754,7 +1753,7 @@ int main(int argc, char *argv[]) { case MANAGER_REEXECUTE: - if (prepare_reexecute(m, &serialization, &fds, true) < 0) + if (prepare_reexecute(m, &serialization, &fds, false) < 0) goto finish; reexecute = true; @@ -1768,7 +1767,7 @@ int main(int argc, char *argv[]) { m->switch_root = m->switch_root_init = NULL; if (!switch_root_init) - if (prepare_reexecute(m, &serialization, &fds, false) < 0) + if (prepare_reexecute(m, &serialization, &fds, true) < 0) goto finish; reexecute = true; diff --git a/src/core/manager.c b/src/core/manager.c index df0fd63e87..c7f8f20a70 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -70,6 +70,7 @@ #include "cgroup-util.h" #include "path-util.h" #include "audit-fd.h" +#include "env-util.h" /* As soon as 16 units are in our GC queue, make sure to run a gc sweep */ #define GC_QUEUE_ENTRIES_MAX 16 @@ -77,18 +78,28 @@ /* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */ #define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC) +/* Initial delay and the interval for printing status messages about running jobs */ +#define JOBS_IN_PROGRESS_WAIT_SEC 5 +#define JOBS_IN_PROGRESS_PERIOD_SEC 1 +#define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3 + /* Where clients shall send notification messages to */ #define NOTIFY_SOCKET "@/org/freedesktop/systemd1/notify" +#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1) + static int manager_setup_notify(Manager *m) { union { struct sockaddr sa; struct sockaddr_un un; - } sa; - struct epoll_event ev; - int one = 1; - - assert(m); + } sa = { + .sa.sa_family = AF_UNIX, + }; + struct epoll_event ev = { + .events = EPOLLIN, + .data.ptr = &m->notify_watch, + }; + int one = 1, r; m->notify_watch.type = WATCH_NOTIFY; m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); @@ -97,9 +108,6 @@ static int manager_setup_notify(Manager *m) { return -errno; } - zero(sa); - sa.sa.sa_family = AF_UNIX; - if (getpid() != 1 || detect_container(NULL) > 0) snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET "/%llu", random_ull()); else @@ -107,22 +115,22 @@ static int manager_setup_notify(Manager *m) { sa.un.sun_path[0] = 0; - if (bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { + r = bind(m->notify_watch.fd, &sa.sa, + offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)); + if (r < 0) { log_error("bind() failed: %m"); return -errno; } - if (setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { + r = setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) { log_error("SO_PASSCRED failed: %m"); return -errno; } - zero(ev); - ev.events = EPOLLIN; - ev.data.ptr = &m->notify_watch; - - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) { - log_error("Failed to add timer change fd to epoll: %m"); + r = epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev); + if (r < 0) { + log_error("Failed to add notification socket fd to epoll: %m"); return -errno; } @@ -136,11 +144,151 @@ static int manager_setup_notify(Manager *m) { return 0; } +static int manager_jobs_in_progress_mod_timer(Manager *m) { + struct itimerspec its = { + .it_value.tv_sec = JOBS_IN_PROGRESS_WAIT_SEC, + .it_interval.tv_sec = JOBS_IN_PROGRESS_PERIOD_SEC, + }; + + if (m->jobs_in_progress_watch.type != WATCH_JOBS_IN_PROGRESS) + return 0; + + if (timerfd_settime(m->jobs_in_progress_watch.fd, 0, &its, NULL) < 0) + return -errno; + + return 0; +} + +static int manager_watch_jobs_in_progress(Manager *m) { + struct epoll_event ev = { + .events = EPOLLIN, + .data.ptr = &m->jobs_in_progress_watch, + }; + int r; + + if (m->jobs_in_progress_watch.type != WATCH_INVALID) + return 0; + + m->jobs_in_progress_watch.type = WATCH_JOBS_IN_PROGRESS; + m->jobs_in_progress_watch.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC); + if (m->jobs_in_progress_watch.fd < 0) { + log_error("Failed to create timerfd: %m"); + r = -errno; + goto err; + } + + r = manager_jobs_in_progress_mod_timer(m); + if (r < 0) { + log_error("Failed to set up timer for jobs progress watch: %s", strerror(-r)); + goto err; + } + + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->jobs_in_progress_watch.fd, &ev) < 0) { + log_error("Failed to add jobs progress timer fd to epoll: %m"); + r = -errno; + goto err; + } + + log_debug("Set up jobs progress timerfd."); + + return 0; + +err: + if (m->jobs_in_progress_watch.fd >= 0) + close_nointr_nofail(m->jobs_in_progress_watch.fd); + watch_init(&m->jobs_in_progress_watch); + return r; +} + +static void manager_unwatch_jobs_in_progress(Manager *m) { + if (m->jobs_in_progress_watch.type != WATCH_JOBS_IN_PROGRESS) + return; + + assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->jobs_in_progress_watch.fd, NULL) >= 0); + close_nointr_nofail(m->jobs_in_progress_watch.fd); + watch_init(&m->jobs_in_progress_watch); + m->jobs_in_progress_iteration = 0; + + log_debug("Closed jobs progress timerfd."); +} + +#define CYLON_BUFFER_EXTRA (2*strlen(ANSI_RED_ON) + strlen(ANSI_HIGHLIGHT_RED_ON) + 2*strlen(ANSI_HIGHLIGHT_OFF)) +static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned pos) { + char *p = buffer; + + assert(buflen >= CYLON_BUFFER_EXTRA + width + 1); + assert(pos <= width+1); /* 0 or width+1 mean that the center light is behind the corner */ + + if (pos > 1) { + if (pos > 2) + p = mempset(p, ' ', pos-2); + p = stpcpy(p, ANSI_RED_ON); + *p++ = '*'; + } + + if (pos > 0 && pos <= width) { + p = stpcpy(p, ANSI_HIGHLIGHT_RED_ON); + *p++ = '*'; + } + + p = stpcpy(p, ANSI_HIGHLIGHT_OFF); + + if (pos < width) { + p = stpcpy(p, ANSI_RED_ON); + *p++ = '*'; + if (pos < width-1) + p = mempset(p, ' ', width-1-pos); + p = stpcpy(p, ANSI_HIGHLIGHT_OFF); + } +} + +static void manager_print_jobs_in_progress(Manager *m) { + Iterator i; + Job *j; + char *job_of_n = NULL; + unsigned counter = 0, print_nr; + char cylon[6 + CYLON_BUFFER_EXTRA + 1]; + unsigned cylon_pos; + + print_nr = (m->jobs_in_progress_iteration / JOBS_IN_PROGRESS_PERIOD_DIVISOR) % m->n_running_jobs; + + HASHMAP_FOREACH(j, m->jobs, i) + if (j->state == JOB_RUNNING && counter++ == print_nr) + break; + + /* m->n_running_jobs must be consistent with the contents of m->jobs, + * so the above loop must have succeeded in finding j. */ + assert(counter == print_nr + 1); + + cylon_pos = m->jobs_in_progress_iteration % 14; + if (cylon_pos >= 8) + cylon_pos = 14 - cylon_pos; + draw_cylon(cylon, sizeof(cylon), 6, cylon_pos); + + if (m->n_running_jobs > 1) + if (asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs) < 0) + job_of_n = NULL; + + manager_status_printf(m, true, cylon, "%sA %s job is running for %s", + strempty(job_of_n), job_type_to_string(j->type), unit_description(j->unit)); + free(job_of_n); + + m->jobs_in_progress_iteration++; +} + static int manager_setup_time_change(Manager *m) { - struct epoll_event ev; - struct itimerspec its; + struct epoll_event ev = { + .events = EPOLLIN, + .data.ptr = &m->time_change_watch, + }; + + /* We only care for the cancellation event, hence we set the + * timeout to the latest possible value. */ + struct itimerspec its = { + .it_value.tv_sec = TIME_T_MAX, + }; + assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); - assert(m); assert(m->time_change_watch.type == WATCH_INVALID); /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever @@ -153,13 +301,6 @@ static int manager_setup_time_change(Manager *m) { return -errno; } - zero(its); - - /* We only care for the cancellation event, hence we set the - * timeout to the latest possible value. */ - assert_cc(sizeof(time_t) == sizeof(long)); - its.it_value.tv_sec = LONG_MAX; - if (timerfd_settime(m->time_change_watch.fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) { log_debug("Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m"); close_nointr_nofail(m->time_change_watch.fd); @@ -167,10 +308,6 @@ static int manager_setup_time_change(Manager *m) { return 0; } - zero(ev); - ev.events = EPOLLIN; - ev.data.ptr = &m->time_change_watch; - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->time_change_watch.fd, &ev) < 0) { log_error("Failed to add timer change fd to epoll: %m"); return -errno; @@ -210,15 +347,18 @@ static int enable_special_signals(Manager *m) { static int manager_setup_signals(Manager *m) { sigset_t mask; - struct epoll_event ev; - struct sigaction sa; + struct epoll_event ev = { + .events = EPOLLIN, + .data.ptr = &m->signal_watch, + }; + struct sigaction sa = { + .sa_handler = SIG_DFL, + .sa_flags = SA_NOCLDSTOP|SA_RESTART, + }; assert(m); /* We are not interested in SIGSTOP and friends. */ - zero(sa); - sa.sa_handler = SIG_DFL; - sa.sa_flags = SA_NOCLDSTOP|SA_RESTART; assert_se(sigaction(SIGCHLD, &sa, NULL) == 0); assert_se(sigemptyset(&mask) == 0); @@ -260,10 +400,6 @@ static int manager_setup_signals(Manager *m) { if (m->signal_watch.fd < 0) return -errno; - zero(ev); - ev.events = EPOLLIN; - ev.data.ptr = &m->signal_watch; - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0) return -errno; @@ -286,6 +422,9 @@ static void manager_strip_environment(Manager *m) { * the initrd interface: * http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface */ strv_remove_prefix(m->environment, "RD_"); + + /* Drop invalid entries */ + strv_env_clean(m->environment); } int manager_new(SystemdRunningAs running_as, Manager **_m) { @@ -300,9 +439,6 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) { if (!m) return -ENOMEM; - dual_timestamp_get(&m->userspace_timestamp); - dual_timestamp_from_monotonic(&m->kernel_timestamp, 0); - m->running_as = running_as; m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1; m->exit_code = _MANAGER_EXIT_CODE_INVALID; @@ -314,6 +450,7 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) { watch_init(&m->swap_watch); watch_init(&m->udev_watch); watch_init(&m->time_change_watch); + watch_init(&m->jobs_in_progress_watch); m->epoll_fd = m->dev_autofs_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ @@ -519,6 +656,9 @@ static void manager_clear_jobs_and_units(Manager *m) { assert(hashmap_isempty(m->jobs)); assert(hashmap_isempty(m->units)); + + m->n_on_console = 0; + m->n_running_jobs = 0; } void manager_free(Manager *m) { @@ -554,6 +694,8 @@ void manager_free(Manager *m) { close_nointr_nofail(m->notify_watch.fd); if (m->time_change_watch.fd >= 0) close_nointr_nofail(m->time_change_watch.fd); + if (m->jobs_in_progress_watch.fd >= 0) + close_nointr_nofail(m->jobs_in_progress_watch.fd); free(m->notify_socket); @@ -617,14 +759,15 @@ int manager_coldplug(Manager *m) { static void manager_build_unit_path_cache(Manager *m) { char **i; - DIR *d = NULL; + _cleanup_free_ DIR *d = NULL; int r; assert(m); set_free_free(m->unit_path_cache); - if (!(m->unit_path_cache = set_new(string_hash_func, string_compare_func))) { + m->unit_path_cache = set_new(string_hash_func, string_compare_func); + if (!m->unit_path_cache) { log_error("Failed to allocate unit path cache."); return; } @@ -637,7 +780,8 @@ static void manager_build_unit_path_cache(Manager *m) { d = opendir(*i); if (!d) { - log_error("Failed to open directory: %m"); + if (errno != ENOENT) + log_error("Failed to open directory %s: %m", *i); continue; } @@ -653,10 +797,9 @@ static void manager_build_unit_path_cache(Manager *m) { goto fail; } - if ((r = set_put(m->unit_path_cache, p)) < 0) { - free(p); + r = set_consume(m->unit_path_cache, p); + if (r < 0) goto fail; - } } closedir(d); @@ -670,9 +813,6 @@ fail: set_free_free(m->unit_path_cache); m->unit_path_cache = NULL; - - if (d) - closedir(d); } int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { @@ -756,7 +896,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove job_type_collapse(&type, unit); - tr = transaction_new(); + tr = transaction_new(mode == JOB_REPLACE_IRREVERSIBLY); if (!tr) return -ENOMEM; @@ -978,6 +1118,10 @@ unsigned manager_dispatch_run_queue(Manager *m) { } m->dispatching_run_queue = false; + + if (m->n_running_jobs > 0) + manager_watch_jobs_in_progress(m); + return n; } @@ -1018,30 +1162,29 @@ static int manager_process_notify_fd(Manager *m) { for (;;) { char buf[4096]; - struct msghdr msghdr; - struct iovec iovec; - struct ucred *ucred; + struct iovec iovec = { + .iov_base = buf, + .iov_len = sizeof(buf)-1, + }; + union { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control; + } control = {}; + + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct ucred *ucred; Unit *u; - char **tags; - - zero(iovec); - iovec.iov_base = buf; - iovec.iov_len = sizeof(buf)-1; - - zero(control); - zero(msghdr); - msghdr.msg_iov = &iovec; - msghdr.msg_iovlen = 1; - msghdr.msg_control = &control; - msghdr.msg_controllen = sizeof(control); + _cleanup_strv_free_ char **tags = NULL; n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT); if (n <= 0) { - if (n >= 0) + if (n == 0) return -EIO; if (errno == EAGAIN || errno == EINTR) @@ -1079,8 +1222,6 @@ static int manager_process_notify_fd(Manager *m) { if (UNIT_VTABLE(u)->notify_message) UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags); - - strv_free(tags); } return 0; @@ -1090,12 +1231,10 @@ static int manager_dispatch_sigchld(Manager *m) { assert(m); for (;;) { - siginfo_t si; + siginfo_t si = {}; Unit *u; int r; - zero(si); - /* First we call waitd() for a PID and do not reap the * zombie. That way we can still access /proc/$PID for * it while it is a zombie. */ @@ -1114,7 +1253,7 @@ static int manager_dispatch_sigchld(Manager *m) { break; if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) { - char _cleanup_free_ *name = NULL; + _cleanup_free_ char *name = NULL; get_process_comm(si.si_pid, &name); log_debug("Got SIGCHLD for process %lu (%s)", (unsigned long) si.si_pid, strna(name)); @@ -1505,6 +1644,8 @@ static int process_event(Manager *m, struct epoll_event *ev) { NULL); /* Restart the watch */ + epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->time_change_watch.fd, + NULL); close_nointr_nofail(m->time_change_watch.fd); watch_init(&m->time_change_watch); manager_setup_time_change(m); @@ -1517,6 +1658,16 @@ static int process_event(Manager *m, struct epoll_event *ev) { break; } + case WATCH_JOBS_IN_PROGRESS: { + uint64_t v; + + /* not interested in the data */ + read(w->fd, &v, sizeof(v)); + + manager_print_jobs_in_progress(m); + break; + } + default: log_error("event type=%i", w->type); assert_not_reached("Unknown epoll event type."); @@ -1724,7 +1875,8 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { /* We set SOCK_NONBLOCK here so that we rather drop the * message then wait for plymouth */ - if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) { + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) { log_error("socket() failed: %m"); return; } @@ -1807,7 +1959,6 @@ void manager_dispatch_bus_query_pid_done( int manager_open_serialization(Manager *m, FILE **_f) { char *path = NULL; - mode_t saved_umask; int fd; FILE *f; @@ -1821,9 +1972,9 @@ int manager_open_serialization(Manager *m, FILE **_f) { if (!path) return -ENOMEM; - saved_umask = umask(0077); - fd = mkostemp(path, O_RDWR|O_CLOEXEC); - umask(saved_umask); + RUN_WITH_UMASK(0077) { + fd = mkostemp(path, O_RDWR|O_CLOEXEC); + } if (fd < 0) { free(path); @@ -1844,10 +1995,11 @@ int manager_open_serialization(Manager *m, FILE **_f) { return 0; } -int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) { +int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { Iterator i; Unit *u; const char *t; + char **e; int r; assert(m); @@ -1871,6 +2023,16 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) { dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp); } + if (!switching_root) { + STRV_FOREACH(e, m->environment) { + _cleanup_free_ char *ce; + + ce = cescape(*e); + if (ce) + fprintf(f, "env=%s\n", *e); + } + } + fputc('\n', f); HASHMAP_FOREACH_KEY(u, t, m->units, i) { @@ -1884,7 +2046,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) { fputs(u->id, f); fputc('\n', f); - if ((r = unit_serialize(u, f, fds, serialize_jobs)) < 0) { + if ((r = unit_serialize(u, f, fds, !switching_root)) < 0) { m->n_reloading --; return r; } @@ -1971,7 +2133,25 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { dual_timestamp_deserialize(l+20, &m->userspace_timestamp); else if (startswith(l, "finish-timestamp=")) dual_timestamp_deserialize(l+17, &m->finish_timestamp); - else + else if (startswith(l, "env=")) { + _cleanup_free_ char *uce = NULL; + char **e; + + uce = cunescape(l+4); + if (!uce) { + r = -ENOMEM; + goto finish; + } + + e = strv_env_set(m->environment, uce); + if (!e) { + r = -ENOMEM; + goto finish; + } + + strv_free(m->environment); + m->environment = e; + } else log_debug("Unknown serialization item '%s'", l); } @@ -2054,7 +2234,7 @@ int manager_reload(Manager *m) { goto finish; } - r = manager_serialize(m, f, fds, true); + r = manager_serialize(m, f, fds, false); if (r < 0) { m->n_reloading --; goto finish; @@ -2115,7 +2295,7 @@ finish: return r; } -bool manager_is_booting_or_shutting_down(Manager *m) { +static bool manager_is_booting_or_shutting_down(Manager *m) { Unit *u; assert(m); @@ -2132,6 +2312,12 @@ bool manager_is_booting_or_shutting_down(Manager *m) { return false; } +bool manager_is_reloading_or_reexecuting(Manager *m) { + assert(m); + + return m->n_reloading != 0; +} + void manager_reset_failed(Manager *m) { Unit *u; Iterator i; @@ -2142,7 +2328,7 @@ void manager_reset_failed(Manager *m) { unit_reset_failed(u); } -bool manager_unit_pending_inactive(Manager *m, const char *name) { +bool manager_unit_inactive_or_pending(Manager *m, const char *name) { Unit *u; assert(m); @@ -2153,7 +2339,7 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) { if (!u) return true; - return unit_pending_inactive(u); + return unit_inactive_or_pending(u); } void manager_check_finished(Manager *m) { @@ -2162,8 +2348,13 @@ void manager_check_finished(Manager *m) { assert(m); - if (hashmap_size(m->jobs) > 0) + if (m->n_running_jobs == 0) + manager_unwatch_jobs_in_progress(m); + + if (hashmap_size(m->jobs) > 0) { + manager_jobs_in_progress_mod_timer(m); return; + } /* Notify Type=idle units that we are done now */ close_pipe(m->idle_pipe); @@ -2200,10 +2391,10 @@ void manager_check_finished(Manager *m) { "INITRD_USEC=%llu", (unsigned long long) initrd_usec, "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, "MESSAGE=Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec), - format_timespan(initrd, sizeof(initrd), initrd_usec), - format_timespan(userspace, sizeof(userspace), userspace_usec), - format_timespan(sum, sizeof(sum), total_usec), + format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), + format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC), + format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC), NULL); } else { kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic; @@ -2215,9 +2406,9 @@ void manager_check_finished(Manager *m) { "KERNEL_USEC=%llu", (unsigned long long) kernel_usec, "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, "MESSAGE=Startup finished in %s (kernel) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec), - format_timespan(userspace, sizeof(userspace), userspace_usec), - format_timespan(sum, sizeof(sum), total_usec), + format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), + format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC), NULL); } } else { @@ -2229,7 +2420,7 @@ void manager_check_finished(Manager *m) { MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, "MESSAGE=Startup finished in %s.", - format_timespan(sum, sizeof(sum), total_usec), + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC), NULL); } @@ -2237,7 +2428,7 @@ void manager_check_finished(Manager *m) { sd_notifyf(false, "READY=1\nSTATUS=Startup finished in %s.", - format_timespan(sum, sizeof(sum), total_usec)); + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)); } static int create_generator_dir(Manager *m, char **generator, const char *name) { @@ -2259,7 +2450,8 @@ static int create_generator_dir(Manager *m, char **generator, const char *name) r = mkdir_p_label(p, 0755); if (r < 0) { - log_error("Failed to create generator directory: %s", strerror(-r)); + log_error("Failed to create generator directory %s: %s", + p, strerror(-r)); free(p); return r; } @@ -2269,8 +2461,9 @@ static int create_generator_dir(Manager *m, char **generator, const char *name) return log_oom(); if (!mkdtemp(p)) { + log_error("Failed to create generator directory %s: %m", + p); free(p); - log_error("Failed to create generator directory: %m"); return -errno; } } @@ -2298,7 +2491,6 @@ void manager_run_generators(Manager *m) { DIR *d = NULL; const char *generator_path; const char *argv[5]; - mode_t u; int r; assert(m); @@ -2309,7 +2501,8 @@ void manager_run_generators(Manager *m) { if (errno == ENOENT) return; - log_error("Failed to enumerate generator directory: %m"); + log_error("Failed to enumerate generator directory %s: %m", + generator_path); return; } @@ -2331,9 +2524,9 @@ void manager_run_generators(Manager *m) { argv[3] = m->generator_unit_path_late; argv[4] = NULL; - u = umask(0022); - execute_directory(generator_path, d, (char**) argv); - umask(u); + RUN_WITH_UMASK(0022) { + execute_directory(generator_path, d, (char**) argv); + } trim_generator_dir(m, &m->generator_unit_path); trim_generator_dir(m, &m->generator_unit_path_early); @@ -2439,7 +2632,7 @@ void manager_set_show_status(Manager *m, bool b) { unlink("/run/systemd/show-status"); } -bool manager_get_show_status(Manager *m) { +static bool manager_get_show_status(Manager *m) { assert(m); if (m->running_as != SYSTEMD_SYSTEM) @@ -2454,6 +2647,25 @@ bool manager_get_show_status(Manager *m) { return plymouth_running(); } +void manager_status_printf(Manager *m, bool ephemeral, const char *status, const char *format, ...) { + va_list ap; + + if (!manager_get_show_status(m)) + return; + + /* XXX We should totally drop the check for ephemeral here + * and thus effectively make 'Type=idle' pointless. */ + if (ephemeral && m->n_on_console > 0) + return; + + if (!manager_is_booting_or_shutting_down(m)) + return; + + va_start(ap, format); + status_vprintf(status, true, ephemeral, format, ap); + va_end(ap); +} + void watch_init(Watch *w) { assert(w); diff --git a/src/core/manager.h b/src/core/manager.h index cc4edf8f1e..bf833540ae 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -61,7 +61,8 @@ enum WatchType { WATCH_UDEV, WATCH_DBUS_WATCH, WATCH_DBUS_TIMEOUT, - WATCH_TIME_CHANGE + WATCH_TIME_CHANGE, + WATCH_JOBS_IN_PROGRESS }; struct Watch { @@ -84,6 +85,7 @@ struct Watch { #include "set.h" #include "dbus.h" #include "path-lookup.h" +#include "execute.h" struct Manager { /* Note that the set of units we know of is allowed to be @@ -127,6 +129,7 @@ struct Manager { Watch notify_watch; Watch signal_watch; Watch time_change_watch; + Watch jobs_in_progress_watch; int epoll_fd; @@ -225,6 +228,11 @@ struct Manager { unsigned n_installed_jobs; unsigned n_failed_jobs; + /* Jobs in progress watching */ + unsigned n_running_jobs; + unsigned n_on_console; + unsigned jobs_in_progress_iteration; + /* Type=idle pipes */ int idle_pipe[2]; @@ -270,20 +278,20 @@ void manager_dispatch_bus_query_pid_done(Manager *m, const char *name, pid_t pid int manager_open_serialization(Manager *m, FILE **_f); -int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs); +int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root); int manager_deserialize(Manager *m, FILE *f, FDSet *fds); int manager_distribute_fds(Manager *m, FDSet *fds); int manager_reload(Manager *m); -bool manager_is_booting_or_shutting_down(Manager *m); +bool manager_is_reloading_or_reexecuting(Manager *m) _pure_; void manager_reset_failed(Manager *m); void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success); void manager_send_unit_plymouth(Manager *m, Unit *u); -bool manager_unit_pending_inactive(Manager *m, const char *name); +bool manager_unit_inactive_or_pending(Manager *m, const char *name); void manager_check_finished(Manager *m); @@ -293,6 +301,6 @@ void manager_undo_generators(Manager *m); void manager_recheck_journal(Manager *m); void manager_set_show_status(Manager *m, bool b); -bool manager_get_show_status(Manager *m); +void manager_status_printf(Manager *m, bool ephemeral, const char *status, const char *format, ...) _printf_attr_(4,5); void watch_init(Watch *w); diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 98614d0c3e..4629808a7a 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -41,6 +41,7 @@ #include "path-util.h" #include "missing.h" #include "virt.h" +#include "efivars.h" #ifndef TTY_GID #define TTY_GID 5 @@ -65,7 +66,7 @@ typedef struct MountPoint { /* The first three entries we might need before SELinux is up. The * fourth (securityfs) is needed by IMA to load a custom policy. The * other ones we can delay until SELinux and IMA are loaded. */ -#define N_EARLY_MOUNT 4 +#define N_EARLY_MOUNT 5 static const MountPoint mount_table[] = { { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, @@ -76,8 +77,8 @@ static const MountPoint mount_table[] = { NULL, MNT_FATAL|MNT_IN_CONTAINER }, { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, MNT_NONE }, - { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - is_efiboot, MNT_NONE }, + { "smackfs", "/sys/fs/smackfs", "smackfs", "smackfsdef=*", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, + NULL, MNT_NONE }, { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, NULL, MNT_FATAL|MNT_IN_CONTAINER }, { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, @@ -86,8 +87,18 @@ static const MountPoint mount_table[] = { NULL, MNT_FATAL|MNT_IN_CONTAINER }, { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, NULL, MNT_IN_CONTAINER }, +#ifdef HAVE_XATTR + { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_IN_CONTAINER }, +#endif { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, MNT_IN_CONTAINER }, + { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_NONE }, +#ifdef ENABLE_EFI + { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + is_efi_boot, MNT_NONE }, +#endif }; /* These are API file systems that might be mounted by other software, @@ -200,9 +211,9 @@ int mount_setup_early(void) { int mount_cgroup_controllers(char ***join_controllers) { int r; - FILE *f; char buf[LINE_MAX]; - Set *controllers; + _cleanup_set_free_free_ Set *controllers = NULL; + _cleanup_fclose_ FILE *f; /* Mount all available cgroup controllers that are built into the kernel. */ @@ -213,10 +224,8 @@ int mount_cgroup_controllers(char ***join_controllers) { } controllers = set_new(string_hash_func, string_compare_func); - if (!controllers) { - r = log_oom(); - goto finish; - } + if (!controllers) + return log_oom(); /* Ignore the header line */ (void) fgets(buf, sizeof(buf), f); @@ -231,8 +240,7 @@ int mount_cgroup_controllers(char ***join_controllers) { break; log_error("Failed to parse /proc/cgroups."); - r = -EIO; - goto finish; + return -EIO; } if (!enabled) { @@ -240,18 +248,22 @@ int mount_cgroup_controllers(char ***join_controllers) { continue; } - r = set_put(controllers, controller); + r = set_consume(controllers, controller); if (r < 0) { log_error("Failed to add controller to set."); - free(controller); - goto finish; + return r; } } for (;;) { - MountPoint p; - char *controller, *where, *options; + MountPoint p = { + .what = "cgroup", + .type = "cgroup", + .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV, + .mode = MNT_IN_CONTAINER, + }; char ***k = NULL; + _cleanup_free_ char *options = NULL, *controller; controller = set_steal_first(controllers); if (!controller) @@ -268,14 +280,13 @@ int mount_cgroup_controllers(char ***join_controllers) { for (i = *k, j = *k; *i; i++) { if (!streq(*i, controller)) { - char *t; + char _cleanup_free_ *t; t = set_remove(controllers, *i); if (!t) { free(*i); continue; } - free(t); } *(j++) = *i; @@ -284,76 +295,36 @@ int mount_cgroup_controllers(char ***join_controllers) { *j = NULL; options = strv_join(*k, ","); - if (!options) { - free(controller); - r = log_oom(); - goto finish; - } - + if (!options) + return log_oom(); } else { options = controller; controller = NULL; } - where = strappend("/sys/fs/cgroup/", options); - if (!where) { - free(options); - r = log_oom(); - goto finish; - } - - zero(p); - p.what = "cgroup"; - p.where = where; - p.type = "cgroup"; + p.where = strappenda("/sys/fs/cgroup/", options); p.options = options; - p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV; r = mount_one(&p, true); - free(controller); - free(where); - - if (r < 0) { - free(options); - goto finish; - } + if (r < 0) + return r; if (r > 0 && k && *k) { char **i; for (i = *k; *i; i++) { - char *t; - - t = strappend("/sys/fs/cgroup/", *i); - if (!t) { - r = log_oom(); - free(options); - goto finish; - } + char *t = strappenda("/sys/fs/cgroup/", *i); r = symlink(options, t); - free(t); - if (r < 0 && errno != EEXIST) { - log_error("Failed to create symlink: %m"); - r = -errno; - free(options); - goto finish; + log_error("Failed to create symlink %s: %m", t); + return -errno; } } } - - free(options); } - r = 0; - -finish: - set_free_free(controllers); - - fclose(f); - - return r; + return 0; } static int nftw_cb( @@ -415,7 +386,7 @@ int mount_setup(bool loaded_policy) { after_relabel = now(CLOCK_MONOTONIC); log_info("Relabelled /dev and /run in %s.", - format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel)); + format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0)); } /* Create a few default symlinks, which are normally created @@ -433,9 +404,14 @@ int mount_setup(bool loaded_policy) { if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0) log_warning("Failed to set up the root directory for shared mount propagation: %m"); - /* Create a few directories we always want around */ + /* Create a few directories we always want around, Note that + * sd_booted() checks for /run/systemd/system, so this mkdir + * really needs to stay for good, otherwise software that + * copied sd-daemon.c into their sources will misdetect + * systemd. */ mkdir_label("/run/systemd", 0755); mkdir_label("/run/systemd/system", 0755); + mkdir_label("/run/systemd/inaccessible", 0000); return 0; } diff --git a/src/core/mount.c b/src/core/mount.c index 5d2b010013..10073b50be 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -25,6 +25,7 @@ #include <sys/epoll.h> #include <signal.h> +#include "manager.h" #include "unit.h" #include "mount.h" #include "load-fragment.h" @@ -126,7 +127,7 @@ static void mount_done(Unit *u) { mount_parameters_done(&m->parameters_proc_self_mountinfo); mount_parameters_done(&m->parameters_fragment); - exec_context_done(&m->exec_context); + exec_context_done(&m->exec_context, manager_is_reloading_or_reexecuting(u->manager)); exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX); m->control_command = NULL; @@ -135,7 +136,7 @@ static void mount_done(Unit *u) { unit_unwatch_timer(u, &m->timer_watch); } -static MountParameters* get_mount_parameters_fragment(Mount *m) { +_pure_ static MountParameters* get_mount_parameters_fragment(Mount *m) { assert(m); if (m->from_fragment) @@ -144,7 +145,7 @@ static MountParameters* get_mount_parameters_fragment(Mount *m) { return NULL; } -static MountParameters* get_mount_parameters(Mount *m) { +_pure_ static MountParameters* get_mount_parameters(Mount *m) { assert(m); if (m->from_proc_self_mountinfo) @@ -292,7 +293,7 @@ static int mount_add_requires_mounts_links(Mount *m) { } static char* mount_test_option(const char *haystack, const char *needle) { - struct mntent me; + struct mntent me = { .mnt_opts = (char*) haystack }; assert(needle); @@ -302,9 +303,6 @@ static char* mount_test_option(const char *haystack, const char *needle) { if (!haystack) return NULL; - zero(me); - me.mnt_opts = (char*) haystack; - return hasmntopt(&me, needle); } @@ -329,6 +327,12 @@ static bool mount_is_bind(MountParameters *p) { if (p->fstype && streq(p->fstype, "bind")) return true; + if (mount_test_option(p->options, "rbind")) + return true; + + if (p->fstype && streq(p->fstype, "rbind")) + return true; + return false; } @@ -432,31 +436,49 @@ static int mount_add_quota_links(Mount *m) { } static int mount_add_default_dependencies(Mount *m) { - int r; + const char *after, *after2, *online; MountParameters *p; - const char *after; + int r; assert(m); if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM) return 0; - p = get_mount_parameters_fragment(m); + p = get_mount_parameters(m); + if (!p) return 0; if (path_equal(m->where, "/")) return 0; - if (mount_is_network(p)) + if (mount_is_network(p)) { after = SPECIAL_REMOTE_FS_PRE_TARGET; - else + after2 = SPECIAL_NETWORK_TARGET; + online = SPECIAL_NETWORK_ONLINE_TARGET; + } else { after = SPECIAL_LOCAL_FS_PRE_TARGET; + after2 = NULL; + online = NULL; + } - r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, after, NULL, true); + r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true); if (r < 0) return r; + if (after2) { + r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after2, NULL, true); + if (r < 0) + return r; + } + + if (online) { + r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, online, NULL, true); + if (r < 0) + return r; + } + r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); if (r < 0) return r; @@ -495,7 +517,7 @@ static int mount_fix_timeouts(Mount *m) { if (!t) return -ENOMEM; - r = parse_usec(t, &u); + r = parse_sec(t, &u); free(t); if (r < 0) { @@ -717,9 +739,9 @@ static void mount_set_state(Mount *m, MountState state) { state == MOUNT_UNMOUNTING_SIGTERM || state == MOUNT_UNMOUNTING_SIGKILL || state == MOUNT_FAILED) { - if (state != old_state) - mount_notify_automount(m, -ENODEV); - } + if (state != old_state) + mount_notify_automount(m, -ENODEV); + } if (state != old_state) log_debug_unit(UNIT(m)->id, @@ -863,6 +885,7 @@ static void mount_enter_dead(Mount *m, MountResult f) { if (f != MOUNT_SUCCESS) m->result = f; + exec_context_tmp_dirs_done(&m->exec_context); mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); } @@ -877,56 +900,23 @@ static void mount_enter_mounted(Mount *m, MountResult f) { static void mount_enter_signal(Mount *m, MountState state, MountResult f) { int r; - Set *pid_set = NULL; - bool wait_for_exit = false; assert(m); if (f != MOUNT_SUCCESS) m->result = f; - if (m->kill_context.kill_mode != KILL_NONE) { - int sig = (state == MOUNT_MOUNTING_SIGTERM || - state == MOUNT_UNMOUNTING_SIGTERM || - state == MOUNT_REMOUNTING_SIGTERM) ? m->kill_context.kill_signal : SIGKILL; - - if (m->control_pid > 0) { - if (kill_and_sigcont(m->control_pid, sig) < 0 && errno != ESRCH) - - log_warning_unit(UNIT(m)->id, - "Failed to kill control process %li: %m", - (long) m->control_pid); - else - wait_for_exit = true; - } - - if (m->kill_context.kill_mode == KILL_CONTROL_GROUP) { - - if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) { - r = -ENOMEM; - goto fail; - } - - /* Exclude the control pid from being killed via the cgroup */ - if (m->control_pid > 0) - if ((r = set_put(pid_set, LONG_TO_PTR(m->control_pid))) < 0) - goto fail; - - r = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, sig, true, false, pid_set, NULL); - if (r < 0) { - if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) - log_warning_unit(UNIT(m)->id, - "Failed to kill control group: %s", - strerror(-r)); - } else if (r > 0) - wait_for_exit = true; - - set_free(pid_set); - pid_set = NULL; - } - } + r = unit_kill_context( + UNIT(m), + &m->kill_context, + state != MOUNT_MOUNTING_SIGTERM && state != MOUNT_UNMOUNTING_SIGTERM && state != MOUNT_REMOUNTING_SIGTERM, + -1, + m->control_pid, + false); + if (r < 0) + goto fail; - if (wait_for_exit) { + if (r > 0) { r = unit_watch_timer(UNIT(m), CLOCK_MONOTONIC, true, m->timeout_usec, &m->timer_watch); if (r < 0) goto fail; @@ -947,19 +937,20 @@ fail: mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); else mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); - - if (pid_set) - set_free(pid_set); } void warn_if_dir_nonempty(const char *unit, const char* where) { + assert(unit); + assert(where); + if (dir_is_empty(where) > 0) return; - log_struct(LOG_NOTICE, + + log_struct_unit(LOG_NOTICE, + unit, "MESSAGE=%s: Directory %s to mount over is not empty, mounting anyway.", unit, where, "WHERE=%s", where, - "_SYSTEMD_UNIT=%s", unit, MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), NULL); } @@ -1188,6 +1179,8 @@ static int mount_serialize(Unit *u, FILE *f, FDSet *fds) { if (m->control_command_id >= 0) unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id)); + exec_context_serialize(&m->exec_context, UNIT(m), f); + return 0; } @@ -1244,7 +1237,22 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F m->control_command_id = id; m->control_command = m->exec_command + id; } + } else if (streq(key, "tmp-dir")) { + char *t; + + t = strdup(value); + if (!t) + return log_oom(); + + m->exec_context.tmp_dir = t; + } else if (streq(key, "var-tmp-dir")) { + char *t; + t = strdup(value); + if (!t) + return log_oom(); + + m->exec_context.var_tmp_dir = t; } else log_debug_unit(UNIT(m)->id, "Unknown serialization key '%s'", key); @@ -1252,19 +1260,19 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F return 0; } -static UnitActiveState mount_active_state(Unit *u) { +_pure_ static UnitActiveState mount_active_state(Unit *u) { assert(u); return state_translation_table[MOUNT(u)->state]; } -static const char *mount_sub_state_to_string(Unit *u) { +_pure_ static const char *mount_sub_state_to_string(Unit *u) { assert(u); return mount_state_to_string(MOUNT(u)->state); } -static bool mount_check_gc(Unit *u) { +_pure_ static bool mount_check_gc(Unit *u) { Mount *m = MOUNT(u); assert(m); @@ -1520,6 +1528,20 @@ static int mount_add_one( goto fail; } + u->source_path = strdup("/proc/self/mountinfo"); + if (!u->source_path) { + r = -ENOMEM; + goto fail; + } + + r = unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_LOCAL_FS_TARGET, NULL, true); + if (r < 0) + goto fail; + + r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); + if (r < 0) + goto fail; + unit_add_to_load_queue(u); } else { delete = false; @@ -1681,25 +1703,27 @@ static void mount_shutdown(Manager *m) { static int mount_enumerate(Manager *m) { int r; - struct epoll_event ev; assert(m); if (!m->proc_self_mountinfo) { - if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"))) + struct epoll_event ev = { + .events = EPOLLPRI, + .data.ptr = &m->mount_watch, + }; + + m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); + if (!m->proc_self_mountinfo) return -errno; m->mount_watch.type = WATCH_MOUNT; m->mount_watch.fd = fileno(m->proc_self_mountinfo); - zero(ev); - ev.events = EPOLLPRI; - ev.data.ptr = &m->mount_watch; - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0) return -errno; } - if ((r = mount_load_proc_self_mountinfo(m, false)) < 0) + r = mount_load_proc_self_mountinfo(m, false); + if (r < 0) goto fail; return 0; @@ -1800,53 +1824,7 @@ static void mount_reset_failed(Unit *u) { } static int mount_kill(Unit *u, KillWho who, int signo, DBusError *error) { - Mount *m = MOUNT(u); - int r = 0; - Set *pid_set = NULL; - - assert(m); - - if (who == KILL_MAIN) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Mount units have no main processes"); - return -ESRCH; - } - - if (m->control_pid <= 0 && who == KILL_CONTROL) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - return -ESRCH; - } - - if (who == KILL_CONTROL || who == KILL_ALL) - if (m->control_pid > 0) - if (kill(m->control_pid, signo) < 0) - r = -errno; - - if (who == KILL_ALL) { - int q; - - pid_set = set_new(trivial_hash_func, trivial_compare_func); - if (!pid_set) - return -ENOMEM; - - /* Exclude the control pid from being killed via the cgroup */ - if (m->control_pid > 0) { - q = set_put(pid_set, LONG_TO_PTR(m->control_pid)); - if (q < 0) { - r = q; - goto finish; - } - } - - q = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, signo, false, false, pid_set, NULL); - if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) - r = q; - } - -finish: - if (pid_set) - set_free(pid_set); - - return r; + return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error); } static const char* const mount_state_table[_MOUNT_STATE_MAX] = { @@ -1888,13 +1866,15 @@ DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult); const UnitVTable mount_vtable = { .object_size = sizeof(Mount), - .exec_context_offset = offsetof(Mount, exec_context), .sections = "Unit\0" "Mount\0" "Install\0", + .exec_context_offset = offsetof(Mount, exec_context), + .exec_section = "Mount", + .no_alias = true, .no_instances = true, diff --git a/src/core/mount.h b/src/core/mount.h index 30c6d9b249..bcc10ee0d4 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -111,13 +111,13 @@ extern const UnitVTable mount_vtable; void mount_fd_event(Manager *m, int events); -const char* mount_state_to_string(MountState i); -MountState mount_state_from_string(const char *s); +const char* mount_state_to_string(MountState i) _const_; +MountState mount_state_from_string(const char *s) _pure_; -const char* mount_exec_command_to_string(MountExecCommand i); -MountExecCommand mount_exec_command_from_string(const char *s); +const char* mount_exec_command_to_string(MountExecCommand i) _const_; +MountExecCommand mount_exec_command_from_string(const char *s) _pure_; -const char* mount_result_to_string(MountResult i); -MountResult mount_result_from_string(const char *s); +const char* mount_result_to_string(MountResult i) _const_; +MountResult mount_result_from_string(const char *s) _pure_; void warn_if_dir_nonempty(const char *unit, const char* where); diff --git a/src/core/namespace.c b/src/core/namespace.c index ba18ddc5b0..7e33d84156 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -36,23 +36,24 @@ #include "path-util.h" #include "namespace.h" #include "missing.h" +#include "execute.h" -typedef enum PathMode { +typedef enum MountMode { /* This is ordered by priority! */ INACCESSIBLE, READONLY, PRIVATE_TMP, PRIVATE_VAR_TMP, READWRITE -} PathMode; +} MountMode; -typedef struct Path { +typedef struct BindMount { const char *path; - PathMode mode; + MountMode mode; bool done; -} Path; +} BindMount; -static int append_paths(Path **p, char **strv, PathMode mode) { +static int append_mounts(BindMount **p, char **strv, MountMode mode) { char **i; STRV_FOREACH(i, strv) { @@ -68,8 +69,8 @@ static int append_paths(Path **p, char **strv, PathMode mode) { return 0; } -static int path_compare(const void *a, const void *b) { - const Path *p = a, *q = b; +static int mount_path_compare(const void *a, const void *b) { + const BindMount *p = a, *q = b; if (path_equal(p->path, q->path)) { @@ -93,14 +94,13 @@ static int path_compare(const void *a, const void *b) { return 0; } -static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) { - Path *f, *t, *previous; +static void drop_duplicates(BindMount *m, unsigned *n) { + BindMount *f, *t, *previous; - assert(p); + assert(m); assert(n); - assert(need_inaccessible); - for (f = p, t = p, previous = NULL; f < p+*n; f++) { + for (f = m, t = m, previous = NULL; f < m+*n; f++) { /* The first one wins */ if (previous && path_equal(f->path, previous->path)) @@ -109,37 +109,33 @@ static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) { t->path = f->path; t->mode = f->mode; - if (t->mode == INACCESSIBLE) - *need_inaccessible = true; - previous = t; t++; } - *n = t - p; + *n = t - m; } static int apply_mount( - Path *p, + BindMount *m, const char *tmp_dir, - const char *var_tmp_dir, - const char *inaccessible_dir) { + const char *var_tmp_dir) { const char *what; int r; - assert(p); + assert(m); - switch (p->mode) { + switch (m->mode) { case INACCESSIBLE: - what = inaccessible_dir; + what = "/run/systemd/inaccessible"; break; case READONLY: case READWRITE: - what = p->path; + what = m->path; break; case PRIVATE_TMP: @@ -156,155 +152,115 @@ static int apply_mount( assert(what); - r = mount(what, p->path, NULL, MS_BIND|MS_REC, NULL); + r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL); if (r >= 0) - log_debug("Successfully mounted %s to %s", what, p->path); + log_debug("Successfully mounted %s to %s", what, m->path); return r; } -static int make_read_only(Path *p) { +static int make_read_only(BindMount *m) { int r; - assert(p); + assert(m); - if (p->mode != INACCESSIBLE && p->mode != READONLY) + if (m->mode != INACCESSIBLE && m->mode != READONLY) return 0; - r = mount(NULL, p->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL); + r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL); if (r < 0) return -errno; return 0; } -int setup_namespace( - char **writable, - char **readable, - char **inaccessible, - bool private_tmp, - unsigned long flags) { - - char - tmp_dir[] = "/tmp/systemd-private-XXXXXX", - var_tmp_dir[] = "/var/tmp/systemd-private-XXXXXX", - inaccessible_dir[] = "/tmp/systemd-inaccessible-XXXXXX"; - - Path *paths, *p; - unsigned n; - bool need_inaccessible = false; - bool remove_tmp = false, remove_var_tmp = false, remove_inaccessible = false; - int r; - - if (!flags) - flags = MS_SHARED; - - n = - strv_length(writable) + - strv_length(readable) + - strv_length(inaccessible) + - (private_tmp ? 2 : 0); +int setup_tmpdirs(char **tmp_dir, + char **var_tmp_dir) { + int r = 0; + char tmp_dir_template[] = "/tmp/systemd-private-XXXXXX", + var_tmp_dir_template[] = "/var/tmp/systemd-private-XXXXXX"; - p = paths = alloca(sizeof(Path) * n); - if ((r = append_paths(&p, writable, READWRITE)) < 0 || - (r = append_paths(&p, readable, READONLY)) < 0 || - (r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0) - goto fail; - - if (private_tmp) { - p->path = "/tmp"; - p->mode = PRIVATE_TMP; - p++; - - p->path = "/var/tmp"; - p->mode = PRIVATE_VAR_TMP; - p++; - } + assert(tmp_dir); + assert(var_tmp_dir); - assert(paths + n == p); + r = create_tmp_dir(tmp_dir_template, tmp_dir); + if (r < 0) + return r; - qsort(paths, n, sizeof(Path), path_compare); - drop_duplicates(paths, &n, &need_inaccessible); + r = create_tmp_dir(var_tmp_dir_template, var_tmp_dir); + if (r == 0) + return 0; - if (need_inaccessible) { - mode_t u; - char *d; + /* failure */ + rmdir(*tmp_dir); + rmdir(tmp_dir_template); + free(*tmp_dir); + *tmp_dir = NULL; - u = umask(0777); - d = mkdtemp(inaccessible_dir); - umask(u); + return r; +} - if (!d) { - r = -errno; - goto fail; - } +int setup_namespace(char** read_write_dirs, + char** read_only_dirs, + char** inaccessible_dirs, + char* tmp_dir, + char* var_tmp_dir, + bool private_tmp, + unsigned mount_flags) { + + unsigned n = strv_length(read_write_dirs) + + strv_length(read_only_dirs) + + strv_length(inaccessible_dirs) + + (private_tmp ? 2 : 0); + BindMount *m, *mounts; + int r = 0; + + if (!mount_flags) + mount_flags = MS_SHARED; + + if (unshare(CLONE_NEWNS) < 0) + return -errno; - remove_inaccessible = true; - } + m = mounts = (BindMount *) alloca(n * sizeof(BindMount)); + if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 || + (r = append_mounts(&m, read_only_dirs, READONLY)) < 0 || + (r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0) + return r; if (private_tmp) { - mode_t u; - char *d; - - u = umask(0000); - d = mkdtemp(tmp_dir); - umask(u); - - if (!d) { - r = -errno; - goto fail; - } + m->path = "/tmp"; + m->mode = PRIVATE_TMP; + m++; - remove_tmp = true; - - u = umask(0000); - d = mkdtemp(var_tmp_dir); - umask(u); - - if (!d) { - r = -errno; - goto fail; - } - - remove_var_tmp = true; - - if (chmod(tmp_dir, 0777 + S_ISVTX) < 0) { - r = -errno; - goto fail; - } - - if (chmod(var_tmp_dir, 0777 + S_ISVTX) < 0) { - r = -errno; - goto fail; - } + m->path = "/var/tmp"; + m->mode = PRIVATE_VAR_TMP; + m++; } - if (unshare(CLONE_NEWNS) < 0) { - r = -errno; - goto fail; - } + assert(mounts + n == m); + + qsort(mounts, n, sizeof(BindMount), mount_path_compare); + drop_duplicates(mounts, &n); /* Remount / as SLAVE so that nothing now mounted in the namespace shows up in the parent */ - if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) { - r = -errno; - goto fail; - } + if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) + return -errno; - for (p = paths; p < paths + n; p++) { - r = apply_mount(p, tmp_dir, var_tmp_dir, inaccessible_dir); + for (m = mounts; m < mounts + n; ++m) { + r = apply_mount(m, tmp_dir, var_tmp_dir); if (r < 0) goto undo_mounts; } - for (p = paths; p < paths + n; p++) { - r = make_read_only(p); + for (m = mounts; m < mounts + n; ++m) { + r = make_read_only(m); if (r < 0) goto undo_mounts; } /* Remount / as the desired mode */ - if (mount(NULL, "/", NULL, flags|MS_REC, NULL) < 0) { + if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) { r = -errno; goto undo_mounts; } @@ -312,19 +268,10 @@ int setup_namespace( return 0; undo_mounts: - for (p = paths; p < paths + n; p++) - if (p->done) - umount2(p->path, MNT_DETACH); - -fail: - if (remove_inaccessible) - rmdir(inaccessible_dir); - - if (remove_tmp) - rmdir(tmp_dir); - - if (remove_var_tmp) - rmdir(var_tmp_dir); + for (m = mounts; m < mounts + n; ++m) { + if (m->done) + umount2(m->path, MNT_DETACH); + } return r; } diff --git a/src/core/namespace.h b/src/core/namespace.h index 5d72ed91fb..ddb579468d 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -23,9 +23,11 @@ #include <stdbool.h> -int setup_namespace( - char **writable, - char **readable, - char **inaccessible, - bool private_tmp, - unsigned long flags); +int setup_tmpdirs(char **tmp_dir, char **var_tmp_dir); +int setup_namespace(char **read_write_dirs, + char **read_only_dirs, + char **inaccessible_dirs, + char *tmp_dir, + char *var_tmp_dir, + bool private_tmp, + unsigned mount_flags); diff --git a/src/core/path.c b/src/core/path.c index 767620ba75..8a09deb4ff 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -31,8 +31,9 @@ #include "mkdir.h" #include "dbus-path.h" #include "special.h" -#include "bus-errors.h" +#include "dbus-common.h" #include "path-util.h" +#include "macro.h" static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = { [PATH_DEAD] = UNIT_INACTIVE, @@ -52,7 +53,7 @@ int path_spec_watch(PathSpec *s, Unit *u) { }; bool exists = false; - char *k, *slash; + char *slash, *oldslash = NULL; int r; assert(u); @@ -60,47 +61,85 @@ int path_spec_watch(PathSpec *s, Unit *u) { path_spec_unwatch(s, u); - if (!(k = strdup(s->path))) - return -ENOMEM; - - if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) { + s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (s->inotify_fd < 0) { r = -errno; goto fail; } - if (unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch) < 0) { - r = -errno; + r = unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch); + if (r < 0) goto fail; - } - s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type]); - if (s->primary_wd >= 0) - exists = true; + /* This assumes the path was passed through path_kill_slashes()! */ - do { + for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) { + char *cut = NULL; int flags; + char tmp; + + if (slash) { + cut = slash + (slash == s->path); + tmp = *cut; + *cut = '\0'; + + flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; + } else + flags = flags_table[s->type]; + + r = inotify_add_watch(s->inotify_fd, s->path, flags); + if (r < 0) { + if (errno == EACCES || errno == ENOENT) { + if (cut) + *cut = tmp; + break; + } + + log_warning("Failed to add watch on %s: %m", s->path); + r = -errno; + if (cut) + *cut = tmp; + goto fail; + } else { + exists = true; - /* This assumes the path was passed through path_kill_slashes()! */ - slash = strrchr(k, '/'); - if (!slash) - break; + /* Path exists, we don't need to watch parent + too closely. */ + if (oldslash) { + char *cut2 = oldslash + (oldslash == s->path); + char tmp2 = *cut2; + *cut2 = '\0'; + + inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF); + /* Error is ignored, the worst can happen is + we get spurious events. */ - /* Trim the path at the last slash. Keep the slash if it's the root dir. */ - slash[slash == k] = 0; + *cut2 = tmp2; + } + } - flags = IN_MOVE_SELF; - if (!exists) - flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; + if (cut) + *cut = tmp; - if (inotify_add_watch(s->inotify_fd, k, flags) >= 0) - exists = true; - } while (slash != k); + if (slash) + oldslash = slash; + else { + /* whole path has been iterated over */ + s->primary_wd = r; + break; + } + } + + if (!exists) { + log_error("Failed to add watch on any of the components of %s: %m", + s->path); + r = -errno; /* either EACCESS or ENOENT */ + goto fail; + } return 0; fail: - free(k); - path_spec_unwatch(s, u); return r; } @@ -117,7 +156,7 @@ void path_spec_unwatch(PathSpec *s, Unit *u) { } int path_spec_fd_event(PathSpec *s, uint32_t events) { - uint8_t *buf = NULL; + _cleanup_free_ uint8_t *buf = NULL; struct inotify_event *e; ssize_t k; int l; @@ -125,30 +164,24 @@ int path_spec_fd_event(PathSpec *s, uint32_t events) { if (events != EPOLLIN) { log_error("Got invalid poll event on inotify."); - r = -EINVAL; - goto out; + return -EINVAL; } if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) { log_error("FIONREAD failed: %m"); - r = -errno; - goto out; + return -errno; } assert(l > 0); buf = malloc(l); - if (!buf) { - log_error("Failed to allocate buffer: %m"); - r = -errno; - goto out; - } + if (!buf) + return log_oom(); k = read(s->inotify_fd, buf, l); if (k < 0) { log_error("Failed to read inotify event: %m"); - r = -errno; - goto out; + return -errno; } e = (struct inotify_event*) buf; @@ -166,8 +199,7 @@ int path_spec_fd_event(PathSpec *s, uint32_t events) { e = (struct inotify_event*) ((uint8_t*) e + step); k -= step; } -out: - free(buf); + return r; } @@ -248,22 +280,27 @@ static void path_init(Unit *u) { p->directory_mode = 0755; } -static void path_done(Unit *u) { - Path *p = PATH(u); +void path_free_specs(Path *p) { PathSpec *s; assert(p); - unit_ref_unset(&p->unit); - while ((s = p->specs)) { - path_spec_unwatch(s, u); + path_spec_unwatch(s, UNIT(p)); LIST_REMOVE(PathSpec, spec, p->specs, s); path_spec_done(s); free(s); } } +static void path_done(Unit *u) { + Path *p = PATH(u); + + assert(p); + + path_free_specs(p); +} + int path_add_one_mount_link(Path *p, Mount *m) { PathSpec *s; int r; @@ -276,11 +313,12 @@ int path_add_one_mount_link(Path *p, Mount *m) { return 0; LIST_FOREACH(spec, s, p->specs) { - if (!path_spec_startswith(s, m->where)) continue; - if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0) + r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, + UNIT(m), true); + if (r < 0) return r; } @@ -293,9 +331,11 @@ static int path_add_mount_links(Path *p) { assert(p); - LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) - if ((r = path_add_one_mount_link(p, MOUNT(other))) < 0) + LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) { + r = path_add_one_mount_link(p, MOUNT(other)); + if (r < 0) return r; + } return 0; } @@ -320,15 +360,20 @@ static int path_add_default_dependencies(Path *p) { assert(p); - if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) { - if ((r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0) - return r; + r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, + SPECIAL_PATHS_TARGET, NULL, true); + if (r < 0) + return r; - if ((r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0) + if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) { + r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, + SPECIAL_SYSINIT_TARGET, NULL, true); + if (r < 0) return r; } - return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); + return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, NULL, true); } static int path_load(Unit *u) { @@ -338,31 +383,33 @@ static int path_load(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); - if ((r = unit_load_fragment_and_dropin(u)) < 0) + r = unit_load_fragment_and_dropin(u); + if (r < 0) return r; if (u->load_state == UNIT_LOADED) { - if (!UNIT_DEREF(p->unit)) { + if (set_isempty(u->dependencies[UNIT_TRIGGERS])) { Unit *x; r = unit_load_related_unit(u, ".service", &x); if (r < 0) return r; - unit_ref_set(&p->unit, x); + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true); + if (r < 0) + return r; } - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(p->unit), true); + r = path_add_mount_links(p); if (r < 0) return r; - if ((r = path_add_mount_links(p)) < 0) - return r; - - if (UNIT(p)->default_dependencies) - if ((r = path_add_default_dependencies(p)) < 0) + if (UNIT(p)->default_dependencies) { + r = path_add_default_dependencies(p); + if (r < 0) return r; + } } return path_verify(p); @@ -370,11 +417,14 @@ static int path_load(Unit *u) { static void path_dump(Unit *u, FILE *f, const char *prefix) { Path *p = PATH(u); + Unit *trigger; PathSpec *s; assert(p); assert(f); + trigger = UNIT_TRIGGER(u); + fprintf(f, "%sPath State: %s\n" "%sResult: %s\n" @@ -383,7 +433,7 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) { "%sDirectoryMode: %04o\n", prefix, path_state_to_string(p->state), prefix, path_result_to_string(p->result), - prefix, UNIT_DEREF(p->unit)->id, + prefix, trigger ? trigger->id : "n/a", prefix, yes_no(p->make_directory), prefix, p->directory_mode); @@ -406,9 +456,11 @@ static int path_watch(Path *p) { assert(p); - LIST_FOREACH(spec, s, p->specs) - if ((r = path_spec_watch(s, UNIT(p))) < 0) + LIST_FOREACH(spec, s, p->specs) { + r = path_spec_watch(s, UNIT(p)); + if (r < 0) return r; + } return 0; } @@ -463,32 +515,35 @@ static void path_enter_dead(Path *p, PathResult f) { } static void path_enter_running(Path *p) { + _cleanup_dbus_error_free_ DBusError error; int r; - DBusError error; assert(p); + dbus_error_init(&error); /* Don't start job if we are supposed to go down */ - if (UNIT(p)->job && UNIT(p)->job->type == JOB_STOP) + if (unit_stop_pending(UNIT(p))) return; - if ((r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_DEREF(p->unit), JOB_REPLACE, true, &error, NULL)) < 0) + r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)), + JOB_REPLACE, true, &error, NULL); + if (r < 0) goto fail; p->inotify_triggered = false; - if ((r = path_watch(p)) < 0) + r = path_watch(p); + if (r < 0) goto fail; path_set_state(p, PATH_RUNNING); return; fail: - log_warning("%s failed to queue unit startup job: %s", UNIT(p)->id, bus_error(&error, r)); + log_warning("%s failed to queue unit startup job: %s", + UNIT(p)->id, bus_error(&error, r)); path_enter_dead(p, PATH_FAILURE_RESOURCES); - - dbus_error_free(&error); } static bool path_check_good(Path *p, bool initial) { @@ -517,7 +572,8 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) { return; } - if ((r = path_watch(p)) < 0) + r = path_watch(p); + if (r < 0) goto fail; /* Hmm, so now we have created inotify watches, but the file @@ -535,7 +591,8 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) { return; fail: - log_warning("%s failed to enter waiting state: %s", UNIT(p)->id, strerror(-r)); + log_warning("%s failed to enter waiting state: %s", + UNIT(p)->id, strerror(-r)); path_enter_dead(p, PATH_FAILURE_RESOURCES); } @@ -557,7 +614,7 @@ static int path_start(Unit *u) { assert(p); assert(p->state == PATH_DEAD || p->state == PATH_FAILED); - if (UNIT_DEREF(p->unit)->load_state != UNIT_LOADED) + if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED) return -ENOENT; path_mkdir(p); @@ -602,7 +659,8 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD if (streq(key, "state")) { PathState state; - if ((state = path_state_from_string(value)) < 0) + state = path_state_from_string(value); + if (state < 0) log_debug("Failed to parse state value %s", value); else p->deserialized_state = state; @@ -622,13 +680,13 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD return 0; } -static UnitActiveState path_active_state(Unit *u) { +_pure_ static UnitActiveState path_active_state(Unit *u) { assert(u); return state_translation_table[PATH(u)->state]; } -static const char *path_sub_state_to_string(Unit *u) { +_pure_ static const char *path_sub_state_to_string(Unit *u) { assert(u); return path_state_to_string(PATH(u)->state); @@ -677,32 +735,28 @@ fail: path_enter_dead(p, PATH_FAILURE_RESOURCES); } -void path_unit_notify(Unit *u, UnitActiveState new_state) { - Iterator i; - Unit *k; - - if (u->type == UNIT_PATH) - return; - - SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) { - Path *p; +static void path_trigger_notify(Unit *u, Unit *other) { + Path *p = PATH(u); - if (k->type != UNIT_PATH) - continue; + assert(u); + assert(other); - if (k->load_state != UNIT_LOADED) - continue; + /* Invoked whenever the unit we trigger changes state or gains + * or loses a job */ - p = PATH(k); + if (other->load_state != UNIT_LOADED) + return; - if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) { - log_debug("%s got notified about unit deactivation.", UNIT(p)->id); + if (p->state == PATH_RUNNING && + UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) { + log_debug_unit(UNIT(p)->id, + "%s got notified about unit deactivation.", + UNIT(p)->id); - /* Hmm, so inotify was triggered since the - * last activation, so I guess we need to - * recheck what is going on. */ - path_enter_waiting(p, false, p->inotify_triggered); - } + /* Hmm, so inotify was triggered since the + * last activation, so I guess we need to + * recheck what is going on. */ + path_enter_waiting(p, false, p->inotify_triggered); } } @@ -769,6 +823,8 @@ const UnitVTable path_vtable = { .fd_event = path_fd_event, + .trigger_notify = path_trigger_notify, + .reset_failed = path_reset_failed, .bus_interface = "org.freedesktop.systemd1.Path", diff --git a/src/core/path.h b/src/core/path.h index 77926888ae..6adab5897d 100644 --- a/src/core/path.h +++ b/src/core/path.h @@ -80,8 +80,6 @@ struct Path { LIST_HEAD(PathSpec, specs); - UnitRef unit; - PathState state, deserialized_state; bool inotify_triggered; @@ -92,19 +90,19 @@ struct Path { PathResult result; }; -void path_unit_notify(Unit *u, UnitActiveState new_state); - /* Called from the mount code figure out if a mount is a dependency of * any of the paths of this path object */ int path_add_one_mount_link(Path *p, Mount *m); +void path_free_specs(Path *p); + extern const UnitVTable path_vtable; -const char* path_state_to_string(PathState i); -PathState path_state_from_string(const char *s); +const char* path_state_to_string(PathState i) _const_; +PathState path_state_from_string(const char *s) _pure_; -const char* path_type_to_string(PathType i); -PathType path_type_from_string(const char *s); +const char* path_type_to_string(PathType i) _const_; +PathType path_type_from_string(const char *s) _pure_; -const char* path_result_to_string(PathResult i); -PathResult path_result_from_string(const char *s); +const char* path_result_to_string(PathResult i) _const_; +PathResult path_result_from_string(const char *s) _pure_; diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index 6dfe8b45f3..426aed07d2 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -104,8 +104,6 @@ static int bus_get_selinux_security_context( *scon = b; - log_debug("GetConnectionSELinuxSecurityContext %s (pid %ld)", *scon, (long) bus_get_unix_process_id(connection, name, error)); - return 0; } @@ -134,7 +132,7 @@ static int bus_get_audit_data( if (r < 0) return r; - r = get_process_cmdline(pid, LINE_MAX, true, &audit->cmdline); + r = get_process_cmdline(pid, 0, true, &audit->cmdline); if (r < 0) return r; @@ -176,20 +174,25 @@ static int audit_callback( user_avc's into the /var/log/audit/audit.log, otherwise they will be sent to syslog. */ -static int log_callback(int type, const char *fmt, ...) { +_printf_attr_(2, 3) static int log_callback(int type, const char *fmt, ...) { va_list ap; va_start(ap, fmt); #ifdef HAVE_AUDIT if (get_audit_fd() >= 0) { - char buf[LINE_MAX]; + _cleanup_free_ char *buf = NULL; + int r; - vsnprintf(buf, sizeof(buf), fmt, ap); - audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0); + r = vasprintf(&buf, fmt, ap); va_end(ap); - return 0; + if (r >= 0) { + audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0); + return 0; + } + + va_start(ap, fmt); } #endif log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap); @@ -258,7 +261,7 @@ static int get_audit_data( const char *sender; int r, fd; struct ucred ucred; - socklen_t len; + socklen_t len = sizeof(ucred); sender = dbus_message_get_sender(message); if (sender) @@ -280,7 +283,7 @@ static int get_audit_data( if (r < 0) return r; - r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline); + r = get_process_cmdline(ucred.pid, 0, true, &audit->cmdline); if (r < 0) return r; @@ -308,8 +311,6 @@ static int get_calling_context( */ sender = dbus_message_get_sender(message); if (sender) { - log_error("SELinux Got Sender %s", sender); - r = bus_get_selinux_security_context(connection, sender, scon, error); if (r >= 0) return r; @@ -318,7 +319,6 @@ static int get_calling_context( return r; } - log_debug("SELinux No Sender"); if (!dbus_connection_get_unix_fd(connection, &fd)) { log_error("bus_connection_get_unix_fd failed %m"); return -EINVAL; @@ -363,8 +363,6 @@ int selinux_access_check( if (r < 0) return r; - log_debug("SELinux access check for path=%s permission=%s", strna(path), permission); - audit.uid = audit.loginuid = (uid_t) -1; audit.gid = (gid_t) -1; audit.cmdline = NULL; diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c index e9c0de92f1..7a32ed59a0 100644 --- a/src/core/selinux-setup.c +++ b/src/core/selinux-setup.c @@ -58,9 +58,6 @@ int selinux_setup(bool *loaded_policy) { cb.func_log = null_log; selinux_set_callback(SELINUX_CB_LOG, cb); - /* Make sure getcon() works, which needs /proc and /sys */ - mount_setup_early(); - /* Already initialized by somebody else? */ r = getcon_raw(&con); if (r == 0) { @@ -104,7 +101,7 @@ int selinux_setup(bool *loaded_policy) { after_load = now(CLOCK_MONOTONIC); log_info("Successfully loaded SELinux policy in %s.", - format_timespan(timespan, sizeof(timespan), after_load - before_load)); + format_timespan(timespan, sizeof(timespan), after_load - before_load, 0)); *loaded_policy = true; diff --git a/src/core/service.c b/src/core/service.c index 2a4e691e78..3617c24711 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -36,12 +36,14 @@ #include "unit-printf.h" #include "dbus-service.h" #include "special.h" -#include "bus-errors.h" +#include "dbus-common.h" #include "exit-status.h" #include "def.h" #include "path-util.h" #include "util.h" #include "utf8.h" +#include "env-util.h" +#include "fileio.h" #ifdef HAVE_SYSV_COMPAT @@ -281,7 +283,7 @@ static void service_done(Unit *u) { free(s->status_text); s->status_text = NULL; - exec_context_done(&s->exec_context); + exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager)); exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX); s->control_command = NULL; s->main_command = NULL; @@ -322,17 +324,15 @@ static void service_done(Unit *u) { static char *sysv_translate_name(const char *name) { char *r; - if (!(r = new(char, strlen(name) + sizeof(".service")))) + r = new(char, strlen(name) + sizeof(".service")); + if (!r) return NULL; if (endswith(name, ".sh")) - /* Drop Debian-style .sh suffix */ + /* Drop .sh suffix */ strcpy(stpcpy(r, name) - 3, ".service"); - if (startswith(name, "rc.")) - /* Drop Frugalware-style rc. prefix */ - strcpy(stpcpy(r, name + 3), ".service"); else - /* Normal init scripts */ + /* Normal init script name */ strcpy(stpcpy(r, name), ".service"); return r; @@ -348,24 +348,13 @@ static int sysv_translate_facility(const char *name, const char *filename, char static const char * const table[] = { /* LSB defined facilities */ - "local_fs", SPECIAL_LOCAL_FS_TARGET, - /* Due to unfortunate name selection in Mandriva, - * $network is provided by network-up which is ordered - * after network which actually starts interfaces. - * To break the loop, just ignore it */ + "local_fs", NULL, "network", SPECIAL_NETWORK_TARGET, "named", SPECIAL_NSS_LOOKUP_TARGET, "portmap", SPECIAL_RPCBIND_TARGET, "remote_fs", SPECIAL_REMOTE_FS_TARGET, - "syslog", SPECIAL_SYSLOG_TARGET, + "syslog", NULL, "time", SPECIAL_TIME_SYNC_TARGET, - - /* common extensions */ - "mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, - "x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE, - "null", NULL, - "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, - "smtp", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, }; unsigned i; @@ -385,8 +374,9 @@ static int sysv_translate_facility(const char *name, const char *filename, char if (!table[i+1]) return 0; - if (!(r = strdup(table[i+1]))) - return -ENOMEM; + r = strdup(table[i+1]); + if (!r) + return log_oom(); goto finish; } @@ -772,7 +762,7 @@ static int service_load_sysv_path(Service *s, const char *path) { continue; if (unit_name_to_type(m) == UNIT_SERVICE) - r = unit_add_name(u, m); + r = unit_merge_by_name(u, m); else /* NB: SysV targets * which are provided @@ -815,7 +805,6 @@ static int service_load_sysv_path(Service *s, const char *path) { } r = sysv_translate_facility(n, path_get_file_name(path), &m); - if (r < 0) { log_error_unit(u->id, "[%s:%u] Failed to translate LSB dependency %s, ignoring: %s", @@ -992,10 +981,8 @@ static int service_load_sysv_name(Service *s, const char *name) { assert(s); assert(name); - /* For SysV services we strip the rc.* and *.sh - * prefixes/suffixes. */ - if (startswith(name, "rc.") || - endswith(name, ".sh.service")) + /* For SysV services we strip the *.sh suffixes. */ + if (endswith(name, ".sh.service")) return -ENOENT; STRV_FOREACH(p, UNIT(s)->manager->lookup_paths.sysvinit_path) { @@ -1012,25 +999,12 @@ static int service_load_sysv_name(Service *s, const char *name) { r = service_load_sysv_path(s, path); if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { - /* Try Debian style *.sh source'able init scripts */ + /* Try *.sh source'able init scripts */ strcat(path, ".sh"); r = service_load_sysv_path(s, path); } free(path); - if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { - /* Try Frugalware style rc.* init scripts */ - - path = strjoin(*p, "/rc.", name, NULL); - if (!path) - return -ENOMEM; - - /* Drop .service suffix */ - path[strlen(path)-8] = 0; - r = service_load_sysv_path(s, path); - free(path); - } - if (r < 0) return r; @@ -1108,7 +1082,8 @@ static int fsck_fix_order(Service *s) { else continue; - if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0) + r = unit_add_dependency(UNIT(s), d, UNIT(t), true); + if (r < 0) return r; } @@ -1163,18 +1138,32 @@ static int service_add_default_dependencies(Service *s) { /* First, pull in base system */ if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) { - - if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0) + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, + SPECIAL_BASIC_TARGET, NULL, true); + if (r < 0) return r; } else if (UNIT(s)->manager->running_as == SYSTEMD_USER) { + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, + SPECIAL_SOCKETS_TARGET, NULL, true); + if (r < 0) + return r; - if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0) + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, + SPECIAL_TIMERS_TARGET, NULL, true); + if (r < 0) + return r; + + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, + SPECIAL_PATHS_TARGET, NULL, true); + if (r < 0) return r; } /* Second, activate normal shutdown */ - return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, NULL, true); + return r; } static void service_fix_output(Service *s) { @@ -1232,18 +1221,22 @@ static int service_load(Unit *u) { service_fix_output(s); - if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) + r = unit_add_exec_dependencies(u, &s->exec_context); + if (r < 0) return r; - if ((r = unit_add_default_cgroups(u)) < 0) + r = unit_add_default_cgroups(u); + if (r < 0) return r; #ifdef HAVE_SYSV_COMPAT - if ((r = sysv_fix_order(s)) < 0) + r = sysv_fix_order(s); + if (r < 0) return r; #endif - if ((r = fsck_fix_order(s)) < 0) + r = fsck_fix_order(s); + if (r < 0) return r; if (s->bus_name) @@ -1256,13 +1249,18 @@ static int service_load(Unit *u) { if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE) s->notify_access = NOTIFY_MAIN; - if (s->type == SERVICE_DBUS || s->bus_name) - if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true)) < 0) + if (s->type == SERVICE_DBUS || s->bus_name) { + r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, + SPECIAL_DBUS_SOCKET, NULL, true); + if (r < 0) return r; + } - if (UNIT(s)->default_dependencies) - if ((r = service_add_default_dependencies(s)) < 0) + if (UNIT(s)->default_dependencies) { + r = service_add_default_dependencies(s); + if (r < 0) return r; + } r = unit_exec_context_defaults(u, &s->exec_context); if (r < 0) @@ -1277,7 +1275,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { ServiceExecCommand c; Service *s = SERVICE(u); const char *prefix2; - char *p2; + _cleanup_free_ char *p2 = NULL; assert(s); @@ -1372,12 +1370,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { if (s->status_text) fprintf(f, "%sStatus Text: %s\n", prefix, s->status_text); - - free(p2); } static int service_load_pid_file(Service *s, bool may_warn) { - char *k; + _cleanup_free_ char *k = NULL; int r; pid_t pid; @@ -1386,7 +1382,8 @@ static int service_load_pid_file(Service *s, bool may_warn) { if (!s->pid_file) return -ENOENT; - if ((r = read_one_line_file(s->pid_file, &k)) < 0) { + r = read_one_line_file(s->pid_file, &k); + if (r < 0) { if (may_warn) log_info_unit(UNIT(s)->id, "PID file %s not readable (yet?) after %s.", @@ -1395,10 +1392,13 @@ static int service_load_pid_file(Service *s, bool may_warn) { } r = parse_pid(k, &pid); - free(k); - - if (r < 0) + if (r < 0) { + if (may_warn) + log_info_unit(UNIT(s)->id, + "Failed to read PID from file %s: %s", + s->pid_file, strerror(-r)); return r; + } if (kill(pid, 0) < 0 && errno != EPERM) { if (may_warn) @@ -1421,12 +1421,18 @@ static int service_load_pid_file(Service *s, bool may_warn) { log_debug_unit(UNIT(s)->id, "Main PID loaded: %lu", (unsigned long) pid); - if ((r = service_set_main_pid(s, pid)) < 0) + r = service_set_main_pid(s, pid); + if (r < 0) return r; - if ((r = unit_watch_pid(UNIT(s), pid)) < 0) + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) { /* FIXME: we need to do something here */ + log_warning_unit(UNIT(s)->id, + "Failed to watch PID %lu from service %s", + (unsigned long) pid, UNIT(s)->id); return r; + } return 0; } @@ -1447,16 +1453,22 @@ static int service_search_main_pid(Service *s) { assert(s->main_pid <= 0); - if ((pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings)) <= 0) + pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings); + if (pid <= 0) return -ENOENT; log_debug_unit(UNIT(s)->id, "Main PID guessed: %lu", (unsigned long) pid); - if ((r = service_set_main_pid(s, pid)) < 0) + r = service_set_main_pid(s, pid); + if (r < 0) return r; - if ((r = unit_watch_pid(UNIT(s), pid)) < 0) + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) /* FIXME: we need to do something here */ + log_warning_unit(UNIT(s)->id, + "Failed to watch PID %lu from service %s", + (unsigned long) pid, UNIT(s)->id); return r; return 0; @@ -1531,6 +1543,9 @@ static void service_set_state(Service *s, ServiceState state) { s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; } + if (state == SERVICE_FAILED) + service_notify_sockets_dead(s, s->result == SERVICE_FAILURE_START_LIMIT); + if (state == SERVICE_DEAD || state == SERVICE_STOP || state == SERVICE_STOP_SIGTERM || @@ -1538,7 +1553,6 @@ static void service_set_state(Service *s, ServiceState state) { state == SERVICE_STOP_POST || state == SERVICE_FINAL_SIGTERM || state == SERVICE_FINAL_SIGKILL || - state == SERVICE_FAILED || state == SERVICE_AUTO_RESTART) service_notify_sockets_dead(s, false); @@ -1558,7 +1572,7 @@ static void service_set_state(Service *s, ServiceState state) { service_connection_unref(s); } - if (state == SERVICE_STOP) + if (state == SERVICE_STOP || state == SERVICE_STOP_SIGTERM) service_stop_watchdog(s); /* For the inactive states unit_notify() will trim the cgroup, @@ -1618,9 +1632,11 @@ static int service_coldplug(Unit *u) { s->deserialized_state == SERVICE_STOP || s->deserialized_state == SERVICE_STOP_SIGTERM || s->deserialized_state == SERVICE_STOP_SIGKILL) - if (s->main_pid > 0) - if ((r = unit_watch_pid(UNIT(s), s->main_pid)) < 0) + if (s->main_pid > 0) { + r = unit_watch_pid(UNIT(s), s->main_pid); + if (r < 0) return r; + } if (s->deserialized_state == SERVICE_START_PRE || s->deserialized_state == SERVICE_START || @@ -1632,9 +1648,11 @@ static int service_coldplug(Unit *u) { s->deserialized_state == SERVICE_STOP_POST || s->deserialized_state == SERVICE_FINAL_SIGTERM || s->deserialized_state == SERVICE_FINAL_SIGKILL) - if (s->control_pid > 0) - if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0) + if (s->control_pid > 0) { + r = unit_watch_pid(UNIT(s), s->control_pid); + if (r < 0) return r; + } if (s->deserialized_state == SERVICE_START_POST || s->deserialized_state == SERVICE_RUNNING) @@ -1669,7 +1687,8 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { sock = SOCKET(u); - if ((r = socket_collect_fds(sock, &cfds, &cn_fds)) < 0) + r = socket_collect_fds(sock, &cfds, &cn_fds); + if (r < 0) goto fail; if (!cfds) @@ -1681,7 +1700,8 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { } else { int *t; - if (!(t = new(int, rn_fds+cn_fds))) { + t = new(int, rn_fds+cn_fds); + if (!t) { free(cfds); r = -ENOMEM; goto fail; @@ -1722,9 +1742,11 @@ static int service_spawn( pid_t pid; int r; - int *fds = NULL, *fdsbuf = NULL; + int *fds = NULL; + _cleanup_free_ int *fdsbuf = NULL; unsigned n_fds = 0, n_env = 0; - char **argv = NULL, **final_env = NULL, **our_env = NULL; + _cleanup_strv_free_ char + **argv = NULL, **final_env = NULL, **our_env = NULL; assert(s); assert(c); @@ -1739,7 +1761,8 @@ static int service_spawn( fds = &s->socket_fd; n_fds = 1; } else { - if ((r = service_collect_fds(s, &fdsbuf, &n_fds)) < 0) + r = service_collect_fds(s, &fdsbuf, &n_fds); + if (r < 0) goto fail; fds = fdsbuf; @@ -1747,13 +1770,15 @@ static int service_spawn( } if (timeout && s->timeout_start_usec) { - r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_start_usec, &s->timer_watch); + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, + s->timeout_start_usec, &s->timer_watch); if (r < 0) goto fail; } else unit_unwatch_timer(UNIT(s), &s->timer_watch); - if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) { + argv = unit_full_printf_strv(UNIT(s), c->argv); + if (!argv) { r = -ENOMEM; goto fail; } @@ -1809,29 +1834,19 @@ static int service_spawn( UNIT(s)->id, s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL, &pid); - if (r < 0) goto fail; - if ((r = unit_watch_pid(UNIT(s), pid)) < 0) + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) /* FIXME: we need to do something here */ goto fail; - free(fdsbuf); - strv_free(argv); - strv_free(our_env); - strv_free(final_env); - *_pid = pid; return 0; fail: - free(fdsbuf); - strv_free(argv); - strv_free(our_env); - strv_free(final_env); - if (timeout) unit_unwatch_timer(UNIT(s), &s->timer_watch); @@ -1863,7 +1878,7 @@ static int main_pid_good(Service *s) { return -EAGAIN; } -static int control_pid_good(Service *s) { +_pure_ static int control_pid_good(Service *s) { assert(s); return s->control_pid > 0; @@ -1874,7 +1889,8 @@ static int cgroup_good(Service *s) { assert(s); - if ((r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings)) < 0) + r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings); + if (r < 0) return r; return !r; @@ -1911,6 +1927,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) s->forbid_restart = false; + /* we want fresh tmpdirs in case service is started again immediately */ + exec_context_tmp_dirs_done(&s->exec_context); + return; fail: @@ -1931,7 +1950,8 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { service_unwatch_control_pid(s); - if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) { + s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST]; + if (s->control_command) { s->control_command_id = SERVICE_EXEC_STOP_POST; r = service_spawn(s, @@ -1963,66 +1983,26 @@ fail: static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) { int r; - Set *pid_set = NULL; - bool wait_for_exit = false; assert(s); if (f != SERVICE_SUCCESS) s->result = f; - if (s->kill_context.kill_mode != KILL_NONE) { - int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->kill_context.kill_signal : SIGKILL; - - if (s->main_pid > 0) { - if (kill_and_sigcont(s->main_pid, sig) < 0 && errno != ESRCH) - log_warning_unit(UNIT(s)->id, - "Failed to kill main process %li: %m", (long) s->main_pid); - else - wait_for_exit = !s->main_pid_alien; - } - - if (s->control_pid > 0) { - if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH) - log_warning_unit(UNIT(s)->id, - "Failed to kill control process %li: %m", (long) s->control_pid); - else - wait_for_exit = true; - } - - if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) { - - pid_set = set_new(trivial_hash_func, trivial_compare_func); - if (!pid_set) { - r = -ENOMEM; - goto fail; - } - - /* Exclude the main/control pids from being killed via the cgroup */ - if (s->main_pid > 0) - if ((r = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0) - goto fail; - - if (s->control_pid > 0) - if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) - goto fail; - - r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL); - if (r < 0) { - if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) - log_warning_unit(UNIT(s)->id, - "Failed to kill control group: %s", strerror(-r)); - } else if (r > 0) - wait_for_exit = true; - - set_free(pid_set); - pid_set = NULL; - } - } + r = unit_kill_context( + UNIT(s), + &s->kill_context, + state != SERVICE_STOP_SIGTERM && state != SERVICE_FINAL_SIGTERM, + s->main_pid, + s->control_pid, + s->main_pid_alien); + if (r < 0) + goto fail; - if (wait_for_exit) { + if (r > 0) { if (s->timeout_stop_usec > 0) { - r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_stop_usec, &s->timer_watch); + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, + s->timeout_stop_usec, &s->timer_watch); if (r < 0) goto fail; } @@ -2043,9 +2023,6 @@ fail: service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES); else service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); - - if (pid_set) - set_free(pid_set); } static void service_enter_stop(Service *s, ServiceResult f) { @@ -2058,7 +2035,8 @@ static void service_enter_stop(Service *s, ServiceResult f) { service_unwatch_control_pid(s); - if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) { + s->control_command = s->exec_command[SERVICE_EXEC_STOP]; + if (s->control_command) { s->control_command_id = SERVICE_EXEC_STOP; r = service_spawn(s, @@ -2114,7 +2092,8 @@ static void service_enter_start_post(Service *s) { if (s->watchdog_usec > 0) service_reset_watchdog(s); - if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) { + s->control_command = s->exec_command[SERVICE_EXEC_START_POST]; + if (s->control_command) { s->control_command_id = SERVICE_EXEC_START_POST; r = service_spawn(s, @@ -2176,7 +2155,8 @@ static void service_enter_start(Service *s) { r = service_spawn(s, c, - s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY || s->type == SERVICE_ONESHOT, + s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || + s->type == SERVICE_NOTIFY || s->type == SERVICE_ONESHOT, true, true, true, @@ -2233,11 +2213,13 @@ static void service_enter_start_pre(Service *s) { service_unwatch_control_pid(s); - if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) { + s->control_command = s->exec_command[SERVICE_EXEC_START_PRE]; + if (s->control_command) { /* Before we start anything, let's clear up what might * be left from previous runs. */ - cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control"); + cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, + true,true, NULL, "control"); s->control_command_id = SERVICE_EXEC_START_PRE; @@ -2317,7 +2299,8 @@ static void service_enter_reload(Service *s) { service_unwatch_control_pid(s); - if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) { + s->control_command = s->exec_command[SERVICE_EXEC_RELOAD]; + if (s->control_command) { s->control_command_id = SERVICE_EXEC_RELOAD; r = service_spawn(s, @@ -2451,7 +2434,9 @@ static int service_start_limit_test(Service *s) { log_warning_unit(UNIT(s)->id, "%s start request repeated too quickly, rebooting.", UNIT(s)->id); - r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL); + r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, + SPECIAL_REBOOT_TARGET, JOB_REPLACE, + true, &error, NULL); if (r < 0) { log_error_unit(UNIT(s)->id, "Failed to reboot: %s.", bus_error(&error, r)); @@ -2585,7 +2570,7 @@ static int service_reload(Unit *u) { return 0; } -static bool service_can_reload(Unit *u) { +_pure_ static bool service_can_reload(Unit *u) { Service *s = SERVICE(u); assert(s); @@ -2605,7 +2590,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { unit_serialize_item(u, f, "reload-result", service_result_to_string(s->reload_result)); if (s->control_pid > 0) - unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid); + unit_serialize_item_format(u, f, "control-pid", "%lu", + (unsigned long) s->control_pid); if (s->main_pid_known && s->main_pid > 0) unit_serialize_item_format(u, f, "main-pid", "%lu", (unsigned long) s->main_pid); @@ -2619,7 +2605,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { * multiple commands attached here, we will start from the * first one again */ if (s->control_command_id >= 0) - unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id)); + unit_serialize_item(u, f, "control-command", + service_exec_command_to_string(s->control_command_id)); if (s->socket_fd >= 0) { int copy; @@ -2631,17 +2618,29 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { } if (s->main_exec_status.pid > 0) { - unit_serialize_item_format(u, f, "main-exec-status-pid", "%lu", (unsigned long) s->main_exec_status.pid); - dual_timestamp_serialize(f, "main-exec-status-start", &s->main_exec_status.start_timestamp); - dual_timestamp_serialize(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp); + unit_serialize_item_format(u, f, "main-exec-status-pid", "%lu", + (unsigned long) s->main_exec_status.pid); + dual_timestamp_serialize(f, "main-exec-status-start", + &s->main_exec_status.start_timestamp); + dual_timestamp_serialize(f, "main-exec-status-exit", + &s->main_exec_status.exit_timestamp); if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) { - unit_serialize_item_format(u, f, "main-exec-status-code", "%i", s->main_exec_status.code); - unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status); + unit_serialize_item_format(u, f, "main-exec-status-code", "%i", + s->main_exec_status.code); + unit_serialize_item_format(u, f, "main-exec-status-status", "%i", + s->main_exec_status.status); } } if (dual_timestamp_is_set(&s->watchdog_timestamp)) - dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp); + dual_timestamp_serialize(f, "watchdog-timestamp", + &s->watchdog_timestamp); + + if (s->exec_context.tmp_dir) + unit_serialize_item(u, f, "tmp-dir", s->exec_context.tmp_dir); + + if (s->exec_context.var_tmp_dir) + unit_serialize_item(u, f, "var-tmp-dir", s->exec_context.var_tmp_dir); return 0; } @@ -2657,7 +2656,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, if (streq(key, "state")) { ServiceState state; - if ((state = service_state_from_string(value)) < 0) + state = service_state_from_string(value); + if (state < 0) log_debug_unit(u->id, "Failed to parse state value %s", value); else s->deserialized_state = state; @@ -2696,14 +2696,18 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, } else if (streq(key, "main-pid-known")) { int b; - if ((b = parse_boolean(value)) < 0) + b = parse_boolean(value); + if (b < 0) log_debug_unit(u->id, "Failed to parse main-pid-known value %s", value); else s->main_pid_known = b; } else if (streq(key, "status-text")) { char *t; - if ((t = strdup(value))) { + t = strdup(value); + if (!t) + log_oom(); + else { free(s->status_text); s->status_text = t; } @@ -2711,7 +2715,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, } else if (streq(key, "control-command")) { ServiceExecCommand id; - if ((id = service_exec_command_from_string(value)) < 0) + id = service_exec_command_from_string(value); + if (id < 0) log_debug_unit(u->id, "Failed to parse exec-command value %s", value); else { s->control_command_id = id; @@ -2755,13 +2760,29 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp); else if (streq(key, "watchdog-timestamp")) dual_timestamp_deserialize(value, &s->watchdog_timestamp); - else + else if (streq(key, "tmp-dir")) { + char *t; + + t = strdup(value); + if (!t) + return log_oom(); + + s->exec_context.tmp_dir = t; + } else if (streq(key, "var-tmp-dir")) { + char *t; + + t = strdup(value); + if (!t) + return log_oom(); + + s->exec_context.var_tmp_dir = t; + } else log_debug_unit(u->id, "Unknown serialization key '%s'", key); return 0; } -static UnitActiveState service_active_state(Unit *u) { +_pure_ static UnitActiveState service_active_state(Unit *u) { const UnitActiveState *table; assert(u); @@ -2797,7 +2818,7 @@ static bool service_check_gc(Unit *u) { return false; } -static bool service_check_snapshot(Unit *u) { +_pure_ static bool service_check_snapshot(Unit *u) { Service *s = SERVICE(u); assert(s); @@ -2832,6 +2853,9 @@ static int service_watch_pid_file(Service *s) { goto fail; /* the pidfile might have appeared just before we set the watch */ + log_debug_unit(UNIT(s)->id, + "Trying to read %s's PID file %s in case it changed", + UNIT(s)->id, s->pid_file_pathspec->path); service_retry_pid_file(s); return 0; @@ -2926,24 +2950,34 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->main_pid = 0; exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status); - /* If this is not a forking service than the main - * process got started and hence we copy the exit - * status so that it is recorded both as main and as - * control process exit status */ if (s->main_command) { + /* If this is not a forking service than the + * main process got started and hence we copy + * the exit status so that it is recorded both + * as main and as control process exit + * status */ + s->main_command->exec_status = s->main_exec_status; if (s->main_command->ignore) f = SERVICE_SUCCESS; + } else if (s->exec_command[SERVICE_EXEC_START]) { + + /* If this is a forked process, then we should + * ignore the return value if this was + * configured for the starter process */ + + if (s->exec_command[SERVICE_EXEC_START]->ignore) + f = SERVICE_SUCCESS; } - log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + log_struct_unit(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + u->id, "MESSAGE=%s: main process exited, code=%s, status=%i/%s", u->id, sigchld_code_to_string(code), status, strna(code == CLD_EXITED ? exit_status_to_string(status, EXIT_STATUS_FULL) : signal_to_string(status)), - "UNIT=%s", u->id, "EXIT_CODE=%s", sigchld_code_to_string(code), "EXIT_STATUS=%i", status, NULL); @@ -3013,7 +3047,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_pid = 0; if (s->control_command) { - exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); + exec_status_exit(&s->control_command->exec_status, + &s->exec_context, pid, code, status); if (s->control_command->ignore) f = SERVICE_SUCCESS; @@ -3029,7 +3064,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* Immediately get rid of the cgroup, so that the * kernel doesn't delay the cgroup empty messages for * the service cgroup any longer than necessary */ - cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control"); + cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, + true, true, NULL, "control"); if (s->control_command && s->control_command->command_next && @@ -3398,7 +3434,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { if (strv_find(tags, "WATCHDOG=1")) { log_debug_unit(u->id, "%s: got WATCHDOG=1", u->id); - service_reset_watchdog(s); + if (dual_timestamp_is_set(&s->watchdog_timestamp)) + service_reset_watchdog(s); } /* Notify clients about changed status or main pid */ @@ -3410,9 +3447,10 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { static int service_enumerate(Manager *m) { char **p; unsigned i; - DIR *d = NULL; - char *path = NULL, *fpath = NULL, *name = NULL; - Set *runlevel_services[ELEMENTSOF(rcnd_table)], *shutdown_services = NULL; + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *path = NULL, *fpath = NULL, *name = NULL; + Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {}; + _cleanup_set_free_ Set *shutdown_services = NULL; Unit *service; Iterator j; int r; @@ -3422,8 +3460,6 @@ static int service_enumerate(Manager *m) { if (m->running_as != SYSTEMD_SYSTEM) return 0; - zero(runlevel_services); - STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path) for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) { struct dirent *de; @@ -3438,9 +3474,10 @@ static int service_enumerate(Manager *m) { if (d) closedir(d); - if (!(d = opendir(path))) { + d = opendir(path); + if (!d) { if (errno != ENOENT) - log_warning("opendir() failed on %s: %s", path, strerror(errno)); + log_warning("opendir(%s) failed: %s", path, strerror(errno)); continue; } @@ -3479,8 +3516,9 @@ static int service_enumerate(Manager *m) { } free(name); - if (!(name = sysv_translate_name(de->d_name + 3))) { - r = -ENOMEM; + name = sysv_translate_name(de->d_name + 3); + if (!name) { + r = log_oom(); goto finish; } @@ -3499,19 +3537,25 @@ static int service_enumerate(Manager *m) { SERVICE(service)->sysv_enabled = true; } - if ((r = set_ensure_allocated(&runlevel_services[i], trivial_hash_func, trivial_compare_func)) < 0) + r = set_ensure_allocated(&runlevel_services[i], + trivial_hash_func, trivial_compare_func); + if (r < 0) goto finish; - if ((r = set_put(runlevel_services[i], service)) < 0) + r = set_put(runlevel_services[i], service); + if (r < 0) goto finish; } else if (de->d_name[0] == 'K' && (rcnd_table[i].type == RUNLEVEL_DOWN)) { - if ((r = set_ensure_allocated(&shutdown_services, trivial_hash_func, trivial_compare_func)) < 0) + r = set_ensure_allocated(&shutdown_services, + trivial_hash_func, trivial_compare_func); + if (r < 0) goto finish; - if ((r = set_put(shutdown_services, service)) < 0) + r = set_put(shutdown_services, service); + if (r < 0) goto finish; } } @@ -3532,7 +3576,10 @@ static int service_enumerate(Manager *m) { if (service->fragment_path) continue; - if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_WANTS, rcnd_table[i].target, NULL, true)) < 0) + r = unit_add_two_dependencies_by_name_inverse( + service, UNIT_AFTER, UNIT_WANTS, + rcnd_table[i].target, NULL, true); + if (r < 0) goto finish; } @@ -3547,23 +3594,19 @@ static int service_enumerate(Manager *m) { if (service->fragment_path) continue; - if ((r = unit_add_two_dependencies_by_name(service, UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true)) < 0) + r = unit_add_two_dependencies_by_name( + service, UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, NULL, true); + if (r < 0) goto finish; } r = 0; finish: - free(path); - free(fpath); - free(name); for (i = 0; i < ELEMENTSOF(rcnd_table); i++) set_free(runlevel_services[i]); - set_free(shutdown_services); - - if (d) - closedir(d); return r; } @@ -3686,65 +3729,7 @@ static void service_reset_failed(Unit *u) { static int service_kill(Unit *u, KillWho who, int signo, DBusError *error) { Service *s = SERVICE(u); - int r = 0; - Set *pid_set = NULL; - - assert(s); - - if (s->main_pid <= 0 && who == KILL_MAIN) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); - return -ESRCH; - } - - if (s->control_pid <= 0 && who == KILL_CONTROL) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - return -ESRCH; - } - - if (who == KILL_CONTROL || who == KILL_ALL) - if (s->control_pid > 0) - if (kill(s->control_pid, signo) < 0) - r = -errno; - - if (who == KILL_MAIN || who == KILL_ALL) - if (s->main_pid > 0) - if (kill(s->main_pid, signo) < 0) - r = -errno; - - if (who == KILL_ALL) { - int q; - - pid_set = set_new(trivial_hash_func, trivial_compare_func); - if (!pid_set) - return -ENOMEM; - - /* Exclude the control/main pid from being killed via the cgroup */ - if (s->control_pid > 0) { - q = set_put(pid_set, LONG_TO_PTR(s->control_pid)); - if (q < 0) { - r = q; - goto finish; - } - } - - if (s->main_pid > 0) { - q = set_put(pid_set, LONG_TO_PTR(s->main_pid)); - if (q < 0) { - r = q; - goto finish; - } - } - - q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL); - if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) - r = q; - } - -finish: - if (pid_set) - set_free(pid_set); - - return r; + return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error); } static const char* const service_state_table[_SERVICE_STATE_MAX] = { @@ -3830,13 +3815,15 @@ DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction); const UnitVTable service_vtable = { .object_size = sizeof(Service), - .exec_context_offset = offsetof(Service, exec_context), .sections = "Unit\0" "Service\0" "Install\0", + .exec_context_offset = offsetof(Service, exec_context), + .exec_section = "Service", + .init = service_init, .done = service_done, .load = service_load, diff --git a/src/core/service.h b/src/core/service.h index d1e53bf727..703d3faa45 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -203,23 +203,23 @@ struct Socket; int service_set_socket_fd(Service *s, int fd, struct Socket *socket); -const char* service_state_to_string(ServiceState i); -ServiceState service_state_from_string(const char *s); +const char* service_state_to_string(ServiceState i) _const_; +ServiceState service_state_from_string(const char *s) _pure_; -const char* service_restart_to_string(ServiceRestart i); -ServiceRestart service_restart_from_string(const char *s); +const char* service_restart_to_string(ServiceRestart i) _const_; +ServiceRestart service_restart_from_string(const char *s) _pure_; -const char* service_type_to_string(ServiceType i); -ServiceType service_type_from_string(const char *s); +const char* service_type_to_string(ServiceType i) _const_; +ServiceType service_type_from_string(const char *s) _pure_; -const char* service_exec_command_to_string(ServiceExecCommand i); -ServiceExecCommand service_exec_command_from_string(const char *s); +const char* service_exec_command_to_string(ServiceExecCommand i) _const_; +ServiceExecCommand service_exec_command_from_string(const char *s) _pure_; -const char* notify_access_to_string(NotifyAccess i); -NotifyAccess notify_access_from_string(const char *s); +const char* notify_access_to_string(NotifyAccess i) _const_; +NotifyAccess notify_access_from_string(const char *s) _pure_; -const char* service_result_to_string(ServiceResult i); -ServiceResult service_result_from_string(const char *s); +const char* service_result_to_string(ServiceResult i) _const_; +ServiceResult service_result_from_string(const char *s) _pure_; -const char* start_limit_action_to_string(StartLimitAction i); -StartLimitAction start_limit_action_from_string(const char *s); +const char* start_limit_action_to_string(StartLimitAction i) _const_; +StartLimitAction start_limit_action_from_string(const char *s) _pure_; diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 0b0e0c3d47..2db761de36 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -261,6 +261,8 @@ int main(int argc, char *argv[]) { if (retries >= FINALIZE_ATTEMPTS) log_error("Too many iterations, giving up."); + else + log_info("Storage is finalized."); arguments[0] = NULL; arguments[1] = argv[1]; @@ -272,6 +274,9 @@ int main(int argc, char *argv[]) { if (prepare_new_root() >= 0 && pivot_to_new_root() >= 0) { + + log_info("Returning to initrd..."); + execv("/shutdown", argv); log_error("Failed to execute shutdown binary: %m"); } diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c new file mode 100644 index 0000000000..73eeb04190 --- /dev/null +++ b/src/core/smack-setup.c @@ -0,0 +1,151 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation + Authors: + Nathaniel Chen <nathaniel.chen@intel.com> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/vfs.h> +#include <fcntl.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/mount.h> +#include <stdint.h> + +#include "macro.h" +#include "smack-setup.h" +#include "util.h" +#include "log.h" +#include "label.h" + +#define SMACK_CONFIG "/etc/smack/accesses.d/" +#define CIPSO_CONFIG "/etc/smack/cipso/" + +static int write_rules(const char* dstpath, const char* srcdir) { + _cleanup_fclose_ FILE *dst = NULL; + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *entry; + char buf[NAME_MAX]; + int dfd = -1; + int r = 0; + + dst = fopen(dstpath, "we"); + if (!dst) { + if (errno != ENOENT) + log_warning("Failed to open %s: %m", dstpath); + return -errno; /* negative error */ + } + + /* write rules to dst from every file in the directory */ + dir = opendir(srcdir); + if (!dir) { + if (errno != ENOENT) + log_warning("Failed to opendir %s: %m", srcdir); + return errno; /* positive on purpose */ + } + + dfd = dirfd(dir); + assert(dfd >= 0); + + FOREACH_DIRENT(entry, dir, return 0) { + int fd; + _cleanup_fclose_ FILE *policy = NULL; + + fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC); + if (fd < 0) { + if (r == 0) + r = -errno; + log_warning("Failed to open %s: %m", entry->d_name); + continue; + } + + policy = fdopen(fd, "re"); + if (!policy) { + if (r == 0) + r = -errno; + close_nointr_nofail(fd); + log_error("Failed to open %s: %m", entry->d_name); + continue; + } + + /* load2 write rules in the kernel require a line buffered stream */ + FOREACH_LINE(buf, policy, + log_error("Failed to read line from %s: %m", + entry->d_name)) { + if (!fputs(buf, dst)) { + if (r == 0) + r = -EINVAL; + log_error("Failed to write line to %s", dstpath); + break; + } + if (fflush(dst)) { + if (r == 0) + r = -errno; + log_error("Failed to flush writes to %s: %m", dstpath); + break; + } + } + } + + return r; +} + + +int smack_setup(void) { + int r; + + r = write_rules("/sys/fs/smackfs/load2", SMACK_CONFIG); + switch(r) { + case -ENOENT: + log_debug("Smack is not enabled in the kernel."); + return 0; + case ENOENT: + log_debug("Smack access rules directory " SMACK_CONFIG " not found"); + return 0; + case 0: + log_info("Successfully loaded Smack policies."); + break; + default: + log_warning("Failed to load Smack access rules: %s, ignoring.", + strerror(abs(r))); + return 0; + } + + r = write_rules("/sys/fs/smackfs/cipso2", CIPSO_CONFIG); + switch(r) { + case -ENOENT: + log_debug("Smack/CIPSO is not enabled in the kernel."); + return 0; + case ENOENT: + log_debug("Smack/CIPSO access rules directory " CIPSO_CONFIG " not found"); + return 0; + case 0: + log_info("Successfully loaded Smack/CIPSO policies."); + return 0; + default: + log_warning("Failed to load Smack/CIPSO access rules: %s, ignoring.", + strerror(abs(r))); + return 0; + } +} diff --git a/src/core/smack-setup.h b/src/core/smack-setup.h new file mode 100644 index 0000000000..ffe91843c3 --- /dev/null +++ b/src/core/smack-setup.h @@ -0,0 +1,26 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation + Authors: + Nathaniel Chen <nathaniel.chen@intel.com> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +int smack_setup(void); diff --git a/src/core/snapshot.c b/src/core/snapshot.c index 5c2a319cb6..a63eccd8de 100644 --- a/src/core/snapshot.c +++ b/src/core/snapshot.c @@ -173,13 +173,13 @@ static int snapshot_deserialize_item(Unit *u, const char *key, const char *value return 0; } -static UnitActiveState snapshot_active_state(Unit *u) { +_pure_ static UnitActiveState snapshot_active_state(Unit *u) { assert(u); return state_translation_table[SNAPSHOT(u)->state]; } -static const char *snapshot_sub_state_to_string(Unit *u) { +_pure_ static const char *snapshot_sub_state_to_string(Unit *u) { assert(u); return snapshot_state_to_string(SNAPSHOT(u)->state); @@ -256,6 +256,7 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Sn } SNAPSHOT(u)->cleanup = cleanup; + u->allow_isolate = true; *_s = SNAPSHOT(u); return 0; diff --git a/src/core/snapshot.h b/src/core/snapshot.h index 9662d93164..56f87cff4d 100644 --- a/src/core/snapshot.h +++ b/src/core/snapshot.h @@ -46,5 +46,5 @@ extern const UnitVTable snapshot_vtable; int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Snapshot **s); void snapshot_remove(Snapshot *s); -const char* snapshot_state_to_string(SnapshotState i); -SnapshotState snapshot_state_from_string(const char *s); +const char* snapshot_state_to_string(SnapshotState i) _const_; +SnapshotState snapshot_state_from_string(const char *s) _pure_; diff --git a/src/core/socket.c b/src/core/socket.c index fcbcdbe192..1b08f0a5fd 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -28,7 +28,7 @@ #include <signal.h> #include <arpa/inet.h> #include <mqueue.h> -#ifdef HAVE_ATTR_XATTR_H +#ifdef HAVE_XATTR #include <attr/xattr.h> #endif @@ -46,7 +46,7 @@ #include "dbus-socket.h" #include "missing.h" #include "special.h" -#include "bus-errors.h" +#include "dbus-common.h" #include "label.h" #include "exit-status.h" #include "def.h" @@ -102,8 +102,7 @@ static void socket_unwatch_control_pid(Socket *s) { s->control_pid = 0; } -static void socket_done(Unit *u) { - Socket *s = SOCKET(u); +void socket_free_ports(Socket *s) { SocketPort *p; assert(s); @@ -119,8 +118,16 @@ static void socket_done(Unit *u) { free(p->path); free(p); } +} + +static void socket_done(Unit *u) { + Socket *s = SOCKET(u); + + assert(s); + + socket_free_ports(s); - exec_context_done(&s->exec_context); + exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager)); exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); s->control_command = NULL; @@ -308,7 +315,7 @@ static int socket_add_device_link(Socket *s) { assert(s); - if (!s->bind_to_device) + if (!s->bind_to_device || streq(s->bind_to_device, "lo")) return 0; if (asprintf(&t, "/sys/subsystem/net/devices/%s", s->bind_to_device) < 0) @@ -324,18 +331,20 @@ static int socket_add_default_dependencies(Socket *s) { int r; assert(s); - if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) { - if ((r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0) - return r; + r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true); + if (r < 0) + return r; - if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0) + if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) { + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); + if (r < 0) return r; } return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); } -static bool socket_has_exec(Socket *s) { +_pure_ static bool socket_has_exec(Socket *s) { unsigned i; assert(s); @@ -401,7 +410,7 @@ static int socket_load(Unit *u) { return socket_verify(s); } -static const char* listen_lookup(int family, int type) { +_const_ static const char* listen_lookup(int family, int type) { if (family == AF_NETLINK) return "ListenNetlink"; @@ -779,7 +788,7 @@ static void socket_apply_socket_options(Socket *s, int fd) { if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0) log_warning_unit(UNIT(s)->id, "TCP_CONGESTION failed: %m"); -#ifdef HAVE_ATTR_XATTR_H +#ifdef HAVE_XATTR if (s->smack_ip_in) if (fsetxattr(fd, "security.SMACK64IPIN", s->smack_ip_in, strlen(s->smack_ip_in), 0) < 0) log_error_unit(UNIT(s)->id, @@ -801,7 +810,7 @@ static void socket_apply_fifo_options(Socket *s, int fd) { log_warning_unit(UNIT(s)->id, "F_SETPIPE_SZ: %m"); -#ifdef HAVE_ATTR_XATTR_H +#ifdef HAVE_XATTR if (s->smack) if (fsetxattr(fd, "security.SMACK64", s->smack, strlen(s->smack), 0) < 0) log_error_unit(UNIT(s)->id, @@ -1246,6 +1255,7 @@ static void socket_enter_dead(Socket *s, SocketResult f) { if (f != SOCKET_SUCCESS) s->result = f; + exec_context_tmp_dirs_done(&s->exec_context); socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); } @@ -1281,54 +1291,23 @@ fail: static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { int r; - Set *pid_set = NULL; - bool wait_for_exit = false; assert(s); if (f != SOCKET_SUCCESS) s->result = f; - if (s->kill_context.kill_mode != KILL_NONE) { - int sig = (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_FINAL_SIGTERM) ? s->kill_context.kill_signal : SIGKILL; - - if (s->control_pid > 0) { - if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH) - - log_warning_unit(UNIT(s)->id, - "Failed to kill control process %li: %m", - (long) s->control_pid); - else - wait_for_exit = true; - } - - if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) { - - if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) { - r = -ENOMEM; - goto fail; - } - - /* Exclude the control pid from being killed via the cgroup */ - if (s->control_pid > 0) - if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) - goto fail; - - r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL); - if (r < 0) { - if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) - log_warning_unit(UNIT(s)->id, - "Failed to kill control group: %s", - strerror(-r)); - } else if (r > 0) - wait_for_exit = true; - - set_free(pid_set); - pid_set = NULL; - } - } + r = unit_kill_context( + UNIT(s), + &s->kill_context, + state != SOCKET_STOP_PRE_SIGTERM && state != SOCKET_FINAL_SIGTERM, + -1, + s->control_pid, + false); + if (r < 0) + goto fail; - if (wait_for_exit) { + if (r > 0) { r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch); if (r < 0) goto fail; @@ -1350,9 +1329,6 @@ fail: socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); else socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); - - if (pid_set) - set_free(pid_set); } static void socket_enter_stop_pre(Socket *s, SocketResult f) { @@ -1471,7 +1447,7 @@ static void socket_enter_running(Socket *s, int cfd) { /* We don't take connections anymore if we are supposed to * shut down anyway */ - if (unit_pending_inactive(UNIT(s))) { + if (unit_stop_pending(UNIT(s))) { log_debug_unit(UNIT(s)->id, "Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id); @@ -1502,7 +1478,7 @@ static void socket_enter_running(Socket *s, int cfd) { /* If there's already a start pending don't bother to * do anything */ SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERS], i) - if (unit_pending_active(u)) { + if (unit_active_or_pending(u)) { pending = true; break; } @@ -1658,7 +1634,7 @@ static int socket_start(Unit *u) { service = SERVICE(UNIT_DEREF(s->service)); if (UNIT(service)->load_state != UNIT_LOADED) { - log_error_unit(UNIT(service)->id, + log_error_unit(u->id, "Socket service %s not loaded, refusing.", UNIT(service)->id); return -ENOENT; @@ -1669,7 +1645,7 @@ static int socket_start(Unit *u) { if (service->state != SERVICE_DEAD && service->state != SERVICE_FAILED && service->state != SERVICE_AUTO_RESTART) { - log_error_unit(UNIT(service)->id, + log_error_unit(u->id, "Socket service %s already active, refusing.", UNIT(service)->id); return -EBUSY; @@ -1677,7 +1653,7 @@ static int socket_start(Unit *u) { #ifdef HAVE_SYSV_COMPAT if (service->is_sysv) { - log_error_unit(UNIT(s)->id, + log_error_unit(u->id, "Using SysV services for socket activation is not supported. Refusing."); return -ENOENT; } @@ -1769,6 +1745,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { } } + exec_context_serialize(&s->exec_context, UNIT(s), f); + return 0; } @@ -1928,7 +1906,22 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, p->fd = fdset_remove(fds, fd); } } + } else if (streq(key, "tmp-dir")) { + char *t; + + t = strdup(value); + if (!t) + return log_oom(); + + s->exec_context.tmp_dir = t; + } else if (streq(key, "var-tmp-dir")) { + char *t; + t = strdup(value); + if (!t) + return log_oom(); + + s->exec_context.var_tmp_dir = t; } else log_debug_unit(UNIT(s)->id, "Unknown serialization key '%s'", key); @@ -1964,19 +1957,41 @@ static int socket_distribute_fds(Unit *u, FDSet *fds) { return 0; } -static UnitActiveState socket_active_state(Unit *u) { +_pure_ static UnitActiveState socket_active_state(Unit *u) { assert(u); return state_translation_table[SOCKET(u)->state]; } -static const char *socket_sub_state_to_string(Unit *u) { +_pure_ static const char *socket_sub_state_to_string(Unit *u) { assert(u); return socket_state_to_string(SOCKET(u)->state); } -static bool socket_check_gc(Unit *u) { +const char* socket_port_type_to_string(SocketPort *p) { + + assert(p); + + switch (p->type) { + case SOCKET_SOCKET: + switch (p->address.type) { + case SOCK_STREAM: return "Stream"; + case SOCK_DGRAM: return "Datagram"; + case SOCK_SEQPACKET: return "SequentialPacket"; + case SOCK_RAW: + if (socket_address_family(&p->address) == AF_NETLINK) + return "Netlink"; + default: return "Invalid"; + } + case SOCKET_SPECIAL: return "Special"; + case SOCKET_MQUEUE: return "MessageQueue"; + case SOCKET_FIFO: return "FIFO"; + default: return NULL; + } +} + +_pure_ static bool socket_check_gc(Unit *u) { Socket *s = SOCKET(u); assert(u); @@ -2292,53 +2307,7 @@ static void socket_reset_failed(Unit *u) { } static int socket_kill(Unit *u, KillWho who, int signo, DBusError *error) { - Socket *s = SOCKET(u); - int r = 0; - Set *pid_set = NULL; - - assert(s); - - if (who == KILL_MAIN) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Socket units have no main processes"); - return -ESRCH; - } - - if (s->control_pid <= 0 && who == KILL_CONTROL) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - return -ESRCH; - } - - if (who == KILL_CONTROL || who == KILL_ALL) - if (s->control_pid > 0) - if (kill(s->control_pid, signo) < 0) - r = -errno; - - if (who == KILL_ALL) { - int q; - - pid_set = set_new(trivial_hash_func, trivial_compare_func); - if (!pid_set) - return -ENOMEM; - - /* Exclude the control pid from being killed via the cgroup */ - if (s->control_pid > 0) { - q = set_put(pid_set, LONG_TO_PTR(s->control_pid)); - if (q < 0) { - r = q; - goto finish; - } - } - - q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL); - if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) - r = q; - } - -finish: - if (pid_set) - set_free(pid_set); - - return r; + return unit_kill_common(u, who, signo, -1, SOCKET(u)->control_pid, error); } static const char* const socket_state_table[_SOCKET_STATE_MAX] = { @@ -2381,13 +2350,15 @@ DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult); const UnitVTable socket_vtable = { .object_size = sizeof(Socket), - .exec_context_offset = offsetof(Socket, exec_context), .sections = "Unit\0" "Socket\0" "Install\0", + .exec_context_offset = offsetof(Socket, exec_context), + .exec_section = "Socket", + .init = socket_init, .done = socket_done, .load = socket_load, diff --git a/src/core/socket.h b/src/core/socket.h index f099520dce..9d48cde0a6 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -130,6 +130,10 @@ struct Socket { bool broadcast; bool pass_cred; bool pass_sec; + + /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ + SocketAddressBindIPv6Only bind_ipv6_only; + int priority; int mark; size_t receive_buffer; @@ -142,9 +146,6 @@ struct Socket { long mq_maxmsg; long mq_msgsize; - /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ - SocketAddressBindIPv6Only bind_ipv6_only; - char *smack; char *smack_ip_in; char *smack_ip_out; @@ -163,13 +164,17 @@ int socket_add_one_mount_link(Socket *s, Mount *m); /* Called from the service code when a per-connection service ended */ void socket_connection_unref(Socket *s); +void socket_free_ports(Socket *s); + extern const UnitVTable socket_vtable; -const char* socket_state_to_string(SocketState i); -SocketState socket_state_from_string(const char *s); +const char* socket_state_to_string(SocketState i) _const_; +SocketState socket_state_from_string(const char *s) _pure_; + +const char* socket_exec_command_to_string(SocketExecCommand i) _const_; +SocketExecCommand socket_exec_command_from_string(const char *s) _pure_; -const char* socket_exec_command_to_string(SocketExecCommand i); -SocketExecCommand socket_exec_command_from_string(const char *s); +const char* socket_result_to_string(SocketResult i) _const_; +SocketResult socket_result_from_string(const char *s) _pure_; -const char* socket_result_to_string(SocketResult i); -SocketResult socket_result_from_string(const char *s); +const char* socket_port_type_to_string(SocketPort *p) _pure_; diff --git a/src/core/special.h b/src/core/special.h index ef72260ecd..a9b50bce05 100644 --- a/src/core/special.h +++ b/src/core/special.h @@ -46,21 +46,23 @@ /* Early boot targets */ #define SPECIAL_SYSINIT_TARGET "sysinit.target" #define SPECIAL_SOCKETS_TARGET "sockets.target" -#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" /* LSB's $local_fs */ +#define SPECIAL_TIMERS_TARGET "timers.target" +#define SPECIAL_PATHS_TARGET "paths.target" +#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" #define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target" +#define SPECIAL_INITRD_FS_TARGET "initrd-fs.target" +#define SPECIAL_INITRD_ROOT_FS_TARGET "initrd-root-fs.target" #define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */ #define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target" #define SPECIAL_SWAP_TARGET "swap.target" +#define SPECIAL_NETWORK_ONLINE_TARGET "network-online.target" #define SPECIAL_BASIC_TARGET "basic.target" /* LSB compatibility */ #define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */ #define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */ #define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */ -#define SPECIAL_SYSLOG_TARGET "syslog.target" /* LSB's $syslog */ #define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */ -#define SPECIAL_DISPLAY_MANAGER_SERVICE "display-manager.service" /* Common extension of LSB */ -#define SPECIAL_MAIL_TRANSFER_AGENT_TARGET "mail-transfer-agent.target" /* Common extension of LSB */ /* * Rules regarding adding further high level targets like the above: diff --git a/src/core/swap.c b/src/core/swap.c index c8e25d0665..d503fe20df 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -125,7 +125,7 @@ static void swap_done(Unit *u) { free(s->parameters_fragment.what); s->parameters_fragment.what = NULL; - exec_context_done(&s->exec_context); + exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager)); exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX); s->control_command = NULL; @@ -214,7 +214,7 @@ static int swap_add_default_dependencies(Swap *s) { static int swap_verify(Swap *s) { bool b; - char _cleanup_free_ *e = NULL; + _cleanup_free_ char *e = NULL; if (UNIT(s)->load_state != UNIT_LOADED) return 0; @@ -315,7 +315,7 @@ static int swap_add_one( bool set_flags) { Unit *u = NULL; - char _cleanup_free_ *e = NULL; + _cleanup_free_ char *e = NULL; char *wp = NULL; bool delete = false; int r; @@ -632,6 +632,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) { if (f != SWAP_SUCCESS) s->result = f; + exec_context_tmp_dirs_done(&s->exec_context); swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); } @@ -646,57 +647,23 @@ static void swap_enter_active(Swap *s, SwapResult f) { static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { int r; - Set *pid_set = NULL; - bool wait_for_exit = false; assert(s); if (f != SWAP_SUCCESS) s->result = f; - if (s->kill_context.kill_mode != KILL_NONE) { - int sig = (state == SWAP_ACTIVATING_SIGTERM || - state == SWAP_DEACTIVATING_SIGTERM) ? s->kill_context.kill_signal : SIGKILL; - - if (s->control_pid > 0) { - if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH) - - log_warning_unit(UNIT(s)->id, - "Failed to kill control process %li: %m", - (long) s->control_pid); - else - wait_for_exit = true; - } - - if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) { - - pid_set = set_new(trivial_hash_func, trivial_compare_func); - if (!pid_set) { - r = log_oom(); - goto fail; - } - - /* Exclude the control pid from being killed via the cgroup */ - if (s->control_pid > 0) { - r = set_put(pid_set, LONG_TO_PTR(s->control_pid)); - if (r < 0) - goto fail; - } - - r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL); - if (r < 0) { - if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) - log_warning_unit(UNIT(s)->id, - "Failed to kill control group: %s", strerror(-r)); - } else if (r > 0) - wait_for_exit = true; - - set_free(pid_set); - pid_set = NULL; - } - } + r = unit_kill_context( + UNIT(s), + &s->kill_context, + state != SWAP_ACTIVATING_SIGTERM && state != SWAP_DEACTIVATING_SIGTERM, + -1, + s->control_pid, + false); + if (r < 0) + goto fail; - if (wait_for_exit) { + if (r > 0) { r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch); if (r < 0) goto fail; @@ -712,9 +679,6 @@ fail: "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); swap_enter_dead(s, SWAP_FAILURE_RESOURCES); - - if (pid_set) - set_free(pid_set); } static void swap_enter_activating(Swap *s) { @@ -868,6 +832,8 @@ static int swap_serialize(Unit *u, FILE *f, FDSet *fds) { if (s->control_command_id >= 0) unit_serialize_item(u, f, "control-command", swap_exec_command_to_string(s->control_command_id)); + exec_context_serialize(&s->exec_context, UNIT(s), f); + return 0; } @@ -911,26 +877,41 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD s->control_command_id = id; s->control_command = s->exec_command + id; } + } else if (streq(key, "tmp-dir")) { + char *t; + t = strdup(value); + if (!t) + return log_oom(); + + s->exec_context.tmp_dir = t; + } else if (streq(key, "var-tmp-dir")) { + char *t; + + t = strdup(value); + if (!t) + return log_oom(); + + s->exec_context.var_tmp_dir = t; } else log_debug_unit(u->id, "Unknown serialization key '%s'", key); return 0; } -static UnitActiveState swap_active_state(Unit *u) { +_pure_ static UnitActiveState swap_active_state(Unit *u) { assert(u); return state_translation_table[SWAP(u)->state]; } -static const char *swap_sub_state_to_string(Unit *u) { +_pure_ static const char *swap_sub_state_to_string(Unit *u) { assert(u); return swap_state_to_string(SWAP(u)->state); } -static bool swap_check_gc(Unit *u) { +_pure_ static bool swap_check_gc(Unit *u) { Swap *s = SWAP(u); assert(s); @@ -1261,10 +1242,14 @@ static void swap_shutdown(Manager *m) { static int swap_enumerate(Manager *m) { int r; - struct epoll_event ev; assert(m); if (!m->proc_swaps) { + struct epoll_event ev = { + .events = EPOLLPRI, + .data.ptr = &m->swap_watch, + }; + m->proc_swaps = fopen("/proc/swaps", "re"); if (!m->proc_swaps) return (errno == ENOENT) ? 0 : -errno; @@ -1272,10 +1257,6 @@ static int swap_enumerate(Manager *m) { m->swap_watch.type = WATCH_SWAP; m->swap_watch.fd = fileno(m->proc_swaps); - zero(ev); - ev.events = EPOLLPRI; - ev.data.ptr = &m->swap_watch; - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->swap_watch.fd, &ev) < 0) return -errno; } @@ -1299,53 +1280,7 @@ static void swap_reset_failed(Unit *u) { } static int swap_kill(Unit *u, KillWho who, int signo, DBusError *error) { - Swap *s = SWAP(u); - int r = 0; - Set *pid_set = NULL; - - assert(s); - - if (who == KILL_MAIN) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Swap units have no main processes"); - return -ESRCH; - } - - if (s->control_pid <= 0 && who == KILL_CONTROL) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - return -ESRCH; - } - - if (who == KILL_CONTROL || who == KILL_ALL) - if (s->control_pid > 0) - if (kill(s->control_pid, signo) < 0) - r = -errno; - - if (who == KILL_ALL) { - int q; - - pid_set = set_new(trivial_hash_func, trivial_compare_func); - if (!pid_set) - return -ENOMEM; - - /* Exclude the control pid from being killed via the cgroup */ - if (s->control_pid > 0) { - q = set_put(pid_set, LONG_TO_PTR(s->control_pid)); - if (q < 0) { - r = q; - goto finish; - } - } - - q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL); - if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) - r = q; - } - -finish: - if (pid_set) - set_free(pid_set); - - return r; + return unit_kill_common(u, who, signo, -1, SWAP(u)->control_pid, error); } static const char* const swap_state_table[_SWAP_STATE_MAX] = { @@ -1382,13 +1317,15 @@ DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult); const UnitVTable swap_vtable = { .object_size = sizeof(Swap), - .exec_context_offset = offsetof(Swap, exec_context), .sections = "Unit\0" "Swap\0" "Install\0", + .exec_context_offset = offsetof(Swap, exec_context), + .exec_section = "Swap", + .no_alias = true, .no_instances = true, diff --git a/src/core/swap.h b/src/core/swap.h index 35d47fd46f..121889d1d5 100644 --- a/src/core/swap.h +++ b/src/core/swap.h @@ -111,11 +111,11 @@ int swap_add_one_mount_link(Swap *s, Mount *m); int swap_dispatch_reload(Manager *m); int swap_fd_event(Manager *m, int events); -const char* swap_state_to_string(SwapState i); -SwapState swap_state_from_string(const char *s); +const char* swap_state_to_string(SwapState i) _const_; +SwapState swap_state_from_string(const char *s) _pure_; -const char* swap_exec_command_to_string(SwapExecCommand i); -SwapExecCommand swap_exec_command_from_string(const char *s); +const char* swap_exec_command_to_string(SwapExecCommand i) _const_; +SwapExecCommand swap_exec_command_from_string(const char *s) _pure_; -const char* swap_result_to_string(SwapResult i); -SwapResult swap_result_from_string(const char *s); +const char* swap_result_to_string(SwapResult i) _const_; +SwapResult swap_result_from_string(const char *s) _pure_; diff --git a/src/core/sync.c b/src/core/sync.c new file mode 100644 index 0000000000..7e74b63071 --- /dev/null +++ b/src/core/sync.c @@ -0,0 +1,65 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <pthread.h> +#include <unistd.h> + +#include "sync.h" + +static void *sync_thread(void *p) { + sync(); + return NULL; +} + +int asynchronous_sync(void) { + pthread_attr_t a; + pthread_t t; + int r; + + /* It kinda sucks that we have to resort to threads to + * implement an asynchronous sync(), but well, such is + * life. + * + * Note that issuing this command right before exiting a + * process will cause the process to wait for the sync() to + * complete. This function hence is nicely asynchronous really + * only in long running processes. */ + + r = pthread_attr_init(&a); + if (r != 0) + return -r; + + r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); + if (r != 0) { + r = -r; + goto finish; + } + + r = pthread_create(&t, &a, sync_thread, NULL); + if (r != 0) { + r = -r; + goto finish; + } + +finish: + pthread_attr_destroy(&a); + return r; +} diff --git a/src/core/sync.h b/src/core/sync.h new file mode 100644 index 0000000000..eb26c88deb --- /dev/null +++ b/src/core/sync.h @@ -0,0 +1,24 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +int asynchronous_sync(void); diff --git a/src/core/syscall-list.c b/src/core/syscall-list.c index 05fad3e158..35216b2a88 100644 --- a/src/core/syscall-list.c +++ b/src/core/syscall-list.c @@ -26,12 +26,14 @@ #include "syscall-list.h" -const struct syscall_name *lookup_syscall(register const char *str, register unsigned int len); +static const struct syscall_name* lookup_syscall(register const char *str, + register unsigned int len); #include "syscall-to-name.h" #include "syscall-from-name.h" const char *syscall_to_name(int id) { + id = SYSCALL_TO_INDEX(id); if (id < 0 || id >= (int) ELEMENTSOF(syscall_names)) return NULL; diff --git a/src/core/syscall-list.h b/src/core/syscall-list.h index 0fc6859605..6bb4d91f91 100644 --- a/src/core/syscall-list.h +++ b/src/core/syscall-list.h @@ -22,6 +22,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#if defined __x86_64__ && defined __ILP32__ +/* The x32 ABI defines all of its syscalls with bit 30 set, which causes + issues when attempting to use syscalls as simple indices into an array. + Instead, use the syscall id & ~SYSCALL_MASK as the index, and | the + internal id with the syscall mask as needed. +*/ +#include <asm/unistd.h> +#define SYSCALL_TO_INDEX(x) ((x) & ~__X32_SYSCALL_BIT) +#define INDEX_TO_SYSCALL(x) ((x) | __X32_SYSCALL_BIT) +#else +#define SYSCALL_TO_INDEX(x) (x) +#define INDEX_TO_SYSCALL(x) (x) +#endif + const char *syscall_to_name(int id); int syscall_from_name(const char *name); diff --git a/src/core/system.conf b/src/core/system.conf index 68076d9735..508e0f5fa4 100644 --- a/src/core/system.conf +++ b/src/core/system.conf @@ -5,7 +5,7 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # -# See systemd.conf(5) for details +# See systemd-system.conf(5) for details [Manager] #LogLevel=info diff --git a/src/core/target.c b/src/core/target.c index 981424132b..3fffa0d2f5 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -184,13 +184,13 @@ static int target_deserialize_item(Unit *u, const char *key, const char *value, return 0; } -static UnitActiveState target_active_state(Unit *u) { +_pure_ static UnitActiveState target_active_state(Unit *u) { assert(u); return state_translation_table[TARGET(u)->state]; } -static const char *target_sub_state_to_string(Unit *u) { +_pure_ static const char *target_sub_state_to_string(Unit *u) { assert(u); return target_state_to_string(TARGET(u)->state); diff --git a/src/core/target.h b/src/core/target.h index 1676553add..a5398d9e98 100644 --- a/src/core/target.h +++ b/src/core/target.h @@ -40,5 +40,5 @@ struct Target { extern const UnitVTable target_vtable; -const char* target_state_to_string(TargetState i); -TargetState target_state_from_string(const char *s); +const char* target_state_to_string(TargetState i) _const_; +TargetState target_state_from_string(const char *s) _pure_; diff --git a/src/core/timer.c b/src/core/timer.c index 31ef176e7e..9166c1e2fc 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -26,7 +26,7 @@ #include "timer.h" #include "dbus-timer.h" #include "special.h" -#include "bus-errors.h" +#include "dbus-common.h" static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = { [TIMER_DEAD] = UNIT_INACTIVE, @@ -48,8 +48,7 @@ static void timer_init(Unit *u) { watch_init(&t->realtime_watch); } -static void timer_done(Unit *u) { - Timer *t = TIMER(u); +void timer_free_values(Timer *t) { TimerValue *v; assert(t); @@ -62,11 +61,17 @@ static void timer_done(Unit *u) { free(v); } +} + +static void timer_done(Unit *u) { + Timer *t = TIMER(u); + + assert(t); + + timer_free_values(t); unit_unwatch_timer(u, &t->monotonic_watch); unit_unwatch_timer(u, &t->realtime_watch); - - unit_ref_unset(&t->unit); } static int timer_verify(Timer *t) { @@ -89,11 +94,11 @@ static int timer_add_default_dependencies(Timer *t) { assert(t); - if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) { - r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true); - if (r < 0) - return r; + r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true); + if (r < 0) + return r; + if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) { r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); if (r < 0) return r; @@ -115,20 +120,18 @@ static int timer_load(Unit *u) { if (u->load_state == UNIT_LOADED) { - if (!UNIT_DEREF(t->unit)) { + if (set_isempty(u->dependencies[UNIT_TRIGGERS])) { Unit *x; r = unit_load_related_unit(u, ".service", &x); if (r < 0) return r; - unit_ref_set(&t->unit, x); + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true); + if (r < 0) + return r; } - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true); - if (r < 0) - return r; - if (UNIT(t)->default_dependencies) { r = timer_add_default_dependencies(t); if (r < 0) @@ -141,15 +144,18 @@ static int timer_load(Unit *u) { static void timer_dump(Unit *u, FILE *f, const char *prefix) { Timer *t = TIMER(u); + Unit *trigger; TimerValue *v; + trigger = UNIT_TRIGGER(u); + fprintf(f, "%sTimer State: %s\n" "%sResult: %s\n" "%sUnit: %s\n", prefix, timer_state_to_string(t->state), prefix, timer_result_to_string(t->result), - prefix, UNIT_DEREF(t->unit)->id); + prefix, trigger ? trigger->id : "n/a"); LIST_FOREACH(value, v, t->values) { @@ -170,7 +176,7 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) { "%s%s: %s\n", prefix, timer_base_to_string(v->base), - strna(format_timespan(timespan1, sizeof(timespan1), v->value))); + strna(format_timespan(timespan1, sizeof(timespan1), v->value, 0))); } } } @@ -245,11 +251,6 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (r < 0) continue; - if (!initial && v->next_elapse < ts.realtime) { - v->disabled = true; - continue; - } - if (!found_realtime) t->next_elapse_realtime = v->next_elapse; else @@ -278,18 +279,26 @@ static void timer_enter_waiting(Timer *t, bool initial) { case TIMER_UNIT_ACTIVE: - if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0) + base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic; + + if (base <= 0) + base = t->last_trigger_monotonic; + + if (base <= 0) continue; - base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic; break; case TIMER_UNIT_INACTIVE: - if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0) + base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic; + + if (base <= 0) + base = t->last_trigger_monotonic; + + if (base <= 0) continue; - base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic; break; default: @@ -298,7 +307,10 @@ static void timer_enter_waiting(Timer *t, bool initial) { v->next_elapse = base + v->value; - if (!initial && v->next_elapse < ts.monotonic) { + if (!initial && + v->next_elapse < ts.monotonic && + (v->base == TIMER_ACTIVE || v->base == TIMER_BOOT || v->base == TIMER_STARTUP)) { + /* This is a one time trigger, disable it now */ v->disabled = true; continue; } @@ -321,9 +333,9 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (found_monotonic) { char buf[FORMAT_TIMESPAN_MAX]; log_debug_unit(UNIT(t)->id, - "%s: Monotonic timer elapses in %s the next time.", + "%s: Monotonic timer elapses in %s.", UNIT(t)->id, - format_timespan(buf, sizeof(buf), t->next_elapse_monotonic - ts.monotonic)); + format_timespan(buf, sizeof(buf), t->next_elapse_monotonic > ts.monotonic ? t->next_elapse_monotonic - ts.monotonic : 0, 0)); r = unit_watch_timer(UNIT(t), CLOCK_MONOTONIC, false, t->next_elapse_monotonic, &t->monotonic_watch); if (r < 0) @@ -334,7 +346,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (found_realtime) { char buf[FORMAT_TIMESTAMP_MAX]; log_debug_unit(UNIT(t)->id, - "%s: Realtime timer elapses at %s the next time.", + "%s: Realtime timer elapses at %s.", UNIT(t)->id, format_timestamp(buf, sizeof(buf), t->next_elapse_realtime)); @@ -362,13 +374,16 @@ static void timer_enter_running(Timer *t) { dbus_error_init(&error); /* Don't start job if we are supposed to go down */ - if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP) + if (unit_stop_pending(UNIT(t))) return; - r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)), + JOB_REPLACE, true, &error, NULL); if (r < 0) goto fail; + t->last_trigger_monotonic = now(CLOCK_MONOTONIC); + timer_set_state(t, TIMER_RUNNING); return; @@ -387,7 +402,7 @@ static int timer_start(Unit *u) { assert(t); assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED); - if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED) + if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED) return -ENOENT; t->result = TIMER_SUCCESS; @@ -449,13 +464,13 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F return 0; } -static UnitActiveState timer_active_state(Unit *u) { +_pure_ static UnitActiveState timer_active_state(Unit *u) { assert(u); return state_translation_table[TIMER(u)->state]; } -static const char *timer_sub_state_to_string(Unit *u) { +_pure_ static const char *timer_sub_state_to_string(Unit *u) { assert(u); return timer_state_to_string(TIMER(u)->state); @@ -474,58 +489,47 @@ static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) { timer_enter_running(t); } -void timer_unit_notify(Unit *u, UnitActiveState new_state) { - Iterator i; - Unit *k; - - if (u->type == UNIT_TIMER) - return; +static void timer_trigger_notify(Unit *u, Unit *other) { + Timer *t = TIMER(u); + TimerValue *v; - SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) { - Timer *t; - TimerValue *v; + assert(u); + assert(other); - if (k->type != UNIT_TIMER) - continue; + if (other->load_state != UNIT_LOADED) + return; - if (k->load_state != UNIT_LOADED) - continue; + /* Reenable all timers that depend on unit state */ + LIST_FOREACH(value, v, t->values) + if (v->base == TIMER_UNIT_ACTIVE || + v->base == TIMER_UNIT_INACTIVE) + v->disabled = false; - t = TIMER(k); + switch (t->state) { - /* Reenable all timers that depend on unit state */ - LIST_FOREACH(value, v, t->values) - if (v->base == TIMER_UNIT_ACTIVE || - v->base == TIMER_UNIT_INACTIVE) - v->disabled = false; + case TIMER_WAITING: + case TIMER_ELAPSED: - switch (t->state) { + /* Recalculate sleep time */ + timer_enter_waiting(t, false); + break; - case TIMER_WAITING: - case TIMER_ELAPSED: + case TIMER_RUNNING: - /* Recalculate sleep time */ + if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) { + log_debug_unit(UNIT(t)->id, + "%s got notified about unit deactivation.", + UNIT(t)->id); timer_enter_waiting(t, false); - break; - - case TIMER_RUNNING: - - if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) { - log_debug_unit(UNIT(t)->id, - "%s got notified about unit deactivation.", - UNIT(t)->id); - timer_enter_waiting(t, false); - } - - break; + } + break; - case TIMER_DEAD: - case TIMER_FAILED: - break; + case TIMER_DEAD: + case TIMER_FAILED: + break; - default: - assert_not_reached("Unknown timer state"); - } + default: + assert_not_reached("Unknown timer state"); } } @@ -548,8 +552,8 @@ static void timer_time_change(Unit *u) { if (t->state != TIMER_WAITING) return; - log_info_unit(u->id, - "%s: time change, recalculating next elapse.", u->id); + log_debug_unit(u->id, + "%s: time change, recalculating next elapse.", u->id); timer_enter_waiting(t, false); } @@ -607,6 +611,8 @@ const UnitVTable timer_vtable = { .timer_event = timer_timer_event, + .trigger_notify = timer_trigger_notify, + .reset_failed = timer_reset_failed, .time_change = timer_time_change, diff --git a/src/core/timer.h b/src/core/timer.h index 57a514a68c..4168553e9d 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -52,8 +52,8 @@ typedef struct TimerValue { bool disabled; clockid_t clock_id; - usec_t value; - CalendarSpec *calendar_spec; + usec_t value; /* only for monotonic events */ + CalendarSpec *calendar_spec; /* only for calendar events */ usec_t next_elapse; LIST_FIELDS(struct TimerValue, value); @@ -74,23 +74,24 @@ struct Timer { usec_t next_elapse_realtime; TimerState state, deserialized_state; - UnitRef unit; Watch monotonic_watch; Watch realtime_watch; TimerResult result; + + usec_t last_trigger_monotonic; }; -void timer_unit_notify(Unit *u, UnitActiveState new_state); +void timer_free_values(Timer *t); extern const UnitVTable timer_vtable; -const char *timer_state_to_string(TimerState i); -TimerState timer_state_from_string(const char *s); +const char *timer_state_to_string(TimerState i) _const_; +TimerState timer_state_from_string(const char *s) _pure_; -const char *timer_base_to_string(TimerBase i); -TimerBase timer_base_from_string(const char *s); +const char *timer_base_to_string(TimerBase i) _const_; +TimerBase timer_base_from_string(const char *s) _pure_; -const char* timer_result_to_string(TimerResult i); -TimerResult timer_result_from_string(const char *s); +const char* timer_result_to_string(TimerResult i) _const_; +TimerResult timer_result_from_string(const char *s) _pure_; diff --git a/src/core/transaction.c b/src/core/transaction.c index 1854047afd..fa97b69755 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -24,6 +24,7 @@ #include "transaction.h" #include "bus-errors.h" +#include "dbus-common.h" static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies); @@ -97,6 +98,7 @@ static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other j->type = t; j->state = JOB_WAITING; j->override = j->override || other->override; + j->irreversible = j->irreversible || other->irreversible; j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor; @@ -138,7 +140,7 @@ static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other transaction_delete_job(tr, other, true); } -static bool job_is_conflicted_by(Job *j) { +_pure_ static bool job_is_conflicted_by(Job *j) { JobDependency *l; assert(j); @@ -318,7 +320,7 @@ rescan: } } -static bool unit_matters_to_anchor(Unit *u, Job *j) { +_pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) { assert(u); assert(!j->transaction_prev); @@ -395,8 +397,8 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi "Job %s/%s deleted to break ordering cycle starting with %s/%s", delete->unit->id, job_type_to_string(delete->type), j->unit->id, job_type_to_string(j->type)); - status_printf(ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF, true, - "Ordering cycle found, skipping %s", unit_description(delete->unit)); + unit_status_printf(delete->unit, ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF, + "Ordering cycle found, skipping %s"); transaction_delete_unit(tr, delete->unit); return -EAGAIN; } @@ -488,7 +490,7 @@ rescan: } } -static int transaction_is_destructive(Transaction *tr, DBusError *e) { +static int transaction_is_destructive(Transaction *tr, JobMode mode, DBusError *e) { Iterator i; Job *j; @@ -503,7 +505,7 @@ static int transaction_is_destructive(Transaction *tr, DBusError *e) { assert(!j->transaction_prev); assert(!j->transaction_next); - if (j->unit->job && + if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) && !job_type_is_superset(j->type, j->unit->job->type)) { dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive."); @@ -620,6 +622,7 @@ static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) { job_add_to_run_queue(j); job_add_to_dbus_queue(j); job_start_timer(j); + job_shutdown_magic(j); } return 0; @@ -708,12 +711,10 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e transaction_drop_redundant(tr); /* Ninth step: check whether we can actually apply this */ - if (mode == JOB_FAIL) { - r = transaction_is_destructive(tr, e); - if (r < 0) { - log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r)); - return r; - } + r = transaction_is_destructive(tr, mode, e); + if (r < 0) { + log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r)); + return r; } /* Tenth step: apply changes */ @@ -769,6 +770,7 @@ static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, b j->marker = NULL; j->matters_to_anchor = false; j->override = override; + j->irreversible = tr->irreversible; LIST_PREPEND(Job, transaction, f, j); @@ -1105,7 +1107,7 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) { return 0; } -Transaction *transaction_new(void) { +Transaction *transaction_new(bool irreversible) { Transaction *tr; tr = new0(Transaction, 1); @@ -1118,6 +1120,8 @@ Transaction *transaction_new(void) { return NULL; } + tr->irreversible = irreversible; + return tr; } diff --git a/src/core/transaction.h b/src/core/transaction.h index 67ace4da0b..12f9194927 100644 --- a/src/core/transaction.h +++ b/src/core/transaction.h @@ -33,9 +33,10 @@ struct Transaction { /* Jobs to be added */ Hashmap *jobs; /* Unit object => Job object list 1:1 */ Job *anchor_job; /* the job the user asked for */ + bool irreversible; }; -Transaction *transaction_new(void); +Transaction *transaction_new(bool irreversible); void transaction_free(Transaction *tr); int transaction_add_job_and_dependencies( diff --git a/src/core/umount.c b/src/core/umount.c index 96232d38db..1e95ad70dd 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -219,7 +219,8 @@ static int loopback_list_get(MountPoint **head) { } if (udev_enumerate_add_match_subsystem(e, "block") < 0 || - udev_enumerate_add_match_sysname(e, "loop*") < 0) { + udev_enumerate_add_match_sysname(e, "loop*") < 0 || + udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL) < 0) { r = -EIO; goto finish; } @@ -378,25 +379,23 @@ static int delete_loopback(const char *device) { } static int delete_dm(dev_t devnum) { - int fd, r; - struct dm_ioctl dm; + _cleanup_close_ int fd = -1; + int r; + struct dm_ioctl dm = { + .version = {DM_VERSION_MAJOR, + DM_VERSION_MINOR, + DM_VERSION_PATCHLEVEL}, + .data_size = sizeof(dm), + .dev = devnum, + }; assert(major(devnum) != 0); - if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0) + fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC); + if (fd < 0) return -errno; - zero(dm); - dm.version[0] = DM_VERSION_MAJOR; - dm.version[1] = DM_VERSION_MINOR; - dm.version[2] = DM_VERSION_PATCHLEVEL; - - dm.data_size = sizeof(dm); - dm.dev = devnum; - r = ioctl(fd, DM_DEV_REMOVE, &dm); - close_nointr_nofail(fd); - return r >= 0 ? 0 : -errno; } @@ -419,7 +418,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e * * Mount points can be stacked. If a mount * point is stacked below / or /usr, we - * cannnot umount or remount it directly, + * cannot umount or remount it directly, * since there is no way to refer to the * underlying mount. There's nothing we can do * about it for the general case, but we can @@ -442,9 +441,11 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e ) continue; - /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */ + /* Trying to umount. We don't force here since we rely + * on busy NFS and FUSE file systems to return EBUSY + * until we closed everything on top of them. */ log_info("Unmounting %s.", m->path); - if (umount2(m->path, MNT_FORCE) == 0) { + if (umount2(m->path, 0) == 0) { if (changed) *changed = true; diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index a58c96c238..85a05b872a 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -26,6 +26,7 @@ #include "strv.h" #include "unit-name.h" #include "unit-printf.h" +#include "macro.h" static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) { Unit *u = userdata; @@ -123,6 +124,7 @@ static char *specifier_user_name(char specifier, void *data, void *userdata) { ExecContext *c; int r; const char *username; + _cleanup_free_ char *tmp = NULL; uid_t uid; char *printed = NULL; @@ -130,12 +132,13 @@ static char *specifier_user_name(char specifier, void *data, void *userdata) { c = unit_get_exec_context(u); - /* get USER env from our own env if set */ - if (!c || !c->user) - return getusername_malloc(); + if (c && c->user) + username = c->user; + else + /* get USER env from env or our own uid */ + username = tmp = getusername_malloc(); /* fish username from passwd */ - username = c->user; r = get_user_creds(&username, &uid, NULL, NULL, NULL); if (r < 0) return NULL; @@ -187,64 +190,37 @@ static char *specifier_user_shell(char specifier, void *data, void *userdata) { ExecContext *c; int r; const char *username, *shell; + char *ret; assert(u); c = unit_get_exec_context(u); - /* return HOME if set, otherwise from passwd */ - if (!c || !c->user) { - char *sh; - - r = get_shell(&sh); - if (r < 0) - return strdup("/bin/sh"); - - return sh; - } + if (c && c->user) + username = c->user; + else + username = "root"; - username = c->user; + /* return /bin/sh for root, otherwise the value from passwd */ r = get_user_creds(&username, NULL, NULL, NULL, &shell); - if (r < 0) - return strdup("/bin/sh"); - - return strdup(shell); -} - -static char *specifier_machine_id(char specifier, void *data, void *userdata) { - sd_id128_t id; - char *buf; - int r; - - r = sd_id128_get_machine(&id); - if (r < 0) - return NULL; - - buf = new(char, 33); - if (!buf) - return NULL; - - return sd_id128_to_string(id, buf); -} - -static char *specifier_boot_id(char specifier, void *data, void *userdata) { - sd_id128_t id; - char *buf; - int r; - - r = sd_id128_get_boot(&id); - if (r < 0) + if (r < 0) { + log_warning_unit(u->id, + "Failed to determine shell: %s", + strerror(-r)); return NULL; + } - buf = new(char, 33); - if (!buf) - return NULL; + if (!path_is_absolute(shell)) { + log_warning_unit(u->id, + "Shell %s is not absolute, ignoring.", + shell); + } - return sd_id128_to_string(id, buf); -} + ret = strdup(shell); + if (!ret) + log_oom(); -static char *specifier_host_name(char specifier, void *data, void *userdata) { - return gethostname_malloc(); + return ret; } char *unit_name_printf(Unit *u, const char* format) { @@ -283,12 +259,13 @@ char *unit_full_printf(Unit *u, const char *format) { * %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711") * %R parent of root cgroup path (e.g. "/usr/lennart/shared") * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR) + * %U the UID of the configured user or running user * %u the username of the configured user or running user * %h the homedir of the configured user or running user * %s the shell of the configured user or running user * %m the machine ID of the running system - * %b the boot ID of the running system * %H the host name of the running system + * %b the boot ID of the running system */ const Specifier table[] = { @@ -315,7 +292,6 @@ char *unit_full_printf(Unit *u, const char *format) { { 0, NULL, NULL } }; - assert(u); assert(format); return specifier_printf(format, table, u); diff --git a/src/core/unit.c b/src/core/unit.c index 45453dce64..9b36b225fa 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -45,6 +45,10 @@ #include "cgroup-util.h" #include "missing.h" #include "cgroup-attr.h" +#include "mkdir.h" +#include "label.h" +#include "fileio-label.h" +#include "bus-errors.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -404,6 +408,7 @@ void unit_free(Unit *u) { strv_free(u->documentation); free(u->fragment_path); free(u->source_path); + strv_free(u->dropin_paths); free(u->instance); set_free_free(u->names); @@ -613,9 +618,11 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { /* If syslog or kernel logging is requested, make sure our own * logging daemon is run first. */ - if (u->manager->running_as == SYSTEMD_SYSTEM) - if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true)) < 0) + if (u->manager->running_as == SYSTEMD_SYSTEM) { + r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true); + if (r < 0) return r; + } return 0; } @@ -690,8 +697,11 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { if (u->source_path) fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path); + STRV_FOREACH(j, u->dropin_paths) + fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j); + if (u->job_timeout > 0) - fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout)); + fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0)); condition_dump_list(u->conditions, f, prefix); @@ -744,15 +754,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, b->controller, b->path); LIST_FOREACH(by_unit, a, u->cgroup_attributes) { - char *v = NULL; + _cleanup_free_ char *v = NULL; - if (a->map_callback) - a->map_callback(a->controller, a->name, a->value, &v); + if (a->semantics && a->semantics->map_write) + a->semantics->map_write(a->semantics, a->value, &v); fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n", prefix, a->controller, a->name, v ? v : a->value); - - free(v); } if (UNIT_VTABLE(u)->dump) @@ -906,8 +914,8 @@ int unit_load(Unit *u) { if (u->on_failure_isolate && set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { - log_error("More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", - u->id); + log_error_unit(u->id, + "More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", u->id); r = -EINVAL; goto fail; @@ -926,7 +934,8 @@ fail: unit_add_to_dbus_queue(u); unit_add_to_gc_queue(u); - log_debug("Failed to load configuration for %s: %s", u->id, strerror(-r)); + log_debug_unit(u->id, "Failed to load configuration for %s: %s", + u->id, strerror(-r)); return r; } @@ -940,7 +949,7 @@ bool unit_condition_test(Unit *u) { return u->condition_result; } -static const char* unit_get_status_message_format(Unit *u, JobType t) { +_pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) { const UnitStatusMessageFormats *format_table; assert(u); @@ -957,7 +966,7 @@ static const char* unit_get_status_message_format(Unit *u, JobType t) { return format_table->starting_stopping[t == JOB_STOP]; } -static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) { +_pure_ static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) { const char *format; assert(u); @@ -991,7 +1000,7 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) { if (!format) return; - unit_status_printf(u, "", format, unit_description(u)); + unit_status_printf(u, "", format); } #pragma GCC diagnostic push @@ -1022,11 +1031,11 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING : SD_MESSAGE_UNIT_RELOADING; - log_struct(LOG_INFO, - MESSAGE_ID(mid), - "UNIT=%s", u->id, - "MESSAGE=%s", buf, - NULL); + log_struct_unit(LOG_INFO, + u->id, + MESSAGE_ID(mid), + "MESSAGE=%s", buf, + NULL); } #pragma GCC diagnostic pop @@ -1059,13 +1068,14 @@ int unit_start(Unit *u) { * but we don't want to recheck the condition in that case. */ if (state != UNIT_ACTIVATING && !unit_condition_test(u)) { - log_debug("Starting of %s requested but condition failed. Ignoring.", u->id); + log_debug_unit(u->id, "Starting of %s requested but condition failed. Ignoring.", u->id); return -EALREADY; } /* Forward to the main object, if we aren't it. */ if ((following = unit_following(u))) { - log_debug("Redirecting start request from %s to %s.", u->id, following->id); + log_debug_unit(u->id, "Redirecting start request from %s to %s.", + u->id, following->id); return unit_start(following); } @@ -1116,7 +1126,8 @@ int unit_stop(Unit *u) { return -EALREADY; if ((following = unit_following(u))) { - log_debug("Redirecting stop request from %s to %s.", u->id, following->id); + log_debug_unit(u->id, "Redirecting stop request from %s to %s.", + u->id, following->id); return unit_stop(following); } @@ -1156,7 +1167,8 @@ int unit_reload(Unit *u) { return -ENOEXEC; if ((following = unit_following(u))) { - log_debug("Redirecting reload request from %s to %s.", u->id, following->id); + log_debug_unit(u->id, "Redirecting reload request from %s to %s.", + u->id, following->id); return unit_reload(following); } @@ -1194,22 +1206,22 @@ static void unit_check_unneeded(Unit *u) { return; SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; - log_info("Service %s is not needed anymore. Stopping.", u->id); + log_info_unit(u->id, "Service %s is not needed anymore. Stopping.", u->id); /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); @@ -1237,11 +1249,6 @@ static void retroactively_start_dependencies(Unit *u) { !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); - SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i) - if (!set_get(u->dependencies[UNIT_AFTER], other) && - !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); - SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) if (!set_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) @@ -1297,7 +1304,7 @@ static void check_unneeded_dependencies(Unit *u) { unit_check_unneeded(other); } -void unit_trigger_on_failure(Unit *u) { +void unit_start_on_failure(Unit *u) { Unit *other; Iterator i; @@ -1306,17 +1313,30 @@ void unit_trigger_on_failure(Unit *u) { if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0) return; - log_info("Triggering OnFailure= dependencies of %s.", u->id); + log_info_unit(u->id, "Triggering OnFailure= dependencies of %s.", u->id); SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) { int r; - if ((r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL)) < 0) - log_error("Failed to enqueue OnFailure= job: %s", strerror(-r)); + r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL); + if (r < 0) + log_error_unit(u->id, "Failed to enqueue OnFailure= job: %s", strerror(-r)); } } +void unit_trigger_notify(Unit *u) { + Unit *other; + Iterator i; + + assert(u); + + SET_FOREACH(other, u->dependencies[UNIT_TRIGGERED_BY], i) + if (UNIT_VTABLE(other)->trigger_notify) + UNIT_VTABLE(other)->trigger_notify(other, u); +} + void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) { + Manager *m; bool unexpected; assert(u); @@ -1329,7 +1349,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su * behavior here. For example: if a mount point is remounted * this function will be called too! */ - if (u->manager->n_reloading <= 0) { + m = u->manager; + + if (m->n_reloading <= 0) { dual_timestamp ts; dual_timestamp_get(&ts); @@ -1343,14 +1365,21 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su u->active_enter_timestamp = ts; else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) u->active_exit_timestamp = ts; - - timer_unit_notify(u, ns); - path_unit_notify(u, ns); } if (UNIT_IS_INACTIVE_OR_FAILED(ns)) cgroup_bonding_trim_list(u->cgroup_bondings, true); + if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) { + ExecContext *ec = unit_get_exec_context(u); + if (ec && exec_context_may_touch_console(ec)) { + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) + m->n_on_console--; + else + m->n_on_console++; + } + } + if (u->job) { unexpected = false; @@ -1417,7 +1446,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } else unexpected = true; - if (u->manager->n_reloading <= 0) { + if (m->n_reloading <= 0) { /* If this state change happened without being * requested by a job, then let's retroactively start @@ -1438,11 +1467,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su check_unneeded_dependencies(u); if (ns != os && ns == UNIT_FAILED) { - log_struct(LOG_NOTICE, - "MESSAGE=Unit %s entered failed state", u->id, - "UNIT=%s", u->id, - NULL); - unit_trigger_on_failure(u); + log_notice_unit(u->id, + "Unit %s entered failed state.", u->id); + unit_start_on_failure(u); } } @@ -1453,18 +1480,18 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su /* The bus just might have become available, * hence try to connect to it, if we aren't * yet connected. */ - bus_init(u->manager, true); + bus_init(m, true); if (u->type == UNIT_SERVICE && !UNIT_IS_ACTIVE_OR_RELOADING(os) && - u->manager->n_reloading <= 0) { + m->n_reloading <= 0) { /* Write audit record if we have just finished starting up */ - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true); + manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true); u->in_audit = true; } if (!UNIT_IS_ACTIVE_OR_RELOADING(os)) - manager_send_unit_plymouth(u->manager, u); + manager_send_unit_plymouth(m, u); } else { @@ -1474,46 +1501,47 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->type == UNIT_SERVICE && UNIT_IS_INACTIVE_OR_FAILED(ns) && !UNIT_IS_INACTIVE_OR_FAILED(os) && - u->manager->n_reloading <= 0) { + m->n_reloading <= 0) { /* Hmm, if there was no start record written * write it now, so that we always have a nice * pair */ if (!u->in_audit) { - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE); + manager_send_unit_audit(m, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE); if (ns == UNIT_INACTIVE) - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true); + manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, true); } else /* Write audit record if we have just finished shutting down */ - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE); + manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE); u->in_audit = false; } } - manager_recheck_journal(u->manager); + manager_recheck_journal(m); + unit_trigger_notify(u); /* Maybe we finished startup and are now ready for being * stopped because unneeded? */ - unit_check_unneeded(u); + if (u->manager->n_reloading <= 0) + unit_check_unneeded(u); unit_add_to_dbus_queue(u); unit_add_to_gc_queue(u); } int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) { - struct epoll_event ev; + struct epoll_event ev = { + .data.ptr = w, + .events = events, + }; assert(u); assert(fd >= 0); assert(w); assert(w->type == WATCH_INVALID || (w->type == WATCH_FD && w->fd == fd && w->data.unit == u)); - zero(ev); - ev.data.ptr = w; - ev.events = events; - if (epoll_ctl(u->manager->epoll_fd, w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, @@ -1561,7 +1589,7 @@ void unit_unwatch_pid(Unit *u, pid_t pid) { } int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Watch *w) { - struct itimerspec its; + struct itimerspec its = {}; int flags, fd; bool ours; @@ -1586,8 +1614,6 @@ int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Wa } else assert_not_reached("Invalid watch type"); - zero(its); - if (usec <= 0) { /* Set absolute time in the past, but not 0, since we * don't want to disarm the timer */ @@ -1605,11 +1631,10 @@ int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Wa goto fail; if (w->type == WATCH_INVALID) { - struct epoll_event ev; - - zero(ev); - ev.data.ptr = w; - ev.events = EPOLLIN; + struct epoll_event ev = { + .data.ptr = w, + .events = EPOLLIN, + }; if (epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) goto fail; @@ -1781,6 +1806,7 @@ static const char *resolve_template(Unit *u, const char *name, const char*path, assert(u); assert(name || path); + assert(p); if (!name) name = path_get_file_name(path); @@ -1793,13 +1819,13 @@ static const char *resolve_template(Unit *u, const char *name, const char*path, if (u->instance) s = unit_name_replace_instance(name, u->instance); else { - char *i; + _cleanup_free_ char *i = NULL; - if (!(i = unit_name_to_prefix(u->id))) + i = unit_name_to_prefix(u->id); + if (!i) return NULL; s = unit_name_replace_instance(name, i); - free(i); } if (!s) @@ -1812,22 +1838,20 @@ static const char *resolve_template(Unit *u, const char *name, const char*path, int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { Unit *other; int r; - char *s; + _cleanup_free_ char *s = NULL; assert(u); assert(name || path); - if (!(name = resolve_template(u, name, path, &s))) + name = resolve_template(u, name, path, &s); + if (!name) return -ENOMEM; - if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0) - goto finish; - - r = unit_add_dependency(u, d, other, add_reference); + r = manager_load_unit(u->manager, name, path, NULL, &other); + if (r < 0) + return r; -finish: - free(s); - return r; + return unit_add_dependency(u, d, other, add_reference); } int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) { @@ -1895,30 +1919,12 @@ finish: } int set_unit_path(const char *p) { - char *cwd, *c; - int r; + _cleanup_free_ char *c = NULL; /* This is mostly for debug purposes */ - - if (path_is_absolute(p)) { - if (!(c = strdup(p))) - return -ENOMEM; - } else { - if (!(cwd = get_current_dir_name())) - return -errno; - - r = asprintf(&c, "%s/%s", cwd, p); - free(cwd); - - if (r < 0) - return -ENOMEM; - } - - if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) { - r = -errno; - free(c); - return r; - } + c = path_make_absolute_cwd(p); + if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) + return -errno; return 0; } @@ -1932,7 +1938,7 @@ char *unit_dbus_path(Unit *u) { return unit_dbus_path_from_name(u->id); } -int unit_add_cgroup(Unit *u, CGroupBonding *b) { +static int unit_add_cgroup(Unit *u, CGroupBonding *b) { int r; assert(u); @@ -1941,8 +1947,9 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { assert(b->path); if (!b->controller) { - if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER))) - return -ENOMEM; + b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER); + if (!b->controller) + return log_oom(); b->ours = true; } @@ -1956,7 +1963,8 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { l = hashmap_get(u->manager->cgroup_bondings, b->path); LIST_PREPEND(CGroupBonding, by_path, l, b); - if ((r = hashmap_replace(u->manager->cgroup_bondings, b->path, l)) < 0) { + r = hashmap_replace(u->manager->cgroup_bondings, b->path, l); + if (r < 0) { LIST_REMOVE(CGroupBonding, by_path, l, b); return r; } @@ -1969,26 +1977,31 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { } char *unit_default_cgroup_path(Unit *u) { - char *p; + _cleanup_free_ char *escaped_instance = NULL; assert(u); + escaped_instance = cg_escape(u->id); + if (!escaped_instance) + return NULL; + if (u->instance) { - char *t; + _cleanup_free_ char *t = NULL, *escaped_template = NULL; t = unit_name_template(u->id); if (!t) return NULL; - p = strjoin(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL); - free(t); - } else - p = strjoin(u->manager->cgroup_hierarchy, "/", u->id, NULL); + escaped_template = cg_escape(t); + if (!escaped_template) + return NULL; - return p; + return strjoin(u->manager->cgroup_hierarchy, "/", escaped_template, "/", escaped_instance, NULL); + } else + return strjoin(u->manager->cgroup_hierarchy, "/", escaped_instance, NULL); } -int unit_add_cgroup_from_text(Unit *u, const char *name) { +int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret) { char *controller = NULL, *path = NULL; CGroupBonding *b = NULL; bool ours = false; @@ -1997,7 +2010,8 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { assert(u); assert(name); - if ((r = cg_split_spec(name, &controller, &path)) < 0) + r = cg_split_spec(name, &controller, &path); + if (r < 0) return r; if (!path) { @@ -2006,23 +2020,59 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { } if (!controller) { - controller = strdup(SYSTEMD_CGROUP_CONTROLLER); + controller = strdup("systemd"); ours = true; } if (!path || !controller) { free(path); free(controller); + return log_oom(); + } - return -ENOMEM; + if (streq(controller, "systemd")) { + /* Within the systemd unit hierarchy we do not allow changes. */ + if (path_startswith(path, "/system")) { + log_warning_unit(u->id, "Manipulating the systemd:/system cgroup hierarchy is not permitted."); + free(path); + free(controller); + return -EPERM; + } } - if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) { + b = cgroup_bonding_find_list(u->cgroup_bondings, controller); + if (b) { + if (streq(path, b->path)) { + free(path); + free(controller); + + if (ret) + *ret = b; + return 0; + } + + if (overwrite && !b->essential) { + free(controller); + + free(b->path); + b->path = path; + + b->ours = ours; + b->realized = false; + + if (ret) + *ret = b; + + return 1; + } + r = -EEXIST; + b = NULL; goto fail; } - if (!(b = new0(CGroupBonding, 1))) { + b = new0(CGroupBonding, 1); + if (!b) { r = -ENOMEM; goto fail; } @@ -2032,10 +2082,14 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { b->ours = ours; b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER); - if ((r = unit_add_cgroup(u, b)) < 0) + r = unit_add_cgroup(u, b); + if (r < 0) goto fail; - return 0; + if (ret) + *ret = b; + + return 1; fail: free(path); @@ -2051,16 +2105,21 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) { assert(u); + if (controller && !cg_controller_is_valid(controller, true)) + return -EINVAL; + if (!controller) controller = SYSTEMD_CGROUP_CONTROLLER; if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) return 0; - if (!(b = new0(CGroupBonding, 1))) + b = new0(CGroupBonding, 1); + if (!b) return -ENOMEM; - if (!(b->controller = strdup(controller))) + b->controller = strdup(controller); + if (!b->controller) goto fail; b->path = unit_default_cgroup_path(u); @@ -2070,10 +2129,11 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) { b->ours = true; b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER); - if ((r = unit_add_cgroup(u, b)) < 0) + r = unit_add_cgroup(u, b); + if (r < 0) goto fail; - return 0; + return 1; fail: free(b->path); @@ -2096,7 +2156,8 @@ int unit_add_default_cgroups(Unit *u) { if (!u->manager->cgroup_hierarchy) return 0; - if ((r = unit_add_one_default_cgroup(u, NULL)) < 0) + r = unit_add_one_default_cgroup(u, NULL); + if (r < 0) return r; STRV_FOREACH(c, u->manager->default_controllers) @@ -2111,42 +2172,90 @@ int unit_add_default_cgroups(Unit *u) { CGroupBonding* unit_get_default_cgroup(Unit *u) { assert(u); - return cgroup_bonding_find_list(u->cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER); + return cgroup_bonding_find_list(u->cgroup_bondings, NULL); } -int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) { - int r; - char *c = NULL; +int unit_add_cgroup_attribute( + Unit *u, + const CGroupSemantics *semantics, + const char *controller, + const char *name, + const char *value, + CGroupAttribute **ret) { + + _cleanup_free_ char *c = NULL; CGroupAttribute *a; + int r; assert(u); - assert(name); assert(value); - if (!controller) { - const char *dot; + if (semantics) { + /* Semantics always take precedence */ + if (semantics->name) + name = semantics->name; - dot = strchr(name, '.'); - if (!dot) - return -EINVAL; + if (semantics->controller) + controller = semantics->controller; + } - c = strndup(name, dot - name); - if (!c) - return -ENOMEM; + if (!name) + return -EINVAL; + + if (!controller) { + r = cg_controller_from_attr(name, &c); + if (r < 0) + return -EINVAL; controller = c; } - if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { - r = -EINVAL; - goto finish; + if (!controller || + streq(controller, SYSTEMD_CGROUP_CONTROLLER) || + streq(controller, "systemd")) + return -EINVAL; + + if (!filename_is_safe(name)) + return -EINVAL; + + if (!cg_controller_is_valid(controller, false)) + return -EINVAL; + + /* Check if this attribute already exists. Note that we will + * explicitly check for the value here too, as there are + * attributes which accept multiple values. */ + a = cgroup_attribute_find_list(u->cgroup_attributes, controller, name); + if (a) { + if (streq(value, a->value)) { + /* Exactly the same value is always OK, let's ignore this */ + if (ret) + *ret = a; + + return 0; + } + + if (semantics && !semantics->multiple) { + char *v; + + /* If this is a single-item entry, we can + * simply patch the existing attribute */ + + v = strdup(value); + if (!v) + return -ENOMEM; + + free(a->value); + a->value = v; + + if (ret) + *ret = a; + return 1; + } } a = new0(CGroupAttribute, 1); - if (!a) { - r = -ENOMEM; - goto finish; - } + if (!a) + return -ENOMEM; if (c) { a->controller = c; @@ -2162,58 +2271,54 @@ int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, free(a->name); free(a->value); free(a); - return -ENOMEM; } - a->map_callback = map_callback; + a->semantics = semantics; + a->unit = u; LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a); - r = 0; + if (ret) + *ret = a; -finish: - free(c); - return r; + return 1; } int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { - char *t; + _cleanup_free_ char *t = NULL; int r; assert(u); assert(type); assert(_found); - if (!(t = unit_name_change_suffix(u->id, type))) + t = unit_name_change_suffix(u->id, type); + if (!t) return -ENOMEM; assert(!unit_has_name(u, t)); r = manager_load_unit(u->manager, t, NULL, NULL, _found); - free(t); - assert(r < 0 || *_found != u); - return r; } int unit_get_related_unit(Unit *u, const char *type, Unit **_found) { + _cleanup_free_ char *t = NULL; Unit *found; - char *t; assert(u); assert(type); assert(_found); - if (!(t = unit_name_change_suffix(u->id, type))) + t = unit_name_change_suffix(u->id, type); + if (!t) return -ENOMEM; assert(!unit_has_name(u, t)); found = manager_get_unit(u->manager, t); - free(t); - if (!found) return -ENOENT; @@ -2371,6 +2476,9 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { job_free(j); return r; } + + if (j->state == JOB_RUNNING) + u->manager->n_running_jobs++; } else { /* legacy */ JobType type = job_type_from_string(v); @@ -2473,25 +2581,18 @@ int unit_coldplug(Unit *u) { return 0; } -void unit_status_printf(Unit *u, const char *status, const char *format, ...) { - va_list ap; - - assert(u); - assert(format); - - if (!manager_get_show_status(u->manager)) - return; - - if (!manager_is_booting_or_shutting_down(u->manager)) - return; - - va_start(ap, format); - status_vprintf(status, true, format, ap); - va_end(ap); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) { + manager_status_printf(u->manager, false, status, unit_status_msg_format, unit_description(u)); } +#pragma GCC diagnostic pop bool unit_need_daemon_reload(Unit *u) { + _cleanup_strv_free_ char **t = NULL; + char **path; struct stat st; + unsigned loaded_cnt, current_cnt; assert(u); @@ -2516,7 +2617,30 @@ bool unit_need_daemon_reload(Unit *u) { return true; } - return false; + t = unit_find_dropin_paths(u); + loaded_cnt = strv_length(t); + current_cnt = strv_length(u->dropin_paths); + + if (loaded_cnt == current_cnt) { + if (loaded_cnt == 0) + return false; + + if (strv_overlap(u->dropin_paths, t)) { + STRV_FOREACH(path, u->dropin_paths) { + zero(st); + if (stat(*path, &st) < 0) + return true; + + if (u->dropin_mtime > 0 && + timespec_load(&st.st_mtim) > u->dropin_mtime) + return true; + } + + return false; + } else + return true; + } else + return true; } void unit_reset_failed(Unit *u) { @@ -2535,7 +2659,19 @@ Unit *unit_following(Unit *u) { return NULL; } -bool unit_pending_inactive(Unit *u) { +bool unit_stop_pending(Unit *u) { + assert(u); + + /* This call does check the current state of the unit. It's + * hence useful to be called from state change calls of the + * unit itself, where the state isn't updated yet. This is + * different from unit_inactive_or_pending() which checks both + * the current state and for a queued job. */ + + return u->job && u->job->type == JOB_STOP; +} + +bool unit_inactive_or_pending(Unit *u) { assert(u); /* Returns true if the unit is inactive or going down */ @@ -2543,13 +2679,13 @@ bool unit_pending_inactive(Unit *u) { if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))) return true; - if (u->job && u->job->type == JOB_STOP) + if (unit_stop_pending(u)) return true; return false; } -bool unit_pending_active(Unit *u) { +bool unit_active_or_pending(Unit *u) { assert(u); /* Returns true if the unit is active or going up */ @@ -2578,6 +2714,71 @@ int unit_kill(Unit *u, KillWho w, int signo, DBusError *error) { return UNIT_VTABLE(u)->kill(u, w, signo, error); } +int unit_kill_common( + Unit *u, + KillWho who, + int signo, + pid_t main_pid, + pid_t control_pid, + DBusError *error) { + + int r = 0; + + if (who == KILL_MAIN && main_pid <= 0) { + if (main_pid < 0) + dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type)); + else + dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); + return -ESRCH; + } + + if (who == KILL_CONTROL && control_pid <= 0) { + if (control_pid < 0) + dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type)); + else + dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); + return -ESRCH; + } + + if (who == KILL_CONTROL || who == KILL_ALL) + if (control_pid > 0) + if (kill(control_pid, signo) < 0) + r = -errno; + + if (who == KILL_MAIN || who == KILL_ALL) + if (main_pid > 0) + if (kill(main_pid, signo) < 0) + r = -errno; + + if (who == KILL_ALL) { + _cleanup_set_free_ Set *pid_set = NULL; + int q; + + pid_set = set_new(trivial_hash_func, trivial_compare_func); + if (!pid_set) + return -ENOMEM; + + /* Exclude the control/main pid from being killed via the cgroup */ + if (control_pid > 0) { + q = set_put(pid_set, LONG_TO_PTR(control_pid)); + if (q < 0) + return q; + } + + if (main_pid > 0) { + q = set_put(pid_set, LONG_TO_PTR(main_pid)); + if (q < 0) + return q; + } + + q = cgroup_bonding_kill_list(u->cgroup_bondings, signo, false, false, pid_set, NULL); + if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) + r = q; + } + + return r; +} + int unit_following_set(Unit *u, Set **s) { assert(u); assert(s); @@ -2699,6 +2900,155 @@ ExecContext *unit_get_exec_context(Unit *u) { return (ExecContext*) ((uint8_t*) u + offset); } +static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char **_q) { + char *p, *q; + int r; + + assert(u); + assert(name); + assert(_p); + assert(_q); + + if (u->manager->running_as == SYSTEMD_USER && runtime) + return -ENOTSUP; + + if (!filename_is_safe(name)) + return -EINVAL; + + if (u->manager->running_as == SYSTEMD_USER) { + _cleanup_free_ char *c = NULL; + + r = user_config_home(&c); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + p = strjoin(c, "/", u->id, ".d", NULL); + } else if (runtime) + p = strjoin("/run/systemd/system/", u->id, ".d", NULL); + else + p = strjoin("/etc/systemd/system/", u->id, ".d", NULL); + if (!p) + return -ENOMEM; + + q = strjoin(p, "/50-", name, ".conf", NULL); + if (!q) { + free(p); + return -ENOMEM; + } + + *_p = p; + *_q = q; + return 0; +} + +int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) { + _cleanup_free_ char *p = NULL, *q = NULL; + int r; + + assert(u); + + r = drop_in_file(u, runtime, name, &p, &q); + if (r < 0) + return r; + + mkdir_p(p, 0755); + return write_string_file_atomic_label(q, data); +} + +int unit_remove_drop_in(Unit *u, bool runtime, const char *name) { + _cleanup_free_ char *p = NULL, *q = NULL; + int r; + + assert(u); + + r = drop_in_file(u, runtime, name, &p, &q); + if (unlink(q) < 0) + r = -errno; + else + r = 0; + + rmdir(p); + return r; +} + +int unit_kill_context( + Unit *u, + KillContext *c, + bool sigkill, + pid_t main_pid, + pid_t control_pid, + bool main_pid_alien) { + + int sig, wait_for_exit = 0, r; + + assert(u); + assert(c); + + if (c->kill_mode == KILL_NONE) + return 0; + + sig = sigkill ? SIGKILL : c->kill_signal; + + if (main_pid > 0) { + r = kill_and_sigcont(main_pid, sig); + + if (r < 0 && r != -ESRCH) { + _cleanup_free_ char *comm = NULL; + get_process_comm(main_pid, &comm); + + log_warning_unit(u->id, "Failed to kill main process %li (%s): %s", + (long) main_pid, strna(comm), strerror(-r)); + } else + wait_for_exit = !main_pid_alien; + } + + if (control_pid > 0) { + r = kill_and_sigcont(control_pid, sig); + + if (r < 0 && r != -ESRCH) { + _cleanup_free_ char *comm = NULL; + get_process_comm(control_pid, &comm); + + log_warning_unit(u->id, + "Failed to kill control process %li (%s): %s", + (long) control_pid, strna(comm), strerror(-r)); + } else + wait_for_exit = true; + } + + if (c->kill_mode == KILL_CONTROL_GROUP) { + _cleanup_set_free_ Set *pid_set = NULL; + + pid_set = set_new(trivial_hash_func, trivial_compare_func); + if (!pid_set) + return -ENOMEM; + + /* Exclude the main/control pids from being killed via the cgroup */ + if (main_pid > 0) { + r = set_put(pid_set, LONG_TO_PTR(main_pid)); + if (r < 0) + return r; + } + + if (control_pid > 0) { + r = set_put(pid_set, LONG_TO_PTR(control_pid)); + if (r < 0) + return r; + } + + r = cgroup_bonding_kill_list(u->cgroup_bondings, sig, true, false, pid_set, NULL); + if (r < 0) { + if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) + log_warning_unit(u->id, "Failed to kill control group: %s", strerror(-r)); + } else if (r > 0) + wait_for_exit = true; + } + + return wait_for_exit; +} + static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { [UNIT_ACTIVE] = "active", [UNIT_RELOADING] = "reloading", diff --git a/src/core/unit.h b/src/core/unit.h index 702bfeece6..b04475e4fb 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -23,6 +23,7 @@ #include <stdbool.h> #include <stdlib.h> +#include <unistd.h> typedef struct Unit Unit; typedef struct UnitVTable UnitVTable; @@ -39,6 +40,7 @@ typedef struct UnitStatusMessageFormats UnitStatusMessageFormats; #include "condition.h" #include "install.h" #include "unit-name.h" +#include "cgroup-semantics.h" enum UnitActiveState { UNIT_ACTIVE, @@ -136,8 +138,10 @@ struct Unit { char *fragment_path; /* if loaded from a config file this is the primary path to it */ char *source_path; /* if converted, the source file */ + char **dropin_paths; usec_t fragment_mtime; usec_t source_mtime; + usec_t dropin_mtime; /* If there is something to do with this unit, then this is the installed job for it */ Job *job; @@ -270,6 +274,9 @@ struct UnitVTable { * ExecContext is found, if the unit type has that */ size_t exec_context_offset; + /* The name of the section with the exec settings of ExecContext */ + const char *exec_section; + /* Config file sections this unit type understands, separated * by NUL chars */ const char *sections; @@ -345,7 +352,7 @@ struct UnitVTable { /* Called whenever a process of this unit sends us a message */ void (*notify_message)(Unit *u, pid_t pid, char **tags); - /* Called whenever a name thus Unit registered for comes or + /* Called whenever a name this Unit registered for comes or * goes away. */ void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner); @@ -361,6 +368,10 @@ struct UnitVTable { /* Return the set of units that are following each other */ int (*following_set)(Unit *u, Set **s); + /* Invoked each time a unit this unit is triggering changes + * state or gains/loses a job */ + void (*trigger_notify)(Unit *u, Unit *trigger); + /* Called whenever CLOCK_REALTIME made a jump */ void (*time_change)(Unit *u); @@ -410,6 +421,8 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; /* For casting the various unit types into a unit */ #define UNIT(u) (&(u)->meta) +#define UNIT_TRIGGER(u) ((Unit*) set_first((u)->dependencies[UNIT_TRIGGERS])) + DEFINE_CAST(SOCKET, Socket); DEFINE_CAST(TIMER, Timer); DEFINE_CAST(SERVICE, Service); @@ -437,11 +450,10 @@ int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDep int unit_add_exec_dependencies(Unit *u, ExecContext *c); -int unit_add_cgroup(Unit *u, CGroupBonding *b); -int unit_add_cgroup_from_text(Unit *u, const char *name); +int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret); int unit_add_default_cgroups(Unit *u); CGroupBonding* unit_get_default_cgroup(Unit *u); -int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback); +int unit_add_cgroup_attribute(Unit *u, const CGroupSemantics *semantics, const char *controller, const char *name, const char *value, CGroupAttribute **ret); int unit_choose_id(Unit *u, const char *name); int unit_set_description(Unit *u, const char *description); @@ -456,13 +468,13 @@ void unit_add_to_gc_queue(Unit *u); int unit_merge(Unit *u, Unit *other); int unit_merge_by_name(Unit *u, const char *other); -Unit *unit_follow_merge(Unit *u); +Unit *unit_follow_merge(Unit *u) _pure_; int unit_load_fragment_and_dropin(Unit *u); int unit_load_fragment_and_dropin_optional(Unit *u); int unit_load(Unit *unit); -const char *unit_description(Unit *u); +const char *unit_description(Unit *u) _pure_; bool unit_has_name(Unit *u, const char *name); @@ -472,15 +484,16 @@ const char* unit_sub_state_to_string(Unit *u); void unit_dump(Unit *u, FILE *f, const char *prefix); -bool unit_can_reload(Unit *u); -bool unit_can_start(Unit *u); -bool unit_can_isolate(Unit *u); +bool unit_can_reload(Unit *u) _pure_; +bool unit_can_start(Unit *u) _pure_; +bool unit_can_isolate(Unit *u) _pure_; int unit_start(Unit *u); int unit_stop(Unit *u); int unit_reload(Unit *u); int unit_kill(Unit *u, KillWho w, int signo, DBusError *error); +int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t control_pid, DBusError *error); void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success); @@ -505,7 +518,7 @@ char *unit_dbus_path(Unit *u); int unit_load_related_unit(Unit *u, const char *type, Unit **_found); int unit_get_related_unit(Unit *u, const char *type, Unit **_found); -bool unit_can_serialize(Unit *u); +bool unit_can_serialize(Unit *u) _pure_; int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs); void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_attr_(4,5); void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value); @@ -515,7 +528,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants); int unit_coldplug(Unit *u); -void unit_status_printf(Unit *u, const char *status, const char *format, ...); +void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_attr_(3, 0); bool unit_need_daemon_reload(Unit *u); @@ -523,8 +536,9 @@ void unit_reset_failed(Unit *u); Unit *unit_following(Unit *u); -bool unit_pending_inactive(Unit *u); -bool unit_pending_active(Unit *u); +bool unit_stop_pending(Unit *u) _pure_; +bool unit_inactive_or_pending(Unit *u) _pure_; +bool unit_active_or_pending(Unit *u); int unit_add_default_target_dependency(Unit *u, Unit *target); @@ -532,7 +546,8 @@ char *unit_default_cgroup_path(Unit *u); int unit_following_set(Unit *u, Set **s); -void unit_trigger_on_failure(Unit *u); +void unit_start_on_failure(Unit *u); +void unit_trigger_notify(Unit *u); bool unit_condition_test(Unit *u); @@ -548,17 +563,26 @@ int unit_add_mount_links(Unit *u); int unit_exec_context_defaults(Unit *u, ExecContext *c); -ExecContext *unit_get_exec_context(Unit *u); +ExecContext *unit_get_exec_context(Unit *u) _pure_; + +int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data); +int unit_remove_drop_in(Unit *u, bool runtime, const char *name); -const char *unit_active_state_to_string(UnitActiveState i); -UnitActiveState unit_active_state_from_string(const char *s); +int unit_kill_context(Unit *u, KillContext *c, bool sigkill, pid_t main_pid, pid_t control_pid, bool main_pid_alien); -const char *unit_dependency_to_string(UnitDependency i); -UnitDependency unit_dependency_from_string(const char *s); +const char *unit_active_state_to_string(UnitActiveState i) _const_; +UnitActiveState unit_active_state_from_string(const char *s) _pure_; -#define log_full_unit(level, unit, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "UNIT=", unit, __VA_ARGS__) +const char *unit_dependency_to_string(UnitDependency i) _const_; +UnitDependency unit_dependency_from_string(const char *s) _pure_; + +/* Macros which append UNIT= or USER_UNIT= to the message */ + +#define log_full_unit(level, unit, ...) log_meta_object(level, __FILE__, __LINE__, __func__, getpid() == 1 ? "UNIT=" : "USER_UNIT=", unit, __VA_ARGS__) #define log_debug_unit(unit, ...) log_full_unit(LOG_DEBUG, unit, __VA_ARGS__) #define log_info_unit(unit, ...) log_full_unit(LOG_INFO, unit, __VA_ARGS__) #define log_notice_unit(unit, ...) log_full_unit(LOG_NOTICE, unit, __VA_ARGS__) #define log_warning_unit(unit, ...) log_full_unit(LOG_WARNING, unit, __VA_ARGS__) #define log_error_unit(unit, ...) log_full_unit(LOG_ERR, unit, __VA_ARGS__) + +#define log_struct_unit(level, unit, ...) log_struct(level, getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, __VA_ARGS__) diff --git a/src/core/user.conf b/src/core/user.conf index e02b46bc3a..4252451eb7 100644 --- a/src/core/user.conf +++ b/src/core/user.conf @@ -5,7 +5,7 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # -# See systemd.conf(5) for details +# See systemd-user.conf(5) for details [Manager] #LogLevel=info |