diff options
Diffstat (limited to 'src/core')
108 files changed, 4960 insertions, 3867 deletions
diff --git a/src/core/.gitignore b/src/core/.gitignore index f293bbdc93..465b4fcc20 100644 --- a/src/core/.gitignore +++ b/src/core/.gitignore @@ -1,2 +1,3 @@ /macros.systemd +/triggers.systemd /systemd.pc diff --git a/src/core/audit-fd.c b/src/core/audit-fd.c index 5a18e263a8..0a484d89fc 100644 --- a/src/core/audit-fd.c +++ b/src/core/audit-fd.c @@ -21,13 +21,15 @@ #include <errno.h> + #include "audit-fd.h" #ifdef HAVE_AUDIT -#include <stdbool.h> #include <libaudit.h> +#include <stdbool.h> +#include "fd-util.h" #include "log.h" #include "util.h" diff --git a/src/core/automount.c b/src/core/automount.c index 5bb61b95d3..85b7b4e842 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -20,29 +20,37 @@ ***/ #include <errno.h> -#include <limits.h> -#include <sys/mount.h> -#include <unistd.h> #include <fcntl.h> +#include <limits.h> +#include <linux/auto_dev-ioctl.h> +#include <linux/auto_fs4.h> #include <sys/epoll.h> +#include <sys/mount.h> #include <sys/stat.h> -#include <linux/auto_fs4.h> -#include <linux/auto_dev-ioctl.h> +#include <unistd.h> -#include "unit.h" +#include "alloc-util.h" +#include "async.h" #include "automount.h" -#include "mount.h" -#include "unit-name.h" -#include "special.h" +#include "bus-error.h" +#include "bus-util.h" +#include "dbus-automount.h" +#include "fd-util.h" +#include "formats-util.h" +#include "io-util.h" #include "label.h" #include "mkdir.h" +#include "mount-util.h" +#include "mount.h" +#include "parse-util.h" #include "path-util.h" -#include "dbus-automount.h" -#include "bus-util.h" -#include "bus-error.h" -#include "formats-util.h" #include "process-util.h" -#include "async.h" +#include "special.h" +#include "stdio-util.h" +#include "string-table.h" +#include "string-util.h" +#include "unit-name.h" +#include "unit.h" static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = { [AUTOMOUNT_DEAD] = UNIT_INACTIVE, @@ -81,26 +89,11 @@ static void automount_init(Unit *u) { UNIT(a)->ignore_on_isolate = true; } -static void repeat_unmount(const char *path) { - assert(path); - - for (;;) { - /* If there are multiple mounts on a mount point, this - * removes them all */ - - if (umount2(path, MNT_DETACH) >= 0) - continue; - - if (errno != EINVAL) - log_error_errno(errno, "Failed to unmount: %m"); - - break; - } -} - static int automount_send_ready(Automount *a, Set *tokens, int status); static void unmount_autofs(Automount *a) { + int r; + assert(a); if (a->pipe_fd < 0) @@ -116,8 +109,11 @@ static void unmount_autofs(Automount *a) { * around */ if (a->where && (UNIT(a)->manager->exit_code != MANAGER_RELOAD && - UNIT(a)->manager->exit_code != MANAGER_REEXECUTE)) - repeat_unmount(a->where); + UNIT(a)->manager->exit_code != MANAGER_REEXECUTE)) { + r = repeat_unmount(a->where, MNT_DETACH); + if (r < 0) + log_error_errno(r, "Failed to unmount: %m"); + } } static void automount_done(Unit *u) { @@ -129,23 +125,20 @@ static void automount_done(Unit *u) { a->where = mfree(a->where); - set_free(a->tokens); - a->tokens = NULL; - set_free(a->expire_tokens); - a->expire_tokens = NULL; + a->tokens = set_free(a->tokens); + a->expire_tokens = set_free(a->expire_tokens); a->expire_event_source = sd_event_source_unref(a->expire_event_source); } static int automount_add_mount_links(Automount *a) { _cleanup_free_ char *parent = NULL; - int r; assert(a); - r = path_get_parent(a->where, &parent); - if (r < 0) - return r; + parent = dirname_malloc(a->where); + if (!parent) + return -ENOMEM; return unit_require_mounts_for(UNIT(a), parent); } @@ -155,6 +148,9 @@ static int automount_add_default_dependencies(Automount *a) { assert(a); + if (!UNIT(a)->default_dependencies) + return 0; + if (UNIT(a)->manager->running_as != MANAGER_SYSTEM) return 0; @@ -226,11 +222,9 @@ static int automount_load(Unit *u) { if (r < 0) return r; - if (UNIT(a)->default_dependencies) { - r = automount_add_default_dependencies(a); - if (r < 0) - return r; - } + r = automount_add_default_dependencies(a); + if (r < 0) + return r; } return automount_verify(a); @@ -610,12 +604,16 @@ static void automount_enter_waiting(Automount *a) { return; fail: + log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m"); + safe_close_pair(p); - if (mounted) - repeat_unmount(a->where); + if (mounted) { + r = repeat_unmount(a->where, MNT_DETACH); + if (r < 0) + log_error_errno(r, "Failed to unmount, ignoring: %m"); + } - log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m"); automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); } @@ -730,8 +728,7 @@ static void automount_enter_runnning(Automount *a) { if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) log_unit_info(UNIT(a), "Automount point already active?"); else { - r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)), - JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, &error, NULL); if (r < 0) { log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r)); goto fail; @@ -776,8 +773,9 @@ static int automount_stop(Unit *u) { static int automount_serialize(Unit *u, FILE *f, FDSet *fds) { Automount *a = AUTOMOUNT(u); - void *p; Iterator i; + void *p; + int r; assert(a); assert(f); @@ -792,15 +790,9 @@ static int automount_serialize(Unit *u, FILE *f, FDSet *fds) { SET_FOREACH(p, a->expire_tokens, i) unit_serialize_item_format(u, f, "expire-token", "%u", PTR_TO_UINT(p)); - if (a->pipe_fd >= 0) { - int copy; - - copy = fdset_put_dup(fds, a->pipe_fd); - if (copy < 0) - return copy; - - unit_serialize_item_format(u, f, "pipe-fd", "%i", copy); - } + r = unit_serialize_item_fd(u, f, fds, "pipe-fd", a->pipe_fd); + if (r < 0) + return r; return 0; } @@ -981,7 +973,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo break; } - r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, &error, NULL); if (r < 0) { log_unit_warning(UNIT(a), "Failed to queue umount startup job: %s", bus_error_message(&error, r)); goto fail; @@ -1026,15 +1018,6 @@ static bool automount_supported(void) { return supported; } -static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = { - [AUTOMOUNT_DEAD] = "dead", - [AUTOMOUNT_WAITING] = "waiting", - [AUTOMOUNT_RUNNING] = "running", - [AUTOMOUNT_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState); - static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = { [AUTOMOUNT_SUCCESS] = "success", [AUTOMOUNT_FAILURE_RESOURCES] = "resources" diff --git a/src/core/automount.h b/src/core/automount.h index 2a50fef68d..43ea9f772d 100644 --- a/src/core/automount.h +++ b/src/core/automount.h @@ -25,15 +25,6 @@ typedef struct Automount Automount; #include "unit.h" -typedef enum AutomountState { - AUTOMOUNT_DEAD, - AUTOMOUNT_WAITING, - AUTOMOUNT_RUNNING, - AUTOMOUNT_FAILED, - _AUTOMOUNT_STATE_MAX, - _AUTOMOUNT_STATE_INVALID = -1 -} AutomountState; - typedef enum AutomountResult { AUTOMOUNT_SUCCESS, AUTOMOUNT_FAILURE_RESOURCES, @@ -66,8 +57,5 @@ extern const UnitVTable automount_vtable; int automount_update_mount(Automount *a, MountState old_state, MountState state); -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) _const_; AutomountResult automount_result_from_string(const char *s) _pure_; diff --git a/src/core/bus-endpoint.c b/src/core/bus-endpoint.c index 0c4b3e7c8b..d22a80c91f 100644 --- a/src/core/bus-endpoint.c +++ b/src/core/bus-endpoint.c @@ -19,10 +19,11 @@ #include <stdlib.h> -#include "kdbus.h" +#include "alloc-util.h" +#include "bus-endpoint.h" #include "bus-kernel.h" #include "bus-policy.h" -#include "bus-endpoint.h" +#include "kdbus.h" int bus_kernel_set_endpoint_policy(int fd, uid_t uid, BusEndpoint *ep) { diff --git a/src/core/bus-endpoint.h b/src/core/bus-endpoint.h index 4a31f4c4be..f6c5f7c5af 100644 --- a/src/core/bus-endpoint.h +++ b/src/core/bus-endpoint.h @@ -24,8 +24,8 @@ typedef struct BusEndpoint BusEndpoint; typedef struct BusEndpointPolicy BusEndpointPolicy; -#include "hashmap.h" #include "bus-policy.h" +#include "hashmap.h" struct BusEndpointPolicy { char *name; diff --git a/src/core/bus-policy.c b/src/core/bus-policy.c index a6a8fcd4d3..4907c268e8 100644 --- a/src/core/bus-policy.c +++ b/src/core/bus-policy.c @@ -19,10 +19,13 @@ #include <stdlib.h> -#include "kdbus.h" -#include "util.h" +#include "alloc-util.h" #include "bus-kernel.h" #include "bus-policy.h" +#include "kdbus.h" +#include "string-table.h" +#include "user-util.h" +#include "util.h" int bus_kernel_translate_access(BusPolicyAccess access) { assert(access >= 0); diff --git a/src/core/bus-policy.h b/src/core/bus-policy.h index 3b04f5457a..2f61289185 100644 --- a/src/core/bus-policy.h +++ b/src/core/bus-policy.h @@ -21,9 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "kdbus.h" #include "list.h" #include "macro.h" -#include "kdbus.h" typedef struct BusNamePolicy BusNamePolicy; diff --git a/src/core/busname.c b/src/core/busname.c index 4020e9dd3c..04fa12a4da 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -21,17 +21,23 @@ #include <sys/mman.h> -#include "special.h" -#include "formats-util.h" -#include "signal-util.h" -#include "bus-kernel.h" +#include "alloc-util.h" #include "bus-internal.h" +#include "bus-kernel.h" +#include "bus-policy.h" #include "bus-util.h" +#include "busname.h" +#include "dbus-busname.h" +#include "fd-util.h" +#include "formats-util.h" #include "kdbus.h" -#include "bus-policy.h" +#include "parse-util.h" +#include "process-util.h" #include "service.h" -#include "dbus-busname.h" -#include "busname.h" +#include "signal-util.h" +#include "special.h" +#include "string-table.h" +#include "string-util.h" static const UnitActiveState state_translation_table[_BUSNAME_STATE_MAX] = { [BUSNAME_DEAD] = UNIT_INACTIVE, @@ -358,10 +364,9 @@ static int busname_coldplug(Unit *u) { if (n->deserialized_state == n->state) return 0; - if (IN_SET(n->deserialized_state, BUSNAME_MAKING, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) { - - if (n->control_pid <= 0) - return -EBADMSG; + if (n->control_pid > 0 && + pid_is_unwaited(n->control_pid) && + IN_SET(n->deserialized_state, BUSNAME_MAKING, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) { r = unit_watch_pid(UNIT(n), n->control_pid); if (r < 0) @@ -585,7 +590,13 @@ static void busname_enter_running(BusName *n) { } if (!pending) { - r = manager_add_job(UNIT(n)->manager, JOB_START, UNIT_DEREF(n->service), JOB_REPLACE, true, &error, NULL); + if (!UNIT_ISSET(n->service)) { + log_unit_error(UNIT(n), "Service to activate vanished, refusing activation."); + r = -ENOENT; + goto fail; + } + + r = manager_add_job(UNIT(n)->manager, JOB_START, UNIT_DEREF(n->service), JOB_REPLACE, &error, NULL); if (r < 0) goto fail; } @@ -656,6 +667,7 @@ static int busname_stop(Unit *u) { static int busname_serialize(Unit *u, FILE *f, FDSet *fds) { BusName *n = BUSNAME(u); + int r; assert(n); assert(f); @@ -667,15 +679,9 @@ static int busname_serialize(Unit *u, FILE *f, FDSet *fds) { if (n->control_pid > 0) unit_serialize_item_format(u, f, "control-pid", PID_FMT, n->control_pid); - if (n->starter_fd >= 0) { - int copy; - - copy = fdset_put_dup(fds, n->starter_fd); - if (copy < 0) - return copy; - - unit_serialize_item_format(u, f, "starter-fd", "%i", copy); - } + r = unit_serialize_item_fd(u, f, fds, "starter-fd", n->starter_fd); + if (r < 0) + return r; return 0; } @@ -991,19 +997,6 @@ static bool busname_supported(void) { return supported; } -static const char* const busname_state_table[_BUSNAME_STATE_MAX] = { - [BUSNAME_DEAD] = "dead", - [BUSNAME_MAKING] = "making", - [BUSNAME_REGISTERED] = "registered", - [BUSNAME_LISTENING] = "listening", - [BUSNAME_RUNNING] = "running", - [BUSNAME_SIGTERM] = "sigterm", - [BUSNAME_SIGKILL] = "sigkill", - [BUSNAME_FAILED] = "failed", -}; - -DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState); - static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = { [BUSNAME_SUCCESS] = "success", [BUSNAME_FAILURE_RESOURCES] = "resources", diff --git a/src/core/busname.h b/src/core/busname.h index 69528a2aef..46f7b6f097 100644 --- a/src/core/busname.h +++ b/src/core/busname.h @@ -24,19 +24,7 @@ typedef struct BusName BusName; typedef struct BusNamePolicy BusNamePolicy; - -typedef enum BusNameState { - BUSNAME_DEAD, - BUSNAME_MAKING, - BUSNAME_REGISTERED, - BUSNAME_LISTENING, - BUSNAME_RUNNING, - BUSNAME_SIGTERM, - BUSNAME_SIGKILL, - BUSNAME_FAILED, - _BUSNAME_STATE_MAX, - _BUSNAME_STATE_INVALID = -1 -} BusNameState; +#include "unit.h" typedef enum BusNameResult { BUSNAME_SUCCESS, @@ -77,8 +65,5 @@ struct BusName { extern const UnitVTable busname_vtable; -const char* busname_state_to_string(BusNameState i) _const_; -BusNameState busname_state_from_string(const char *s) _pure_; - const char* busname_result_to_string(BusNameResult i) _const_; BusNameResult busname_result_from_string(const char *s) _pure_; diff --git a/src/core/cgroup.c b/src/core/cgroup.c index baa7cc5488..d122175417 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -22,11 +22,18 @@ #include <fcntl.h> #include <fnmatch.h> -#include "process-util.h" -#include "path-util.h" -#include "special.h" +#include "alloc-util.h" #include "cgroup-util.h" #include "cgroup.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "process-util.h" +#include "special.h" +#include "string-table.h" +#include "string-util.h" #define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) @@ -36,13 +43,18 @@ void cgroup_context_init(CGroupContext *c) { /* Initialize everything to the kernel defaults, assuming the * structure is preinitialized to 0 */ - c->cpu_shares = (unsigned long) -1; - c->startup_cpu_shares = (unsigned long) -1; + c->cpu_shares = CGROUP_CPU_SHARES_INVALID; + c->startup_cpu_shares = CGROUP_CPU_SHARES_INVALID; + c->cpu_quota_per_sec_usec = USEC_INFINITY; + c->memory_limit = (uint64_t) -1; - c->blockio_weight = (unsigned long) -1; - c->startup_blockio_weight = (unsigned long) -1; - c->cpu_quota_per_sec_usec = USEC_INFINITY; + c->blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; + c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; + + c->tasks_max = (uint64_t) -1; + + c->netclass_type = CGROUP_NETCLASS_TYPE_NONE; } void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) { @@ -100,23 +112,27 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { "%sCPUAccounting=%s\n" "%sBlockIOAccounting=%s\n" "%sMemoryAccounting=%s\n" - "%sCPUShares=%lu\n" - "%sStartupCPUShares=%lu\n" + "%sTasksAccounting=%s\n" + "%sCPUShares=%" PRIu64 "\n" + "%sStartupCPUShares=%" PRIu64 "\n" "%sCPUQuotaPerSecSec=%s\n" - "%sBlockIOWeight=%lu\n" - "%sStartupBlockIOWeight=%lu\n" + "%sBlockIOWeight=%" PRIu64 "\n" + "%sStartupBlockIOWeight=%" PRIu64 "\n" "%sMemoryLimit=%" PRIu64 "\n" + "%sTasksMax=%" PRIu64 "\n" "%sDevicePolicy=%s\n" "%sDelegate=%s\n", prefix, yes_no(c->cpu_accounting), prefix, yes_no(c->blockio_accounting), prefix, yes_no(c->memory_accounting), + prefix, yes_no(c->tasks_accounting), prefix, c->cpu_shares, prefix, c->startup_cpu_shares, prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1), prefix, c->blockio_weight, prefix, c->startup_blockio_weight, prefix, c->memory_limit, + prefix, c->tasks_max, prefix, cgroup_device_policy_to_string(c->device_policy), prefix, yes_no(c->delegate)); @@ -129,7 +145,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { LIST_FOREACH(device_weights, w, c->blockio_device_weights) fprintf(f, - "%sBlockIODeviceWeight=%s %lu", + "%sBlockIODeviceWeight=%s %" PRIu64, prefix, w->path, w->weight); @@ -201,7 +217,7 @@ static int whitelist_device(const char *path, const char *node, const char *acc) r = cg_set_attribute("devices", path, "devices.allow", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set devices.allow on %s: %m", path); return r; @@ -272,7 +288,7 @@ static int whitelist_major(const char *path, const char *name, char type, const r = cg_set_attribute("devices", path, "devices.allow", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set devices.allow on %s: %m", path); } @@ -283,7 +299,7 @@ fail: return -errno; } -void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state) { +void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, uint32_t netclass, ManagerState state) { bool is_root; int r; @@ -305,20 +321,20 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M * and missing cgroups, i.e. EROFS and ENOENT. */ if ((mask & CGROUP_MASK_CPU) && !is_root) { - char buf[MAX(DECIMAL_STR_MAX(unsigned long), DECIMAL_STR_MAX(usec_t)) + 1]; + char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1]; - sprintf(buf, "%lu\n", - IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != (unsigned long) -1 ? c->startup_cpu_shares : - c->cpu_shares != (unsigned long) -1 ? c->cpu_shares : 1024); + sprintf(buf, "%" PRIu64 "\n", + IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->startup_cpu_shares : + c->cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->cpu_shares : CGROUP_CPU_SHARES_DEFAULT); r = cg_set_attribute("cpu", path, "cpu.shares", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set cpu.shares on %s: %m", path); sprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC); r = cg_set_attribute("cpu", path, "cpu.cfs_period_us", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set cpu.cfs_period_us on %s: %m", path); if (c->cpu_quota_per_sec_usec != USEC_INFINITY) { @@ -327,23 +343,23 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M } else r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", "-1"); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set cpu.cfs_quota_us on %s: %m", path); } if (mask & CGROUP_MASK_BLKIO) { - char buf[MAX3(DECIMAL_STR_MAX(unsigned long)+1, - DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(unsigned long)*1, - DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)]; + char buf[MAX(DECIMAL_STR_MAX(uint64_t)+1, + DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)]; CGroupBlockIODeviceWeight *w; CGroupBlockIODeviceBandwidth *b; if (!is_root) { - sprintf(buf, "%lu\n", IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_blockio_weight != (unsigned long) -1 ? c->startup_blockio_weight : - c->blockio_weight != (unsigned long) -1 ? c->blockio_weight : 1000); + sprintf(buf, "%" PRIu64 "\n", + IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ? c->startup_blockio_weight : + c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ? c->blockio_weight : CGROUP_BLKIO_WEIGHT_DEFAULT); r = cg_set_attribute("blkio", path, "blkio.weight", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set blkio.weight on %s: %m", path); /* FIXME: no way to reset this list */ @@ -354,10 +370,10 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M if (r < 0) continue; - sprintf(buf, "%u:%u %lu", major(dev), minor(dev), w->weight); + sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), w->weight); r = cg_set_attribute("blkio", path, "blkio.weight_device", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set blkio.weight_device on %s: %m", path); } } @@ -376,7 +392,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), b->bandwidth); r = cg_set_attribute("blkio", path, a, buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set %s on %s: %m", a, path); } } @@ -400,7 +416,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M } if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set memory.limit_in_bytes/memory.max on %s: %m", path); } @@ -416,7 +432,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M else r = cg_set_attribute("devices", path, "devices.allow", "a"); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to reset devices.list on %s: %m", path); if (c->device_policy == CGROUP_CLOSED || @@ -466,6 +482,32 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M log_debug("Ignoring device %s while writing cgroup attribute.", a->path); } } + + if ((mask & CGROUP_MASK_PIDS) && !is_root) { + + if (c->tasks_max != (uint64_t) -1) { + char buf[DECIMAL_STR_MAX(uint64_t) + 2]; + + sprintf(buf, "%" PRIu64 "\n", c->tasks_max); + r = cg_set_attribute("pids", path, "pids.max", buf); + } else + r = cg_set_attribute("pids", path, "pids.max", "max"); + + if (r < 0) + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set pids.max on %s: %m", path); + } + + if (mask & CGROUP_MASK_NET_CLS) { + char buf[DECIMAL_STR_MAX(uint32_t)]; + + sprintf(buf, "%" PRIu32, netclass); + + r = cg_set_attribute("net_cls", path, "net_cls.classid", buf); + if (r < 0) + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set net_cls.classid on %s: %m", path); + } } CGroupMask cgroup_context_get_mask(CGroupContext *c) { @@ -474,14 +516,14 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) { /* Figure out which controllers we need */ if (c->cpu_accounting || - c->cpu_shares != (unsigned long) -1 || - c->startup_cpu_shares != (unsigned long) -1 || + c->cpu_shares != CGROUP_CPU_SHARES_INVALID || + c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID || c->cpu_quota_per_sec_usec != USEC_INFINITY) mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU; if (c->blockio_accounting || - c->blockio_weight != (unsigned long) -1 || - c->startup_blockio_weight != (unsigned long) -1 || + c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || + c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || c->blockio_device_weights || c->blockio_device_bandwidths) mask |= CGROUP_MASK_BLKIO; @@ -494,6 +536,13 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) { c->device_policy != CGROUP_AUTO) mask |= CGROUP_MASK_DEVICES; + if (c->tasks_accounting || + c->tasks_max != (uint64_t) -1) + mask |= CGROUP_MASK_PIDS; + + if (c->netclass_type != CGROUP_NETCLASS_TYPE_NONE) + mask |= CGROUP_MASK_NET_CLS; + return mask; } @@ -861,6 +910,103 @@ static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask) { return u->cgroup_realized && u->cgroup_realized_mask == target_mask; } +static int unit_find_free_netclass_cgroup(Unit *u, uint32_t *ret) { + + uint32_t start, i; + Manager *m; + + assert(u); + + m = u->manager; + + i = start = m->cgroup_netclass_registry_last; + + do { + i++; + + if (!hashmap_get(m->cgroup_netclass_registry, UINT_TO_PTR(i))) { + m->cgroup_netclass_registry_last = i; + *ret = i; + return 0; + } + + if (i == UINT32_MAX) + i = CGROUP_NETCLASS_FIXED_MAX; + + } while (i != start); + + return -ENOBUFS; +} + +int unit_add_to_netclass_cgroup(Unit *u) { + + CGroupContext *cc; + Unit *first; + void *key; + int r; + + assert(u); + + cc = unit_get_cgroup_context(u); + if (!cc) + return 0; + + switch (cc->netclass_type) { + case CGROUP_NETCLASS_TYPE_NONE: + return 0; + + case CGROUP_NETCLASS_TYPE_FIXED: + u->cgroup_netclass_id = cc->netclass_id; + break; + + case CGROUP_NETCLASS_TYPE_AUTO: + /* Allocate a new ID in case it was requested and not done yet */ + if (u->cgroup_netclass_id == 0) { + r = unit_find_free_netclass_cgroup(u, &u->cgroup_netclass_id); + if (r < 0) + return r; + + log_debug("Dynamically assigned netclass cgroup id %" PRIu32 " to %s", u->cgroup_netclass_id, u->id); + } + + break; + } + + r = hashmap_ensure_allocated(&u->manager->cgroup_netclass_registry, &trivial_hash_ops); + if (r < 0) + return r; + + key = UINT32_TO_PTR(u->cgroup_netclass_id); + first = hashmap_get(u->manager->cgroup_netclass_registry, key); + + if (first) { + LIST_PREPEND(cgroup_netclass, first, u); + return hashmap_replace(u->manager->cgroup_netclass_registry, key, u); + } + + return hashmap_put(u->manager->cgroup_netclass_registry, key, u); +} + +int unit_remove_from_netclass_cgroup(Unit *u) { + + Unit *head; + void *key; + + assert(u); + + key = UINT32_TO_PTR(u->cgroup_netclass_id); + + LIST_FIND_HEAD(cgroup_netclass, u, head); + LIST_REMOVE(cgroup_netclass, head, u); + + if (head) + return hashmap_replace(u->manager->cgroup_netclass_registry, key, head); + + hashmap_remove(u->manager->cgroup_netclass_registry, key); + + return 0; +} + /* Check if necessary controllers and attributes for a unit are in place. * * If so, do nothing. @@ -896,7 +1042,7 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { return r; /* Finally, apply the necessary attributes. */ - cgroup_context_apply(unit_get_cgroup_context(u), target_mask, u->cgroup_path, state); + cgroup_context_apply(unit_get_cgroup_context(u), target_mask, u->cgroup_path, u->cgroup_netclass_id, state); return 0; } @@ -1063,7 +1209,7 @@ int unit_search_main_pid(Unit *u, pid_t *ret) { continue; /* Ignore processes that aren't our kids */ - if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid) + if (get_process_ppid(npid, &ppid) >= 0 && ppid != mypid) continue; if (pid != 0) @@ -1459,6 +1605,28 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) { return safe_atou64(v, ret); } +int unit_get_tasks_current(Unit *u, uint64_t *ret) { + _cleanup_free_ char *v = NULL; + int r; + + assert(u); + assert(ret); + + if (!u->cgroup_path) + return -ENODATA; + + if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0) + return -ENODATA; + + r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + + return safe_atou64(v, ret); +} + static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { _cleanup_free_ char *v = NULL; uint64_t ns; @@ -1532,6 +1700,32 @@ bool unit_cgroup_delegate(Unit *u) { return c->delegate; } +void unit_invalidate_cgroup(Unit *u, CGroupMask m) { + assert(u); + + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return; + + if (m == 0) + return; + + if ((u->cgroup_realized_mask & m) == 0) + return; + + u->cgroup_realized_mask &= ~m; + unit_add_to_cgroup_queue(u); +} + +void manager_invalidate_startup_units(Manager *m) { + Iterator i; + Unit *u; + + assert(m); + + SET_FOREACH(u, m->startup_units, i) + unit_invalidate_cgroup(u, CGROUP_MASK_CPU|CGROUP_MASK_BLKIO); +} + static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = { [CGROUP_AUTO] = "auto", [CGROUP_CLOSED] = "closed", diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 438f5bf50f..1b18d06652 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -26,6 +26,11 @@ #include "list.h" #include "time-util.h" +/* Maximum value for fixed (manual) net class ID assignment, + * and also the value at which the range of automatic assignments starts + */ +#define CGROUP_NETCLASS_FIXED_MAX UINT32_C(65535) + typedef struct CGroupContext CGroupContext; typedef struct CGroupDeviceAllow CGroupDeviceAllow; typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight; @@ -47,6 +52,17 @@ typedef enum CGroupDevicePolicy { _CGROUP_DEVICE_POLICY_INVALID = -1 } CGroupDevicePolicy; +typedef enum CGroupNetClassType { + /* Default - do not assign a net class */ + CGROUP_NETCLASS_TYPE_NONE, + + /* Automatically assign a net class */ + CGROUP_NETCLASS_TYPE_AUTO, + + /* Assign the net class that was provided by the user */ + CGROUP_NETCLASS_TYPE_FIXED, +} CGroupNetClassType; + struct CGroupDeviceAllow { LIST_FIELDS(CGroupDeviceAllow, device_allow); char *path; @@ -58,7 +74,7 @@ struct CGroupDeviceAllow { struct CGroupBlockIODeviceWeight { LIST_FIELDS(CGroupBlockIODeviceWeight, device_weights); char *path; - unsigned long weight; + uint64_t weight; }; struct CGroupBlockIODeviceBandwidth { @@ -72,13 +88,14 @@ struct CGroupContext { bool cpu_accounting; bool blockio_accounting; bool memory_accounting; + bool tasks_accounting; - unsigned long cpu_shares; - unsigned long startup_cpu_shares; + uint64_t cpu_shares; + uint64_t startup_cpu_shares; usec_t cpu_quota_per_sec_usec; - unsigned long blockio_weight; - unsigned long startup_blockio_weight; + uint64_t blockio_weight; + uint64_t startup_blockio_weight; LIST_HEAD(CGroupBlockIODeviceWeight, blockio_device_weights); LIST_HEAD(CGroupBlockIODeviceBandwidth, blockio_device_bandwidths); @@ -87,16 +104,21 @@ struct CGroupContext { CGroupDevicePolicy device_policy; LIST_HEAD(CGroupDeviceAllow, device_allow); + CGroupNetClassType netclass_type; + uint32_t netclass_id; + + uint64_t tasks_max; + bool delegate; }; -#include "unit.h" #include "cgroup-util.h" +#include "unit.h" void cgroup_context_init(CGroupContext *c); void cgroup_context_done(CGroupContext *c); void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix); -void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state); +void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, uint32_t netclass_id, ManagerState state); CGroupMask cgroup_context_get_mask(CGroupContext *c); @@ -124,6 +146,9 @@ int unit_watch_cgroup(Unit *u); int unit_attach_pids_to_cgroup(Unit *u); +int unit_add_to_netclass_cgroup(Unit *u); +int unit_remove_from_netclass_cgroup(Unit *u); + int manager_setup_cgroup(Manager *m); void manager_shutdown_cgroup(Manager *m, bool delete); @@ -137,6 +162,7 @@ int unit_search_main_pid(Unit *u, pid_t *ret); int unit_watch_all_pids(Unit *u); int unit_get_memory_current(Unit *u, uint64_t *ret); +int unit_get_tasks_current(Unit *u, uint64_t *ret); int unit_get_cpu_usage(Unit *u, nsec_t *ret); int unit_reset_cpu_usage(Unit *u); @@ -145,5 +171,9 @@ bool unit_cgroup_delegate(Unit *u); int unit_notify_cgroup_empty(Unit *u); int manager_notify_cgroup_empty(Manager *m, const char *group); +void unit_invalidate_cgroup(Unit *u, CGroupMask m); + +void manager_invalidate_startup_units(Manager *m); + const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_; CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_; diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c index 5162ce34cb..54830a515b 100644 --- a/src/core/dbus-automount.c +++ b/src/core/dbus-automount.c @@ -20,8 +20,9 @@ ***/ #include "automount.h" -#include "dbus-automount.h" #include "bus-util.h" +#include "dbus-automount.h" +#include "string-util.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, automount_result, AutomountResult); diff --git a/src/core/dbus-busname.c b/src/core/dbus-busname.c index b1ceb05b1a..445b237643 100644 --- a/src/core/dbus-busname.c +++ b/src/core/dbus-busname.c @@ -19,10 +19,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" +#include "bus-util.h" #include "busname.h" #include "dbus-busname.h" -#include "bus-util.h" +#include "string-util.h" +#include "unit.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, busname_result, BusNameResult); diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index ba2f4e53b9..3fd295baa9 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -19,11 +19,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "alloc-util.h" #include "bus-util.h" -#include "path-util.h" #include "cgroup-util.h" #include "cgroup.h" #include "dbus-cgroup.h" +#include "fd-util.h" +#include "fileio.h" +#include "path-util.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy); @@ -133,34 +136,16 @@ static int property_get_device_allow( return sd_bus_message_close_container(reply); } -static int property_get_ulong_as_u64( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - unsigned long *ul = userdata; - - assert(bus); - assert(reply); - assert(ul); - - return sd_bus_message_append(reply, "t", *ul == (unsigned long) -1 ? (uint64_t) -1 : (uint64_t) *ul); -} - const sd_bus_vtable bus_cgroup_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0), SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0), - SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0), - SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0), + SD_BUS_PROPERTY("CPUShares", "t", NULL, offsetof(CGroupContext, cpu_shares), 0), + SD_BUS_PROPERTY("StartupCPUShares", "t", NULL, offsetof(CGroupContext, startup_cpu_shares), 0), SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0), SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0), - SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0), - SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0), + SD_BUS_PROPERTY("BlockIOWeight", "t", NULL, offsetof(CGroupContext, blockio_weight), 0), + SD_BUS_PROPERTY("StartupBlockIOWeight", "t", NULL, offsetof(CGroupContext, startup_blockio_weight), 0), SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0), SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), @@ -168,6 +153,8 @@ const sd_bus_vtable bus_cgroup_vtable[] = { SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0), SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0), SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0), + SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, tasks_accounting), 0), + SD_BUS_PROPERTY("TasksMax", "t", NULL, offsetof(CGroupContext, tasks_max), 0), SD_BUS_VTABLE_END }; @@ -228,56 +215,52 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->cpu_accounting = b; - u->cgroup_realized_mask &= ~CGROUP_MASK_CPUACCT; + unit_invalidate_cgroup(u, CGROUP_MASK_CPUACCT|CGROUP_MASK_CPU); unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no"); } return 1; } else if (streq(name, "CPUShares")) { - uint64_t u64; - unsigned long ul; + uint64_t shares; - r = sd_bus_message_read(message, "t", &u64); + r = sd_bus_message_read(message, "t", &shares); if (r < 0) return r; - if (u64 == (uint64_t) -1) - ul = (unsigned long) -1; - else { - ul = (unsigned long) u64; - if (ul <= 0 || (uint64_t) ul != u64) - return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range"); - } + if (!CGROUP_CPU_SHARES_IS_OK(shares)) + return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range"); if (mode != UNIT_CHECK) { - c->cpu_shares = ul; - u->cgroup_realized_mask &= ~CGROUP_MASK_CPU; - unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul); + c->cpu_shares = shares; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + + if (shares == CGROUP_CPU_SHARES_INVALID) + unit_write_drop_in_private(u, mode, name, "CPUShares="); + else + unit_write_drop_in_private_format(u, mode, name, "CPUShares=%" PRIu64, shares); } return 1; } else if (streq(name, "StartupCPUShares")) { - uint64_t u64; - unsigned long ul; + uint64_t shares; - r = sd_bus_message_read(message, "t", &u64); + r = sd_bus_message_read(message, "t", &shares); if (r < 0) return r; - if (u64 == (uint64_t) -1) - ul = (unsigned long) -1; - else { - ul = (unsigned long) u64; - if (ul <= 0 || (uint64_t) ul != u64) - return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range"); - } + if (!CGROUP_CPU_SHARES_IS_OK(shares)) + return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range"); if (mode != UNIT_CHECK) { - c->startup_cpu_shares = ul; - u->cgroup_realized_mask &= ~CGROUP_MASK_CPU; - unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul); + c->startup_cpu_shares = shares; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + + if (shares == CGROUP_CPU_SHARES_INVALID) + unit_write_drop_in_private(u, mode, name, "StartupCPUShares="); + else + unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%" PRIu64, shares); } return 1; @@ -294,7 +277,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->cpu_quota_per_sec_usec = u64; - u->cgroup_realized_mask &= ~CGROUP_MASK_CPU; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000)); } @@ -309,56 +292,52 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->blockio_accounting = b; - u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no"); } return 1; } else if (streq(name, "BlockIOWeight")) { - uint64_t u64; - unsigned long ul; + uint64_t weight; - r = sd_bus_message_read(message, "t", &u64); + r = sd_bus_message_read(message, "t", &weight); if (r < 0) return r; - if (u64 == (uint64_t) -1) - ul = (unsigned long) -1; - else { - ul = (unsigned long) u64; - if (ul < 10 || ul > 1000) - return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range"); - } + if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight)) + return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range"); if (mode != UNIT_CHECK) { - c->blockio_weight = ul; - u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; - unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul); + c->blockio_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); + + if (weight == CGROUP_BLKIO_WEIGHT_INVALID) + unit_write_drop_in_private(u, mode, name, "BlockIOWeight="); + else + unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%" PRIu64, weight); } return 1; } else if (streq(name, "StartupBlockIOWeight")) { - uint64_t u64; - unsigned long ul; + uint64_t weight; - r = sd_bus_message_read(message, "t", &u64); + r = sd_bus_message_read(message, "t", &weight); if (r < 0) return r; - if (u64 == (uint64_t) -1) - ul = (unsigned long) -1; - else { - ul = (unsigned long) u64; - if (ul < 10 || ul > 1000) - return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range"); - } + if (CGROUP_BLKIO_WEIGHT_IS_OK(weight)) + return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range"); if (mode != UNIT_CHECK) { - c->startup_blockio_weight = ul; - u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; - unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul); + c->startup_blockio_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); + + if (weight == CGROUP_BLKIO_WEIGHT_INVALID) + unit_write_drop_in_private(u, mode, name, "StartupBlockIOWeight="); + else + unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%" PRIu64, weight); } return 1; @@ -427,7 +406,7 @@ int bus_cgroup_set_property( cgroup_context_free_blockio_device_bandwidth(c, a); } - u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); f = open_memstream(&buf, &size); if (!f) @@ -445,7 +424,9 @@ int bus_cgroup_set_property( fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth); } - fflush(f); + r = fflush_and_check(f); + if (r < 0) + return r; unit_write_drop_in_private(u, mode, name, buf); } @@ -453,17 +434,16 @@ int bus_cgroup_set_property( } else if (streq(name, "BlockIODeviceWeight")) { const char *path; - uint64_t u64; + uint64_t weight; unsigned n = 0; r = sd_bus_message_enter_container(message, 'a', "(st)"); if (r < 0) return r; - while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { - unsigned long ul = u64; + while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) { - if (ul < 10 || ul > 1000) + if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID) return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range"); if (mode != UNIT_CHECK) { @@ -489,7 +469,7 @@ int bus_cgroup_set_property( LIST_PREPEND(device_weights,c->blockio_device_weights, a); } - a->weight = ul; + a->weight = weight; } n++; @@ -510,7 +490,7 @@ int bus_cgroup_set_property( cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); } - u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); f = open_memstream(&buf, &size); if (!f) @@ -518,9 +498,11 @@ int bus_cgroup_set_property( fputs("BlockIODeviceWeight=\n", f); LIST_FOREACH(device_weights, a, c->blockio_device_weights) - fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight); + fprintf(f, "BlockIODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight); - fflush(f); + r = fflush_and_check(f); + if (r < 0) + return r; unit_write_drop_in_private(u, mode, name, buf); } @@ -535,7 +517,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->memory_accounting = b; - u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY; + unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no"); } @@ -550,8 +532,12 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->memory_limit = limit; - u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY; - unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit); + unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); + + if (limit == (uint64_t) -1) + unit_write_drop_in_private(u, mode, name, "MemoryLimit=infinity"); + else + unit_write_drop_in_private_format(u, mode, name, "MemoryLimit=%" PRIu64, limit); } return 1; @@ -572,7 +558,7 @@ int bus_cgroup_set_property( char *buf; c->device_policy = p; - u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICES; + unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES); buf = strjoina("DevicePolicy=", policy); unit_write_drop_in_private(u, mode, name, buf); @@ -651,7 +637,7 @@ int bus_cgroup_set_property( cgroup_context_free_device_allow(c, c->device_allow); } - u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICES; + unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES); f = open_memstream(&buf, &size); if (!f) @@ -661,12 +647,47 @@ int bus_cgroup_set_property( LIST_FOREACH(device_allow, a, c->device_allow) fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : ""); - fflush(f); + r = fflush_and_check(f); + if (r < 0) + return r; unit_write_drop_in_private(u, mode, name, buf); } return 1; + } else if (streq(name, "TasksAccounting")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tasks_accounting = b; + unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); + unit_write_drop_in_private(u, mode, name, b ? "TasksAccounting=yes" : "TasksAccounting=no"); + } + + return 1; + + } else if (streq(name, "TasksMax")) { + uint64_t limit; + + r = sd_bus_message_read(message, "t", &limit); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tasks_max = limit; + unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); + + if (limit == (uint64_t) -1) + unit_write_drop_in_private(u, mode, name, "TasksMax=infinity"); + else + unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu64, limit); + } + + return 1; } if (u->transient && u->load_state == UNIT_STUB) { diff --git a/src/core/dbus-cgroup.h b/src/core/dbus-cgroup.h index c2a3910f3d..9dc187c066 100644 --- a/src/core/dbus-cgroup.h +++ b/src/core/dbus-cgroup.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "cgroup.h" extern const sd_bus_vtable bus_cgroup_vtable[]; diff --git a/src/core/dbus-device.c b/src/core/dbus-device.c index cb156fd37c..97e4a47556 100644 --- a/src/core/dbus-device.c +++ b/src/core/dbus-device.c @@ -19,9 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" -#include "device.h" #include "dbus-device.h" +#include "device.h" +#include "unit.h" const sd_bus_vtable bus_device_vtable[] = { SD_BUS_VTABLE_START(0), diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index ed55fcfca2..093179c003 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -25,22 +25,28 @@ #include <seccomp.h> #endif +#include "af-list.h" +#include "alloc-util.h" #include "bus-util.h" -#include "missing.h" -#include "ioprio.h" -#include "strv.h" -#include "fileio.h" -#include "execute.h" -#include "capability.h" +#include "capability-util.h" +#include "dbus-execute.h" #include "env-util.h" -#include "af-list.h" +#include "execute.h" +#include "fd-util.h" +#include "fileio.h" +#include "ioprio.h" +#include "missing.h" #include "namespace.h" +#include "parse-util.h" #include "path-util.h" -#include "dbus-execute.h" - +#include "process-util.h" +#include "rlimit-util.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" #endif +#include "strv.h" +#include "syslog-util.h" +#include "utf8.h" BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput); @@ -83,45 +89,6 @@ static int property_get_environment_files( return sd_bus_message_close_container(reply); } -static int property_get_rlimit( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - struct rlimit *rl; - uint64_t u; - rlim_t x; - - assert(bus); - assert(reply); - assert(userdata); - - rl = *(struct rlimit**) userdata; - if (rl) - x = rl->rlim_max; - else { - struct rlimit buf = {}; - int z; - - z = rlimit_from_string(property); - assert(z >= 0); - - getrlimit(z, &buf); - x = buf.rlim_max; - } - - /* rlim_t might have different sizes, let's map - * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on - * all archs */ - u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x; - - return sd_bus_message_append(reply, "t", u); -} - static int property_get_oom_score_adjust( sd_bus *bus, const char *path, @@ -146,7 +113,7 @@ static int property_get_oom_score_adjust( n = 0; if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0) - safe_atoi(t, &n); + safe_atoi32(t, &n); } return sd_bus_message_append(reply, "i", n); @@ -595,28 +562,92 @@ static int property_get_address_families( return sd_bus_message_close_container(reply); } +static int property_get_working_directory( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = userdata; + const char *wd; + + assert(bus); + assert(reply); + assert(c); + + if (c->working_directory_home) + wd = "~"; + else + wd = c->working_directory; + + if (c->working_directory_missing_ok) + wd = strjoina("!", wd); + + return sd_bus_message_append(reply, "s", wd); +} + +static int property_get_syslog_level( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = userdata; + + assert(bus); + assert(reply); + assert(c); + + return sd_bus_message_append(reply, "i", LOG_PRI(c->syslog_priority)); +} + +static int property_get_syslog_facility( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = userdata; + + assert(bus); + assert(reply); + assert(c); + + return sd_bus_message_append(reply, "i", LOG_FAC(c->syslog_priority)); +} + const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("EnvironmentFiles", "a(sb)", property_get_environment_files, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PassEnvironment", "as", NULL, offsetof(ExecContext, pass_environment), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitCPU", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitFSIZE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitDATA", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitSTACK", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitCORE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitRSS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitNOFILE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitAS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitNPROC", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitMEMLOCK", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitLOCKS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitSIGPENDING", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitMSGQUEUE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitNICE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitRTPRIO", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitRTTIME", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("WorkingDirectory", "s", NULL, offsetof(ExecContext, working_directory), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitCPU", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitFSIZE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitDATA", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitSTACK", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitCORE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitRSS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitNOFILE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitAS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitNPROC", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitMEMLOCK", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitLOCKS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitSIGPENDING", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitMSGQUEUE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitNICE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitRTPRIO", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitRTTIME", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -637,6 +668,8 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("SyslogPriority", "i", bus_property_get_int, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SyslogIdentifier", "s", NULL, offsetof(ExecContext, syslog_identifier), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SyslogLevelPrefix", "b", bus_property_get_bool, offsetof(ExecContext, syslog_level_prefix), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Capabilities", "s", property_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -829,7 +862,64 @@ int bus_exec_context_set_transient_property( } return 1; + } else if (streq(name, "SyslogIdentifier")) { + const char *id; + r = sd_bus_message_read(message, "s", &id); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + + if (isempty(id)) { + c->syslog_identifier = mfree(c->syslog_identifier); + } else { + char *t; + + t = strdup(id); + if (!t) + return -ENOMEM; + + free(c->syslog_identifier); + c->syslog_identifier = t; + } + + unit_write_drop_in_private_format(u, mode, name, "SyslogIdentifier=%s\n", id); + } + + return 1; + } else if (streq(name, "SyslogLevel")) { + int level; + + r = sd_bus_message_read(message, "i", &level); + if (r < 0) + return r; + + if (!log_level_is_valid(level)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log level value out of range"); + + if (mode != UNIT_CHECK) { + c->syslog_priority = (c->syslog_priority & LOG_FACMASK) | level; + unit_write_drop_in_private_format(u, mode, name, "SyslogLevel=%i\n", level); + } + + return 1; + } else if (streq(name, "SyslogFacility")) { + int facility; + + r = sd_bus_message_read(message, "i", &facility); + if (r < 0) + return r; + + if (!log_facility_unshifted_is_valid(facility)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log facility value out of range"); + + if (mode != UNIT_CHECK) { + c->syslog_priority = (facility << 3) | LOG_PRI(c->syslog_priority); + unit_write_drop_in_private_format(u, mode, name, "SyslogFacility=%i\n", facility); + } + + return 1; } else if (streq(name, "Nice")) { int n; @@ -847,27 +937,62 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "TTYPath")) { - const char *tty; + } else if (STR_IN_SET(name, "TTYPath", "RootDirectory")) { + const char *s; - r = sd_bus_message_read(message, "s", &tty); + r = sd_bus_message_read(message, "s", &s); if (r < 0) return r; - if (!path_is_absolute(tty)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TTY device not absolute path"); + if (!path_is_absolute(s)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s takes an absolute path", name); if (mode != UNIT_CHECK) { - char *t; + if (streq(name, "TTYPath")) + r = free_and_strdup(&c->tty_path, s); + else { + assert(streq(name, "RootDirectory")); + r = free_and_strdup(&c->root_directory, s); + } + if (r < 0) + return r; - t = strdup(tty); - if (!t) - return -ENOMEM; + unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, s); + } + + return 1; + + } else if (streq(name, "WorkingDirectory")) { + const char *s; + bool missing_ok; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + if (s[0] == '-') { + missing_ok = true; + s++; + } else + missing_ok = false; + + if (!streq(s, "~") && !path_is_absolute(s)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "WorkingDirectory= expects an absolute path or '~'"); + + if (mode != UNIT_CHECK) { + if (streq(s, "~")) { + c->working_directory = mfree(c->working_directory); + c->working_directory_home = true; + } else { + r = free_and_strdup(&c->working_directory, s); + if (r < 0) + return r; - free(c->tty_path); - c->tty_path = t; + c->working_directory_home = false; + } - unit_write_drop_in_private_format(u, mode, name, "TTYPath=%s\n", tty); + c->working_directory_missing_ok = missing_ok; + unit_write_drop_in_private_format(u, mode, name, "WorkingDirectory=%s%s", missing_ok ? "-" : "", s); } return 1; @@ -933,37 +1058,10 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "IgnoreSIGPIPE")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->ignore_sigpipe = b; - - unit_write_drop_in_private_format(u, mode, name, "IgnoreSIGPIPE=%s\n", yes_no(b)); - } - - return 1; - - } else if (streq(name, "TTYVHangup")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->tty_vhangup = b; - - unit_write_drop_in_private_format(u, mode, name, "TTYVHangup=%s\n", yes_no(b)); - } - - return 1; - - } else if (streq(name, "TTYReset")) { + } else if (STR_IN_SET(name, + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", + "NoNewPrivileges", "SyslogLevelPrefix")) { int b; r = sd_bus_message_read(message, "b", &b); @@ -971,9 +1069,24 @@ int bus_exec_context_set_transient_property( return r; if (mode != UNIT_CHECK) { - c->tty_reset = b; - - unit_write_drop_in_private_format(u, mode, name, "TTYReset=%s\n", yes_no(b)); + if (streq(name, "IgnoreSIGPIPE")) + c->ignore_sigpipe = b; + else if (streq(name, "TTYVHangup")) + c->tty_vhangup = b; + else if (streq(name, "TTYReset")) + c->tty_reset = b; + else if (streq(name, "PrivateTmp")) + c->private_tmp = b; + else if (streq(name, "PrivateDevices")) + c->private_devices = b; + else if (streq(name, "PrivateNetwork")) + c->private_network = b; + else if (streq(name, "NoNewPrivileges")) + c->no_new_privileges = b; + else if (streq(name, "SyslogLevelPrefix")) + c->syslog_level_prefix = b; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, yes_no(b)); } return 1; @@ -1049,18 +1162,299 @@ int bus_exec_context_set_transient_property( _cleanup_free_ char *joined = NULL; char **e; - e = strv_env_merge(2, c->environment, l); - if (!e) - return -ENOMEM; + if (strv_length(l) == 0) { + c->environment = strv_free(c->environment); + unit_write_drop_in_private_format(u, mode, name, "Environment=\n"); + } else { + e = strv_env_merge(2, c->environment, l); + if (!e) + return -ENOMEM; - strv_free(c->environment); - c->environment = e; + strv_free(c->environment); + c->environment = e; - joined = strv_join_quoted(c->environment); - if (!joined) - return -ENOMEM; + joined = strv_join_quoted(c->environment); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "Environment=%s\n", joined); + } + } + + return 1; + + } else if (streq(name, "TimerSlackNSec")) { + + nsec_t n; + + r = sd_bus_message_read(message, "t", &n); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->timer_slack_nsec = n; + unit_write_drop_in_private_format(u, mode, name, "TimerSlackNSec=" NSEC_FMT "\n", n); + } + + return 1; + + } else if (streq(name, "OOMScoreAdjust")) { + int oa; + + r = sd_bus_message_read(message, "i", &oa); + if (r < 0) + return r; + + if (!oom_score_adjust_is_valid(oa)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "OOM score adjust value out of range"); + + if (mode != UNIT_CHECK) { + c->oom_score_adjust = oa; + c->oom_score_adjust_set = true; + unit_write_drop_in_private_format(u, mode, name, "OOMScoreAdjust=%i\n", oa); + } + + return 1; + + } else if (streq(name, "EnvironmentFiles")) { + + _cleanup_free_ char *joined = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char **l = NULL; + size_t size = 0; + char **i; + + r = sd_bus_message_enter_container(message, 'a', "(sb)"); + if (r < 0) + return r; + + f = open_memstream(&joined, &size); + if (!f) + return -ENOMEM; + + STRV_FOREACH(i, c->environment_files) + fprintf(f, "EnvironmentFile=%s\n", *i); + + while ((r = sd_bus_message_enter_container(message, 'r', "sb")) > 0) { + const char *path; + int b; + + r = sd_bus_message_read(message, "sb", &path, &b); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!isempty(path) && !path_is_absolute(path)) + return sd_bus_error_set_errnof(error, EINVAL, "Path %s is not absolute.", path); + + if (mode != UNIT_CHECK) { + char *buf = NULL; + + buf = strjoin(b ? "-" : "", path, NULL); + if (!buf) + return -ENOMEM; + + fprintf(f, "EnvironmentFile=%s\n", buf); + + r = strv_consume(&l, buf); + if (r < 0) + return r; + } + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + r = fflush_and_check(f); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + if (strv_isempty(l)) { + c->environment_files = strv_free(c->environment_files); + unit_write_drop_in_private(u, mode, name, "EnvironmentFile=\n"); + } else { + r = strv_extend_strv(&c->environment_files, l, true); + if (r < 0) + return r; + + unit_write_drop_in_private(u, mode, name, joined); + } + } + + return 1; + + } else if (streq(name, "PassEnvironment")) { + + _cleanup_strv_free_ char **l = NULL; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + if (!strv_env_name_is_valid(l)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment block."); + + if (mode != UNIT_CHECK) { + if (strv_isempty(l)) { + c->pass_environment = strv_free(c->pass_environment); + unit_write_drop_in_private_format(u, mode, name, "PassEnvironment=\n"); + } else { + _cleanup_free_ char *joined = NULL; + + r = strv_extend_strv(&c->pass_environment, l, true); + if (r < 0) + return r; + + joined = strv_join_quoted(c->pass_environment); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "PassEnvironment=%s\n", joined); + } + } + + return 1; + + } else if (STR_IN_SET(name, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) { + + _cleanup_strv_free_ char **l = NULL; + char ***dirs; + char **p; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + STRV_FOREACH(p, l) { + int offset; + if (!utf8_is_valid(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name); + + offset = **p == '-'; + if (!path_is_absolute(*p + offset)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name); + } + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *joined = NULL; + + if (streq(name, "ReadWriteDirectories")) + dirs = &c->read_write_dirs; + else if (streq(name, "ReadOnlyDirectories")) + dirs = &c->read_only_dirs; + else if (streq(name, "InaccessibleDirectories")) + dirs = &c->inaccessible_dirs; + + if (strv_length(l) == 0) { + *dirs = strv_free(*dirs); + unit_write_drop_in_private_format(u, mode, name, "%s=\n", name); + } else { + r = strv_extend_strv(dirs, l, true); + + if (r < 0) + return -ENOMEM; + + joined = strv_join_quoted(*dirs); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, joined); + } + + } + + return 1; + + } else if (streq(name, "ProtectSystem")) { + const char *s; + ProtectSystem ps; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; - unit_write_drop_in_private_format(u, mode, name, "Environment=%s\n", joined); + r = parse_boolean(s); + if (r > 0) + ps = PROTECT_SYSTEM_YES; + else if (r == 0) + ps = PROTECT_SYSTEM_NO; + else { + ps = protect_system_from_string(s); + if (ps < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse protect system value"); + } + + if (mode != UNIT_CHECK) { + c->protect_system = ps; + unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, s); + } + + return 1; + + } else if (streq(name, "ProtectHome")) { + const char *s; + ProtectHome ph; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + r = parse_boolean(s); + if (r > 0) + ph = PROTECT_HOME_YES; + else if (r == 0) + ph = PROTECT_HOME_NO; + else { + ph = protect_home_from_string(s); + if (ph < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse protect home value"); + } + + if (mode != UNIT_CHECK) { + c->protect_home = ph; + unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, s); + } + + return 1; + + } else if (streq(name, "RuntimeDirectory")) { + _cleanup_strv_free_ char **l = NULL; + char **p; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + STRV_FOREACH(p, l) { + if (!filename_is_valid(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Runtime directory is not valid %s", *p); + } + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *joined = NULL; + + if (strv_isempty(l)) { + c->runtime_directory = strv_free(c->runtime_directory); + unit_write_drop_in_private_format(u, mode, name, "%s=\n", name); + } else { + r = strv_extend_strv(&c->runtime_directory, l, true); + + if (r < 0) + return -ENOMEM; + + joined = strv_join_quoted(c->runtime_directory); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, joined); + } } return 1; diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h index e4c2d5ddf6..c44517ea22 100644 --- a/src/core/dbus-execute.h +++ b/src/core/dbus-execute.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "execute.h" #define BUS_EXEC_STATUS_VTABLE(prefix, offset, flags) \ diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c index cd6b909426..8c30d66250 100644 --- a/src/core/dbus-job.c +++ b/src/core/dbus-job.c @@ -19,12 +19,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "log.h" #include "sd-bus.h" -#include "selinux-access.h" -#include "job.h" + +#include "alloc-util.h" #include "dbus-job.h" #include "dbus.h" +#include "job.h" +#include "log.h" +#include "selinux-access.h" +#include "string-util.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, job_type, JobType); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_state, job_state, JobState); diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h index fb5f1b513e..0f2fbe2ee2 100644 --- a/src/core/dbus-job.h +++ b/src/core/dbus-job.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "job.h" extern const sd_bus_vtable bus_job_vtable[]; diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c index 3b8116281c..c633eb1b76 100644 --- a/src/core/dbus-kill.c +++ b/src/core/dbus-kill.c @@ -19,11 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "signal-util.h" #include "bus-util.h" - -#include "kill.h" #include "dbus-kill.h" +#include "kill.h" +#include "signal-util.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_kill_mode, kill_mode, KillMode); diff --git a/src/core/dbus-kill.h b/src/core/dbus-kill.h index 7c15f3a90b..1d32fca547 100644 --- a/src/core/dbus-kill.h +++ b/src/core/dbus-kill.h @@ -22,8 +22,9 @@ ***/ #include "sd-bus.h" -#include "unit.h" + #include "kill.h" +#include "unit.h" extern const sd_bus_vtable bus_kill_vtable[]; diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 4e5d67fc19..4d730290b2 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -20,27 +20,33 @@ ***/ #include <errno.h> +#include <sys/prctl.h> #include <unistd.h> -#include "log.h" -#include "strv.h" +#include "alloc-util.h" +#include "architecture.h" #include "build.h" -#include "install.h" -#include "selinux-access.h" -#include "watchdog.h" +#include "bus-common-errors.h" #include "clock-util.h" -#include "path-util.h" -#include "virt.h" -#include "architecture.h" -#include "env-util.h" -#include "dbus.h" +#include "dbus-execute.h" #include "dbus-job.h" #include "dbus-manager.h" #include "dbus-unit.h" -#include "dbus-snapshot.h" -#include "dbus-execute.h" -#include "bus-common-errors.h" +#include "dbus.h" +#include "env-util.h" +#include "fd-util.h" +#include "fileio.h" #include "formats-util.h" +#include "install.h" +#include "log.h" +#include "path-util.h" +#include "selinux-access.h" +#include "stat-util.h" +#include "string-util.h" +#include "strv.h" +#include "syslog-util.h" +#include "virt.h" +#include "watchdog.h" static int property_get_version( sd_bus *bus, @@ -81,10 +87,21 @@ static int property_get_virtualization( void *userdata, sd_bus_error *error) { + int v; + assert(bus); assert(reply); - return sd_bus_message_append(reply, "s", virtualization_to_string(detect_virtualization())); + v = detect_virtualization(); + + /* Make sure to return the empty string when we detect no virtualization, as that is the API. + * + * https://github.com/systemd/systemd/issues/1423 + */ + + return sd_bus_message_append( + reply, "s", + v == VIRTUALIZATION_NONE ? "" : virtualization_to_string(v)); } static int property_get_architecture( @@ -111,8 +128,7 @@ static int property_get_tainted( void *userdata, sd_bus_error *error) { - char buf[sizeof("split-usr:mtab-not-symlink:cgroups-missing:local-hwclock:")] = "", *e = buf; - _cleanup_free_ char *p = NULL; + char buf[sizeof("split-usr:cgroups-missing:local-hwclock:")] = "", *e = buf; Manager *m = userdata; assert(bus); @@ -122,9 +138,6 @@ static int property_get_tainted( if (m->taint_usr) e = stpcpy(e, "split-usr:"); - if (readlink_malloc("/etc/mtab", &p) < 0) - e = stpcpy(e, "mtab-not-symlink:"); - if (access("/proc/cgroups", F_OK) < 0) e = stpcpy(e, "cgroups-missing:"); @@ -339,6 +352,21 @@ static int property_set_runtime_watchdog( return watchdog_set_timeout(t); } +static int property_get_timer_slack_nsec( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "t", (uint64_t) prctl(PR_GET_TIMERSLACK)); +} + static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_free_ char *path = NULL; Manager *m = userdata; @@ -616,6 +644,7 @@ static int transient_unit_from_message( Unit **unit, sd_bus_error *error) { + UnitType t; Unit *u; int r; @@ -623,12 +652,18 @@ static int transient_unit_from_message( assert(message); assert(name); + t = unit_name_to_type(name); + if (t < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name or type."); + + if (!unit_vtable[t]->can_transient) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t)); + r = manager_load_unit(m, name, NULL, error, &u); if (r < 0) return r; - if (u->load_state != UNIT_NOT_FOUND || - set_size(u->dependencies[UNIT_REFERENCED_BY]) > 0) + if (!unit_is_pristine(u)) return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit %s already exists.", name); /* OK, the unit failed to load and is unreferenced, now let's @@ -642,6 +677,9 @@ static int transient_unit_from_message( if (r < 0) return r; + /* Now load the missing bits of the unit we just created */ + manager_dispatch_load_queue(m); + *unit = u; return 0; @@ -652,8 +690,6 @@ static int transient_aux_units_from_message( sd_bus_message *message, sd_bus_error *error) { - Unit *u; - char *name = NULL; int r; assert(m); @@ -664,20 +700,17 @@ static int transient_aux_units_from_message( return r; while ((r = sd_bus_message_enter_container(message, 'r', "sa(sv)")) > 0) { + const char *name = NULL; + Unit *u; + r = sd_bus_message_read(message, "s", &name); if (r < 0) return r; r = transient_unit_from_message(m, message, name, &u, error); - if (r < 0 && r != -EEXIST) + if (r < 0) return r; - if (r != -EEXIST) { - r = unit_load(u); - if (r < 0) - return r; - } - r = sd_bus_message_exit_container(message); if (r < 0) return r; @@ -696,7 +729,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata, const char *name, *smode; Manager *m = userdata; JobMode mode; - UnitType t; Unit *u; int r; @@ -711,13 +743,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata, if (r < 0) return r; - t = unit_name_to_type(name); - if (t < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit type."); - - if (!unit_vtable[t]->can_transient) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t)); - mode = job_mode_from_string(smode); if (mode < 0) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s is invalid.", smode); @@ -736,13 +761,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata, if (r < 0) return r; - /* And load this stub fully */ - r = unit_load(u); - if (r < 0) - return r; - - manager_dispatch_load_queue(m); - /* Finally, start it */ return bus_unit_queue_job(message, u, JOB_START, mode, false, error); } @@ -1072,66 +1090,8 @@ static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *er return sd_bus_reply_method_return(message, "s", dump); } -static int method_create_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *path = NULL; - Manager *m = userdata; - const char *name; - int cleanup; - Snapshot *s = NULL; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "start", error); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "sb", &name, &cleanup); - if (r < 0) - return r; - - if (isempty(name)) - name = NULL; - - r = bus_verify_manage_units_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = snapshot_create(m, name, cleanup, error, &s); - if (r < 0) - return r; - - path = unit_dbus_path(UNIT(s)); - if (!path) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", path); -} - -static int method_remove_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *name; - Unit *u; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - u = manager_get_unit(m, name); - if (!u) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s does not exist.", name); - - if (u->type != UNIT_SNAPSHOT) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not a snapshot", name); - - return bus_snapshot_method_remove(message, u, error); +static int method_refuse_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed."); } static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -1201,8 +1161,10 @@ static int method_exit(sd_bus_message *message, void *userdata, sd_bus_error *er if (r < 0) return r; - if (m->running_as == MANAGER_SYSTEM) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers."); + /* Exit() (in contrast to SetExitCode()) is actually allowed even if + * we are running on the host. It will fall back on reboot() in + * systemd-shutdown if it cannot do the exit() because it isn't a + * container. */ m->exit_code = MANAGER_EXIT; @@ -1450,6 +1412,30 @@ static int method_unset_and_set_environment(sd_bus_message *message, void *userd return sd_bus_reply_method_return(message, NULL); } +static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_error *error) { + uint8_t code; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "exit", error); + if (r < 0) + return r; + + r = sd_bus_message_read_basic(message, 'y', &code); + if (r < 0) + return r; + + if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers."); + + m->return_value = code; + + return sd_bus_reply_method_return(message, NULL); +} + static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; Manager *m = userdata; @@ -1525,9 +1511,9 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - state = unit_file_get_state(scope, NULL, name); - if (state < 0) - return state; + r = unit_file_get_state(scope, NULL, name, &state); + if (r < 0) + return r; return sd_bus_reply_method_return(message, "s", unit_file_state_to_string(state)); } @@ -1655,6 +1641,8 @@ static int method_enable_unit_files_generic( scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; r = call(scope, runtime, NULL, l, force, &changes, &n_changes); + if (r == -ESHUTDOWN) + return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked"); if (r < 0) return r; @@ -1889,6 +1877,8 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; r = unit_file_add_dependency(scope, runtime, NULL, l, target, dep, force, &changes, &n_changes); + if (r == -ESHUTDOWN) + return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked"); if (r < 0) return r; @@ -1933,6 +1923,35 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0), SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0), SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0), + SD_BUS_PROPERTY("ExitCode", "y", bus_property_get_unsigned, offsetof(Manager, return_value), 0), + SD_BUS_PROPERTY("DefaultTimerAccuracyUSec", "t", bus_property_get_usec, offsetof(Manager, default_timer_accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultTimeoutStartUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultTimeoutStopUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultRestartUSec", "t", bus_property_get_usec, offsetof(Manager, default_restart_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultStartLimitBurst", "u", bus_property_get_unsigned, offsetof(Manager, default_start_limit_burst), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultCPUAccounting", "b", bus_property_get_bool, offsetof(Manager, default_cpu_accounting), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultBlockIOAccounting", "b", bus_property_get_bool, offsetof(Manager, default_blockio_accounting), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultMemoryAccounting", "b", bus_property_get_bool, offsetof(Manager, default_memory_accounting), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultTasksAccounting", "b", bus_property_get_bool, offsetof(Manager, default_tasks_accounting), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitCPU", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitFSIZE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitDATA", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitSTACK", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitCORE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitRSS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitNOFILE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitAS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitNPROC", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitMEMLOCK", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitLOCKS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitSIGPENDING", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitMSGQUEUE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitNICE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitRTPRIO", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitRTTIME", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultTasksMax", "t", NULL, offsetof(Manager, default_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), @@ -1959,8 +1978,8 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Dump", NULL, "s", method_dump, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_create_snapshot, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_remove_snapshot, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Reload", NULL, NULL, method_reload, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Reexecute", NULL, NULL, method_reexecute, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Exit", NULL, NULL, method_exit, 0), @@ -1986,6 +2005,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("AddDependencyUnitFiles", "asssbb", "a(sss)", method_add_dependency_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("UnitNew", "so", 0), SD_BUS_SIGNAL("UnitRemoved", "so", 0), diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index 24813c6d20..bc5751a10d 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -19,13 +19,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" -#include "mount.h" +#include "bus-util.h" +#include "dbus-cgroup.h" #include "dbus-execute.h" #include "dbus-kill.h" -#include "dbus-cgroup.h" #include "dbus-mount.h" -#include "bus-util.h" +#include "mount.h" +#include "string-util.h" +#include "unit.h" static int property_get_what( sd_bus *bus, diff --git a/src/core/dbus-mount.h b/src/core/dbus-mount.h index f7004d252f..dd0bf51bb0 100644 --- a/src/core/dbus-mount.h +++ b/src/core/dbus-mount.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_mount_vtable[]; diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c index 683561999b..e0544e9161 100644 --- a/src/core/dbus-path.c +++ b/src/core/dbus-path.c @@ -19,10 +19,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" -#include "path.h" -#include "dbus-path.h" #include "bus-util.h" +#include "dbus-path.h" +#include "path.h" +#include "string-util.h" +#include "unit.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, path_result, PathResult); diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c index f8fb373bf0..16375b2311 100644 --- a/src/core/dbus-scope.c +++ b/src/core/dbus-scope.c @@ -19,17 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "selinux-access.h" -#include "unit.h" -#include "scope.h" -#include "dbus.h" -#include "bus-util.h" -#include "bus-internal.h" +#include "alloc-util.h" #include "bus-common-errors.h" -#include "dbus-unit.h" +#include "bus-internal.h" +#include "bus-util.h" #include "dbus-cgroup.h" #include "dbus-kill.h" #include "dbus-scope.h" +#include "dbus-unit.h" +#include "dbus.h" +#include "scope.h" +#include "selinux-access.h" +#include "unit.h" static int bus_scope_abandon(sd_bus_message *message, void *userdata, sd_bus_error *error) { Scope *s = userdata; diff --git a/src/core/dbus-scope.h b/src/core/dbus-scope.h index 33beda47b7..4fb0b25e09 100644 --- a/src/core/dbus-scope.h +++ b/src/core/dbus-scope.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_scope_vtable[]; diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index e1f3d56495..24f611a593 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -19,15 +19,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "strv.h" -#include "path-util.h" -#include "unit.h" -#include "service.h" +#include "alloc-util.h" +#include "async.h" +#include "bus-util.h" +#include "dbus-cgroup.h" #include "dbus-execute.h" #include "dbus-kill.h" -#include "dbus-cgroup.h" #include "dbus-service.h" -#include "bus-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "path-util.h" +#include "service.h" +#include "string-util.h" +#include "strv.h" +#include "unit.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, service_type, ServiceType); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, service_result, ServiceResult); @@ -58,10 +63,13 @@ const sd_bus_vtable bus_service_vtable[] = { SD_BUS_PROPERTY("MainPID", "u", bus_property_get_pid, offsetof(Service, main_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Service, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("BusName", "s", NULL, offsetof(Service, bus_name), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", NULL, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NFileDescriptorStore", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store), 0), SD_BUS_PROPERTY("StatusText", "s", NULL, offsetof(Service, status_text), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("StatusErrno", "i", NULL, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), @@ -118,6 +126,37 @@ static int bus_service_set_transient_property( return 1; + } else if (STR_IN_SET(name, + "StandardInputFileDescriptor", + "StandardOutputFileDescriptor", + "StandardErrorFileDescriptor")) { + int fd; + + r = sd_bus_message_read(message, "h", &fd); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + int copy; + + copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (copy < 0) + return -errno; + + if (streq(name, "StandardInputFileDescriptor")) { + asynchronous_close(s->stdin_fd); + s->stdin_fd = copy; + } else if (streq(name, "StandardOutputFileDescriptor")) { + asynchronous_close(s->stdout_fd); + s->stdout_fd = copy; + } else { + asynchronous_close(s->stderr_fd); + s->stderr_fd = copy; + } + } + + return 1; + } else if (streq(name, "ExecStart")) { unsigned n = 0; @@ -209,7 +248,9 @@ static int bus_service_set_transient_property( a); } - fflush(f); + r = fflush_and_check(f); + if (r < 0) + return r; unit_write_drop_in_private(UNIT(s), mode, name, buf); } diff --git a/src/core/dbus-service.h b/src/core/dbus-service.h index aab9f7aa26..a67b64ab5b 100644 --- a/src/core/dbus-service.h +++ b/src/core/dbus-service.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_service_vtable[]; diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c index 09e78d1f33..469e3e1c93 100644 --- a/src/core/dbus-slice.c +++ b/src/core/dbus-slice.c @@ -19,10 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" -#include "slice.h" #include "dbus-cgroup.h" #include "dbus-slice.h" +#include "slice.h" +#include "unit.h" const sd_bus_vtable bus_slice_vtable[] = { SD_BUS_VTABLE_START(0), diff --git a/src/core/dbus-slice.h b/src/core/dbus-slice.h index eadc3b1a9c..117d11471b 100644 --- a/src/core/dbus-slice.h +++ b/src/core/dbus-slice.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_slice_vtable[]; diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c deleted file mode 100644 index cfe44c9c15..0000000000 --- a/src/core/dbus-snapshot.c +++ /dev/null @@ -1,55 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 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 "selinux-access.h" -#include "unit.h" -#include "dbus.h" -#include "snapshot.h" -#include "dbus-snapshot.h" - -int bus_snapshot_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Snapshot *s = userdata; - int r; - - assert(message); - assert(s); - - r = mac_selinux_unit_access_check(UNIT(s), message, "stop", error); - if (r < 0) - return r; - - r = bus_verify_manage_units_async(UNIT(s)->manager, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - snapshot_remove(s); - - return sd_bus_reply_method_return(message, NULL); -} - -const sd_bus_vtable bus_snapshot_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Cleanup", "b", bus_property_get_bool, offsetof(Snapshot, cleanup), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_METHOD("Remove", NULL, NULL, bus_snapshot_method_remove, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END -}; diff --git a/src/core/dbus-snapshot.h b/src/core/dbus-snapshot.h deleted file mode 100644 index 9288f44e15..0000000000 --- a/src/core/dbus-snapshot.h +++ /dev/null @@ -1,28 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 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 "sd-bus.h" - -extern const sd_bus_vtable bus_snapshot_vtable[]; - -int bus_snapshot_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 02599a9e55..895dd07753 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -19,12 +19,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" -#include "socket.h" -#include "dbus-execute.h" +#include "alloc-util.h" +#include "bus-util.h" #include "dbus-cgroup.h" +#include "dbus-execute.h" #include "dbus-socket.h" -#include "bus-util.h" +#include "socket.h" +#include "string-util.h" +#include "unit.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, socket_result, SocketResult); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); @@ -68,6 +70,7 @@ static int property_get_listen( case SOCKET_SPECIAL: case SOCKET_MQUEUE: case SOCKET_FIFO: + case SOCKET_USB_FUNCTION: a = p->path; break; @@ -83,6 +86,25 @@ static int property_get_listen( return sd_bus_message_close_container(reply); } + +static int property_get_fdname( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Socket *s = SOCKET(userdata); + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "s", socket_fdname(s)); +} + const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("BindIPv6Only", "s", property_get_bind_ipv6_only, offsetof(Socket, bind_ipv6_only), SD_BUS_VTABLE_PROPERTY_CONST), @@ -94,6 +116,7 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("SocketMode", "u", bus_property_get_mode, offsetof(Socket, socket_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Socket, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Accept", "b", bus_property_get_bool, offsetof(Socket, accept), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Writable", "b", bus_property_get_bool, offsetof(Socket, writable), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KeepAlive", "b", bus_property_get_bool, offsetof(Socket, keep_alive), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KeepAliveTimeUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_time), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KeepAliveIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_interval), SD_BUS_VTABLE_PROPERTY_CONST), @@ -126,6 +149,8 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Socket, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("NConnections", "u", bus_property_get_unsigned, offsetof(Socket, n_connections), 0), SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0), + SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0), + SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/dbus-socket.h b/src/core/dbus-socket.h index 17164d9871..8dad6ea2e9 100644 --- a/src/core/dbus-socket.h +++ b/src/core/dbus-socket.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_socket_vtable[]; diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c index 0093371306..f2a0f1d172 100644 --- a/src/core/dbus-swap.c +++ b/src/core/dbus-swap.c @@ -20,12 +20,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" -#include "swap.h" -#include "dbus-execute.h" +#include "bus-util.h" #include "dbus-cgroup.h" +#include "dbus-execute.h" #include "dbus-swap.h" -#include "bus-util.h" +#include "string-util.h" +#include "swap.h" +#include "unit.h" static int property_get_priority( sd_bus *bus, diff --git a/src/core/dbus-swap.h b/src/core/dbus-swap.h index 9469f68ab8..a414ca7f75 100644 --- a/src/core/dbus-swap.h +++ b/src/core/dbus-swap.h @@ -23,6 +23,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_swap_vtable[]; diff --git a/src/core/dbus-target.c b/src/core/dbus-target.c index 350f5c3ed2..654bcf1a29 100644 --- a/src/core/dbus-target.c +++ b/src/core/dbus-target.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" #include "dbus-target.h" +#include "unit.h" const sd_bus_vtable bus_target_vtable[] = { SD_BUS_VTABLE_START(0), diff --git a/src/core/dbus-target.h b/src/core/dbus-target.h index 4c4297bc9e..6be9c9f708 100644 --- a/src/core/dbus-target.h +++ b/src/core/dbus-target.h @@ -21,5 +21,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "sd-bus.h" extern const sd_bus_vtable bus_target_vtable[]; diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index 8ea2cf84a4..4bee82df07 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -19,11 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" -#include "timer.h" -#include "dbus-timer.h" +#include "alloc-util.h" #include "bus-util.h" +#include "dbus-timer.h" #include "strv.h" +#include "timer.h" +#include "unit.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, timer_result, TimerResult); @@ -179,8 +180,10 @@ const sd_bus_vtable bus_timer_vtable[] = { BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RandomUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; @@ -281,8 +284,23 @@ static int bus_timer_set_transient_property( return 1; - } else if (streq(name, "WakeSystem")) { + } else if (streq(name, "RandomUSec")) { + usec_t u = 0; + + r = sd_bus_message_read(message, "t", &u); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + char time[FORMAT_TIMESPAN_MAX]; + t->random_usec = u; + unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomSec=%s\n", format_timespan(time, sizeof(time), u, USEC_PER_MSEC)); + } + + return 1; + + } else if (streq(name, "WakeSystem")) { int b; r = sd_bus_message_read(message, "b", &b); @@ -291,11 +309,24 @@ static int bus_timer_set_transient_property( if (mode != UNIT_CHECK) { t->wake_system = b; - unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(t->wake_system)); + unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b)); } return 1; + } else if (streq(name, "RemainAfterElapse")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + t->remain_after_elapse = b; + unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b)); + } + + return 1; } return 0; diff --git a/src/core/dbus-timer.h b/src/core/dbus-timer.h index 103172f055..ca35c4b8c1 100644 --- a/src/core/dbus-timer.h +++ b/src/core/dbus-timer.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_timer_vtable[]; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index f9275ed935..d9b7382c82 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -20,14 +20,19 @@ ***/ #include "sd-bus.h" + +#include "alloc-util.h" +#include "bus-common-errors.h" +#include "cgroup-util.h" +#include "dbus-unit.h" +#include "dbus.h" +#include "locale-util.h" #include "log.h" #include "selinux-access.h" -#include "cgroup-util.h" -#include "strv.h" -#include "bus-common-errors.h" #include "special.h" -#include "dbus.h" -#include "dbus-unit.h" +#include "string-util.h" +#include "strv.h" +#include "user-util.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode); @@ -113,6 +118,22 @@ static int property_get_dependencies( return sd_bus_message_close_container(reply); } +static int property_get_obsolete_dependencies( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + assert(bus); + assert(reply); + + /* For dependency types we don't support anymore always return an empty array */ + return sd_bus_message_append(reply, "as", 0); +} + static int property_get_description( sd_bus *bus, const char *path, @@ -616,16 +637,12 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0), SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Wants", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTS]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("BindsTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BINDS_TO]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PART_OF]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RequisiteOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BOUND_BY]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), SD_BUS_VTABLE_PROPERTY_CONST), @@ -639,6 +656,10 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("RequiresMountsFor", "as", NULL, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -666,7 +687,6 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("IgnoreOnSnapshot", "b", bus_property_get_bool, offsetof(Unit, ignore_on_snapshot), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_failure_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST), @@ -679,6 +699,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0), SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NetClass", "u", NULL, offsetof(Unit, cgroup_netclass_id), 0), SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED), @@ -736,6 +757,30 @@ static int property_get_current_memory( return sd_bus_message_append(reply, "t", sz); } +static int property_get_current_tasks( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t cn = (uint64_t) -1; + Unit *u = userdata; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = unit_get_tasks_current(u, &cn); + if (r < 0 && r != -ENODATA) + log_unit_warning_errno(u, r, "Failed to get pids.current attribute: %m"); + + return sd_bus_message_append(reply, "t", cn); +} + static int property_get_cpu_usage( sd_bus *bus, const char *path, @@ -796,6 +841,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0), SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), + SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0), SD_BUS_VTABLE_END }; @@ -958,10 +1004,11 @@ int bus_unit_queue_job( if ((type == JOB_START && u->refuse_manual_start) || (type == JOB_STOP && u->refuse_manual_stop) || - ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) + ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)) || + (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start)) return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only.", u->id); - r = manager_add_job(u->manager, type, u, mode, true, error, &j); + r = manager_add_job(u->manager, type, u, mode, error, &j); if (r < 0) return r; @@ -1077,9 +1124,15 @@ static int bus_unit_set_transient_property( UnitDependency d; const char *other; - d = unit_dependency_from_string(name); - if (d < 0) - return -EINVAL; + if (streq(name, "RequiresOverridable")) + d = UNIT_REQUIRES; /* redirect for obsolete unit dependency type */ + else if (streq(name, "RequisiteOverridable")) + d = UNIT_REQUISITE; /* same here */ + else { + d = unit_dependency_from_string(name); + if (d < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit dependency: %s", name); + } r = sd_bus_message_enter_container(message, 'a', "s"); if (r < 0) diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index b622e0ae8d..b8c6ec398a 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_unit_vtable[]; diff --git a/src/core/dbus.c b/src/core/dbus.c index 0a2180c6a7..7932130036 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -19,29 +19,34 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/epoll.h> #include <errno.h> +#include <sys/epoll.h> #include <unistd.h> #include "sd-bus.h" -#include "log.h" -#include "strv.h" -#include "mkdir.h" -#include "missing.h" -#include "dbus-unit.h" -#include "dbus-job.h" -#include "dbus-manager.h" + +#include "alloc-util.h" +#include "bus-common-errors.h" +#include "bus-error.h" +#include "bus-internal.h" +#include "bus-util.h" +#include "dbus-cgroup.h" #include "dbus-execute.h" +#include "dbus-job.h" #include "dbus-kill.h" -#include "dbus-cgroup.h" -#include "special.h" +#include "dbus-manager.h" +#include "dbus-unit.h" #include "dbus.h" -#include "bus-util.h" -#include "bus-error.h" -#include "bus-common-errors.h" -#include "strxcpyx.h" -#include "bus-internal.h" +#include "fd-util.h" +#include "log.h" +#include "missing.h" +#include "mkdir.h" #include "selinux-access.h" +#include "special.h" +#include "string-util.h" +#include "strv.h" +#include "strxcpyx.h" +#include "user-util.h" #define CONNECTIONS_MAX 4096 @@ -172,7 +177,7 @@ static int signal_activation_request(sd_bus_message *message, void *userdata, sd goto failed; } - r = manager_add_job(m, JOB_START, u, JOB_REPLACE, true, &error, NULL); + r = manager_add_job(m, JOB_START, u, JOB_REPLACE, &error, NULL); if (r < 0) goto failed; @@ -777,9 +782,9 @@ static int bus_setup_api(Manager *m, sd_bus *bus) { return r; HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) { - r = unit_install_bus_match(bus, u, name); + r = unit_install_bus_match(u, bus, name); if (r < 0) - log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m"); + log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name); } r = sd_bus_add_match( @@ -1055,12 +1060,10 @@ void bus_done(Manager *m) { while ((b = set_steal_first(m->private_buses))) destroy_bus(m, &b); - set_free(m->private_buses); - m->private_buses = NULL; + m->private_buses = set_free(m->private_buses); m->subscribed = sd_bus_track_unref(m->subscribed); - strv_free(m->deserialized_subscribed); - m->deserialized_subscribed = NULL; + m->deserialized_subscribed = strv_free(m->deserialized_subscribed); if (m->private_listen_event_source) m->private_listen_event_source = sd_event_source_unref(m->private_listen_event_source); @@ -1191,8 +1194,7 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) { } } - strv_free(*l); - *l = NULL; + *l = strv_free(*l); return r; } diff --git a/src/core/device.c b/src/core/device.c index 7fdfacfddf..bcd4d1146b 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -21,16 +21,21 @@ #include <errno.h> #include <sys/epoll.h> -#include <libudev.h> -#include "log.h" -#include "unit-name.h" +#include "libudev.h" + +#include "alloc-util.h" #include "dbus-device.h" +#include "device.h" +#include "log.h" +#include "parse-util.h" #include "path-util.h" +#include "stat-util.h" +#include "string-util.h" +#include "swap.h" #include "udev-util.h" +#include "unit-name.h" #include "unit.h" -#include "swap.h" -#include "device.h" static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = { [DEVICE_DEAD] = UNIT_INACTIVE, @@ -112,7 +117,6 @@ static void device_init(Unit *u) { u->job_timeout = u->manager->default_timeout_start_usec; u->ignore_on_isolate = true; - u->ignore_on_snapshot = true; } static void device_done(Unit *u) { @@ -594,11 +598,10 @@ static void device_shutdown(Manager *m) { m->udev_monitor = NULL; } - hashmap_free(m->devices_by_sysfs); - m->devices_by_sysfs = NULL; + m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs); } -static int device_enumerate(Manager *m) { +static void device_enumerate(Manager *m) { _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; struct udev_list_entry *item = NULL, *first = NULL; int r; @@ -608,7 +611,7 @@ static int device_enumerate(Manager *m) { if (!m->udev_monitor) { m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); if (!m->udev_monitor) { - r = -ENOMEM; + log_oom(); goto fail; } @@ -618,37 +621,49 @@ static int device_enumerate(Manager *m) { (void) udev_monitor_set_receive_buffer_size(m->udev_monitor, 128*1024*1024); r = udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd"); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to add udev tag match: %m"); goto fail; + } r = udev_monitor_enable_receiving(m->udev_monitor); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to enable udev event reception: %m"); goto fail; + } r = sd_event_add_io(m->event, &m->udev_event_source, udev_monitor_get_fd(m->udev_monitor), EPOLLIN, device_dispatch_io, m); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to watch udev file descriptor: %m"); goto fail; + } (void) sd_event_source_set_description(m->udev_event_source, "device"); } e = udev_enumerate_new(m->udev); if (!e) { - r = -ENOMEM; + log_oom(); goto fail; } r = udev_enumerate_add_match_tag(e, "systemd"); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to create udev tag enumeration: %m"); goto fail; + } r = udev_enumerate_add_match_is_initialized(e); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to install initialization match into enumeration: %m"); goto fail; + } r = udev_enumerate_scan_devices(e); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to enumerate devices: %m"); goto fail; + } first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { @@ -671,13 +686,10 @@ static int device_enumerate(Manager *m) { device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false); } - return 0; + return; fail: - log_error_errno(r, "Failed to enumerate devices: %m"); - device_shutdown(m); - return r; } static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { @@ -817,14 +829,6 @@ int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, return device_update_found_by_name(m, node, add, found, now); } -static const char* const device_state_table[_DEVICE_STATE_MAX] = { - [DEVICE_DEAD] = "dead", - [DEVICE_TENTATIVE] = "tentative", - [DEVICE_PLUGGED] = "plugged", -}; - -DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState); - const UnitVTable device_vtable = { .object_size = sizeof(Device), .sections = diff --git a/src/core/device.h b/src/core/device.h index 10ab113176..da8737870b 100644 --- a/src/core/device.h +++ b/src/core/device.h @@ -23,16 +23,6 @@ typedef struct Device Device; -/* We simply watch devices, we cannot plug/unplug them. That - * simplifies the state engine greatly */ -typedef enum DeviceState { - DEVICE_DEAD, - DEVICE_TENTATIVE, /* mounted or swapped, but not (yet) announced by udev */ - DEVICE_PLUGGED, /* announced by udev */ - _DEVICE_STATE_MAX, - _DEVICE_STATE_INVALID = -1 -} DeviceState; - typedef enum DeviceFound { DEVICE_NOT_FOUND = 0, DEVICE_FOUND_UDEV = 1, @@ -56,7 +46,4 @@ struct Device { extern const UnitVTable device_vtable; -const char* device_state_to_string(DeviceState i) _const_; -DeviceState device_state_from_string(const char *s) _pure_; - int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now); diff --git a/src/core/execute.c b/src/core/execute.c index 6abb0a5d5d..07979bf8b3 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -21,18 +21,18 @@ #include <errno.h> #include <fcntl.h> -#include <unistd.h> -#include <string.h> +#include <glob.h> +#include <grp.h> +#include <poll.h> #include <signal.h> -#include <sys/socket.h> -#include <sys/un.h> +#include <string.h> +#include <sys/personality.h> #include <sys/prctl.h> +#include <sys/socket.h> #include <sys/stat.h> -#include <grp.h> -#include <poll.h> -#include <glob.h> +#include <sys/un.h> +#include <unistd.h> #include <utmpx.h> -#include <sys/personality.h> #ifdef HAVE_PAM #include <security/pam_appl.h> @@ -51,45 +51,55 @@ #endif #include "sd-messages.h" -#include "rm-rf.h" -#include "strv.h" -#include "macro.h" -#include "capability.h" -#include "util.h" -#include "log.h" -#include "ioprio.h" -#include "securebits.h" -#include "namespace.h" -#include "exit-status.h" -#include "missing.h" -#include "utmp-wtmp.h" -#include "def.h" -#include "path-util.h" -#include "env-util.h" -#include "fileio.h" -#include "unit.h" -#include "async.h" -#include "selinux-util.h" -#include "errno-list.h" + #include "af-list.h" -#include "mkdir.h" -#include "smack-util.h" +#include "alloc-util.h" +#ifdef HAVE_APPARMOR +#include "apparmor-util.h" +#endif +#include "async.h" +#include "barrier.h" #include "bus-endpoint.h" #include "cap-list.h" +#include "capability-util.h" +#include "def.h" +#include "env-util.h" +#include "errno-list.h" +#include "execute.h" +#include "exit-status.h" +#include "fd-util.h" +#include "fileio.h" #include "formats-util.h" +#include "fs-util.h" +#include "glob-util.h" +#include "io-util.h" +#include "ioprio.h" +#include "log.h" +#include "macro.h" +#include "missing.h" +#include "mkdir.h" +#include "namespace.h" +#include "parse-util.h" +#include "path-util.h" #include "process-util.h" -#include "terminal-util.h" -#include "signal-util.h" - -#ifdef HAVE_APPARMOR -#include "apparmor-util.h" -#endif - +#include "rlimit-util.h" +#include "rm-rf.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" #endif - -#include "execute.h" +#include "securebits.h" +#include "selinux-util.h" +#include "signal-util.h" +#include "smack-util.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "syslog-util.h" +#include "terminal-util.h" +#include "unit.h" +#include "user-util.h" +#include "util.h" +#include "utmp-wtmp.h" #define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC) #define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC) @@ -357,12 +367,28 @@ static int fixup_output(ExecOutput std_output, int socket_fd) { return std_output; } -static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty_stdin) { +static int setup_input( + const ExecContext *context, + const ExecParameters *params, + int socket_fd) { + ExecInput i; assert(context); + assert(params); + + if (params->stdin_fd >= 0) { + if (dup2(params->stdin_fd, STDIN_FILENO) < 0) + return -errno; + + /* Try to make this the controlling tty, if it is a tty, and reset it */ + (void) ioctl(STDIN_FILENO, TIOCSCTTY, context->std_input == EXEC_INPUT_TTY_FORCE); + (void) reset_terminal_fd(STDIN_FILENO, true); - i = fixup_input(context->std_input, socket_fd, apply_tty_stdin); + return STDIN_FILENO; + } + + i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin); switch (i) { @@ -399,16 +425,40 @@ static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty } } -static int setup_output(Unit *unit, const ExecContext *context, int fileno, int socket_fd, const char *ident, bool apply_tty_stdin, uid_t uid, gid_t gid) { +static int setup_output( + Unit *unit, + const ExecContext *context, + const ExecParameters *params, + int fileno, + int socket_fd, + const char *ident, + uid_t uid, gid_t gid) { + ExecOutput o; ExecInput i; int r; assert(unit); assert(context); + assert(params); assert(ident); - i = fixup_input(context->std_input, socket_fd, apply_tty_stdin); + if (fileno == STDOUT_FILENO && params->stdout_fd >= 0) { + + if (dup2(params->stdout_fd, STDOUT_FILENO) < 0) + return -errno; + + return STDOUT_FILENO; + } + + if (fileno == STDERR_FILENO && params->stderr_fd >= 0) { + if (dup2(params->stderr_fd, STDERR_FILENO) < 0) + return -errno; + + return STDERR_FILENO; + } + + i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin); o = fixup_output(context->std_output, socket_fd); if (fileno == STDERR_FILENO) { @@ -501,9 +551,9 @@ static int chown_terminal(int fd, uid_t uid) { return 0; } -static int setup_confirm_stdio(int *_saved_stdin, - int *_saved_stdout) { - int fd = -1, saved_stdin, saved_stdout = -1, r; +static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) { + _cleanup_close_ int fd = -1, saved_stdin = -1, saved_stdout = -1; + int r; assert(_saved_stdin); assert(_saved_stdout); @@ -513,10 +563,8 @@ static int setup_confirm_stdio(int *_saved_stdin, return -errno; saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3); - if (saved_stdout < 0) { - r = errno; - goto fail; - } + if (saved_stdout < 0) + return -errno; fd = acquire_terminal( "/dev/console", @@ -524,39 +572,33 @@ static int setup_confirm_stdio(int *_saved_stdin, false, false, DEFAULT_CONFIRM_USEC); - if (fd < 0) { - r = fd; - goto fail; - } + if (fd < 0) + return fd; r = chown_terminal(fd, getuid()); if (r < 0) - goto fail; + return r; - if (dup2(fd, STDIN_FILENO) < 0) { - r = -errno; - goto fail; - } + r = reset_terminal_fd(fd, true); + if (r < 0) + return r; - if (dup2(fd, STDOUT_FILENO) < 0) { - r = -errno; - goto fail; - } + if (dup2(fd, STDIN_FILENO) < 0) + return -errno; + + if (dup2(fd, STDOUT_FILENO) < 0) + return -errno; if (fd >= 2) safe_close(fd); + fd = -1; *_saved_stdin = saved_stdin; *_saved_stdout = saved_stdout; - return 0; - -fail: - safe_close(saved_stdout); - safe_close(saved_stdin); - safe_close(fd); + saved_stdin = saved_stdout = -1; - return r; + return 0; } _printf_(1, 2) static int write_confirm_message(const char *format, ...) { @@ -576,9 +618,7 @@ _printf_(1, 2) static int write_confirm_message(const char *format, ...) { return 0; } -static int restore_confirm_stdio(int *saved_stdin, - int *saved_stdout) { - +static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) { int r = 0; assert(saved_stdin); @@ -594,8 +634,8 @@ static int restore_confirm_stdio(int *saved_stdin, if (dup2(*saved_stdout, STDOUT_FILENO) < 0) r = -errno; - safe_close(*saved_stdin); - safe_close(*saved_stdout); + *saved_stdin = safe_close(*saved_stdin); + *saved_stdout = safe_close(*saved_stdout); return r; } @@ -629,15 +669,6 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_ * we avoid NSS lookups for gid=0. */ if (context->group || username) { - - if (context->group) { - const char *g = context->group; - - r = get_group_creds(&g, &gid); - if (r < 0) - return r; - } - /* First step, initialize groups from /etc/groups */ if (username && gid != 0) { if (initgroups(username, gid) < 0) @@ -777,10 +808,11 @@ static int setup_pam( .appdata_ptr = NULL }; + _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; pam_handle_t *handle = NULL; sigset_t old_ss; int pam_code = PAM_SUCCESS; - int err; + int err = 0; char **e = NULL; bool close_session = false; pid_t pam_pid = 0, parent_pid; @@ -797,6 +829,10 @@ static int setup_pam( * daemon. We do things this way to ensure that the main PID * of the daemon is the one we initially fork()ed. */ + err = barrier_create(&barrier); + if (err < 0) + goto fail; + if (log_get_max_level() < LOG_DEBUG) flags |= PAM_SILENT; @@ -845,6 +881,7 @@ static int setup_pam( /* The child's job is to reset the PAM session on * termination */ + barrier_set_role(&barrier, BARRIER_CHILD); /* This string must fit in 10 chars (i.e. the length * of "/sbin/init"), to look pretty in /bin/ps */ @@ -872,6 +909,11 @@ static int setup_pam( if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) goto child_finish; + /* Tell the parent that our setup is done. This is especially + * important regarding dropping privileges. Otherwise, unit + * setup might race against our setresuid(2) call. */ + barrier_place(&barrier); + /* Check if our parent process might already have * died? */ if (getppid() == parent_pid) { @@ -907,6 +949,8 @@ static int setup_pam( _exit(r); } + barrier_set_role(&barrier, BARRIER_PARENT); + /* If the child was forked off successfully it will do all the * cleanups, so forget about the handle here. */ handle = NULL; @@ -918,6 +962,11 @@ static int setup_pam( * might have opened it, but we don't want this fd around. */ closelog(); + /* Synchronously wait for the child to initialize. We don't care for + * errors as we cannot recover. However, warn loudly if it happens. */ + if (!barrier_place_and_sync(&barrier)) + log_error("PAM initialization failed"); + *pam_env = e; e = NULL; @@ -928,7 +977,7 @@ fail: log_error("PAM failed: %s", pam_strerror(handle, pam_code)); err = -EPERM; /* PAM errors do not map to errno */ } else { - err = log_error_errno(errno, "PAM failed: %m"); + err = log_error_errno(err < 0 ? err : errno, "PAM failed: %m"); } if (handle) { @@ -1160,8 +1209,8 @@ static void do_idle_pipe_dance(int idle_pipe[4]) { assert(idle_pipe); - safe_close(idle_pipe[1]); - safe_close(idle_pipe[2]); + idle_pipe[1] = safe_close(idle_pipe[1]); + idle_pipe[2] = safe_close(idle_pipe[2]); if (idle_pipe[0] >= 0) { int r; @@ -1169,23 +1218,26 @@ static void do_idle_pipe_dance(int idle_pipe[4]) { r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC); if (idle_pipe[3] >= 0 && r == 0 /* timeout */) { + ssize_t n; + /* Signal systemd that we are bored and want to continue. */ - r = write(idle_pipe[3], "x", 1); - if (r > 0) + n = write(idle_pipe[3], "x", 1); + if (n > 0) /* Wait for systemd to react to the signal above. */ fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC); } - safe_close(idle_pipe[0]); + idle_pipe[0] = safe_close(idle_pipe[0]); } - safe_close(idle_pipe[3]); + idle_pipe[3] = safe_close(idle_pipe[3]); } static int build_environment( const ExecContext *c, unsigned n_fds, + char ** fd_names, usec_t watchdog_usec, const char *home, const char *username, @@ -1199,11 +1251,13 @@ static int build_environment( assert(c); assert(ret); - our_env = new0(char*, 10); + our_env = new0(char*, 11); if (!our_env) return -ENOMEM; if (n_fds > 0) { + _cleanup_free_ char *joined = NULL; + if (asprintf(&x, "LISTEN_PID="PID_FMT, getpid()) < 0) return -ENOMEM; our_env[n_env++] = x; @@ -1211,6 +1265,15 @@ static int build_environment( if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0) return -ENOMEM; our_env[n_env++] = x; + + joined = strv_join(fd_names, ":"); + if (!joined) + return -ENOMEM; + + x = strjoin("LISTEN_FDNAMES=", joined, NULL); + if (!x) + return -ENOMEM; + our_env[n_env++] = x; } if (watchdog_usec > 0) { @@ -1261,7 +1324,7 @@ static int build_environment( } our_env[n_env++] = NULL; - assert(n_env <= 10); + assert(n_env <= 11); *ret = our_env; our_env = NULL; @@ -1269,6 +1332,34 @@ static int build_environment( return 0; } +static int build_pass_environment(const ExecContext *c, char ***ret) { + _cleanup_strv_free_ char **pass_env = NULL; + size_t n_env = 0, n_bufsize = 0; + char **i; + + STRV_FOREACH(i, c->pass_environment) { + _cleanup_free_ char *x = NULL; + char *v; + + v = getenv(*i); + if (!v) + continue; + x = strjoin(*i, "=", v, NULL); + if (!x) + return -ENOMEM; + if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2)) + return -ENOMEM; + pass_env[n_env++] = x; + pass_env[n_env] = NULL; + x = NULL; + } + + *ret = pass_env; + pass_env = NULL; + + return 0; +} + static bool exec_needs_mount_namespace( const ExecContext *context, const ExecParameters *params, @@ -1299,6 +1390,44 @@ static bool exec_needs_mount_namespace( return false; } +static int close_remaining_fds( + const ExecParameters *params, + ExecRuntime *runtime, + int socket_fd, + int *fds, unsigned n_fds) { + + unsigned n_dont_close = 0; + int dont_close[n_fds + 7]; + + assert(params); + + if (params->stdin_fd >= 0) + dont_close[n_dont_close++] = params->stdin_fd; + if (params->stdout_fd >= 0) + dont_close[n_dont_close++] = params->stdout_fd; + if (params->stderr_fd >= 0) + dont_close[n_dont_close++] = params->stderr_fd; + + if (socket_fd >= 0) + dont_close[n_dont_close++] = socket_fd; + if (n_fds > 0) { + memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds); + n_dont_close += n_fds; + } + + if (params->bus_endpoint_fd >= 0) + dont_close[n_dont_close++] = params->bus_endpoint_fd; + + if (runtime) { + if (runtime->netns_storage_socket[0] >= 0) + dont_close[n_dont_close++] = runtime->netns_storage_socket[0]; + if (runtime->netns_storage_socket[1] >= 0) + dont_close[n_dont_close++] = runtime->netns_storage_socket[1]; + } + + return close_all_fds(dont_close, n_dont_close); +} + static int exec_child( Unit *unit, ExecCommand *command, @@ -1311,11 +1440,9 @@ static int exec_child( char **files_env, int *exit_status) { - _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; + _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; _cleanup_free_ char *mac_selinux_context_net = NULL; - const char *username = NULL, *home = NULL, *shell = NULL; - unsigned n_dont_close = 0; - int dont_close[n_fds + 4]; + const char *username = NULL, *home = NULL, *shell = NULL, *wd; uid_t uid = UID_INVALID; gid_t gid = GID_INVALID; int i, r; @@ -1355,22 +1482,7 @@ static int exec_child( log_forget_fds(); - if (socket_fd >= 0) - dont_close[n_dont_close++] = socket_fd; - if (n_fds > 0) { - memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds); - n_dont_close += n_fds; - } - if (params->bus_endpoint_fd >= 0) - dont_close[n_dont_close++] = params->bus_endpoint_fd; - if (runtime) { - if (runtime->netns_storage_socket[0] >= 0) - dont_close[n_dont_close++] = runtime->netns_storage_socket[0]; - if (runtime->netns_storage_socket[1] >= 0) - dont_close[n_dont_close++] = runtime->netns_storage_socket[1]; - } - - r = close_all_fds(dont_close, n_dont_close); + r = close_remaining_fds(params, runtime, socket_fd, fds, n_fds); if (r < 0) { *exit_status = EXIT_FDS; return r; @@ -1412,24 +1524,35 @@ static int exec_child( } } + if (context->group) { + const char *g = context->group; + + r = get_group_creds(&g, &gid); + if (r < 0) { + *exit_status = EXIT_GROUP; + return r; + } + } + + /* If a socket is connected to STDIN/STDOUT/STDERR, we * must sure to drop O_NONBLOCK */ if (socket_fd >= 0) - fd_nonblock(socket_fd, false); + (void) fd_nonblock(socket_fd, false); - r = setup_input(context, socket_fd, params->apply_tty_stdin); + r = setup_input(context, params, socket_fd); if (r < 0) { *exit_status = EXIT_STDIN; return r; } - r = setup_output(unit, context, STDOUT_FILENO, socket_fd, basename(command->path), params->apply_tty_stdin, uid, gid); + r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid); if (r < 0) { *exit_status = EXIT_STDOUT; return r; } - r = setup_output(unit, context, STDERR_FILENO, socket_fd, basename(command->path), params->apply_tty_stdin, uid, gid); + r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid); if (r < 0) { *exit_status = EXIT_STDERR; return r; @@ -1578,25 +1701,50 @@ static int exec_child( } } + umask(context->umask); + if (params->apply_permissions) { r = enforce_groups(context, username, gid); if (r < 0) { *exit_status = EXIT_GROUP; return r; } - } +#ifdef HAVE_SMACK + if (context->smack_process_label) { + r = mac_smack_apply_pid(0, context->smack_process_label); + if (r < 0) { + *exit_status = EXIT_SMACK_PROCESS_LABEL; + return r; + } + } +#ifdef SMACK_DEFAULT_PROCESS_LABEL + else { + _cleanup_free_ char *exec_label = NULL; - umask(context->umask); + r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label); + if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP) { + *exit_status = EXIT_SMACK_PROCESS_LABEL; + return r; + } + r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL); + if (r < 0) { + *exit_status = EXIT_SMACK_PROCESS_LABEL; + return r; + } + } +#endif +#endif #ifdef HAVE_PAM - if (params->apply_permissions && context->pam_name && username) { - r = setup_pam(context->pam_name, username, uid, context->tty_path, &pam_env, fds, n_fds); - if (r < 0) { - *exit_status = EXIT_PAM; - return r; + if (context->pam_name && username) { + r = setup_pam(context->pam_name, username, uid, context->tty_path, &pam_env, fds, n_fds); + if (r < 0) { + *exit_status = EXIT_PAM; + return r; + } } - } #endif + } if (context->private_network && runtime && runtime->netns_storage_socket[0] >= 0) { r = setup_netns(runtime->netns_storage_socket); @@ -1650,6 +1798,13 @@ static int exec_child( } } + if (context->working_directory_home) + wd = home; + else if (context->working_directory) + wd = context->working_directory; + else + wd = "/"; + if (params->apply_chroot) { if (!needs_mount_namespace && context->root_directory) if (chroot(context->root_directory) < 0) { @@ -1657,21 +1812,15 @@ static int exec_child( return -errno; } - if (chdir(context->working_directory ?: "/") < 0 && + if (chdir(wd) < 0 && !context->working_directory_missing_ok) { *exit_status = EXIT_CHDIR; return -errno; } } else { - _cleanup_free_ char *d = NULL; - - if (asprintf(&d, "%s/%s", - context->root_directory ?: "", - context->working_directory ?: "") < 0) { - *exit_status = EXIT_MEMORY; - return -ENOMEM; - } + const char *d; + d = strjoina(strempty(context->root_directory), "/", strempty(wd)); if (chdir(d) < 0 && !context->working_directory_missing_ok) { *exit_status = EXIT_CHDIR; @@ -1725,33 +1874,6 @@ static int exec_child( } } -#ifdef HAVE_SMACK - if (context->smack_process_label) { - r = mac_smack_apply_pid(0, context->smack_process_label); - if (r < 0) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - } -#ifdef SMACK_DEFAULT_PROCESS_LABEL - else { - _cleanup_free_ char *exec_label = NULL; - - r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label); - if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - - r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL); - if (r < 0) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - } -#endif -#endif - if (context->user) { r = enforce_user(context, uid); if (r < 0) { @@ -1828,15 +1950,22 @@ static int exec_child( #endif } - r = build_environment(context, n_fds, params->watchdog_usec, home, username, shell, &our_env); + r = build_environment(context, n_fds, params->fd_names, params->watchdog_usec, home, username, shell, &our_env); + if (r < 0) { + *exit_status = EXIT_MEMORY; + return r; + } + + r = build_pass_environment(context, &pass_env); if (r < 0) { *exit_status = EXIT_MEMORY; return r; } - final_env = strv_env_merge(5, + final_env = strv_env_merge(6, params->environment, our_env, + pass_env, context->environment, files_env, pam_env, @@ -1992,11 +2121,9 @@ void exec_context_done(ExecContext *c) { assert(c); - strv_free(c->environment); - c->environment = NULL; - - strv_free(c->environment_files); - c->environment_files = NULL; + c->environment = strv_free(c->environment); + c->environment_files = strv_free(c->environment_files); + c->pass_environment = strv_free(c->pass_environment); for (l = 0; l < ELEMENTSOF(c->rlimit); l++) c->rlimit[l] = mfree(c->rlimit[l]); @@ -2008,8 +2135,7 @@ void exec_context_done(ExecContext *c) { c->user = mfree(c->user); c->group = mfree(c->group); - strv_free(c->supplementary_groups); - c->supplementary_groups = NULL; + c->supplementary_groups = strv_free(c->supplementary_groups); c->pam_name = mfree(c->pam_name); @@ -2018,14 +2144,9 @@ void exec_context_done(ExecContext *c) { c->capabilities = NULL; } - strv_free(c->read_only_dirs); - c->read_only_dirs = NULL; - - strv_free(c->read_write_dirs); - c->read_write_dirs = NULL; - - strv_free(c->inaccessible_dirs); - c->inaccessible_dirs = NULL; + c->read_only_dirs = strv_free(c->read_only_dirs); + c->read_write_dirs = strv_free(c->read_write_dirs); + c->inaccessible_dirs = strv_free(c->inaccessible_dirs); if (c->cpuset) CPU_FREE(c->cpuset); @@ -2034,17 +2155,11 @@ void exec_context_done(ExecContext *c) { c->selinux_context = mfree(c->selinux_context); c->apparmor_profile = mfree(c->apparmor_profile); - set_free(c->syscall_filter); - c->syscall_filter = NULL; - - set_free(c->syscall_archs); - c->syscall_archs = NULL; - - set_free(c->address_families); - c->address_families = NULL; + c->syscall_filter = set_free(c->syscall_filter); + c->syscall_archs = set_free(c->syscall_archs); + c->address_families = set_free(c->address_families); - strv_free(c->runtime_directory); - c->runtime_directory = NULL; + c->runtime_directory = strv_free(c->runtime_directory); bus_endpoint_free(c->bus_endpoint); c->bus_endpoint = NULL; @@ -2079,8 +2194,7 @@ void exec_command_done(ExecCommand *c) { c->path = mfree(c->path); - strv_free(c->argv); - c->argv = NULL; + c->argv = strv_free(c->argv); } void exec_command_done_array(ExecCommand *c, unsigned n) { @@ -2244,7 +2358,7 @@ static void strv_fprintf(FILE *f, char **l) { } void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { - char **e; + char **e, **d; unsigned i; assert(c); @@ -2280,6 +2394,14 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { STRV_FOREACH(e, c->environment_files) fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e); + STRV_FOREACH(e, c->pass_environment) + fprintf(f, "%sPassEnvironment: %s\n", prefix, *e); + + fprintf(f, "%sRuntimeDirectoryMode: %04o\n", prefix, c->runtime_directory_mode); + + STRV_FOREACH(d, c->runtime_directory) + fprintf(f, "%sRuntimeDirectory: %s\n", prefix, *d); + if (c->nice_set) fprintf(f, "%sNice: %i\n", @@ -2706,7 +2828,7 @@ int exec_command_append(ExecCommand *c, const char *path, ...) { if (!l) return -ENOMEM; - r = strv_extend_strv(&c->argv, l); + r = strv_extend_strv(&c->argv, l, false); if (r < 0) return r; diff --git a/src/core/execute.h b/src/core/execute.h index a750246a89..be5be9f531 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -27,16 +27,16 @@ typedef struct ExecContext ExecContext; typedef struct ExecRuntime ExecRuntime; typedef struct ExecParameters ExecParameters; -#include <sys/capability.h> +#include <sched.h> #include <stdbool.h> #include <stdio.h> -#include <sched.h> +#include <sys/capability.h> -#include "list.h" +#include "bus-endpoint.h" #include "fdset.h" +#include "list.h" #include "missing.h" #include "namespace.h" -#include "bus-endpoint.h" typedef enum ExecUtmpMode { EXEC_UTMP_INIT, @@ -99,10 +99,12 @@ struct ExecRuntime { struct ExecContext { char **environment; char **environment_files; + char **pass_environment; struct rlimit *rlimit[_RLIMIT_MAX]; char *working_directory, *root_directory; bool working_directory_missing_ok; + bool working_directory_home; mode_t umask; int oom_score_adjust; @@ -202,26 +204,40 @@ struct ExecContext { BusEndpoint *bus_endpoint; }; -#include "cgroup.h" #include "cgroup-util.h" +#include "cgroup.h" struct ExecParameters { char **argv; - int *fds; unsigned n_fds; char **environment; - bool apply_permissions; - bool apply_chroot; - bool apply_tty_stdin; - bool confirm_spawn; - bool selinux_context_net; + + int *fds; + char **fd_names; + unsigned n_fds; + + bool apply_permissions:1; + bool apply_chroot:1; + bool apply_tty_stdin:1; + + bool confirm_spawn:1; + bool selinux_context_net:1; + + bool cgroup_delegate:1; CGroupMask cgroup_supported; const char *cgroup_path; - bool cgroup_delegate; + const char *runtime_prefix; + usec_t watchdog_usec; + int *idle_pipe; + char *bus_endpoint_path; int bus_endpoint_fd; + + int stdin_fd; + int stdout_fd; + int stderr_fd; }; int exec_spawn(Unit *unit, diff --git a/src/core/failure-action.c b/src/core/failure-action.c index b06a7d2ae5..f67fb05af0 100644 --- a/src/core/failure-action.c +++ b/src/core/failure-action.c @@ -23,16 +23,17 @@ #include <sys/reboot.h> #include <linux/reboot.h> -#include "bus-util.h" #include "bus-error.h" -#include "special.h" +#include "bus-util.h" #include "failure-action.h" +#include "special.h" +#include "string-table.h" #include "terminal-util.h" static void log_and_status(Manager *m, const char *message) { log_warning("%s", message); manager_status_printf(m, STATUS_TYPE_EMERGENCY, - ANSI_HIGHLIGHT_RED_ON " !! " ANSI_HIGHLIGHT_OFF, + ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL, "%s", message); } @@ -41,8 +42,6 @@ int failure_action( FailureAction action, const char *reboot_arg) { - int r; - assert(m); assert(action >= 0); assert(action < _FAILURE_ACTION_MAX); @@ -61,18 +60,13 @@ int failure_action( switch (action) { - case FAILURE_ACTION_REBOOT: { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - + case FAILURE_ACTION_REBOOT: log_and_status(m, "Rebooting as result of failure."); update_reboot_param_file(reboot_arg); - r = manager_add_job_by_name(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL); - if (r < 0) - log_error("Failed to reboot: %s.", bus_error_message(&error, r)); + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, NULL); break; - } case FAILURE_ACTION_REBOOT_FORCE: log_and_status(m, "Forcibly rebooting as result of failure."); @@ -95,17 +89,10 @@ int failure_action( reboot(RB_AUTOBOOT); break; - case FAILURE_ACTION_POWEROFF: { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - + case FAILURE_ACTION_POWEROFF: log_and_status(m, "Powering off as result of failure."); - - r = manager_add_job_by_name(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE, true, &error, NULL); - if (r < 0) - log_error("Failed to poweroff: %s.", bus_error_message(&error, r)); - + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE, NULL); break; - } case FAILURE_ACTION_POWEROFF_FORCE: log_and_status(m, "Forcibly powering off as result of failure."); diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c index 932ddbf95a..d92a9a764f 100644 --- a/src/core/hostname-setup.c +++ b/src/core/hostname-setup.c @@ -19,16 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <errno.h> +#include <stdio.h> #include <stdlib.h> -#include "macro.h" -#include "util.h" -#include "log.h" +#include "alloc-util.h" #include "fileio.h" -#include "hostname-util.h" #include "hostname-setup.h" +#include "hostname-util.h" +#include "log.h" +#include "macro.h" +#include "string-util.h" +#include "util.h" int hostname_setup(void) { int r; @@ -59,8 +61,9 @@ int hostname_setup(void) { hn = "localhost"; } - if (sethostname_idempotent(hn) < 0) - return log_warning_errno(errno, "Failed to set hostname to <%s>: %m", hn); + r = sethostname_idempotent(hn); + if (r < 0) + return log_warning_errno(r, "Failed to set hostname to <%s>: %m", hn); log_info("Set hostname to <%s>.", hn); return 0; diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c index 42a3e97459..4f42ae6f31 100644 --- a/src/core/ima-setup.c +++ b/src/core/ima-setup.c @@ -21,12 +21,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> #include <errno.h> +#include <unistd.h> +#include "fd-util.h" +#include "fileio.h" #include "ima-setup.h" -#include "util.h" #include "log.h" +#include "util.h" #define IMA_SECFS_DIR "/sys/kernel/security/ima" #define IMA_SECFS_POLICY IMA_SECFS_DIR "/policy" diff --git a/src/core/job.c b/src/core/job.c index 2a35d1e2de..9654590635 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -23,17 +23,24 @@ #include "sd-id128.h" #include "sd-messages.h" -#include "set.h" -#include "unit.h" -#include "macro.h" -#include "strv.h" -#include "log.h" -#include "dbus-job.h" -#include "special.h" + +#include "alloc-util.h" #include "async.h" -#include "virt.h" +#include "dbus-job.h" #include "dbus.h" +#include "escape.h" +#include "job.h" +#include "log.h" +#include "macro.h" +#include "parse-util.h" +#include "set.h" +#include "special.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" #include "terminal-util.h" +#include "unit.h" +#include "virt.h" Job* job_new_raw(Unit *unit) { Job *j; @@ -168,7 +175,6 @@ static void job_merge_into_installed(Job *j, Job *other) { else 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; } @@ -300,12 +306,10 @@ 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\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->irreversible)); } @@ -496,17 +500,26 @@ static void job_change_type(Job *j, JobType newtype) { } static int job_perform_on_unit(Job **j) { - /* While we execute this operation the job might go away (for - * example: because it finishes immediately or is replaced by a new, - * conflicting job.) To make sure we don't access a freed job later on - * we store the id here, so that we can verify the job is still - * valid. */ - Manager *m = (*j)->manager; - Unit *u = (*j)->unit; - JobType t = (*j)->type; - uint32_t id = (*j)->id; + uint32_t id; + Manager *m; + JobType t; + Unit *u; int r; + /* While we execute this operation the job might go away (for + * example: because it finishes immediately or is replaced by + * a new, conflicting job.) To make sure we don't access a + * freed job later on we store the id here, so that we can + * verify the job is still valid. */ + + assert(j); + assert(*j); + + m = (*j)->manager; + u = (*j)->unit; + t = (*j)->type; + id = (*j)->id; + switch (t) { case JOB_START: r = unit_start(u); @@ -514,6 +527,7 @@ static int job_perform_on_unit(Job **j) { case JOB_RESTART: t = JOB_STOP; + /* fall through */ case JOB_STOP: r = unit_stop(u); break; @@ -613,8 +627,7 @@ int job_run_and_invalidate(Job *j) { } _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) { - const char *format; - const UnitStatusMessageFormats *format_table; + static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = { [JOB_DONE] = "Started %s.", [JOB_TIMEOUT] = "Timed out starting %s.", @@ -640,11 +653,14 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR [JOB_SKIPPED] = "%s is not active.", }; + const UnitStatusMessageFormats *format_table; + const char *format; + assert(u); assert(t >= 0); assert(t < _JOB_TYPE_MAX); - if (t == JOB_START || t == JOB_STOP || t == JOB_RESTART) { + if (IN_SET(t, JOB_START, JOB_STOP, JOB_RESTART)) { format_table = &UNIT_VTABLE(u)->status_message_formats; if (format_table) { format = t == JOB_START ? format_table->finished_start_job[result] : @@ -668,21 +684,26 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR } static void job_print_status_message(Unit *u, JobType t, JobResult result) { - const char *format; static const char* const job_result_status_table[_JOB_RESULT_MAX] = { - [JOB_DONE] = ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, - [JOB_TIMEOUT] = ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, - [JOB_FAILED] = ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, - [JOB_DEPENDENCY] = ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, - [JOB_SKIPPED] = ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, - [JOB_ASSERT] = ANSI_HIGHLIGHT_YELLOW_ON "ASSERT" ANSI_HIGHLIGHT_OFF, - [JOB_UNSUPPORTED] = ANSI_HIGHLIGHT_YELLOW_ON "UNSUPP" ANSI_HIGHLIGHT_OFF, + [JOB_DONE] = ANSI_GREEN " OK " ANSI_NORMAL, + [JOB_TIMEOUT] = ANSI_HIGHLIGHT_RED " TIME " ANSI_NORMAL, + [JOB_FAILED] = ANSI_HIGHLIGHT_RED "FAILED" ANSI_NORMAL, + [JOB_DEPENDENCY] = ANSI_HIGHLIGHT_YELLOW "DEPEND" ANSI_NORMAL, + [JOB_SKIPPED] = ANSI_HIGHLIGHT " INFO " ANSI_NORMAL, + [JOB_ASSERT] = ANSI_HIGHLIGHT_YELLOW "ASSERT" ANSI_NORMAL, + [JOB_UNSUPPORTED] = ANSI_HIGHLIGHT_YELLOW "UNSUPP" ANSI_NORMAL, }; + const char *format; + assert(u); assert(t >= 0); assert(t < _JOB_TYPE_MAX); + /* Reload status messages have traditionally not been printed to console. */ + if (t == JOB_RELOAD) + return; + format = job_get_status_message_format(u, t, result); if (!format) return; @@ -695,10 +716,10 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { REENABLE_WARNING; if (t == JOB_START && result == JOB_FAILED) { - _cleanup_free_ char *quoted = shell_maybe_quote(u->id); + _cleanup_free_ char *quoted; - manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, - "See 'systemctl status %s' for details.", strna(quoted)); + quoted = shell_maybe_quote(u->id); + manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted)); } } @@ -736,13 +757,22 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { snprintf(buf, sizeof(buf), format, unit_description(u)); REENABLE_WARNING; - if (t == JOB_START) + switch (t) { + + case JOB_START: mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED; - else if (t == JOB_STOP || t == JOB_RESTART) - mid = SD_MESSAGE_UNIT_STOPPED; - else if (t == JOB_RELOAD) + break; + + case JOB_RELOAD: mid = SD_MESSAGE_UNIT_RELOADED; - else { + break; + + case JOB_STOP: + case JOB_RESTART: + mid = SD_MESSAGE_UNIT_STOPPED; + break; + + default: log_struct(job_result_log_level[result], LOG_UNIT_ID(u), LOG_MESSAGE("%s", buf), @@ -766,10 +796,7 @@ static void job_emit_status_message(Unit *u, JobType t, JobResult result) { return; job_log_status_message(u, t, result); - - /* Reload status messages have traditionally not been printed to console. */ - if (t != JOB_RELOAD) - job_print_status_message(u, t, result); + job_print_status_message(u, t, result); } static void job_fail_dependencies(Unit *u, UnitDependency d) { @@ -834,8 +861,6 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { job_fail_dependencies(u, UNIT_REQUIRED_BY); job_fail_dependencies(u, UNIT_REQUISITE_OF); job_fail_dependencies(u, UNIT_BOUND_BY); - job_fail_dependencies(u, UNIT_REQUIRED_BY_OVERRIDABLE); - job_fail_dependencies(u, UNIT_REQUISITE_OF_OVERRIDABLE); } else if (t == JOB_STOP) job_fail_dependencies(u, UNIT_CONFLICTED_BY); } @@ -960,7 +985,6 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) { fprintf(f, "job-id=%u\n", j->id); 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)); @@ -1028,15 +1052,6 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) { else job_set_state(j, s); - } else if (streq(l, "job-override")) { - int b; - - b = parse_boolean(v); - if (b < 0) - log_debug("Failed to parse job override flag %s", v); - else - j->override = j->override || b; - } else if (streq(l, "job-irreversible")) { int b; diff --git a/src/core/job.h b/src/core/job.h index 1d1b10f1d3..118b24e5b7 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -23,6 +23,11 @@ #include <stdbool.h> +#include "sd-event.h" + +#include "list.h" +#include "unit-name.h" + typedef struct Job Job; typedef struct JobDependency JobDependency; typedef enum JobType JobType; @@ -105,9 +110,7 @@ enum JobResult { _JOB_RESULT_INVALID = -1 }; -#include "sd-event.h" #include "unit.h" -#include "list.h" struct JobDependency { /* Encodes that the 'subject' job needs the 'object' job in @@ -160,7 +163,6 @@ struct Job { bool installed:1; bool in_run_queue:1; bool matters_to_anchor:1; - bool override:1; bool in_dbus_queue:1; bool sent_dbus_new_signal:1; bool ignore_order:1; diff --git a/src/core/kill.c b/src/core/kill.c index 2de71c6bf9..1466d5ce64 100644 --- a/src/core/kill.c +++ b/src/core/kill.c @@ -19,9 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" -#include "signal-util.h" #include "kill.h" +#include "signal-util.h" +#include "string-table.h" +#include "util.h" void kill_context_init(KillContext *c) { assert(c); @@ -60,7 +61,10 @@ DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode); static const char* const kill_who_table[_KILL_WHO_MAX] = { [KILL_MAIN] = "main", [KILL_CONTROL] = "control", - [KILL_ALL] = "all" + [KILL_ALL] = "all", + [KILL_MAIN_FAIL] = "main-fail", + [KILL_CONTROL_FAIL] = "control-fail", + [KILL_ALL_FAIL] = "all-fail" }; DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/core/kill.h b/src/core/kill.h index d5f125fa41..5d97abb104 100644 --- a/src/core/kill.h +++ b/src/core/kill.h @@ -50,6 +50,9 @@ typedef enum KillWho { KILL_MAIN, KILL_CONTROL, KILL_ALL, + KILL_MAIN_FAIL, + KILL_CONTROL_FAIL, + KILL_ALL_FAIL, _KILL_WHO_MAX, _KILL_WHO_INVALID = -1 } KillWho; diff --git a/src/core/killall.c b/src/core/killall.c index ee5d388560..77f145b4d1 100644 --- a/src/core/killall.c +++ b/src/core/killall.c @@ -19,17 +19,21 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/wait.h> -#include <signal.h> #include <errno.h> +#include <signal.h> +#include <sys/wait.h> #include <unistd.h> -#include "util.h" -#include "killall.h" -#include "set.h" +#include "alloc-util.h" +#include "fd-util.h" #include "formats-util.h" +#include "killall.h" +#include "parse-util.h" #include "process-util.h" +#include "set.h" +#include "string-util.h" #include "terminal-util.h" +#include "util.h" #define TIMEOUT_USEC (10 * USEC_PER_SEC) diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c index 2068ffd69b..a6ab8cf4b3 100644 --- a/src/core/kmod-setup.c +++ b/src/core/kmod-setup.c @@ -19,17 +19,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> #include <string.h> +#include <unistd.h> #ifdef HAVE_KMOD #include <libkmod.h> #endif -#include "macro.h" -#include "capability.h" #include "bus-util.h" +#include "capability-util.h" #include "kmod-setup.h" +#include "macro.h" #ifdef HAVE_KMOD static void systemd_kmod_log( diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index 11566af51b..3fa66f91aa 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -20,13 +20,13 @@ ***/ -#include "unit.h" +#include "conf-parser.h" #include "load-dropin.h" +#include "load-fragment.h" #include "log.h" #include "strv.h" #include "unit-name.h" -#include "conf-parser.h" -#include "load-fragment.h" +#include "unit.h" static int add_dependency_consumer( UnitDependency dependency, diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index 1e018c4525..93ffcc4a72 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" #include "dropin.h" +#include "unit.h" /* Read service data supplementary drop-in directories */ diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index edd55b9e45..c64850802e 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -17,7 +17,7 @@ struct ConfigPerfItem; %% m4_dnl Define the context options only once m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', -`$1.WorkingDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.working_directory) +`$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context) $1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory) $1.User, config_parse_unit_string_printf, 0, offsetof($1, exec_context.user) $1.Group, config_parse_unit_string_printf, 0, offsetof($1, exec_context.group) @@ -33,6 +33,7 @@ $1.CPUAffinity, config_parse_exec_cpu_affinity, 0, $1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask) $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.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment) $1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input) $1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output) $1.StandardError, config_parse_output, 0, offsetof($1, exec_context.std_error) @@ -58,22 +59,22 @@ $1.RestrictAddressFamilies, config_parse_address_families, 0, $1.SystemCallArchitectures, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 $1.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 $1.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') -$1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) -$1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit) -$1.LimitDATA, config_parse_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit) -$1.LimitSTACK, config_parse_limit, RLIMIT_STACK, offsetof($1, exec_context.rlimit) -$1.LimitCORE, config_parse_limit, RLIMIT_CORE, offsetof($1, exec_context.rlimit) -$1.LimitRSS, config_parse_limit, RLIMIT_RSS, offsetof($1, exec_context.rlimit) +$1.LimitCPU, config_parse_sec_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) +$1.LimitFSIZE, config_parse_bytes_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit) +$1.LimitDATA, config_parse_bytes_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit) +$1.LimitSTACK, config_parse_bytes_limit, RLIMIT_STACK, offsetof($1, exec_context.rlimit) +$1.LimitCORE, config_parse_bytes_limit, RLIMIT_CORE, offsetof($1, exec_context.rlimit) +$1.LimitRSS, config_parse_bytes_limit, RLIMIT_RSS, offsetof($1, exec_context.rlimit) $1.LimitNOFILE, config_parse_limit, RLIMIT_NOFILE, offsetof($1, exec_context.rlimit) -$1.LimitAS, config_parse_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit) +$1.LimitAS, config_parse_bytes_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit) $1.LimitNPROC, config_parse_limit, RLIMIT_NPROC, offsetof($1, exec_context.rlimit) -$1.LimitMEMLOCK, config_parse_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit) +$1.LimitMEMLOCK, config_parse_bytes_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit) $1.LimitLOCKS, config_parse_limit, RLIMIT_LOCKS, offsetof($1, exec_context.rlimit) $1.LimitSIGPENDING, config_parse_limit, RLIMIT_SIGPENDING, offsetof($1, exec_context.rlimit) -$1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit) +$1.LimitMSGQUEUE, config_parse_bytes_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit) $1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit) $1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit) -$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit) +$1.LimitRTTIME, config_parse_usec_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit) $1.ReadWriteDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_dirs) $1.ReadOnlyDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_dirs) $1.InaccessibleDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs) @@ -124,15 +125,16 @@ $1.StartupBlockIOWeight, config_parse_blockio_weight, 0, $1.BlockIODeviceWeight, config_parse_blockio_device_weight, 0, offsetof($1, cgroup_context) $1.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context) $1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context) -$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate)' +$1.TasksAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.tasks_accounting) +$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context.tasks_max) +$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate) +$1.NetClass, config_parse_netclass, 0, offsetof($1, cgroup_context)' )m4_dnl Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description) Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation) Unit.SourcePath, config_parse_path, 0, offsetof(Unit, source_path) Unit.Requires, config_parse_unit_deps, UNIT_REQUIRES, 0 -Unit.RequiresOverridable, config_parse_unit_deps, UNIT_REQUIRES_OVERRIDABLE, 0 Unit.Requisite, config_parse_unit_deps, UNIT_REQUISITE, 0 -Unit.RequisiteOverridable, config_parse_unit_deps, UNIT_REQUISITE_OVERRIDABLE, 0 Unit.Wants, config_parse_unit_deps, UNIT_WANTS, 0 Unit.BindsTo, config_parse_unit_deps, UNIT_BINDS_TO, 0 Unit.BindTo, config_parse_unit_deps, UNIT_BINDS_TO, 0 @@ -146,6 +148,8 @@ Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0 Unit.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0 Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0 +Unit.RequiresOverridable, config_parse_obsolete_unit_deps, UNIT_REQUIRES, 0 +Unit.RequisiteOverridable, config_parse_obsolete_unit_deps, UNIT_REQUISITE, 0 Unit.RequiresMountsFor, config_parse_unit_requires_mounts_for, 0, 0 Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded) Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start) @@ -155,7 +159,7 @@ Unit.DefaultDependencies, config_parse_bool, 0, Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode) Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode) Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate) -Unit.IgnoreOnSnapshot, config_parse_bool, 0, offsetof(Unit, ignore_on_snapshot) +Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0 Unit.JobTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_timeout) Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action) Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg) @@ -231,6 +235,8 @@ Service.FileDescriptorStoreMax, config_parse_unsigned, 0, Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access) Service.Sockets, config_parse_service_sockets, 0, 0 Service.BusPolicy, config_parse_bus_endpoint_policy, 0, offsetof(Service, exec_context) +Service.USBFunctionDescriptors, config_parse_path, 0, offsetof(Service, usb_function_descriptors) +Service.USBFunctionStrings, config_parse_path, 0, offsetof(Service, usb_function_strings) EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl CGROUP_CONTEXT_CONFIG_ITEMS(Service)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl @@ -242,6 +248,8 @@ Socket.ListenFIFO, config_parse_socket_listen, SOCKET_FIFO 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.ListenUSBFunction, config_parse_socket_listen, SOCKET_USB_FUNCTION, 0 +Socket.SocketProtocol, config_parse_socket_protocol, 0, 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 @@ -255,6 +263,7 @@ Socket.SocketGroup, config_parse_unit_string_printf, 0, Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode) Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) +Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable) Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections) Socket.KeepAlive, config_parse_bool, 0, offsetof(Socket, keep_alive) Socket.KeepAliveTimeSec, config_parse_sec, 0, offsetof(Socket, keep_alive_time) @@ -280,6 +289,7 @@ Socket.MessageQueueMaxMessages, config_parse_long, 0, Socket.MessageQueueMessageSize, config_parse_long, 0, offsetof(Socket, mq_msgsize) Socket.RemoveOnStop, config_parse_bool, 0, offsetof(Socket, remove_on_stop) Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks) +Socket.FileDescriptorName, config_parse_fdname, 0, 0 Socket.Service, config_parse_socket_service, 0, 0 m4_ifdef(`HAVE_SMACK', `Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack) @@ -335,7 +345,9 @@ Timer.OnUnitActiveSec, config_parse_timer, 0, Timer.OnUnitInactiveSec, config_parse_timer, 0, 0 Timer.Persistent, config_parse_bool, 0, offsetof(Timer, persistent) Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system) +Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse) Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec) +Timer.RandomSec, config_parse_sec, 0, offsetof(Timer, random_usec) Timer.Unit, config_parse_trigger_unit, 0, 0 m4_dnl Path.PathExists, config_parse_path_spec, 0, 0 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index b695c57da8..d5cf476fda 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -20,44 +20,52 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <linux/oom.h> #include <errno.h> -#include <string.h> #include <fcntl.h> -#include <sched.h> #include <linux/fs.h> -#include <sys/stat.h> -#include <sys/resource.h> - +#include <linux/oom.h> #ifdef HAVE_SECCOMP #include <seccomp.h> #endif +#include <sched.h> +#include <string.h> +#include <sys/resource.h> +#include <sys/stat.h> -#include "unit.h" -#include "strv.h" +#include "af-list.h" +#include "alloc-util.h" +#include "bus-error.h" +#include "bus-internal.h" +#include "bus-util.h" +#include "cap-list.h" +#include "cgroup.h" #include "conf-parser.h" +#include "cpu-set-util.h" +#include "env-util.h" +#include "errno-list.h" +#include "escape.h" +#include "fd-util.h" +#include "fs-util.h" +#include "ioprio.h" #include "load-fragment.h" #include "log.h" -#include "ioprio.h" -#include "securebits.h" #include "missing.h" -#include "unit-name.h" -#include "unit-printf.h" -#include "utf8.h" +#include "parse-util.h" #include "path-util.h" -#include "env-util.h" -#include "cgroup.h" -#include "bus-util.h" -#include "bus-error.h" -#include "errno-list.h" -#include "af-list.h" -#include "cap-list.h" -#include "signal-util.h" -#include "bus-internal.h" - +#include "process-util.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" #endif +#include "securebits.h" +#include "signal-util.h" +#include "stat-util.h" +#include "string-util.h" +#include "strv.h" +#include "unit-name.h" +#include "unit-printf.h" +#include "unit.h" +#include "utf8.h" +#include "web-util.h" int config_parse_warn_compat( const char *unit, @@ -74,15 +82,15 @@ int config_parse_warn_compat( switch(reason) { case DISABLED_CONFIGURATION: - log_syntax(unit, LOG_DEBUG, filename, line, EINVAL, + log_syntax(unit, LOG_DEBUG, filename, line, 0, "Support for option %s= has been disabled at compile time and it is ignored", lvalue); break; case DISABLED_LEGACY: - log_syntax(unit, LOG_INFO, filename, line, EINVAL, + log_syntax(unit, LOG_INFO, filename, line, 0, "Support for option %s= has been removed and it is ignored", lvalue); break; case DISABLED_EXPERIMENTAL: - log_syntax(unit, LOG_INFO, filename, line, EINVAL, + log_syntax(unit, LOG_INFO, filename, line, 0, "Support for option %s= has not yet been enabled and it is ignored", lvalue); break; }; @@ -90,52 +98,73 @@ int config_parse_warn_compat( return 0; } -int config_parse_unit_deps(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_unit_deps( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { UnitDependency d = ltype; Unit *u = userdata; - const char *word, *state; - size_t l; + const char *p; assert(filename); assert(lvalue); assert(rvalue); - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *t = NULL, *k = NULL; + p = rvalue; + for(;;) { + _cleanup_free_ char *word = NULL, *k = NULL; int r; - t = strndup(word, l); - if (!t) + r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE); + if (r == 0) + break; + if (r == -ENOMEM) return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + break; + } - r = unit_name_printf(u, t, &k); + r = unit_name_printf(u, word, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve specifiers, ignoring: %s", strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); continue; } r = unit_add_dependency_by_name(u, d, k, NULL, true); if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to add dependency on %s, ignoring: %s", k, strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k); } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring."); return 0; } +int config_parse_obsolete_unit_deps( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue, unit_dependency_to_string(ltype)); + + return config_parse_unit_deps(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata); +} + int config_parse_unit_string_printf( const char *unit, const char *filename, @@ -166,16 +195,17 @@ int config_parse_unit_string_printf( return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata); } -int config_parse_unit_strv_printf(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_unit_strv_printf( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Unit *u = userdata; _cleanup_free_ char *k = NULL; @@ -187,24 +217,25 @@ int config_parse_unit_strv_printf(const char *unit, assert(u); r = unit_full_printf(u, rvalue, &k); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r)); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); + return 0; + } - return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, - k ? k : rvalue, data, userdata); + return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata); } -int config_parse_unit_path_printf(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_unit_path_printf( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { _cleanup_free_ char *k = NULL; Unit *u = userdata; @@ -217,7 +248,7 @@ int config_parse_unit_path_printf(const char *unit, r = unit_full_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); return 0; } @@ -256,17 +287,17 @@ int config_parse_unit_path_strv_printf( r = unit_full_printf(u, t, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", t, strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", t); return 0; } if (!utf8_is_valid(k)) { - log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); return 0; } if (!path_is_absolute(k)) { - log_syntax(unit, LOG_ERR, filename, line, -r, "Symlink path %s is not absolute, ignoring: %s", k, strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, 0, "Symlink path %s is not absolute, ignoring: %m", k); return 0; } @@ -279,7 +310,7 @@ int config_parse_unit_path_strv_printf( k = NULL; } if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring."); return 0; } @@ -322,12 +353,8 @@ int config_parse_socket_listen(const char *unit, p->type = ltype; r = unit_full_printf(UNIT(s), rvalue, &p->path); if (r < 0) { - p->path = strdup(rvalue); - if (!p->path) - return log_oom(); - else - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); + return 0; } path_kill_slashes(p->path); @@ -337,14 +364,14 @@ int config_parse_socket_listen(const char *unit, p->type = SOCKET_SOCKET; r = unit_full_printf(UNIT(s), rvalue, &k); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r)); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); + return 0; + } - r = socket_address_parse_netlink(&p->address, k ?: rvalue); + r = socket_address_parse_netlink(&p->address, k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse address value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue); return 0; } @@ -353,14 +380,14 @@ int config_parse_socket_listen(const char *unit, p->type = SOCKET_SOCKET; r = unit_full_printf(UNIT(s), rvalue, &k); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r)); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); + return 0; + } - r = socket_address_parse_and_warn(&p->address, k ? k : rvalue); + r = socket_address_parse_and_warn(&p->address, k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse address value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue); return 0; } @@ -374,13 +401,14 @@ int config_parse_socket_listen(const char *unit, } if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) { - log_syntax(unit, LOG_ERR, filename, line, EOPNOTSUPP, - "Address family not supported, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Address family not supported, ignoring: %s", rvalue); return 0; } } p->fd = -1; + p->auxiliary_fds = NULL; + p->n_auxiliary_fds = 0; p->socket = s; if (s->ports) { @@ -393,6 +421,37 @@ int config_parse_socket_listen(const char *unit, return 0; } +int config_parse_socket_protocol(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Socket *s; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + s = SOCKET(data); + + if (streq(rvalue, "udplite")) + s->socket_protocol = IPPROTO_UDPLITE; + else if (streq(rvalue, "sctp")) + s->socket_protocol = IPPROTO_SCTP; + else { + log_syntax(unit, LOG_ERR, filename, line, 0, "Socket protocol not supported, ignoring: %s", rvalue); + return 0; + } + + return 0; +} + int config_parse_socket_bind(const char *unit, const char *filename, unsigned line, @@ -420,8 +479,7 @@ int config_parse_socket_bind(const char *unit, r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse bind IPv6 only value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue); return 0; } @@ -453,14 +511,12 @@ int config_parse_exec_nice(const char *unit, r = safe_atoi(rvalue, &priority); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse nice priority, ignoring: %s. ", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue); return 0; } if (priority < PRIO_MIN || priority >= PRIO_MAX) { - log_syntax(unit, LOG_ERR, filename, line, ERANGE, - "Nice priority out of range, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Nice priority out of range, ignoring: %s", rvalue); return 0; } @@ -491,14 +547,12 @@ int config_parse_exec_oom_score_adjust(const char* unit, r = safe_atoi(rvalue, &oa); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse the OOM score adjust value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue); return 0; } if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) { - log_syntax(unit, LOG_ERR, filename, line, ERANGE, - "OOM score adjust value out of range, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "OOM score adjust value out of range, ignoring: %s", rvalue); return 0; } @@ -531,9 +585,7 @@ int config_parse_exec( assert(e); e += ltype; - rvalue += strspn(rvalue, WHITESPACE); - p = rvalue; if (isempty(rvalue)) { /* An empty assignment resets the list */ @@ -541,14 +593,15 @@ int config_parse_exec( return 0; } + p = rvalue; do { - int i; + _cleanup_free_ char *path = NULL, *firstword = NULL; + bool separate_argv0 = false, ignore = false; + _cleanup_free_ ExecCommand *nce = NULL; _cleanup_strv_free_ char **n = NULL; size_t nlen = 0, nbufsize = 0; - _cleanup_free_ ExecCommand *nce = NULL; - _cleanup_free_ char *path = NULL, *firstword = NULL; char *f; - bool separate_argv0 = false, ignore = false; + int i; semicolon = false; @@ -572,24 +625,19 @@ int config_parse_exec( if (isempty(f)) { /* First word is either "-" or "@" with no command. */ - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Empty path in command line, ignoring: \"%s\"", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Empty path in command line, ignoring: \"%s\"", rvalue); return 0; } - if (!string_is_safe(f)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Executable path contains special characters, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path contains special characters, ignoring: %s", rvalue); return 0; } if (!path_is_absolute(f)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Executable path is not absolute, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path is not absolute, ignoring: %s", rvalue); return 0; } if (endswith(f, "/")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Executable path specifies a directory, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path specifies a directory, ignoring: %s", rvalue); return 0; } @@ -656,8 +704,7 @@ int config_parse_exec( } if (!n || !n[0]) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Empty executable name or zeroeth argument, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue); return 0; } @@ -741,8 +788,7 @@ int config_parse_exec_io_class(const char *unit, x = ioprio_class_from_string(rvalue); if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse IO scheduling class, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue); return 0; } @@ -773,8 +819,7 @@ int config_parse_exec_io_priority(const char *unit, r = safe_atoi(rvalue, &i); if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse IO priority, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue); return 0; } @@ -806,8 +851,7 @@ int config_parse_exec_cpu_sched_policy(const char *unit, x = sched_policy_from_string(rvalue); if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, -x, - "Failed to parse CPU scheduling policy, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue); return 0; } @@ -840,8 +884,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit, r = safe_atoi(rvalue, &i); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse CPU scheduling policy, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue); return 0; } @@ -850,8 +893,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit, max = sched_get_priority_max(c->cpu_sched_policy); if (i < min || i > max) { - log_syntax(unit, LOG_ERR, filename, line, ERANGE, - "CPU scheduling priority is out of range, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue); return 0; } @@ -873,50 +915,29 @@ int config_parse_exec_cpu_affinity(const char *unit, void *userdata) { ExecContext *c = data; - const char *word, *state; - size_t l; + _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; + int ncpus; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (isempty(rvalue)) { - /* An empty assignment resets the CPU list */ - if (c->cpuset) - CPU_FREE(c->cpuset); - c->cpuset = NULL; - return 0; - } + ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue); + if (ncpus < 0) + return ncpus; - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *t = NULL; - int r; - unsigned cpu; + if (c->cpuset) + CPU_FREE(c->cpuset); - t = strndup(word, l); - if (!t) - return log_oom(); - - r = safe_atou(t, &cpu); - - if (!c->cpuset) { - c->cpuset = cpu_set_malloc(&c->cpuset_ncpus); - if (!c->cpuset) - return log_oom(); - } - - if (r < 0 || cpu >= c->cpuset_ncpus) { - log_syntax(unit, LOG_ERR, filename, line, ERANGE, - "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue); - return 0; - } - - CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset); + if (ncpus == 0) + /* An empty assignment resets the CPU list */ + c->cpuset = NULL; + else { + c->cpuset = cpuset; + cpuset = NULL; } - if (!isempty(state)) - log_syntax(unit, LOG_WARNING, filename, line, EINVAL, - "Trailing garbage, ignoring."); + c->cpuset_ncpus = ncpus; return 0; } @@ -942,8 +963,7 @@ int config_parse_exec_capabilities(const char *unit, cap = cap_from_text(rvalue); if (!cap) { - log_syntax(unit, LOG_ERR, filename, line, errno, - "Failed to parse capabilities, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse capabilities, ignoring: %s", rvalue); return 0; } @@ -994,34 +1014,32 @@ int config_parse_exec_secure_bits(const char *unit, else if (first_word(word, "noroot-locked")) c->secure_bits |= 1<<SECURE_NOROOT_LOCKED; else { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse secure bits, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse secure bits, ignoring: %s", rvalue); return 0; } } if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid syntax, garbage at the end, ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, garbage at the end, ignoring."); return 0; } -int config_parse_bounding_set(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_bounding_set( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { uint64_t *capability_bounding_set_drop = data; - const char *word, *state; - size_t l; + uint64_t capability_bounding_set, sum = 0; bool invert = false; - uint64_t sum = 0; + const char *p; assert(filename); assert(lvalue); @@ -1038,47 +1056,54 @@ int config_parse_bounding_set(const char *unit, * non-inverted everywhere to have a fully normalized * interface. */ - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *t = NULL; - int cap; + p = rvalue; + for (;;) { + _cleanup_free_ char *word = NULL; + int cap, r; - t = strndup(word, l); - if (!t) + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r == 0) + break; + if (r == -ENOMEM) return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse word, ignoring: %s", rvalue); + break; + } - cap = capability_from_name(t); + cap = capability_from_name(word); if (cap < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse capability in bounding set, ignoring: %s", t); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding set, ignoring: %s", word); continue; } - sum |= ((uint64_t) 1ULL) << (uint64_t) cap; + sum |= ((uint64_t) UINT64_C(1)) << (uint64_t) cap; } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); - if (invert) - *capability_bounding_set_drop |= sum; + capability_bounding_set = invert ? ~sum : sum; + if (*capability_bounding_set_drop != 0 && capability_bounding_set != 0) + *capability_bounding_set_drop = ~(~*capability_bounding_set_drop | capability_bounding_set); else - *capability_bounding_set_drop |= ~sum; + *capability_bounding_set_drop = ~capability_bounding_set; return 0; } -int config_parse_limit(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_limit( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { struct rlimit **rl = data; - unsigned long long u; + rlim_t v; + int r; assert(filename); assert(lvalue); @@ -1088,16 +1113,71 @@ int config_parse_limit(const char *unit, rl += ltype; if (streq(rvalue, "infinity")) - u = (unsigned long long) RLIM_INFINITY; + v = RLIM_INFINITY; else { - int r; + uint64_t u; + + /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */ + assert_cc(sizeof(rlim_t) == sizeof(uint64_t)); + + r = safe_atou64(rvalue, &u); + if (r >= 0 && u >= (uint64_t) RLIM_INFINITY) + r = -ERANGE; + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); + return 0; + } + + v = (rlim_t) u; + } + + if (!*rl) { + *rl = new(struct rlimit, 1); + if (!*rl) + return log_oom(); + } + + (*rl)->rlim_cur = (*rl)->rlim_max = v; + return 0; +} + +int config_parse_bytes_limit( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + struct rlimit **rl = data; + rlim_t bytes; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + rl += ltype; - r = safe_atollu(rvalue, &u); + if (streq(rvalue, "infinity")) + bytes = RLIM_INFINITY; + else { + uint64_t u; + + r = parse_size(rvalue, 1024, &u); + if (r >= 0 && u >= (uint64_t) RLIM_INFINITY) + r = -ERANGE; if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse resource value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); return 0; } + + bytes = (rlim_t) u; } if (!*rl) { @@ -1106,7 +1186,108 @@ int config_parse_limit(const char *unit, return log_oom(); } - (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u; + (*rl)->rlim_cur = (*rl)->rlim_max = bytes; + return 0; +} + +int config_parse_sec_limit( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + struct rlimit **rl = data; + rlim_t seconds; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + rl += ltype; + + if (streq(rvalue, "infinity")) + seconds = RLIM_INFINITY; + else { + usec_t t; + + r = parse_sec(rvalue, &t); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); + return 0; + } + + if (t == USEC_INFINITY) + seconds = RLIM_INFINITY; + else + seconds = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC)); + } + + if (!*rl) { + *rl = new(struct rlimit, 1); + if (!*rl) + return log_oom(); + } + + (*rl)->rlim_cur = (*rl)->rlim_max = seconds; + return 0; +} + + +int config_parse_usec_limit( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + struct rlimit **rl = data; + rlim_t useconds; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + rl += ltype; + + if (streq(rvalue, "infinity")) + useconds = RLIM_INFINITY; + else { + usec_t t; + + r = parse_time(rvalue, &t, 1); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); + return 0; + } + + if (t == USEC_INFINITY) + useconds = RLIM_INFINITY; + else + useconds = (rlim_t) t; + } + + if (!*rl) { + *rl = new(struct rlimit, 1); + if (!*rl) + return log_oom(); + } + + (*rl)->rlim_cur = (*rl)->rlim_max = useconds; return 0; } @@ -1132,8 +1313,7 @@ int config_parse_sysv_priority(const char *unit, r = safe_atoi(rvalue, &i); if (r < 0 || i < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse SysV start priority, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SysV start priority, ignoring: %s", rvalue); return 0; } @@ -1180,12 +1360,12 @@ int config_parse_exec_mount_flags(const char *unit, else if (streq(t, "private")) flags = MS_PRIVATE; else { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse mount flag %s, ignoring: %s", t, rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring: %s", t, rvalue); return 0; } } if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); c->mount_flags = flags; return 0; @@ -1228,8 +1408,7 @@ int config_parse_exec_selinux_context( r = unit_name_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve specifiers, ignoring: %s", strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); return 0; } @@ -1277,8 +1456,7 @@ int config_parse_exec_apparmor_profile( r = unit_name_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve specifiers, ignoring: %s", strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); return 0; } @@ -1326,8 +1504,7 @@ int config_parse_exec_smack_process_label( r = unit_name_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve specifiers, ignoring: %s", strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); return 0; } @@ -1368,23 +1545,18 @@ int config_parse_timer(const char *unit, b = timer_base_from_string(lvalue); if (b < 0) { - log_syntax(unit, LOG_ERR, filename, line, -b, - "Failed to parse timer base, ignoring: %s", lvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer base, ignoring: %s", lvalue); return 0; } if (b == TIMER_CALENDAR) { if (calendar_spec_from_string(rvalue, &c) < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse calendar specification, ignoring: %s", - rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", rvalue); return 0; } } else { if (parse_sec(rvalue, &u) < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse timer value, ignoring: %s", - rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", rvalue); return 0; } } @@ -1427,33 +1599,30 @@ int config_parse_trigger_unit( assert(data); if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Multiple units to trigger specified, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue); return 0; } r = unit_name_printf(u, rvalue, &p); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve specifiers, ignoring: %s", strerror(-r)); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); + return 0; + } - type = unit_name_to_type(p ?: rvalue); + type = unit_name_to_type(p); if (type < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Unit type not valid, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Unit type not valid, ignoring: %s", rvalue); return 0; } if (type == u->type) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trigger cannot be of same type, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Trigger cannot be of same type, ignoring: %s", rvalue); return 0; } - r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true); + r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r)); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p); return 0; } @@ -1490,25 +1659,18 @@ int config_parse_path_spec(const char *unit, b = path_type_from_string(lvalue); if (b < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse path type, ignoring: %s", lvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse path type, ignoring: %s", lvalue); return 0; } r = unit_full_printf(UNIT(p), rvalue, &k); if (r < 0) { - k = strdup(rvalue); - if (!k) - return log_oom(); - else - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve unit specifiers on %s. Ignoring.", - rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + return 0; } if (!path_is_absolute(k)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Path is not absolute, ignoring: %s", k); + log_syntax(unit, LOG_ERR, filename, line, 0, "Path is not absolute, ignoring: %s", k); return 0; } @@ -1540,10 +1702,10 @@ int config_parse_socket_service( void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *p = NULL; Socket *s = data; - int r; Unit *x; - _cleanup_free_ char *p = NULL; + int r; assert(filename); assert(lvalue); @@ -1557,13 +1719,13 @@ int config_parse_socket_service( } if (!endswith(p, ".service")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue); return 0; } r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r)); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r)); return 0; } @@ -1572,6 +1734,50 @@ int config_parse_socket_service( return 0; } +int config_parse_fdname( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_free_ char *p = NULL; + Socket *s = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + s->fdname = mfree(s->fdname); + return 0; + } + + r = unit_name_printf(UNIT(s), rvalue, &p); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); + return 0; + } + + if (!fdname_is_valid(p)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p); + return 0; + } + + free(s->fdname); + s->fdname = p; + p = NULL; + + return 0; +} + int config_parse_service_sockets( const char *unit, const char *filename, @@ -1585,8 +1791,7 @@ int config_parse_service_sockets( void *userdata) { Service *s = data; - const char *word, *state; - size_t l; + const char *p; int r; assert(filename); @@ -1594,21 +1799,28 @@ int config_parse_service_sockets( assert(rvalue); assert(data); - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *t = NULL, *k = NULL; + p = rvalue; + for(;;) { + _cleanup_free_ char *word = NULL, *k = NULL; - t = strndup(word, l); - if (!t) + r = extract_first_word(&p, &word, NULL, 0); + if (r == 0) + break; + if (r == -ENOMEM) return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue); + break; + } - r = unit_name_printf(UNIT(s), t, &k); + r = unit_name_printf(UNIT(s), word, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); continue; } if (!endswith(k, ".socket")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type socket, ignoring: %s", k); + log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type socket, ignoring: %s", k); continue; } @@ -1620,8 +1832,6 @@ int config_parse_service_sockets( if (r < 0) log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k); } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring."); return 0; } @@ -1654,7 +1864,7 @@ int config_parse_bus_name( } if (!service_name_is_valid(k)) { - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bus name %s, ignoring.", k); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k); return 0; } @@ -1719,21 +1929,18 @@ int config_parse_busname_service( r = unit_name_printf(UNIT(n), rvalue, &p); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve specifiers, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); return 0; } if (!endswith(p, ".service")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Unit must be of type service, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue); return 0; } r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r)); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r)); return 0; } @@ -1783,8 +1990,7 @@ int config_parse_bus_policy( access_str = strpbrk(id_str, WHITESPACE); if (!access_str) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid busname policy value '%s'", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy value '%s'", rvalue); return 0; } @@ -1794,8 +2000,7 @@ int config_parse_bus_policy( p->access = bus_policy_access_from_string(access_str); if (p->access < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid busname policy access type '%s'", access_str); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy access type '%s'", access_str); return 0; } @@ -1837,8 +2042,7 @@ int config_parse_bus_endpoint_policy( access_str = strpbrk(name, WHITESPACE); if (!access_str) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid endpoint policy value '%s'", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid endpoint policy value '%s'", rvalue); return 0; } @@ -1849,21 +2053,83 @@ int config_parse_bus_endpoint_policy( access = bus_policy_access_from_string(access_str); if (access <= _BUS_POLICY_ACCESS_INVALID || access >= _BUS_POLICY_ACCESS_MAX) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid endpoint policy access type '%s'", access_str); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid endpoint policy access type '%s'", access_str); return 0; } if (!c->bus_endpoint) { r = bus_endpoint_new(&c->bus_endpoint); - if (r < 0) - return r; + return log_error_errno(r, "Failed to create bus endpoint object: %m"); } return bus_endpoint_add_policy(c->bus_endpoint, name, access); } +int config_parse_working_directory( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + ExecContext *c = data; + Unit *u = userdata; + bool missing_ok; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(c); + assert(u); + + if (rvalue[0] == '-') { + missing_ok = true; + rvalue++; + } else + missing_ok = false; + + if (streq(rvalue, "~")) { + c->working_directory_home = true; + c->working_directory = mfree(c->working_directory); + } else { + _cleanup_free_ char *k = NULL; + + r = unit_full_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue); + return 0; + } + + path_kill_slashes(k); + + if (!utf8_is_valid(k)) { + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); + return 0; + } + + if (!path_is_absolute(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue); + return 0; + } + + free(c->working_directory); + c->working_directory = k; + k = NULL; + + c->working_directory_home = false; + } + + c->working_directory_missing_ok = missing_ok; + return 0; +} + int config_parse_unit_env_file(const char *unit, const char *filename, unsigned line, @@ -1878,7 +2144,6 @@ int config_parse_unit_env_file(const char *unit, char ***env = data; Unit *u = userdata; _cleanup_free_ char *n = NULL; - const char *s; int r; assert(filename); @@ -1888,24 +2153,22 @@ int config_parse_unit_env_file(const char *unit, if (isempty(rvalue)) { /* Empty assignment frees the list */ - strv_free(*env); - *env = NULL; + *env = strv_free(*env); return 0; } r = unit_full_printf(u, rvalue, &n); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve specifiers, ignoring: %s", rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); + return 0; + } - s = n ?: rvalue; - if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Path '%s' is not absolute, ignoring.", s); + if (!path_is_absolute(n[0] == '-' ? n + 1 : n)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Path '%s' is not absolute, ignoring.", n); return 0; } - r = strv_extend(env, s); + r = strv_extend(env, n); if (r < 0) return log_oom(); @@ -1937,21 +2200,23 @@ int config_parse_environ(const char *unit, if (isempty(rvalue)) { /* Empty assignment resets the list */ - strv_free(*env); - *env = NULL; + *env = strv_free(*env); return 0; } if (u) { r = unit_full_printf(u, rvalue, &k); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); + return 0; + } } - if (!k) + if (!k) { k = strdup(rvalue); - if (!k) - return log_oom(); + if (!k) + return log_oom(); + } FOREACH_WORD_QUOTED(word, l, k, state) { _cleanup_free_ char *n = NULL; @@ -1964,7 +2229,7 @@ int config_parse_environ(const char *unit, } if (!env_assignment_is_valid(n)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid environment assignment, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid environment assignment, ignoring: %s", rvalue); continue; } @@ -1976,8 +2241,71 @@ int config_parse_environ(const char *unit, *env = x; } if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); + + return 0; +} + +int config_parse_pass_environ(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + const char *whole_rvalue = rvalue; + char*** passenv = data; + _cleanup_strv_free_ char **n = NULL; + size_t nlen = 0, nbufsize = 0; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *passenv = strv_free(*passenv); + return 0; + } + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue); + break; + } + + if (!env_name_is_valid(word)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid environment name for %s, ignoring: %s", lvalue, word); + continue; + } + + if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) + return log_oom(); + n[nlen++] = word; + n[nlen] = NULL; + word = NULL; + } + + if (n) { + r = strv_extend_strv(passenv, n, true); + if (r < 0) + return r; + } return 0; } @@ -2002,8 +2330,7 @@ int config_parse_ip_tos(const char *unit, x = ip_tos_from_string(rvalue); if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse IP TOS value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue); return 0; } @@ -2051,12 +2378,12 @@ int config_parse_unit_condition_path( r = unit_full_printf(u, rvalue, &p); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); return 0; } if (!path_is_absolute(p)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path in condition not absolute, ignoring: %s", p); + log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p); return 0; } @@ -2108,7 +2435,7 @@ int config_parse_unit_condition_string( r = unit_full_printf(u, rvalue, &s); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); return 0; } @@ -2157,7 +2484,7 @@ int config_parse_unit_condition_null( b = parse_boolean(rvalue); if (b < 0) { - log_syntax(unit, LOG_ERR, filename, line, -b, "Failed to parse boolean value in condition, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue); return 0; } @@ -2205,20 +2532,18 @@ int config_parse_unit_requires_mounts_for( return log_oom(); if (!utf8_is_valid(n)) { - log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); continue; } r = unit_require_mounts_for(u, n); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to add required mount for, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount for, ignoring: %s", rvalue); continue; } } if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); return 0; } @@ -2245,8 +2570,7 @@ int config_parse_documentation(const char *unit, if (isempty(rvalue)) { /* Empty assignment resets the list */ - strv_free(u->documentation); - u->documentation = NULL; + u->documentation = strv_free(u->documentation); return 0; } @@ -2260,8 +2584,7 @@ int config_parse_documentation(const char *unit, if (documentation_url_is_valid(*a)) *(b++) = *a; else { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid URL, ignoring: %s", *a); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a); free(*a); } } @@ -2305,8 +2628,7 @@ int config_parse_syscall_filter( if (isempty(rvalue)) { /* Empty assignment resets the list */ - set_free(c->syscall_filter); - c->syscall_filter = NULL; + c->syscall_filter = set_free(c->syscall_filter); c->syscall_whitelist = false; return 0; } @@ -2357,8 +2679,7 @@ int config_parse_syscall_filter( id = seccomp_syscall_resolve_name(t); if (id < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse system call, ignoring: %s", t); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call, ignoring: %s", t); continue; } @@ -2375,8 +2696,7 @@ int config_parse_syscall_filter( set_remove(c->syscall_filter, INT_TO_PTR(id + 1)); } if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); /* Turn on NNP, but only if it wasn't configured explicitly * before, and only if we are in user mode. */ @@ -2404,8 +2724,7 @@ int config_parse_syscall_archs( int r; if (isempty(rvalue)) { - set_free(*archs); - *archs = NULL; + *archs = set_free(*archs); return 0; } @@ -2423,8 +2742,7 @@ int config_parse_syscall_archs( r = seccomp_arch_from_string(t, &a); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse system call architecture, ignoring: %s", t); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call architecture, ignoring: %s", t); continue; } @@ -2435,8 +2753,7 @@ int config_parse_syscall_archs( return log_oom(); } if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); return 0; } @@ -2468,8 +2785,7 @@ int config_parse_syscall_errno( e = errno_from_name(rvalue); if (e < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse error number, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue); return 0; } @@ -2501,8 +2817,7 @@ int config_parse_address_families( if (isempty(rvalue)) { /* Empty assignment resets the list */ - set_free(c->address_families); - c->address_families = NULL; + c->address_families = set_free(c->address_families); c->address_families_whitelist = false; return 0; } @@ -2530,8 +2845,7 @@ int config_parse_address_families( af = af_from_name(t); if (af <= 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse address family, ignoring: %s", t); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse address family, ignoring: %s", t); continue; } @@ -2548,8 +2862,7 @@ int config_parse_address_families( set_remove(c->address_families, INT_TO_PTR(af)); } if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); return 0; } @@ -2611,26 +2924,19 @@ int config_parse_cpu_shares( void *data, void *userdata) { - unsigned long *shares = data, lu; + uint64_t *shares = data; int r; assert(filename); assert(lvalue); assert(rvalue); - if (isempty(rvalue)) { - *shares = (unsigned long) -1; - return 0; - } - - r = safe_atolu(rvalue, &lu); - if (r < 0 || lu <= 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "CPU shares '%s' invalid. Ignoring.", rvalue); + r = cg_cpu_shares_parse(rvalue, shares); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue); return 0; } - *shares = lu; return 0; } @@ -2659,15 +2965,12 @@ int config_parse_cpu_quota( } if (!endswith(rvalue, "%")) { - - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue); return 0; } if (sscanf(rvalue, "%lf%%", &percent) != 1 || percent <= 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "CPU quota '%s' invalid. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' invalid. Ignoring.", rvalue); return 0; } @@ -2689,24 +2992,51 @@ int config_parse_memory_limit( void *userdata) { CGroupContext *c = data; - off_t bytes; + uint64_t bytes; int r; - if (isempty(rvalue)) { + if (isempty(rvalue) || streq(rvalue, "infinity")) { c->memory_limit = (uint64_t) -1; return 0; } - assert_cc(sizeof(uint64_t) == sizeof(off_t)); - r = parse_size(rvalue, 1024, &bytes); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Memory limit '%s' invalid. Ignoring.", rvalue); + if (r < 0 || bytes < 1) { + log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue); return 0; } - c->memory_limit = (uint64_t) bytes; + c->memory_limit = bytes; + return 0; +} + +int config_parse_tasks_max( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint64_t *tasks_max = data, u; + int r; + + if (isempty(rvalue) || streq(rvalue, "infinity")) { + *tasks_max = (uint64_t) -1; + return 0; + } + + r = safe_atou64(rvalue, &u); + if (r < 0 || u < 1) { + log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue); + return 0; + } + + *tasks_max = u; return 0; } @@ -2743,8 +3073,7 @@ int config_parse_device_allow( if (!startswith(path, "/dev/") && !startswith(path, "block-") && !startswith(path, "char-")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid device node path '%s'. Ignoring.", path); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); return 0; } @@ -2753,8 +3082,7 @@ int config_parse_device_allow( m = "rwm"; if (!in_charset(m, "rwm")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid device rights '%s'. Ignoring.", m); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m); return 0; } @@ -2784,26 +3112,19 @@ int config_parse_blockio_weight( void *data, void *userdata) { - unsigned long *weight = data, lu; + uint64_t *weight = data; int r; assert(filename); assert(lvalue); assert(rvalue); - if (isempty(rvalue)) { - *weight = (unsigned long) -1; - return 0; - } - - r = safe_atolu(rvalue, &lu); - if (r < 0 || lu < 10 || lu > 1000) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Block IO weight '%s' invalid. Ignoring.", rvalue); + r = cg_blkio_weight_parse(rvalue, weight); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue); return 0; } - *weight = lu; return 0; } @@ -2822,8 +3143,8 @@ int config_parse_blockio_device_weight( _cleanup_free_ char *path = NULL; CGroupBlockIODeviceWeight *w; CGroupContext *c = data; - unsigned long lu; const char *weight; + uint64_t u; size_t n; int r; @@ -2840,9 +3161,10 @@ int config_parse_blockio_device_weight( n = strcspn(rvalue, WHITESPACE); weight = rvalue + n; - if (!*weight) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Expected block device and device weight. Ignoring."); + weight += strspn(weight, WHITESPACE); + + if (isempty(weight)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring."); return 0; } @@ -2851,19 +3173,18 @@ int config_parse_blockio_device_weight( return log_oom(); if (!path_startswith(path, "/dev")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid device node path '%s'. Ignoring.", path); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); return 0; } - weight += strspn(weight, WHITESPACE); - r = safe_atolu(weight, &lu); - if (r < 0 || lu < 10 || lu > 1000) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Block IO weight '%s' invalid. Ignoring.", rvalue); + r = cg_blkio_weight_parse(weight, &u); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight); return 0; } + assert(u != CGROUP_BLKIO_WEIGHT_INVALID); + w = new0(CGroupBlockIODeviceWeight, 1); if (!w) return log_oom(); @@ -2871,7 +3192,7 @@ int config_parse_blockio_device_weight( w->path = path; path = NULL; - w->weight = lu; + w->weight = u; LIST_PREPEND(device_weights, c->blockio_device_weights, w); return 0; @@ -2893,7 +3214,7 @@ int config_parse_blockio_bandwidth( CGroupBlockIODeviceBandwidth *b; CGroupContext *c = data; const char *bandwidth; - off_t bytes; + uint64_t bytes; bool read; size_t n; int r; @@ -2919,8 +3240,7 @@ int config_parse_blockio_bandwidth( bandwidth += strspn(bandwidth, WHITESPACE); if (!*bandwidth) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Expected space separated pair of device node and bandwidth. Ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring."); return 0; } @@ -2929,15 +3249,13 @@ int config_parse_blockio_bandwidth( return log_oom(); if (!path_startswith(path, "/dev")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid device node path '%s'. Ignoring.", path); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); return 0; } r = parse_size(bandwidth, 1000, &bytes); if (r < 0 || bytes <= 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue); return 0; } @@ -2947,7 +3265,7 @@ int config_parse_blockio_bandwidth( b->path = path; path = NULL; - b->bandwidth = (uint64_t) bytes; + b->bandwidth = bytes; b->read = read; LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b); @@ -2955,6 +3273,47 @@ int config_parse_blockio_bandwidth( return 0; } +int config_parse_netclass( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + CGroupContext *c = data; + unsigned v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (streq(rvalue, "auto")) { + c->netclass_type = CGROUP_NETCLASS_TYPE_AUTO; + return 0; + } + + r = safe_atou32(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Netclass '%s' invalid. Ignoring.", rvalue); + return 0; + } + + if (v > CGROUP_NETCLASS_FIXED_MAX) + log_syntax(unit, LOG_ERR, filename, line, 0, + "Fixed netclass %" PRIu32 " out of allowed range (0-%d). Applying anyway.", v, (uint32_t) CGROUP_NETCLASS_FIXED_MAX); + + c->netclass_id = v; + c->netclass_type = CGROUP_NETCLASS_TYPE_FIXED; + + return 0; +} + DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode"); int config_parse_job_mode_isolate( @@ -2978,8 +3337,7 @@ int config_parse_job_mode_isolate( r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse boolean, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue); return 0; } @@ -3000,6 +3358,7 @@ int config_parse_runtime_directory( void *userdata) { char***rt = data; + Unit *u = userdata; const char *word, *state; size_t l; int r; @@ -3011,21 +3370,25 @@ int config_parse_runtime_directory( if (isempty(rvalue)) { /* Empty assignment resets the list */ - strv_free(*rt); - *rt = NULL; + *rt = strv_free(*rt); return 0; } FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *n; + _cleanup_free_ char *t = NULL, *n = NULL; - n = strndup(word, l); - if (!n) + t = strndup(word, l); + if (!t) return log_oom(); + r = unit_name_printf(u, t, &n); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); + continue; + } + if (!filename_is_valid(n)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Runtime directory is not valid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue); continue; } @@ -3036,8 +3399,7 @@ int config_parse_runtime_directory( n = NULL; } if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); return 0; } @@ -3084,15 +3446,13 @@ int config_parse_set_status( val = signal_from_string_try_harder(temp); if (val <= 0) { - log_syntax(unit, LOG_ERR, filename, line, -val, - "Failed to parse value, ignoring: %s", word); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word); continue; } set = &status_set->signal; } else { if (val < 0 || val > 255) { - log_syntax(unit, LOG_ERR, filename, line, ERANGE, - "Value %d is outside range 0-255, ignoring", val); + log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val); continue; } set = &status_set->status; @@ -3104,14 +3464,12 @@ int config_parse_set_status( r = set_put(*set, INT_TO_PTR(val)); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Unable to store: %s", word); + log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word); return r; } } if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); return 0; } @@ -3129,8 +3487,8 @@ int config_parse_namespace_path_strv( void *userdata) { char*** sv = data; - const char *word, *state; - size_t l; + const char *prev; + const char *cur; int r; assert(filename); @@ -3140,42 +3498,47 @@ int config_parse_namespace_path_strv( if (isempty(rvalue)) { /* Empty assignment resets the list */ - strv_free(*sv); - *sv = NULL; + *sv = strv_free(*sv); return 0; } - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *n; + prev = cur = rvalue; + for (;;) { + _cleanup_free_ char *word = NULL; int offset; - n = strndup(word, l); - if (!n) + r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES); + if (r == 0) + break; + if (r == -ENOMEM) return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage, ignoring: %s", prev); + return 0; + } - if (!utf8_is_valid(n)) { - log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); + if (!utf8_is_valid(word)) { + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word); + prev = cur; continue; } - offset = n[0] == '-'; - if (!path_is_absolute(n + offset)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Not an absolute path, ignoring: %s", rvalue); + offset = word[0] == '-'; + if (!path_is_absolute(word + offset)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", word); + prev = cur; continue; } - path_kill_slashes(n); + path_kill_slashes(word + offset); - r = strv_push(sv, n); + r = strv_push(sv, word); if (r < 0) return log_oom(); - n = NULL; + prev = cur; + word = NULL; } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); return 0; } @@ -3202,8 +3565,7 @@ int config_parse_no_new_privileges( k = parse_boolean(rvalue); if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, -k, - "Failed to parse boolean value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); return 0; } @@ -3246,8 +3608,7 @@ int config_parse_protect_home( h = protect_home_from_string(rvalue); if (h < 0){ - log_syntax(unit, LOG_ERR, filename, line, -h, - "Failed to parse protect home value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue); return 0; } @@ -3290,8 +3651,7 @@ int config_parse_protect_system( s = protect_system_from_string(rvalue); if (s < 0){ - log_syntax(unit, LOG_ERR, filename, line, -s, - "Failed to parse protect system value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue); return 0; } @@ -3622,7 +3982,7 @@ void unit_dump_config_items(FILE *f) { { config_parse_int, "INTEGER" }, { config_parse_unsigned, "UNSIGNED" }, { config_parse_iec_size, "SIZE" }, - { config_parse_iec_off, "SIZE" }, + { config_parse_iec_uint64, "SIZE" }, { config_parse_si_size, "SIZE" }, { config_parse_bool, "BOOLEAN" }, { config_parse_string, "STRING" }, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 1d128716c4..a451fc164a 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -31,12 +31,14 @@ void unit_dump_config_items(FILE *f); int config_parse_warn_compat(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_deps(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_obsolete_unit_deps(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_string_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_strv_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_path_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_path_strv_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_documentation(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_socket_listen(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_socket_protocol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_socket_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_nice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_oom_score_adjust(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); @@ -56,6 +58,9 @@ int config_parse_exec_capabilities(const char *unit, const char *filename, unsig int config_parse_exec_secure_bits(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bounding_set(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bytes_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_sec_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_usec_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_sysv_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_kill_signal(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_mount_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); @@ -81,14 +86,17 @@ int config_parse_syscall_filter(const char *unit, const char *filename, unsigned int config_parse_syscall_archs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_syscall_errno(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_environ(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_pass_environ(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_slice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_cpu_shares(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_memory_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_tasks_max(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_device_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_device_allow(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_blockio_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_blockio_device_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_blockio_bandwidth(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_netclass(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_job_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_job_mode_isolate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_selinux_context(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); @@ -104,6 +112,8 @@ int config_parse_protect_home(const char* unit, const char *filename, unsigned l int config_parse_protect_system(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bus_name(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_working_directory(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_fdname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* 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 6961c26674..4c8d920389 100644 --- a/src/core/locale-setup.c +++ b/src/core/locale-setup.c @@ -19,16 +19,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> #include <errno.h> +#include <stdlib.h> +#include "env-util.h" +#include "fileio.h" #include "locale-setup.h" +#include "locale-util.h" +#include "string-util.h" +#include "strv.h" #include "util.h" #include "virt.h" -#include "fileio.h" -#include "strv.h" -#include "env-util.h" -#include "locale-util.h" int locale_setup(char ***environment) { char **add; diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c index 4503fc9dcc..4a57793104 100644 --- a/src/core/loopback-setup.c +++ b/src/core/loopback-setup.c @@ -23,9 +23,10 @@ #include <stdlib.h> #include "sd-netlink.h" -#include "netlink-util.h" -#include "missing.h" + #include "loopback-setup.h" +#include "missing.h" +#include "netlink-util.h" static int start_loopback(sd_netlink *rtnl) { _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index 8f682c6d10..145ba2a28d 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -19,24 +19,34 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> -#include <stdio.h> #include <errno.h> -#include <string.h> #include <fcntl.h> +#include <sched.h> +#include <stdio.h> +#include <string.h> #include <sys/mount.h> +#include <unistd.h> -#include "systemd/sd-id128.h" +#include "sd-id128.h" +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "hexdecoct.h" +#include "io-util.h" +#include "log.h" #include "machine-id-setup.h" #include "macro.h" -#include "util.h" #include "mkdir.h" -#include "log.h" -#include "virt.h" -#include "fileio.h" +#include "mount-util.h" #include "path-util.h" #include "process-util.h" +#include "stat-util.h" +#include "string-util.h" +#include "umask-util.h" +#include "util.h" +#include "virt.h" static int shorten_uuid(char destination[34], const char source[36]) { unsigned i, j; diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in index bea6ef1da3..8a0e44b58c 100644 --- a/src/core/macros.systemd.in +++ b/src/core/macros.systemd.in @@ -1,4 +1,4 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- */ +# -*- Mode: rpm-spec; indent-tabs-mode: nil -*- */ # # This file is part of systemd. # @@ -39,7 +39,7 @@ Requires(postun): systemd \ %systemd_post() \ if [ $1 -eq 1 ] ; then \ # Initial installation \ - systemctl preset %{?*} >/dev/null 2>&1 || : \ + systemctl --no-reload preset %{?*} >/dev/null 2>&1 || : \ fi \ %{nil} @@ -48,8 +48,7 @@ fi \ %systemd_preun() \ if [ $1 -eq 0 ] ; then \ # Package removal, not upgrade \ - systemctl --no-reload disable %{?*} > /dev/null 2>&1 || : \ - systemctl stop %{?*} > /dev/null 2>&1 || : \ + systemctl --no-reload disable --now %{?*} > /dev/null 2>&1 || : \ fi \ %{nil} @@ -60,14 +59,11 @@ if [ $1 -eq 0 ] ; then \ fi \ %{nil} -%systemd_postun() \ -systemctl daemon-reload >/dev/null 2>&1 || : \ -%{nil} +%systemd_postun() %{nil} %systemd_user_postun() %{nil} %systemd_postun_with_restart() \ -systemctl daemon-reload >/dev/null 2>&1 || : \ if [ $1 -ge 1 ] ; then \ # Package upgrade, not uninstall \ systemctl try-restart %{?*} >/dev/null 2>&1 || : \ diff --git a/src/core/main.c b/src/core/main.c index be95dc68b2..97f904b031 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -19,63 +19,73 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <errno.h> -#include <string.h> -#include <unistd.h> -#include <sys/stat.h> +#include <fcntl.h> #include <getopt.h> #include <signal.h> -#include <fcntl.h> -#include <sys/prctl.h> +#include <stdio.h> +#include <string.h> #include <sys/mount.h> - -#ifdef HAVE_VALGRIND_VALGRIND_H -#include <valgrind/valgrind.h> -#endif +#include <sys/prctl.h> +#include <sys/reboot.h> +#include <sys/stat.h> +#include <unistd.h> #ifdef HAVE_SECCOMP #include <seccomp.h> #endif +#ifdef HAVE_VALGRIND_VALGRIND_H +#include <valgrind/valgrind.h> +#endif -#include "sd-daemon.h" #include "sd-bus.h" -#include "log.h" -#include "fdset.h" -#include "special.h" -#include "conf-parser.h" -#include "missing.h" -#include "pager.h" +#include "sd-daemon.h" + +#include "alloc-util.h" +#include "architecture.h" #include "build.h" -#include "strv.h" +#include "bus-error.h" +#include "bus-util.h" +#include "capability-util.h" +#include "clock-util.h" +#include "conf-parser.h" +#include "cpu-set-util.h" +#include "dbus-manager.h" #include "def.h" -#include "virt.h" -#include "architecture.h" -#include "watchdog.h" -#include "switch-root.h" -#include "capability.h" -#include "killall.h" #include "env-util.h" -#include "clock-util.h" +#include "fd-util.h" +#include "fdset.h" #include "fileio.h" -#include "bus-error.h" -#include "bus-util.h" -#include "selinux-util.h" #include "formats-util.h" -#include "process-util.h" -#include "terminal-util.h" -#include "signal-util.h" -#include "manager.h" -#include "dbus-manager.h" +#include "fs-util.h" +#include "hostname-setup.h" +#include "ima-setup.h" +#include "killall.h" +#include "kmod-setup.h" #include "load-fragment.h" - -#include "mount-setup.h" +#include "log.h" #include "loopback-setup.h" -#include "hostname-setup.h" #include "machine-id-setup.h" +#include "manager.h" +#include "missing.h" +#include "mount-setup.h" +#include "pager.h" +#include "parse-util.h" +#include "proc-cmdline.h" +#include "process-util.h" +#include "rlimit-util.h" #include "selinux-setup.h" -#include "ima-setup.h" +#include "selinux-util.h" +#include "signal-util.h" #include "smack-setup.h" -#include "kmod-setup.h" +#include "special.h" +#include "stat-util.h" +#include "stdio-util.h" +#include "strv.h" +#include "switch-root.h" +#include "terminal-util.h" +#include "user-util.h" +#include "virt.h" +#include "watchdog.h" static enum { ACTION_RUN, @@ -88,8 +98,9 @@ static enum { static char *arg_default_unit = NULL; static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID; static bool arg_dump_core = true; -static bool arg_crash_shell = false; static int arg_crash_chvt = -1; +static bool arg_crash_shell = false; +static bool arg_crash_reboot = false; static bool arg_confirm_spawn = false; static ShowStatus arg_show_status = _SHOW_STATUS_UNSET; static bool arg_switched_root = false; @@ -114,8 +125,8 @@ static FILE* arg_serialization = NULL; static bool arg_default_cpu_accounting = false; static bool arg_default_blockio_accounting = false; static bool arg_default_memory_accounting = false; - -static void nop_handler(int sig) {} +static bool arg_default_tasks_accounting = true; +static uint64_t arg_default_tasks_max = UINT64_C(512); static void pager_open_if_enabled(void) { @@ -125,49 +136,66 @@ static void pager_open_if_enabled(void) { pager_open(false); } +noreturn static void freeze_or_reboot(void) { + + if (arg_crash_reboot) { + log_notice("Rebooting in 10s..."); + (void) sleep(10); + + log_notice("Rebooting now..."); + (void) reboot(RB_AUTOBOOT); + log_emergency_errno(errno, "Failed to reboot: %m"); + } + + log_emergency("Freezing execution."); + freeze(); +} + noreturn static void crash(int sig) { + struct sigaction sa; + pid_t pid; if (getpid() != 1) /* Pass this on immediately, if this is not PID 1 */ - raise(sig); + (void) raise(sig); else if (!arg_dump_core) log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig)); else { - struct sigaction sa = { - .sa_handler = nop_handler, + sa = (struct sigaction) { + .sa_handler = nop_signal_handler, .sa_flags = SA_NOCLDSTOP|SA_RESTART, }; - pid_t pid; /* We want to wait for the core process, hence let's enable SIGCHLD */ - sigaction(SIGCHLD, &sa, NULL); + (void) sigaction(SIGCHLD, &sa, NULL); pid = raw_clone(SIGCHLD, NULL); if (pid < 0) log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig)); - else if (pid == 0) { - struct rlimit rl = {}; + struct rlimit rl = { + .rlim_cur = RLIM_INFINITY, + .rlim_max = RLIM_INFINITY, + }; /* Enable default signal handler for core dump */ - zero(sa); - sa.sa_handler = SIG_DFL; - sigaction(sig, &sa, NULL); + sa = (struct sigaction) { + .sa_handler = SIG_DFL, + }; + (void) sigaction(sig, &sa, NULL); /* Don't limit the core dump size */ - rl.rlim_cur = RLIM_INFINITY; - rl.rlim_max = RLIM_INFINITY; - setrlimit(RLIMIT_CORE, &rl); + (void) setrlimit(RLIMIT_CORE, &rl); /* Just to be sure... */ (void) chdir("/"); /* Raise the signal again */ pid = raw_getpid(); - kill(pid, sig); /* raise() would kill the parent */ + (void) kill(pid, sig); /* raise() would kill the parent */ assert_not_reached("We shouldn't be here..."); - _exit(1); + _exit(EXIT_FAILURE); } else { siginfo_t status; int r; @@ -189,37 +217,38 @@ noreturn static void crash(int sig) { } } - if (arg_crash_chvt) - chvt(arg_crash_chvt); + if (arg_crash_chvt >= 0) + (void) chvt(arg_crash_chvt); - if (arg_crash_shell) { - struct sigaction sa = { - .sa_handler = SIG_IGN, - .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART, - }; - pid_t pid; + sa = (struct sigaction) { + .sa_handler = SIG_IGN, + .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART, + }; - log_info("Executing crash shell in 10s..."); - sleep(10); + /* Let the kernel reap children for us */ + (void) sigaction(SIGCHLD, &sa, NULL); - /* Let the kernel reap children for us */ - assert_se(sigaction(SIGCHLD, &sa, NULL) == 0); + if (arg_crash_shell) { + log_notice("Executing crash shell in 10s..."); + (void) sleep(10); pid = raw_clone(SIGCHLD, NULL); if (pid < 0) log_emergency_errno(errno, "Failed to fork off crash shell: %m"); else if (pid == 0) { - make_console_stdio(); - execle("/bin/sh", "/bin/sh", NULL, environ); + (void) setsid(); + (void) make_console_stdio(); + (void) execle("/bin/sh", "/bin/sh", NULL, environ); log_emergency_errno(errno, "execle() failed: %m"); - _exit(1); - } else - log_info("Successfully spawned crash shell as PID "PID_FMT".", pid); + _exit(EXIT_FAILURE); + } else { + log_info("Spawned crash shell as PID "PID_FMT".", pid); + (void) wait_for_terminate(pid, NULL); + } } - log_emergency("Freezing execution."); - freeze(); + freeze_or_reboot(); } static void install_crash_handler(void) { @@ -253,37 +282,26 @@ static int console_setup(void) { return 0; } -static int set_default_unit(const char *u) { - char *c; +static int parse_crash_chvt(const char *value) { + int b; - assert(u); + if (safe_atoi(value, &arg_crash_chvt) >= 0) + return 0; - c = strdup(u); - if (!c) - return -ENOMEM; + b = parse_boolean(value); + if (b < 0) + return b; - free(arg_default_unit); - arg_default_unit = c; + if (b > 0) + arg_crash_chvt = 0; /* switch to where kmsg goes */ + else + arg_crash_chvt = -1; /* turn off switching */ return 0; } static int parse_proc_cmdline_item(const char *key, const char *value) { - static const char * const rlmap[] = { - "emergency", SPECIAL_EMERGENCY_TARGET, - "-b", SPECIAL_EMERGENCY_TARGET, - "rescue", SPECIAL_RESCUE_TARGET, - "single", SPECIAL_RESCUE_TARGET, - "-s", SPECIAL_RESCUE_TARGET, - "s", SPECIAL_RESCUE_TARGET, - "S", SPECIAL_RESCUE_TARGET, - "1", SPECIAL_RESCUE_TARGET, - "2", SPECIAL_MULTI_USER_TARGET, - "3", SPECIAL_MULTI_USER_TARGET, - "4", SPECIAL_MULTI_USER_TARGET, - "5", SPECIAL_GRAPHICAL_TARGET, - }; int r; assert(key); @@ -291,12 +309,12 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { if (streq(key, "systemd.unit") && value) { if (!in_initrd()) - return set_default_unit(value); + return free_and_strdup(&arg_default_unit, value); } else if (streq(key, "rd.systemd.unit") && value) { if (in_initrd()) - return set_default_unit(value); + return free_and_strdup(&arg_default_unit, value); } else if (streq(key, "systemd.dump_core") && value) { @@ -306,6 +324,11 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { else arg_dump_core = r; + } else if (streq(key, "systemd.crash_chvt") && value) { + + if (parse_crash_chvt(value) < 0) + log_warning("Failed to parse crash chvt switch %s. Ignoring.", value); + } else if (streq(key, "systemd.crash_shell") && value) { r = parse_boolean(value); @@ -314,12 +337,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { else arg_crash_shell = r; - } else if (streq(key, "systemd.crash_chvt") && value) { + } else if (streq(key, "systemd.crash_reboot") && value) { - if (safe_atoi(value, &r) < 0) - log_warning("Failed to parse crash chvt switch %s. Ignoring.", value); + r = parse_boolean(value); + if (r < 0) + log_warning("Failed to parse crash reboot switch %s. Ignoring.", value); else - arg_crash_chvt = r; + arg_crash_reboot = r; } else if (streq(key, "systemd.confirm_spawn") && value) { @@ -378,12 +402,12 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { log_set_target(LOG_TARGET_CONSOLE); } else if (!in_initrd() && !value) { - unsigned i; + const char *target; /* SysV compatibility */ - for (i = 0; i < ELEMENTSOF(rlmap); i += 2) - if (streq(key, rlmap[i])) - return set_default_unit(rlmap[i+1]); + target = runlevel_to_target(key); + if (target) + return free_and_strdup(&arg_default_unit, target); } return 0; @@ -409,9 +433,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { \ r = func(rvalue); \ if (r < 0) \ - log_syntax(unit, LOG_ERR, filename, line, -r, \ - "Invalid " descr "'%s': %s", \ - rvalue, strerror(-r)); \ + log_syntax(unit, LOG_ERR, filename, line, r, \ + "Invalid " descr "'%s': %m", \ + rvalue); \ \ return 0; \ } @@ -433,48 +457,15 @@ static int config_parse_cpu_affinity2( void *data, void *userdata) { - const char *whole_rvalue = rvalue; _cleanup_cpu_free_ cpu_set_t *c = NULL; - unsigned ncpus = 0; - - assert(filename); - assert(lvalue); - assert(rvalue); - - for (;;) { - _cleanup_free_ char *word = NULL; - unsigned cpu; - int r; - - r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue); - return r; - } - if (r == 0) - break; - - r = safe_atou(word, &cpu); - - if (!c) - if (!(c = cpu_set_malloc(&ncpus))) - return log_oom(); - - if (r < 0 || cpu >= ncpus) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse CPU affinity '%s'", rvalue); - return -EBADMSG; - } + int ncpus; - CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c); - } - if (!isempty(rvalue)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); + ncpus = parse_cpu_set_and_warn(rvalue, &c, unit, filename, line, lvalue); + if (ncpus < 0) + return ncpus; - if (c) - if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0) - log_warning("Failed to set CPU affinity: %m"); + if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0) + log_warning("Failed to set CPU affinity: %m"); return 0; } @@ -501,29 +492,38 @@ static int config_parse_show_status( k = parse_show_status(rvalue, b); if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, -k, - "Failed to parse show status setting, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue); return 0; } return 0; } -static void strv_free_free(char ***l) { - char ***i; +static int config_parse_crash_chvt( + const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { - if (!l) - return; + int r; - for (i = l; *i; i++) - strv_free(*i); + assert(filename); + assert(lvalue); + assert(rvalue); - free(l); -} + r = parse_crash_chvt(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue); + return 0; + } -static void free_join_controllers(void) { - strv_free_free(arg_join_controllers); - arg_join_controllers = NULL; + return 0; } static int config_parse_join_controllers(const char *unit, @@ -544,7 +544,7 @@ static int config_parse_join_controllers(const char *unit, assert(lvalue); assert(rvalue); - free_join_controllers(); + arg_join_controllers = strv_free_free(arg_join_controllers); for (;;) { _cleanup_free_ char *word = NULL; @@ -561,7 +561,7 @@ static int config_parse_join_controllers(const char *unit, l = strv_split(word, ","); if (!l) - log_oom(); + return log_oom(); strv_uniq(l); if (strv_length(l) <= 1) { @@ -595,7 +595,7 @@ static int config_parse_join_controllers(const char *unit, for (a = arg_join_controllers; *a; a++) { if (strv_overlap(*a, l)) { - if (strv_extend_strv(&l, *a) < 0) { + if (strv_extend_strv(&l, *a, false) < 0) { strv_free(l); strv_free_free(t); return log_oom(); @@ -622,8 +622,7 @@ static int config_parse_join_controllers(const char *unit, } } if (!isempty(rvalue)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); return 0; } @@ -636,9 +635,11 @@ static int parse_config_file(void) { { "Manager", "LogColor", config_parse_color, 0, NULL }, { "Manager", "LogLocation", config_parse_location, 0, NULL }, { "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core }, + { "Manager", "CrashChVT", /* legacy */ config_parse_crash_chvt, 0, NULL }, + { "Manager", "CrashChangeVT", config_parse_crash_chvt, 0, NULL }, { "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell }, + { "Manager", "CrashReboot", config_parse_bool, 0, &arg_crash_reboot }, { "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status }, - { "Manager", "CrashChVT", config_parse_int, 0, &arg_crash_chvt }, { "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, NULL }, { "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers }, { "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog }, @@ -657,32 +658,40 @@ static int parse_config_file(void) { { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst }, { "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment }, - { "Manager", "DefaultLimitCPU", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CPU] }, - { "Manager", "DefaultLimitFSIZE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_FSIZE] }, - { "Manager", "DefaultLimitDATA", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_DATA] }, - { "Manager", "DefaultLimitSTACK", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_STACK] }, - { "Manager", "DefaultLimitCORE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CORE] }, - { "Manager", "DefaultLimitRSS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RSS] }, + { "Manager", "DefaultLimitCPU", config_parse_sec_limit, 0, &arg_default_rlimit[RLIMIT_CPU] }, + { "Manager", "DefaultLimitFSIZE", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_FSIZE] }, + { "Manager", "DefaultLimitDATA", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_DATA] }, + { "Manager", "DefaultLimitSTACK", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_STACK] }, + { "Manager", "DefaultLimitCORE", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_CORE] }, + { "Manager", "DefaultLimitRSS", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_RSS] }, { "Manager", "DefaultLimitNOFILE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NOFILE] }, - { "Manager", "DefaultLimitAS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_AS] }, + { "Manager", "DefaultLimitAS", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_AS] }, { "Manager", "DefaultLimitNPROC", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NPROC] }, - { "Manager", "DefaultLimitMEMLOCK", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_MEMLOCK] }, + { "Manager", "DefaultLimitMEMLOCK", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_MEMLOCK] }, { "Manager", "DefaultLimitLOCKS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_LOCKS] }, { "Manager", "DefaultLimitSIGPENDING", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_SIGPENDING] }, - { "Manager", "DefaultLimitMSGQUEUE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_MSGQUEUE] }, + { "Manager", "DefaultLimitMSGQUEUE", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_MSGQUEUE] }, { "Manager", "DefaultLimitNICE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NICE] }, { "Manager", "DefaultLimitRTPRIO", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTPRIO] }, - { "Manager", "DefaultLimitRTTIME", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTTIME] }, + { "Manager", "DefaultLimitRTTIME", config_parse_usec_limit, 0, &arg_default_rlimit[RLIMIT_RTTIME] }, { "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting }, { "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting }, { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting }, + { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting }, + { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_default_tasks_max }, {} }; const char *fn, *conf_dirs_nulstr; - fn = arg_running_as == MANAGER_SYSTEM ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf"; - conf_dirs_nulstr = arg_running_as == MANAGER_SYSTEM ? CONF_DIRS_NULSTR("systemd/system.conf") : CONF_DIRS_NULSTR("systemd/user.conf"); + fn = arg_running_as == MANAGER_SYSTEM ? + PKGSYSCONFDIR "/system.conf" : + PKGSYSCONFDIR "/user.conf"; + + conf_dirs_nulstr = arg_running_as == MANAGER_SYSTEM ? + CONF_PATHS_NULSTR("systemd/system.conf.d") : + CONF_PATHS_NULSTR("systemd/user.conf.d"); + config_parse_many(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, false, NULL); @@ -704,6 +713,8 @@ static void manager_set_defaults(Manager *m) { m->default_cpu_accounting = arg_default_cpu_accounting; m->default_blockio_accounting = arg_default_blockio_accounting; m->default_memory_accounting = arg_default_memory_accounting; + m->default_tasks_accounting = arg_default_tasks_accounting; + m->default_tasks_max = arg_default_tasks_max; manager_set_default_rlimits(m, arg_default_rlimit); manager_environment_add(m, NULL, arg_default_environment); @@ -724,7 +735,9 @@ static int parse_argv(int argc, char *argv[]) { ARG_VERSION, ARG_DUMP_CONFIGURATION_ITEMS, ARG_DUMP_CORE, + ARG_CRASH_CHVT, ARG_CRASH_SHELL, + ARG_CRASH_REBOOT, ARG_CONFIRM_SPAWN, ARG_SHOW_STATUS, ARG_DESERIALIZE, @@ -747,7 +760,9 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS }, { "dump-core", optional_argument, NULL, ARG_DUMP_CORE }, + { "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT }, { "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL }, + { "crash-reboot", optional_argument, NULL, ARG_CRASH_REBOOT }, { "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN }, { "show-status", optional_argument, NULL, ARG_SHOW_STATUS }, { "deserialize", required_argument, NULL, ARG_DESERIALIZE }, @@ -832,7 +847,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_UNIT: - r = set_default_unit(optarg); + r = free_and_strdup(&arg_default_unit, optarg); if (r < 0) return log_error_errno(r, "Failed to set default unit %s: %m", optarg); @@ -865,21 +880,42 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_DUMP_CORE: - r = optarg ? parse_boolean(optarg) : 1; - if (r < 0) { - log_error("Failed to parse dump core boolean %s.", optarg); - return r; + if (!optarg) + arg_dump_core = true; + else { + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse dump core boolean: %s", optarg); + arg_dump_core = r; } - arg_dump_core = r; + break; + + case ARG_CRASH_CHVT: + r = parse_crash_chvt(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse crash virtual terminal index: %s", optarg); break; case ARG_CRASH_SHELL: - r = optarg ? parse_boolean(optarg) : 1; - if (r < 0) { - log_error("Failed to parse crash shell boolean %s.", optarg); - return r; + if (!optarg) + arg_crash_shell = true; + else { + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg); + arg_crash_shell = r; + } + break; + + case ARG_CRASH_REBOOT: + if (!optarg) + arg_crash_reboot = true; + else { + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg); + arg_crash_reboot = r; } - arg_crash_shell = r; break; case ARG_CONFIRM_SPAWN: @@ -909,17 +945,16 @@ static int parse_argv(int argc, char *argv[]) { r = safe_atoi(optarg, &fd); if (r < 0 || fd < 0) { log_error("Failed to parse deserialize option %s.", optarg); - return r < 0 ? r : -EINVAL; + return -EINVAL; } - fd_cloexec(fd, true); + (void) fd_cloexec(fd, true); f = fdopen(fd, "r"); if (!f) return log_error_errno(errno, "Failed to open serialization fd: %m"); safe_fclose(arg_serialization); - arg_serialization = f; break; @@ -979,14 +1014,16 @@ static int help(void) { " --unit=UNIT Set default unit\n" " --system Run a system instance, even if PID != 1\n" " --user Run a user instance\n" - " --dump-core[=0|1] Dump core on crash\n" - " --crash-shell[=0|1] Run shell on crash\n" - " --confirm-spawn[=0|1] Ask for confirmation when spawning processes\n" - " --show-status[=0|1] Show status updates on the console during bootup\n" + " --dump-core[=BOOL] Dump core on crash\n" + " --crash-vt=NR Change to specified VT on crash\n" + " --crash-reboot[=BOOL] Reboot on crash\n" + " --crash-shell[=BOOL] Run shell on crash\n" + " --confirm-spawn[=BOOL] Ask for confirmation when spawning processes\n" + " --show-status[=BOOL] Show status updates on the console during bootup\n" " --log-target=TARGET Set log target (console, journal, kmsg, journal-or-kmsg, null)\n" " --log-level=LEVEL Set log level (debug, info, notice, warning, err, crit, alert, emerg)\n" - " --log-color[=0|1] Highlight important log messages\n" - " --log-location[=0|1] Include code location in log messages\n" + " --log-color[=BOOL] Highlight important log messages\n" + " --log-location[=BOOL] Include code location in log messages\n" " --default-standard-output= Set default standard output for services\n" " --default-standard-error= Set default standard error output for services\n", program_invocation_short_name); @@ -994,16 +1031,9 @@ static int help(void) { return 0; } -static int version(void) { - puts(PACKAGE_STRING); - puts(SYSTEMD_FEATURES); - - return 0; -} - static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching_root) { - FILE *f = NULL; - FDSet *fds = NULL; + _cleanup_fdset_free_ FDSet *fds = NULL; + _cleanup_fclose_ FILE *f = NULL; int r; assert(m); @@ -1011,56 +1041,39 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching assert(_fds); r = manager_open_serialization(m, &f); - if (r < 0) { - log_error_errno(r, "Failed to create serialization file: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to create serialization file: %m"); /* Make sure nothing is really destructed when we shut down */ m->n_reloading ++; bus_manager_send_reloading(m, true); fds = fdset_new(); - if (!fds) { - r = -ENOMEM; - log_error_errno(r, "Failed to allocate fd set: %m"); - goto fail; - } + if (!fds) + return log_oom(); r = manager_serialize(m, f, fds, switching_root); - if (r < 0) { - log_error_errno(r, "Failed to serialize state: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to serialize state: %m"); - if (fseeko(f, 0, SEEK_SET) < 0) { - log_error_errno(errno, "Failed to rewind serialization fd: %m"); - goto fail; - } + if (fseeko(f, 0, SEEK_SET) == (off_t) -1) + return log_error_errno(errno, "Failed to rewind serialization fd: %m"); r = fd_cloexec(fileno(f), false); - if (r < 0) { - log_error_errno(r, "Failed to disable O_CLOEXEC for serialization: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization: %m"); r = fdset_cloexec(fds, false); - if (r < 0) { - log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m"); *_f = f; *_fds = fds; - return 0; - -fail: - fdset_free(fds); - - safe_fclose(f); + f = NULL; + fds = NULL; - return r; + return 0; } static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { @@ -1095,33 +1108,6 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { return 0; } -static void test_mtab(void) { - - static const char ok[] = - "/proc/self/mounts\0" - "/proc/mounts\0" - "../proc/self/mounts\0" - "../proc/mounts\0"; - - _cleanup_free_ char *p = NULL; - int r; - - /* Check that /etc/mtab is a symlink to the right place or - * non-existing. But certainly not a file, or a symlink to - * some weird place... */ - - r = readlink_malloc("/etc/mtab", &p); - if (r == -ENOENT) - return; - if (r >= 0 && nulstr_contains(ok, p)) - return; - - log_error("/etc/mtab is not a symlink or not pointing to /proc/self/mounts. " - "This is not supported anymore. " - "Please make sure to replace this file by a symlink to avoid incorrect or misleading mount(8) output."); - freeze(); -} - static void test_usr(void) { /* Check that /usr is not a separate fs */ @@ -1145,15 +1131,19 @@ static int initialize_join_controllers(void) { return -ENOMEM; arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL); - arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL); - arg_join_controllers[2] = NULL; + if (!arg_join_controllers[0]) + goto oom; - if (!arg_join_controllers[0] || !arg_join_controllers[1]) { - free_join_controllers(); - return -ENOMEM; - } + arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL); + if (!arg_join_controllers[1]) + goto oom; + arg_join_controllers[2] = NULL; return 0; + +oom: + arg_join_controllers = strv_free_free(arg_join_controllers); + return -ENOMEM; } static int enforce_syscall_archs(Set *archs) { @@ -1220,12 +1210,50 @@ static int status_welcome(void) { static int write_container_id(void) { const char *c; + int r; c = getenv("container"); if (isempty(c)) return 0; - return write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE); + r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE); + if (r < 0) + return log_warning_errno(r, "Failed to write /run/systemd/container, ignoring: %m"); + + return 1; +} + +static int bump_unix_max_dgram_qlen(void) { + _cleanup_free_ char *qlen = NULL; + unsigned long v; + int r; + + /* Let's bump the net.unix.max_dgram_qlen sysctl. The kernel + * default of 16 is simply too low. We set the value really + * really early during boot, so that it is actually applied to + * all our sockets, including the $NOTIFY_SOCKET one. */ + + r = read_one_line_file("/proc/sys/net/unix/max_dgram_qlen", &qlen); + if (r < 0) + return log_warning_errno(r, "Failed to read AF_UNIX datagram queue length, ignoring: %m"); + + r = safe_atolu(qlen, &v); + if (r < 0) + return log_warning_errno(r, "Failed to parse AF_UNIX datagram queue length, ignoring: %m"); + + if (v >= DEFAULT_UNIX_MAX_DGRAM_QLEN) + return 0; + + qlen = mfree(qlen); + if (asprintf(&qlen, "%lu\n", DEFAULT_UNIX_MAX_DGRAM_QLEN) < 0) + return log_oom(); + + r = write_string_file("/proc/sys/net/unix/max_dgram_qlen", qlen, 0); + if (r < 0) + return log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to bump AF_UNIX datagram queue length, ignoring: %m"); + + return 1; } int main(int argc, char *argv[]) { @@ -1397,7 +1425,7 @@ int main(int argc, char *argv[]) { } /* Initialize default unit */ - r = set_default_unit(SPECIAL_DEFAULT_TARGET); + r = free_and_strdup(&arg_default_unit, SPECIAL_DEFAULT_TARGET); if (r < 0) { log_emergency_errno(r, "Failed to set default unit %s: %m", SPECIAL_DEFAULT_TARGET); error_message = "Failed to set default unit"; @@ -1591,8 +1619,8 @@ int main(int argc, char *argv[]) { hostname_setup(); machine_id_setup(NULL); loopback_setup(); + bump_unix_max_dgram_qlen(); - test_mtab(); test_usr(); } @@ -1674,8 +1702,7 @@ int main(int argc, char *argv[]) { /* This will close all file descriptors that were opened, but * not claimed by any unit. */ - fdset_free(fds); - fds = NULL; + fds = fdset_free(fds); arg_serialization = safe_fclose(arg_serialization); @@ -1720,11 +1747,13 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); } - r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, false, &error, &default_unit_job); + r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, &error, &default_unit_job); if (r == -EPERM) { log_debug("Default target could not be isolated, starting instead: %s", bus_error_message(&error, r)); - r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job); + sd_bus_error_free(&error); + + r = manager_add_job(m, JOB_START, target, JOB_REPLACE, &error, &default_unit_job); if (r < 0) { log_emergency("Failed to start default target: %s", bus_error_message(&error, r)); error_message = "Failed to start default target"; @@ -1761,11 +1790,6 @@ int main(int argc, char *argv[]) { switch (m->exit_code) { - case MANAGER_EXIT: - retval = EXIT_SUCCESS; - log_debug("Exit."); - goto finish; - case MANAGER_RELOAD: log_info("Reloading."); @@ -1807,11 +1831,21 @@ int main(int argc, char *argv[]) { log_notice("Switching root."); goto finish; + case MANAGER_EXIT: + retval = m->return_value; + + if (m->running_as == MANAGER_USER) { + log_debug("Exit."); + goto finish; + } + + /* fallthrough */ case MANAGER_REBOOT: case MANAGER_POWEROFF: case MANAGER_HALT: case MANAGER_KEXEC: { static const char * const table[_MANAGER_EXIT_CODE_MAX] = { + [MANAGER_EXIT] = "exit", [MANAGER_REBOOT] = "reboot", [MANAGER_POWEROFF] = "poweroff", [MANAGER_HALT] = "halt", @@ -1835,19 +1869,16 @@ finish: if (m) arg_shutdown_watchdog = m->shutdown_watchdog; + m = manager_free(m); for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++) arg_default_rlimit[j] = mfree(arg_default_rlimit[j]); arg_default_unit = mfree(arg_default_unit); - - free_join_controllers(); - + arg_join_controllers = strv_free_free(arg_join_controllers); arg_default_environment = strv_free(arg_default_environment); - - set_free(arg_syscall_archs); - arg_syscall_archs = NULL; + arg_syscall_archs = set_free(arg_syscall_archs); mac_selinux_finish(); @@ -1864,7 +1895,7 @@ finish: * that the new systemd can pass the kernel default to * its child processes */ if (saved_rlimit_nofile.rlim_cur > 0) - setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile); + (void) setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile); if (switch_root_dir) { /* Kill all remaining processes from the @@ -1906,10 +1937,10 @@ finish: /* do not pass along the environment we inherit from the kernel or initrd */ if (switch_root_dir) - clearenv(); + (void) clearenv(); assert(i <= args_size); - execv(args[0], (char* const*) args); + (void) execv(args[0], (char* const*) args); } /* Try the fallback, if there is any, without any @@ -1919,14 +1950,10 @@ finish: * but let's hope that doesn't matter.) */ arg_serialization = safe_fclose(arg_serialization); - - if (fds) { - fdset_free(fds); - fds = NULL; - } + fds = fdset_free(fds); /* Reopen the console */ - make_console_stdio(); + (void) make_console_stdio(); for (j = 1, i = 1; j < (unsigned) argc; j++) args[i++] = argv[j]; @@ -1940,30 +1967,26 @@ finish: if (switch_root_init) { args[0] = switch_root_init; - execv(args[0], (char* const*) args); + (void) execv(args[0], (char* const*) args); log_warning_errno(errno, "Failed to execute configured init, trying fallback: %m"); } args[0] = "/sbin/init"; - execv(args[0], (char* const*) args); + (void) execv(args[0], (char* const*) args); if (errno == ENOENT) { log_warning("No /sbin/init, trying fallback"); args[0] = "/bin/sh"; args[1] = NULL; - execv(args[0], (char* const*) args); + (void) execv(args[0], (char* const*) args); log_error_errno(errno, "Failed to execute /bin/sh, giving up: %m"); } else log_warning_errno(errno, "Failed to execute /sbin/init, giving up: %m"); } arg_serialization = safe_fclose(arg_serialization); - - if (fds) { - fdset_free(fds); - fds = NULL; - } + fds = fdset_free(fds); #ifdef HAVE_VALGRIND_VALGRIND_H /* If we are PID 1 and running under valgrind, then let's exit @@ -1976,7 +1999,8 @@ finish: if (shutdown_verb) { char log_level[DECIMAL_STR_MAX(int) + 1]; - const char* command_line[9] = { + char exit_code[DECIMAL_STR_MAX(uint8_t) + 1]; + const char* command_line[11] = { SYSTEMD_SHUTDOWN_BINARY_PATH, shutdown_verb, "--log-level", log_level, @@ -1991,6 +2015,7 @@ finish: xsprintf(log_level, "%d", log_get_max_level()); switch (log_get_target()) { + case LOG_TARGET_KMSG: case LOG_TARGET_JOURNAL_OR_KMSG: case LOG_TARGET_SYSLOG_OR_KMSG: @@ -2013,6 +2038,12 @@ finish: if (log_get_show_location()) command_line[pos++] = "--log-location"; + if (streq(shutdown_verb, "exit")) { + command_line[pos++] = "--exit-code"; + command_line[pos++] = exit_code; + xsprintf(exit_code, "%d", retval); + } + assert(pos < ELEMENTSOF(command_line)); if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) { @@ -2026,7 +2057,7 @@ finish: /* Tell the binary how often to ping, ignore failure */ if (asprintf(&e, "WATCHDOG_USEC="USEC_FMT, arg_shutdown_watchdog) > 0) - strv_push(&env_block, e); + (void) strv_push(&env_block, e); } else watchdog_close(true); @@ -2044,9 +2075,9 @@ finish: if (getpid() == 1) { if (error_message) manager_status_printf(NULL, STATUS_TYPE_EMERGENCY, - ANSI_HIGHLIGHT_RED_ON "!!!!!!" ANSI_HIGHLIGHT_OFF, + ANSI_HIGHLIGHT_RED "!!!!!!" ANSI_NORMAL, "%s, freezing.", error_message); - freeze(); + freeze_or_reboot(); } return retval; diff --git a/src/core/manager.c b/src/core/manager.c index d918007bb8..edff6758c5 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -19,19 +19,19 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <dirent.h> #include <errno.h> -#include <string.h> +#include <fcntl.h> +#include <linux/kd.h> #include <signal.h> -#include <sys/wait.h> -#include <unistd.h> -#include <sys/inotify.h> +#include <string.h> #include <sys/epoll.h> -#include <sys/reboot.h> +#include <sys/inotify.h> #include <sys/ioctl.h> -#include <linux/kd.h> -#include <fcntl.h> -#include <dirent.h> +#include <sys/reboot.h> #include <sys/timerfd.h> +#include <sys/wait.h> +#include <unistd.h> #ifdef HAVE_AUDIT #include <libaudit.h> @@ -40,40 +40,53 @@ #include "sd-daemon.h" #include "sd-messages.h" -#include "hashmap.h" -#include "macro.h" -#include "strv.h" -#include "log.h" -#include "util.h" -#include "mkdir.h" -#include "ratelimit.h" -#include "locale-setup.h" -#include "unit-name.h" -#include "missing.h" -#include "rm-rf.h" -#include "path-lookup.h" -#include "special.h" -#include "exit-status.h" -#include "virt.h" -#include "watchdog.h" -#include "path-util.h" +#include "alloc-util.h" #include "audit-fd.h" #include "boot-timestamps.h" -#include "env-util.h" #include "bus-common-errors.h" #include "bus-error.h" -#include "bus-util.h" #include "bus-kernel.h" -#include "time-util.h" -#include "process-util.h" -#include "terminal-util.h" -#include "signal-util.h" -#include "dbus.h" -#include "dbus-unit.h" +#include "bus-util.h" #include "dbus-job.h" #include "dbus-manager.h" +#include "dbus-unit.h" +#include "dbus.h" +#include "env-util.h" +#include "escape.h" +#include "exit-status.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "hashmap.h" +#include "io-util.h" +#include "locale-setup.h" +#include "log.h" +#include "macro.h" #include "manager.h" +#include "missing.h" +#include "mkdir.h" +#include "parse-util.h" +#include "path-lookup.h" +#include "path-util.h" +#include "process-util.h" +#include "ratelimit.h" +#include "rm-rf.h" +#include "signal-util.h" +#include "special.h" +#include "stat-util.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "terminal-util.h" +#include "time-util.h" #include "transaction.h" +#include "umask-util.h" +#include "unit-name.h" +#include "util.h" +#include "virt.h" +#include "watchdog.h" + +#define NOTIFY_RCVBUF_SIZE (8*1024*1024) /* Initial delay and the interval for printing status messages about running jobs */ #define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC) @@ -111,7 +124,7 @@ static void manager_watch_jobs_in_progress(Manager *m) { (void) sd_event_source_set_description(m->jobs_in_progress_event_source, "manager-jobs-in-progress"); } -#define CYLON_BUFFER_EXTRA (2*(sizeof(ANSI_RED_ON)-1) + sizeof(ANSI_HIGHLIGHT_RED_ON)-1 + 2*(sizeof(ANSI_HIGHLIGHT_OFF)-1)) +#define CYLON_BUFFER_EXTRA (2*(sizeof(ANSI_RED)-1) + sizeof(ANSI_HIGHLIGHT_RED)-1 + 2*(sizeof(ANSI_NORMAL)-1)) static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned pos) { char *p = buffer; @@ -122,23 +135,23 @@ static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned po if (pos > 1) { if (pos > 2) p = mempset(p, ' ', pos-2); - p = stpcpy(p, ANSI_RED_ON); + p = stpcpy(p, ANSI_RED); *p++ = '*'; } if (pos > 0 && pos <= width) { - p = stpcpy(p, ANSI_HIGHLIGHT_RED_ON); + p = stpcpy(p, ANSI_HIGHLIGHT_RED); *p++ = '*'; } - p = stpcpy(p, ANSI_HIGHLIGHT_OFF); + p = stpcpy(p, ANSI_NORMAL); if (pos < width) { - p = stpcpy(p, ANSI_RED_ON); + p = stpcpy(p, ANSI_RED); *p++ = '*'; if (pos < width-1) p = mempset(p, ' ', width-1-pos); - strcpy(p, ANSI_HIGHLIGHT_OFF); + strcpy(p, ANSI_NORMAL); } } @@ -317,6 +330,8 @@ static int manager_watch_idle_pipe(Manager *m) { static void manager_close_idle_pipe(Manager *m) { assert(m); + m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source); + safe_close_pair(m->idle_pipe); safe_close_pair(m->idle_pipe + 2); } @@ -471,7 +486,7 @@ static int manager_setup_signals(Manager *m) { * later than notify_fd processing, so that the notify * processing can still figure out to which process/service a * message belongs, before we reap the process. */ - r = sd_event_source_set_priority(m->signal_event_source, -5); + r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-5); if (r < 0) return r; @@ -493,6 +508,7 @@ static void manager_clean_environment(Manager *m) { "MANAGERPID", "LISTEN_PID", "LISTEN_FDS", + "LISTEN_FDNAMES", "WATCHDOG_PID", "WATCHDOG_USEC", NULL); @@ -561,6 +577,8 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { m->running_as = running_as; m->exit_code = _MANAGER_EXIT_CODE_INVALID; m->default_timer_accuracy_usec = USEC_PER_MINUTE; + m->default_tasks_accounting = true; + m->default_tasks_max = UINT64_C(512); /* Prepare log fields we can use for structured logging */ m->unit_log_field = unit_log_fields[running_as]; @@ -569,14 +587,16 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1; m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = - m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd = - m->cgroup_inotify_fd = -1; + m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = -1; + m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ m->ask_password_inotify_fd = -1; m->have_ask_password = -EINVAL; /* we don't know */ m->first_boot = -1; + m->cgroup_netclass_registry_last = CGROUP_NETCLASS_FIXED_MAX; + m->test_run = test_run; /* Reboot immediately if the user hits C-A-D more often than 7x per 2s */ @@ -602,14 +622,6 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { if (r < 0) goto fail; - r = set_ensure_allocated(&m->startup_units, NULL); - if (r < 0) - goto fail; - - r = set_ensure_allocated(&m->failed_units, NULL); - if (r < 0) - goto fail; - r = sd_event_default(&m->event); if (r < 0) goto fail; @@ -681,6 +693,8 @@ static int manager_setup_notify(Manager *m) { if (fd < 0) return log_error_errno(errno, "Failed to allocate notification socket: %m"); + fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE); + if (m->running_as == MANAGER_SYSTEM) m->notify_socket = strdup("/run/systemd/notify"); else { @@ -722,7 +736,7 @@ static int manager_setup_notify(Manager *m) { /* Process signals a bit earlier than SIGCHLD, so that we can * still identify to which service an exit message belongs */ - r = sd_event_source_set_priority(m->notify_event_source, -7); + r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7); if (r < 0) return log_error_errno(r, "Failed to set priority of notify event source: %m"); @@ -944,7 +958,6 @@ Manager* manager_free(Manager *m) { sd_event_source_unref(m->notify_event_source); sd_event_source_unref(m->time_change_event_source); sd_event_source_unref(m->jobs_in_progress_event_source); - sd_event_source_unref(m->idle_pipe_event_source); sd_event_source_unref(m->run_queue_event_source); safe_close(m->signal_fd); @@ -967,6 +980,8 @@ Manager* manager_free(Manager *m) { hashmap_free(m->cgroup_unit); set_free_free(m->unit_path_cache); + hashmap_free(m->cgroup_netclass_registry); + free(m->switch_root); free(m->switch_root_init); @@ -980,8 +995,7 @@ Manager* manager_free(Manager *m) { return NULL; } -int manager_enumerate(Manager *m) { - int r = 0; +void manager_enumerate(Manager *m) { UnitType c; assert(m); @@ -989,8 +1003,6 @@ int manager_enumerate(Manager *m) { /* Let's ask every type to load all units from disk/kernel * that it might know */ for (c = 0; c < _UNIT_TYPE_MAX; c++) { - int q; - if (!unit_type_supported(c)) { log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); continue; @@ -999,13 +1011,10 @@ int manager_enumerate(Manager *m) { if (!unit_vtable[c]->enumerate) continue; - q = unit_vtable[c]->enumerate(m); - if (q < 0) - r = q; + unit_vtable[c]->enumerate(m); } manager_dispatch_load_queue(m); - return r; } static void manager_coldplug(Manager *m) { @@ -1074,8 +1083,7 @@ static void manager_build_unit_path_cache(Manager *m) { goto fail; } - closedir(d); - d = NULL; + d = safe_closedir(d); } return; @@ -1088,10 +1096,9 @@ fail: } -static int manager_distribute_fds(Manager *m, FDSet *fds) { - Unit *u; +static void manager_distribute_fds(Manager *m, FDSet *fds) { Iterator i; - int r; + Unit *u; assert(m); @@ -1100,14 +1107,11 @@ static int manager_distribute_fds(Manager *m, FDSet *fds) { if (fdset_size(fds) <= 0) break; - if (UNIT_VTABLE(u)->distribute_fds) { - r = UNIT_VTABLE(u)->distribute_fds(u, fds); - if (r < 0) - return r; - } - } + if (!UNIT_VTABLE(u)->distribute_fds) + continue; - return 0; + UNIT_VTABLE(u)->distribute_fds(u, fds); + } } int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { @@ -1140,7 +1144,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { /* First, enumerate what we can from all config files */ dual_timestamp_get(&m->units_load_start_timestamp); - r = manager_enumerate(m); + manager_enumerate(m); dual_timestamp_get(&m->units_load_finish_timestamp); /* Second, deserialize if there is something to deserialize */ @@ -1151,11 +1155,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { * useful to allow container managers to pass some file * descriptors to us pre-initialized. This enables * socket-based activation of entire containers. */ - if (fdset_size(fds) > 0) { - q = manager_distribute_fds(m, fds); - if (q < 0 && r == 0) - r = q; - } + manager_distribute_fds(m, fds); /* We might have deserialized the notify fd, but if we didn't * then let's create the bus now */ @@ -1185,7 +1185,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { return r; } -int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool override, sd_bus_error *e, Job **_ret) { +int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret) { int r; Transaction *tr; @@ -1208,7 +1208,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove if (!tr) return -ENOMEM; - r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, override, false, + r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, false, mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS, mode == JOB_IGNORE_DEPENDENCIES, e); if (r < 0) @@ -1240,7 +1240,7 @@ tr_abort: return r; } -int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool override, sd_bus_error *e, Job **_ret) { +int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **ret) { Unit *unit; int r; @@ -1253,7 +1253,23 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode if (r < 0) return r; - return manager_add_job(m, type, unit, mode, override, e, _ret); + return manager_add_job(m, type, unit, mode, e, ret); +} + +int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(m); + assert(type < _JOB_TYPE_MAX); + assert(name); + assert(mode < _JOB_MODE_MAX); + + r = manager_add_job_by_name(m, type, name, mode, &error, ret); + if (r < 0) + return log_warning_errno(r, "Failed to enqueue %s job for %s: %s", job_mode_to_string(mode), name, bus_error_message(&error, r)); + + return r; } Job *manager_get_job(Manager *m, uint32_t id) { @@ -1480,7 +1496,7 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) { return n; } -static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *buf, size_t n, FDSet *fds) { +static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, size_t n, FDSet *fds) { _cleanup_strv_free_ char **tags = NULL; assert(m); @@ -1501,9 +1517,33 @@ static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char * } static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + _cleanup_fdset_free_ FDSet *fds = NULL; Manager *m = userdata; + + char buf[NOTIFY_BUFFER_MAX+1]; + struct iovec iovec = { + .iov_base = buf, + .iov_len = sizeof(buf)-1, + }; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)]; + } control = {}; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + + struct cmsghdr *cmsg; + struct ucred *ucred = NULL; + bool found = false; + Unit *u1, *u2, *u3; + int r, *fd_array = NULL; + unsigned n_fds = 0; ssize_t n; - int r; assert(m); assert(m->notify_fd == fd); @@ -1513,106 +1553,80 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t return 0; } - for (;;) { - _cleanup_fdset_free_ FDSet *fds = NULL; - char buf[NOTIFY_BUFFER_MAX+1]; - struct iovec iovec = { - .iov_base = buf, - .iov_len = sizeof(buf)-1, - }; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + - CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)]; - } control = {}; - struct msghdr msghdr = { - .msg_iov = &iovec, - .msg_iovlen = 1, - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - struct cmsghdr *cmsg; - struct ucred *ucred = NULL; - bool found = false; - Unit *u1, *u2, *u3; - int *fd_array = NULL; - unsigned n_fds = 0; - - n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); - if (n < 0) { - if (errno == EAGAIN || errno == EINTR) - break; + n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); + if (n < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; - return -errno; - } + return -errno; + } - CMSG_FOREACH(cmsg, &msghdr) { - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + CMSG_FOREACH(cmsg, &msghdr) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - fd_array = (int*) CMSG_DATA(cmsg); - n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + fd_array = (int*) CMSG_DATA(cmsg); + n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); - } else if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_CREDENTIALS && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) { + } else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) { - ucred = (struct ucred*) CMSG_DATA(cmsg); - } + ucred = (struct ucred*) CMSG_DATA(cmsg); } + } - if (n_fds > 0) { - assert(fd_array); + if (n_fds > 0) { + assert(fd_array); - r = fdset_new_array(&fds, fd_array, n_fds); - if (r < 0) { - close_many(fd_array, n_fds); - return log_oom(); - } + r = fdset_new_array(&fds, fd_array, n_fds); + if (r < 0) { + close_many(fd_array, n_fds); + return log_oom(); } + } - if (!ucred || ucred->pid <= 0) { - log_warning("Received notify message without valid credentials. Ignoring."); - continue; - } + if (!ucred || ucred->pid <= 0) { + log_warning("Received notify message without valid credentials. Ignoring."); + return 0; + } - if ((size_t) n >= sizeof(buf)) { - log_warning("Received notify message exceeded maximum size. Ignoring."); - continue; - } + if ((size_t) n >= sizeof(buf)) { + log_warning("Received notify message exceeded maximum size. Ignoring."); + return 0; + } - buf[n] = 0; + buf[n] = 0; - /* Notify every unit that might be interested, but try - * to avoid notifying the same one multiple times. */ - u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid); - if (u1) { - manager_invoke_notify_message(m, u1, ucred->pid, buf, n, fds); - found = true; - } + /* Notify every unit that might be interested, but try + * to avoid notifying the same one multiple times. */ + u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid); + if (u1) { + manager_invoke_notify_message(m, u1, ucred->pid, buf, n, fds); + found = true; + } - u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid)); - if (u2 && u2 != u1) { - manager_invoke_notify_message(m, u2, ucred->pid, buf, n, fds); - found = true; - } + u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid)); + if (u2 && u2 != u1) { + manager_invoke_notify_message(m, u2, ucred->pid, buf, n, fds); + found = true; + } - u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid)); - if (u3 && u3 != u2 && u3 != u1) { - manager_invoke_notify_message(m, u3, ucred->pid, buf, n, fds); - found = true; - } + u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid)); + if (u3 && u3 != u2 && u3 != u1) { + manager_invoke_notify_message(m, u3, ucred->pid, buf, n, fds); + found = true; + } - if (!found) - log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid); + if (!found) + log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid); - if (fdset_size(fds) > 0) - log_warning("Got auxiliary fds with notification message, closing all."); - } + if (fdset_size(fds) > 0) + log_warning("Got auxiliary fds with notification message, closing all."); return 0; } -static void invoke_sigchld_event(Manager *m, Unit *u, siginfo_t *si) { +static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) { assert(m); assert(u); assert(si); @@ -1691,7 +1705,7 @@ static int manager_start_target(Manager *m, const char *name, JobMode mode) { log_debug("Activating special unit %s", name); - r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL); + r = manager_add_job_by_name(m, JOB_START, name, mode, &error, NULL); if (r < 0) log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r)); @@ -1962,7 +1976,6 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32 m->no_console_output = m->n_on_console > 0; - m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source); manager_close_idle_pipe(m); return 0; @@ -1995,8 +2008,7 @@ int manager_loop(Manager *m) { m->exit_code = MANAGER_OK; /* Release the path cache */ - set_free_free(m->unit_path_cache); - m->unit_path_cache = NULL; + m->unit_path_cache = set_free_free(m->unit_path_cache); manager_check_finished(m); @@ -2016,7 +2028,6 @@ int manager_loop(Manager *m) { /* Yay, something is going seriously wrong, pause a little */ log_warning("Looping too fast. Throttling execution a little."); sleep(1); - continue; } if (manager_dispatch_load_queue(m) > 0) @@ -2106,6 +2117,9 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { const char *msg; int audit_fd, r; + if (m->running_as != MANAGER_SYSTEM) + return; + audit_fd = get_audit_fd(); if (audit_fd < 0) return; @@ -2115,9 +2129,6 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { if (m->n_reloading > 0) return; - if (m->running_as != MANAGER_SYSTEM) - return; - if (u->type != UNIT_SERVICE) return; @@ -2547,9 +2558,7 @@ int manager_reload(Manager *m) { manager_build_unit_path_cache(m); /* First, enumerate what we can from all config files */ - q = manager_enumerate(m); - if (q < 0 && r >= 0) - r = q; + manager_enumerate(m); /* Second, deserialize our stored data */ q = manager_deserialize(m, f, fds); @@ -2675,9 +2684,6 @@ static void manager_notify_finished(Manager *m) { } void manager_check_finished(Manager *m) { - Unit *u = NULL; - Iterator i; - assert(m); if (m->n_reloading > 0) @@ -2690,11 +2696,9 @@ void manager_check_finished(Manager *m) { return; if (hashmap_size(m->jobs) > 0) { - if (m->jobs_in_progress_event_source) /* Ignore any failure, this is only for feedback */ - (void) sd_event_source_set_time(m->jobs_in_progress_event_source, - now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC); + (void) sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC); return; } @@ -2702,7 +2706,6 @@ void manager_check_finished(Manager *m) { manager_flip_auto_status(m, false); /* Notify Type=idle units that we are done now */ - m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source); manager_close_idle_pipe(m); /* Turn off confirm spawn now */ @@ -2721,9 +2724,7 @@ void manager_check_finished(Manager *m) { manager_notify_finished(m); - SET_FOREACH(u, m->startup_units, i) - if (u->cgroup_path) - cgroup_context_apply(unit_get_cgroup_context(u), unit_get_own_mask(u), u->cgroup_path, manager_state(m)); + manager_invalidate_startup_units(m); } static int create_generator_dir(Manager *m, char **generator, const char *name) { @@ -2774,8 +2775,7 @@ static int create_generator_dir(Manager *m, char **generator, const char *name) return log_oom(); if (!mkdtemp(p)) { - log_error_errno(errno, "Failed to create generator directory %s: %m", - p); + log_error_errno(errno, "Failed to create generator directory %s: %m", p); free(p); return -errno; } @@ -2964,9 +2964,9 @@ void manager_set_show_status(Manager *m, ShowStatus mode) { m->show_status = mode; if (mode > 0) - touch("/run/systemd/show-status"); + (void) touch("/run/systemd/show-status"); else - unlink("/run/systemd/show-status"); + (void) unlink("/run/systemd/show-status"); } static bool manager_get_show_status(Manager *m, StatusType type) { @@ -3025,30 +3025,6 @@ void manager_status_printf(Manager *m, StatusType type, const char *status, cons va_end(ap); } -int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found) { - _cleanup_free_ char *p = NULL; - Unit *found; - int r; - - assert(m); - assert(path); - assert(suffix); - assert(_found); - - r = unit_name_from_path(path, suffix, &p); - if (r < 0) - return r; - - found = manager_get_unit(m, p); - if (!found) { - *_found = NULL; - return 0; - } - - *_found = found; - return 1; -} - Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) { char p[strlen(path)+1]; @@ -3069,8 +3045,9 @@ const char *manager_get_runtime_prefix(Manager *m) { getenv("XDG_RUNTIME_DIR"); } -void manager_update_failed_units(Manager *m, Unit *u, bool failed) { +int manager_update_failed_units(Manager *m, Unit *u, bool failed) { unsigned size; + int r; assert(m); assert(u->manager == m); @@ -3078,13 +3055,19 @@ void manager_update_failed_units(Manager *m, Unit *u, bool failed) { size = set_size(m->failed_units); if (failed) { + r = set_ensure_allocated(&m->failed_units, NULL); + if (r < 0) + return log_oom(); + if (set_put(m->failed_units, u) < 0) - log_oom(); + return log_oom(); } else - set_remove(m->failed_units, u); + (void) set_remove(m->failed_units, u); if (set_size(m->failed_units) != size) bus_manager_send_change_signal(m); + + return 0; } ManagerState manager_state(Manager *m) { diff --git a/src/core/manager.h b/src/core/manager.h index 9956cb7700..f6903a5c34 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -21,13 +21,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <libmount.h> #include <stdbool.h> #include <stdio.h> #include "sd-bus.h" #include "sd-event.h" -#include "fdset.h" + #include "cgroup-util.h" +#include "fdset.h" #include "hashmap.h" #include "list.h" #include "ratelimit.h" @@ -68,11 +70,11 @@ typedef enum StatusType { STATUS_TYPE_EMERGENCY, } StatusType; +#include "execute.h" #include "job.h" #include "path-lookup.h" -#include "execute.h" -#include "unit-name.h" #include "show-status.h" +#include "unit-name.h" struct Manager { /* Note that the set of units we know of is allowed to be @@ -140,8 +142,6 @@ struct Manager { sd_event_source *jobs_in_progress_event_source; - unsigned n_snapshots; - LookupPaths lookup_paths; Set *unit_path_cache; @@ -176,10 +176,8 @@ struct Manager { Hashmap *devices_by_sysfs; /* Data specific to the mount subsystem */ - FILE *proc_self_mountinfo; + struct libmnt_monitor *mount_monitor; sd_event_source *mount_event_source; - int utab_inotify_fd; - sd_event_source *mount_utab_event_source; /* Data specific to the swap filesystem */ FILE *proc_swaps; @@ -242,6 +240,11 @@ struct Manager { bool test_run:1; + /* If non-zero, exit with the following value when the systemd + * process terminate. Useful for containers: systemd-nspawn could get + * the return value. */ + uint8_t return_value; + ShowStatus show_status; bool confirm_spawn; bool no_console_output; @@ -256,7 +259,9 @@ struct Manager { bool default_cpu_accounting; bool default_memory_accounting; bool default_blockio_accounting; + bool default_tasks_accounting; + uint64_t default_tasks_max; usec_t default_timer_accuracy_usec; struct rlimit *rlimit[_RLIMIT_MAX]; @@ -302,27 +307,30 @@ struct Manager { const char *unit_log_format_string; int first_boot; + + /* Used for NetClass=auto units */ + Hashmap *cgroup_netclass_registry; + uint32_t cgroup_netclass_registry_last; }; int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m); Manager* manager_free(Manager *m); -int manager_enumerate(Manager *m); +void manager_enumerate(Manager *m); int manager_startup(Manager *m, FILE *serialization, FDSet *fds); Job *manager_get_job(Manager *m, uint32_t id); Unit *manager_get_unit(Manager *m, const char *name); -int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found); - int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j); int manager_load_unit_prepare(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret); int manager_load_unit(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret); int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u); -int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, sd_bus_error *e, Job **_ret); -int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool force, sd_bus_error *e, Job **_ret); +int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret); +int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **_ret); +int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret); void manager_dump_units(Manager *s, FILE *f, const char *prefix); void manager_dump_jobs(Manager *s, FILE *f, const char *prefix); @@ -368,7 +376,7 @@ const char *manager_get_runtime_prefix(Manager *m); ManagerState manager_state(Manager *m); -void manager_update_failed_units(Manager *m, Unit *u, bool failed); +int manager_update_failed_units(Manager *m, Unit *u, bool failed); const char *manager_state_to_string(ManagerState m) _const_; ManagerState manager_state_from_string(const char *s) _pure_; diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 65f3d06ad0..2b8d590ed1 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -19,28 +19,31 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/mount.h> #include <errno.h> +#include <ftw.h> #include <stdlib.h> +#include <sys/mount.h> #include <unistd.h> -#include <ftw.h> -#include "mount-setup.h" -#include "dev-setup.h" +#include "alloc-util.h" #include "bus-util.h" +#include "cgroup-util.h" +#include "dev-setup.h" +#include "efivars.h" +#include "label.h" #include "log.h" #include "macro.h" -#include "util.h" -#include "label.h" -#include "set.h" -#include "strv.h" +#include "missing.h" #include "mkdir.h" +#include "mount-setup.h" +#include "mount-util.h" #include "path-util.h" -#include "missing.h" -#include "virt.h" -#include "efivars.h" +#include "set.h" #include "smack-util.h" -#include "cgroup-util.h" +#include "strv.h" +#include "user-util.h" +#include "util.h" +#include "virt.h" typedef enum MountMode { MNT_NONE = 0, @@ -208,7 +211,7 @@ int mount_setup_early(void) { int j; j = mount_one(mount_table + i, false); - if (r == 0) + if (j != 0 && r >= 0) r = j; } @@ -351,7 +354,7 @@ int mount_setup(bool loaded_policy) { int j; j = mount_one(mount_table + i, loaded_policy); - if (r == 0) + if (j != 0 && r >= 0) r = j; } diff --git a/src/core/mount.c b/src/core/mount.c index 1f02aa5566..9b44357e90 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -20,27 +20,33 @@ ***/ #include <errno.h> +#include <signal.h> #include <stdio.h> #include <sys/epoll.h> -#include <signal.h> -#include <libmount.h> -#include <sys/inotify.h> -#include "manager.h" -#include "unit.h" -#include "mount.h" -#include "log.h" #include "sd-messages.h" -#include "strv.h" -#include "mkdir.h" -#include "path-util.h" -#include "mount-setup.h" -#include "unit-name.h" + +#include "alloc-util.h" #include "dbus-mount.h" -#include "special.h" +#include "escape.h" #include "exit-status.h" -#include "fstab-util.h" #include "formats-util.h" +#include "fstab-util.h" +#include "log.h" +#include "manager.h" +#include "mkdir.h" +#include "mount-setup.h" +#include "mount-util.h" +#include "mount.h" +#include "parse-util.h" +#include "path-util.h" +#include "process-util.h" +#include "special.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "unit-name.h" +#include "unit.h" #define RETRY_UMOUNT_MAX 32 @@ -248,9 +254,10 @@ static int mount_add_mount_links(Mount *m) { if (!path_equal(m->where, "/")) { /* Adds in links to other mount points that might lie further * up in the hierarchy */ - r = path_get_parent(m->where, &parent); - if (r < 0) - return r; + + parent = dirname_malloc(m->where); + if (!parent) + return -ENOMEM; r = unit_require_mounts_for(UNIT(m), parent); if (r < 0) @@ -378,12 +385,15 @@ static bool should_umount(Mount *m) { } static int mount_add_default_dependencies(Mount *m) { - const char *after, *after2, *online; MountParameters *p; + const char *after; int r; assert(m); + if (!UNIT(m)->default_dependencies) + return 0; + if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) return 0; @@ -404,30 +414,34 @@ static int mount_add_default_dependencies(Mount *m) { return 0; if (mount_is_network(p)) { - after = SPECIAL_REMOTE_FS_PRE_TARGET; - after2 = SPECIAL_NETWORK_TARGET; - online = SPECIAL_NETWORK_ONLINE_TARGET; - } else { - after = SPECIAL_LOCAL_FS_PRE_TARGET; - after2 = NULL; - online = NULL; - } + /* We order ourselves after network.target. This is + * primarily useful at shutdown: services that take + * down the network should order themselves before + * network.target, so that they are shut down only + * after this mount unit is stopped. */ - 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); + r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, 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); + /* We pull in network-online.target, and order + * ourselves after it. This is useful at start-up to + * actively pull in tools that want to be started + * before we start mounting network file systems, and + * whose purpose it is to delay this until the network + * is "up". */ + + r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, NULL, true); if (r < 0) return r; - } + + after = SPECIAL_REMOTE_FS_PRE_TARGET; + } else + after = SPECIAL_LOCAL_FS_PRE_TARGET; + + r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true); + if (r < 0) + return r; if (should_umount(m)) { r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); @@ -524,11 +538,9 @@ static int mount_add_extras(Mount *m) { if (r < 0) return r; - if (u->default_dependencies) { - r = mount_add_default_dependencies(m); - if (r < 0) - return r; - } + r = mount_add_default_dependencies(m); + if (r < 0) + return r; return 0; } @@ -623,19 +635,19 @@ static int mount_coldplug(Unit *u) { if (new_state == m->state) return 0; - if (new_state == MOUNT_MOUNTING || - new_state == MOUNT_MOUNTING_DONE || - new_state == MOUNT_REMOUNTING || - new_state == MOUNT_UNMOUNTING || - new_state == MOUNT_MOUNTING_SIGTERM || - new_state == MOUNT_MOUNTING_SIGKILL || - new_state == MOUNT_UNMOUNTING_SIGTERM || - new_state == MOUNT_UNMOUNTING_SIGKILL || - new_state == MOUNT_REMOUNTING_SIGTERM || - new_state == MOUNT_REMOUNTING_SIGKILL) { - - if (m->control_pid <= 0) - return -EBADMSG; + if (m->control_pid > 0 && + pid_is_unwaited(m->control_pid) && + IN_SET(new_state, + MOUNT_MOUNTING, + MOUNT_MOUNTING_DONE, + MOUNT_REMOUNTING, + MOUNT_UNMOUNTING, + MOUNT_MOUNTING_SIGTERM, + MOUNT_MOUNTING_SIGKILL, + MOUNT_UNMOUNTING_SIGTERM, + MOUNT_UNMOUNTING_SIGKILL, + MOUNT_REMOUNTING_SIGTERM, + MOUNT_REMOUNTING_SIGKILL)) { r = unit_watch_pid(UNIT(m), m->control_pid); if (r < 0) @@ -696,6 +708,9 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { .apply_chroot = true, .apply_tty_stdin = true, .bus_endpoint_fd = -1, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(m); @@ -851,6 +866,11 @@ fail: mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); } +static int mount_get_opts(Mount *m, char **ret) { + return fstab_filter_options(m->parameters_fragment.options, + "nofail\0" "noauto\0" "auto\0", NULL, NULL, ret); +} + static void mount_enter_mounting(Mount *m) { int r; MountParameters *p; @@ -876,8 +896,7 @@ static void mount_enter_mounting(Mount *m) { if (m->from_fragment) { _cleanup_free_ char *opts = NULL; - r = fstab_filter_options(m->parameters_fragment.options, - "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts); + r = mount_get_opts(m, &opts); if (r < 0) goto fail; @@ -1535,13 +1554,13 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { } static void mount_shutdown(Manager *m) { + assert(m); m->mount_event_source = sd_event_source_unref(m->mount_event_source); - m->mount_utab_event_source = sd_event_source_unref(m->mount_utab_event_source); - m->proc_self_mountinfo = safe_fclose(m->proc_self_mountinfo); - m->utab_inotify_fd = safe_close(m->utab_inotify_fd); + mnt_unref_monitor(m->mount_monitor); + m->mount_monitor = NULL; } static int mount_get_timeout(Unit *u, uint64_t *timeout) { @@ -1558,66 +1577,64 @@ static int mount_get_timeout(Unit *u, uint64_t *timeout) { return 1; } -static int mount_enumerate(Manager *m) { +static void mount_enumerate(Manager *m) { int r; + assert(m); mnt_init_debug(0); - if (!m->proc_self_mountinfo) { - m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); - if (!m->proc_self_mountinfo) - return -errno; + if (!m->mount_monitor) { + int fd; - r = sd_event_add_io(m->event, &m->mount_event_source, fileno(m->proc_self_mountinfo), EPOLLPRI, mount_dispatch_io, m); - if (r < 0) + m->mount_monitor = mnt_new_monitor(); + if (!m->mount_monitor) { + log_oom(); goto fail; + } - /* Dispatch this before we dispatch SIGCHLD, so that - * we always get the events from /proc/self/mountinfo - * before the SIGCHLD of /usr/bin/mount. */ - r = sd_event_source_set_priority(m->mount_event_source, -10); - if (r < 0) + r = mnt_monitor_enable_kernel(m->mount_monitor, 1); + if (r < 0) { + log_error_errno(r, "Failed to enable watching of kernel mount events: %m"); goto fail; + } - (void) sd_event_source_set_description(m->mount_event_source, "mount-mountinfo-dispatch"); - } - - if (m->utab_inotify_fd < 0) { - m->utab_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (m->utab_inotify_fd < 0) { - r = -errno; + r = mnt_monitor_enable_userspace(m->mount_monitor, 1, NULL); + if (r < 0) { + log_error_errno(r, "Failed to enable watching of userspace mount events: %m"); goto fail; } - (void) mkdir_p_label("/run/mount", 0755); - - r = inotify_add_watch(m->utab_inotify_fd, "/run/mount", IN_MOVED_TO); + /* mnt_unref_monitor() will close the fd */ + fd = r = mnt_monitor_get_fd(m->mount_monitor); if (r < 0) { - r = -errno; + log_error_errno(r, "Failed to acquire watch file descriptor: %m"); goto fail; } - r = sd_event_add_io(m->event, &m->mount_utab_event_source, m->utab_inotify_fd, EPOLLIN, mount_dispatch_io, m); - if (r < 0) + r = sd_event_add_io(m->event, &m->mount_event_source, fd, EPOLLIN, mount_dispatch_io, m); + if (r < 0) { + log_error_errno(r, "Failed to watch mount file descriptor: %m"); goto fail; + } - r = sd_event_source_set_priority(m->mount_utab_event_source, -10); - if (r < 0) + r = sd_event_source_set_priority(m->mount_event_source, -10); + if (r < 0) { + log_error_errno(r, "Failed to adjust mount watch priority: %m"); goto fail; + } - (void) sd_event_source_set_description(m->mount_utab_event_source, "mount-utab-dispatch"); + (void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch"); } r = mount_load_proc_self_mountinfo(m, false); if (r < 0) goto fail; - return 0; + return; fail: mount_shutdown(m); - return r; } static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { @@ -1629,45 +1646,27 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, int r; assert(m); - assert(revents & (EPOLLPRI | EPOLLIN)); - - /* The manager calls this for every fd event happening on the - * /proc/self/mountinfo file, which informs us about mounting - * table changes, and for /run/mount events which we watch - * for mount options. */ + assert(revents & EPOLLIN); - if (fd == m->utab_inotify_fd) { + if (fd == mnt_monitor_get_fd(m->mount_monitor)) { bool rescan = false; - /* FIXME: We *really* need to replace this with - * libmount's own API for this, we should not hardcode - * internal behaviour of libmount here. */ - - for (;;) { - union inotify_event_buffer buffer; - struct inotify_event *e; - ssize_t l; - - l = read(fd, &buffer, sizeof(buffer)); - if (l < 0) { - if (errno == EAGAIN || errno == EINTR) - break; - - log_error_errno(errno, "Failed to read utab inotify: %m"); - break; - } - - FOREACH_INOTIFY_EVENT(e, buffer, l) { - /* Only care about changes to utab, - * but we have to monitor the - * directory to reliably get - * notifications about when utab is - * replaced using rename(2) */ - if ((e->mask & IN_Q_OVERFLOW) || streq(e->name, "utab")) - rescan = true; - } - } - + /* Drain all events and verify that the event is valid. + * + * Note that libmount also monitors /run/mount mkdir if the + * directory does not exist yet. The mkdir may generate event + * which is irrelevant for us. + * + * error: r < 0; valid: r == 0, false positive: rc == 1 */ + do { + r = mnt_monitor_next_change(m->mount_monitor, NULL, NULL); + if (r == 0) + rescan = true; + else if (r < 0) + return log_error_errno(r, "Failed to drain libmount events"); + } while (r == 0); + + log_debug("libmount event [rescan: %s]", yes_no(rescan)); if (!rescan) return 0; } @@ -1788,24 +1787,6 @@ static int mount_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error); } -static const char* const mount_state_table[_MOUNT_STATE_MAX] = { - [MOUNT_DEAD] = "dead", - [MOUNT_MOUNTING] = "mounting", - [MOUNT_MOUNTING_DONE] = "mounting-done", - [MOUNT_MOUNTED] = "mounted", - [MOUNT_REMOUNTING] = "remounting", - [MOUNT_UNMOUNTING] = "unmounting", - [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm", - [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill", - [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm", - [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill", - [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm", - [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill", - [MOUNT_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState); - static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = { [MOUNT_EXEC_MOUNT] = "ExecMount", [MOUNT_EXEC_UNMOUNT] = "ExecUnmount", diff --git a/src/core/mount.h b/src/core/mount.h index 280ea0d638..9f78aa9075 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -23,26 +23,8 @@ typedef struct Mount Mount; -#include "kill.h" #include "execute.h" - -typedef enum MountState { - MOUNT_DEAD, - MOUNT_MOUNTING, /* /usr/bin/mount is running, but the mount is not done yet. */ - MOUNT_MOUNTING_DONE, /* /usr/bin/mount is running, and the mount is done. */ - MOUNT_MOUNTED, - MOUNT_REMOUNTING, - MOUNT_UNMOUNTING, - MOUNT_MOUNTING_SIGTERM, - MOUNT_MOUNTING_SIGKILL, - MOUNT_REMOUNTING_SIGTERM, - MOUNT_REMOUNTING_SIGKILL, - MOUNT_UNMOUNTING_SIGTERM, - MOUNT_UNMOUNTING_SIGKILL, - MOUNT_FAILED, - _MOUNT_STATE_MAX, - _MOUNT_STATE_INVALID = -1 -} MountState; +#include "kill.h" typedef enum MountExecCommand { MOUNT_EXEC_MOUNT, @@ -120,9 +102,6 @@ extern const UnitVTable mount_vtable; void mount_fd_event(Manager *m, int events); -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) _const_; MountExecCommand mount_exec_command_from_string(const char *s) _pure_; diff --git a/src/core/namespace.c b/src/core/namespace.c index eb88574f8f..81ba09ea5d 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -20,23 +20,31 @@ ***/ #include <errno.h> -#include <sys/mount.h> -#include <string.h> +#include <sched.h> #include <stdio.h> -#include <unistd.h> +#include <string.h> +#include <sys/mount.h> #include <sys/stat.h> -#include <sched.h> +#include <unistd.h> #include <linux/fs.h> -#include "strv.h" -#include "util.h" -#include "path-util.h" -#include "missing.h" -#include "loopback-setup.h" +#include "alloc-util.h" #include "dev-setup.h" -#include "selinux-util.h" -#include "namespace.h" +#include "fd-util.h" +#include "loopback-setup.h" +#include "missing.h" #include "mkdir.h" +#include "mount-util.h" +#include "namespace.h" +#include "path-util.h" +#include "selinux-util.h" +#include "socket-util.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "umask-util.h" +#include "user-util.h" +#include "util.h" typedef enum MountMode { /* This is ordered by priority! */ @@ -643,16 +651,7 @@ int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) { int setup_netns(int netns_storage_socket[2]) { _cleanup_close_ int netns = -1; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - struct cmsghdr *cmsg; - int r; + int r, q; assert(netns_storage_socket); assert(netns_storage_socket[0] >= 0); @@ -669,12 +668,8 @@ int setup_netns(int netns_storage_socket[2]) { if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0) return -errno; - if (recvmsg(netns_storage_socket[0], &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC) < 0) { - if (errno != EAGAIN) { - r = -errno; - goto fail; - } - + netns = receive_one_fd(netns_storage_socket[0], MSG_DONTWAIT); + if (netns == -EAGAIN) { /* Nothing stored yet, so let's create a new namespace */ if (unshare(CLONE_NEWNET) < 0) { @@ -691,15 +686,13 @@ int setup_netns(int netns_storage_socket[2]) { } r = 1; - } else { - /* Yay, found something, so let's join the namespace */ - CMSG_FOREACH(cmsg, &mh) - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int))); - netns = *(int*) CMSG_DATA(cmsg); - } + } else if (netns < 0) { + r = netns; + goto fail; + } else { + /* Yay, found something, so let's join the namespace */ if (setns(netns, CLONE_NEWNET) < 0) { r = -errno; goto fail; @@ -708,21 +701,14 @@ int setup_netns(int netns_storage_socket[2]) { r = 0; } - cmsg = CMSG_FIRSTHDR(&mh); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - memcpy(CMSG_DATA(cmsg), &netns, sizeof(int)); - mh.msg_controllen = cmsg->cmsg_len; - - if (sendmsg(netns_storage_socket[1], &mh, MSG_DONTWAIT|MSG_NOSIGNAL) < 0) { - r = -errno; + q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT); + if (q < 0) { + r = q; goto fail; } fail: lockf(netns_storage_socket[0], F_ULOCK, 0); - return r; } diff --git a/src/core/path.c b/src/core/path.c index e9111d0612..02fb134bb9 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -19,20 +19,26 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/inotify.h> -#include <sys/epoll.h> #include <errno.h> +#include <sys/epoll.h> +#include <sys/inotify.h> #include <unistd.h> -#include "unit.h" -#include "unit-name.h" -#include "path.h" -#include "mkdir.h" +#include "bus-error.h" +#include "bus-util.h" #include "dbus-path.h" -#include "special.h" +#include "fd-util.h" +#include "fs-util.h" +#include "glob-util.h" #include "macro.h" -#include "bus-util.h" -#include "bus-error.h" +#include "mkdir.h" +#include "path.h" +#include "special.h" +#include "stat-util.h" +#include "string-table.h" +#include "string-util.h" +#include "unit-name.h" +#include "unit.h" static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = { [PATH_DEAD] = UNIT_INACTIVE, @@ -309,20 +315,20 @@ static int path_add_default_dependencies(Path *p) { assert(p); - r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, - SPECIAL_PATHS_TARGET, NULL, true); + if (!UNIT(p)->default_dependencies) + return 0; + + r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, NULL, true); if (r < 0) return r; if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) { - r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, - SPECIAL_SYSINIT_TARGET, NULL, true); + 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) { @@ -354,11 +360,9 @@ static int path_load(Unit *u) { if (r < 0) return r; - if (UNIT(p)->default_dependencies) { - r = path_add_default_dependencies(p); - if (r < 0) - return r; - } + r = path_add_default_dependencies(p); + if (r < 0) + return r; } return path_verify(p); @@ -470,8 +474,7 @@ static void path_enter_running(Path *p) { if (unit_stop_pending(UNIT(p))) return; - r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)), - JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)), JOB_REPLACE, &error, NULL); if (r < 0) goto fail; @@ -715,15 +718,6 @@ static void path_reset_failed(Unit *u) { p->result = PATH_SUCCESS; } -static const char* const path_state_table[_PATH_STATE_MAX] = { - [PATH_DEAD] = "dead", - [PATH_WAITING] = "waiting", - [PATH_RUNNING] = "running", - [PATH_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(path_state, PathState); - static const char* const path_type_table[_PATH_TYPE_MAX] = { [PATH_EXISTS] = "PathExists", [PATH_EXISTS_GLOB] = "PathExistsGlob", diff --git a/src/core/path.h b/src/core/path.h index dec39333e4..deb9bab1e5 100644 --- a/src/core/path.h +++ b/src/core/path.h @@ -26,15 +26,6 @@ typedef struct PathSpec PathSpec; #include "unit.h" -typedef enum PathState { - PATH_DEAD, - PATH_WAITING, - PATH_RUNNING, - PATH_FAILED, - _PATH_STATE_MAX, - _PATH_STATE_INVALID = -1 -} PathState; - typedef enum PathType { PATH_EXISTS, PATH_EXISTS_GLOB, @@ -96,9 +87,6 @@ void path_free_specs(Path *p); extern const UnitVTable path_vtable; -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) _const_; PathType path_type_from_string(const char *s) _pure_; diff --git a/src/core/scope.c b/src/core/scope.c index 98395becfd..1953af1f88 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -22,14 +22,17 @@ #include <errno.h> #include <unistd.h> +#include "alloc-util.h" +#include "dbus-scope.h" +#include "load-dropin.h" #include "log.h" -#include "strv.h" +#include "scope.h" #include "special.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" #include "unit-name.h" #include "unit.h" -#include "scope.h" -#include "dbus-scope.h" -#include "load-dropin.h" static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = { [SCOPE_DEAD] = UNIT_INACTIVE, @@ -51,7 +54,6 @@ static void scope_init(Unit *u) { s->timeout_stop_usec = u->manager->default_timeout_stop_usec; UNIT(s)->ignore_on_isolate = true; - UNIT(s)->ignore_on_snapshot = true; } static void scope_done(Unit *u) { @@ -120,6 +122,9 @@ static int scope_add_default_dependencies(Scope *s) { assert(s); + if (!UNIT(s)->default_dependencies) + return 0; + /* Make sure scopes are unloaded on shutdown */ r = unit_add_two_dependencies_by_name( UNIT(s), @@ -171,11 +176,9 @@ static int scope_load(Unit *u) { if (r < 0) return r; - if (u->default_dependencies) { - r = scope_add_default_dependencies(s); - if (r < 0) - return r; - } + r = scope_add_default_dependencies(s); + if (r < 0) + return r; return scope_verify(s); } @@ -399,15 +402,10 @@ static bool scope_check_gc(Unit *u) { /* Never clean up scopes that still have a process around, * even if the scope is formally dead. */ - if (u->cgroup_path) { - int r; - - r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path); - if (r <= 0) - return true; - } + if (!u->cgroup_path) + return false; - return false; + return cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path) <= 0; } static void scope_notify_cgroup_empty_event(Unit *u) { @@ -507,7 +505,7 @@ _pure_ static const char *scope_sub_state_to_string(Unit *u) { return scope_state_to_string(SCOPE(u)->state); } -static int scope_enumerate(Manager *m) { +static void scope_enumerate(Manager *m) { Unit *u; int r; @@ -521,19 +519,25 @@ static int scope_enumerate(Manager *m) { u = manager_get_unit(m, SPECIAL_INIT_SCOPE); if (!u) { u = unit_new(m, sizeof(Scope)); - if (!u) - return log_oom(); + if (!u) { + log_oom(); + return; + } r = unit_add_name(u, SPECIAL_INIT_SCOPE); if (r < 0) { unit_free(u); - return log_error_errno(r, "Failed to add init.scope name"); + log_error_errno(r, "Failed to add init.scope name"); + return; } } u->transient = true; u->default_dependencies = false; u->no_gc = true; + u->ignore_on_isolate = true; + u->refuse_manual_start = true; + u->refuse_manual_stop = true; SCOPE(u)->deserialized_state = SCOPE_RUNNING; SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14; @@ -545,21 +549,8 @@ static int scope_enumerate(Manager *m) { unit_add_to_load_queue(u); unit_add_to_dbus_queue(u); - - return 0; } -static const char* const scope_state_table[_SCOPE_STATE_MAX] = { - [SCOPE_DEAD] = "dead", - [SCOPE_RUNNING] = "running", - [SCOPE_ABANDONED] = "abandoned", - [SCOPE_STOP_SIGTERM] = "stop-sigterm", - [SCOPE_STOP_SIGKILL] = "stop-sigkill", - [SCOPE_FAILED] = "failed", -}; - -DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState); - static const char* const scope_result_table[_SCOPE_RESULT_MAX] = { [SCOPE_SUCCESS] = "success", [SCOPE_FAILURE_RESOURCES] = "resources", @@ -581,6 +572,7 @@ const UnitVTable scope_vtable = { .no_alias = true, .no_instances = true, + .can_transient = true, .init = scope_init, .load = scope_load, @@ -615,7 +607,5 @@ const UnitVTable scope_vtable = { .bus_set_property = bus_scope_set_property, .bus_commit_properties = bus_scope_commit_properties, - .can_transient = true, - .enumerate = scope_enumerate, }; diff --git a/src/core/scope.h b/src/core/scope.h index 4452fe2c94..f838ee5357 100644 --- a/src/core/scope.h +++ b/src/core/scope.h @@ -25,17 +25,6 @@ typedef struct Scope Scope; #include "kill.h" -typedef enum ScopeState { - SCOPE_DEAD, - SCOPE_RUNNING, - SCOPE_ABANDONED, - SCOPE_STOP_SIGTERM, - SCOPE_STOP_SIGKILL, - SCOPE_FAILED, - _SCOPE_STATE_MAX, - _SCOPE_STATE_INVALID = -1 -} ScopeState; - typedef enum ScopeResult { SCOPE_SUCCESS, SCOPE_FAILURE_RESOURCES, @@ -64,8 +53,5 @@ extern const UnitVTable scope_vtable; int scope_abandon(Scope *s); -const char* scope_state_to_string(ScopeState i) _const_; -ScopeState scope_state_from_string(const char *s) _pure_; - const char* scope_result_to_string(ScopeResult i) _const_; ScopeResult scope_result_from_string(const char *s) _pure_; diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index 40ca0c6166..8856927c88 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -23,22 +23,25 @@ #ifdef HAVE_SELINUX -#include <stdio.h> #include <errno.h> -#include <selinux/selinux.h> #include <selinux/avc.h> +#include <selinux/selinux.h> +#include <stdio.h> #ifdef HAVE_AUDIT #include <libaudit.h> #endif #include "sd-bus.h" + +#include "alloc-util.h" +#include "audit-fd.h" #include "bus-util.h" -#include "util.h" #include "log.h" +#include "path-util.h" #include "selinux-util.h" -#include "audit-fd.h" +#include "stdio-util.h" #include "strv.h" -#include "path-util.h" +#include "util.h" static bool initialized = false; @@ -178,17 +181,6 @@ static int mac_selinux_access_init(sd_bus_error *error) { } #endif -void mac_selinux_access_free(void) { - -#ifdef HAVE_SELINUX - if (!initialized) - return; - - avc_destroy(); - initialized = false; -#endif -} - /* This function communicates with the kernel to check whether or not it should allow the access. diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h index e6b4dd7fee..3566ba529f 100644 --- a/src/core/selinux-access.h +++ b/src/core/selinux-access.h @@ -22,11 +22,10 @@ ***/ #include "sd-bus.h" + #include "bus-util.h" #include "manager.h" -void mac_selinux_access_free(void); - int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error); #ifdef HAVE_SELINUX diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c index e5b457643b..d4757e0853 100644 --- a/src/core/selinux-setup.c +++ b/src/core/selinux-setup.c @@ -19,19 +19,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> -#include <stdio.h> #include <errno.h> +#include <stdio.h> +#include <unistd.h> #ifdef HAVE_SELINUX #include <selinux/selinux.h> #endif +#include "log.h" +#include "macro.h" #include "selinux-setup.h" #include "selinux-util.h" -#include "macro.h" +#include "string-util.h" #include "util.h" -#include "log.h" #ifdef HAVE_SELINUX _printf_(2,3) @@ -78,14 +79,14 @@ int mac_selinux_setup(bool *loaded_policy) { before_load = now(CLOCK_MONOTONIC); r = selinux_init_load_policy(&enforce); if (r == 0) { + _cleanup_(mac_selinux_freep) char *label = NULL; char timespan[FORMAT_TIMESPAN_MAX]; - char *label; mac_selinux_retest(); /* Transition to the new context */ r = mac_selinux_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label); - if (r < 0 || label == NULL) { + if (r < 0 || !label) { log_open(); log_error("Failed to compute init label, ignoring."); } else { @@ -94,8 +95,6 @@ int mac_selinux_setup(bool *loaded_policy) { log_open(); if (r < 0) log_error("Failed to transition into init label '%s', ignoring.", label); - - mac_selinux_free(label); } after_load = now(CLOCK_MONOTONIC); diff --git a/src/core/service.c b/src/core/service.c index 248a9e8c62..c27b70fa3c 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -23,31 +23,38 @@ #include <signal.h> #include <unistd.h> +#include "alloc-util.h" #include "async.h" -#include "manager.h" -#include "unit.h" -#include "service.h" -#include "load-fragment.h" -#include "load-dropin.h" -#include "log.h" -#include "strv.h" -#include "unit-name.h" -#include "unit-printf.h" +#include "bus-error.h" +#include "bus-kernel.h" +#include "bus-util.h" #include "dbus-service.h" -#include "special.h" -#include "exit-status.h" #include "def.h" -#include "path-util.h" -#include "util.h" -#include "utf8.h" #include "env-util.h" +#include "escape.h" +#include "exit-status.h" +#include "fd-util.h" #include "fileio.h" -#include "bus-error.h" -#include "bus-util.h" -#include "bus-kernel.h" #include "formats-util.h" +#include "fs-util.h" +#include "load-dropin.h" +#include "load-fragment.h" +#include "log.h" +#include "manager.h" +#include "parse-util.h" +#include "path-util.h" #include "process-util.h" +#include "service.h" #include "signal-util.h" +#include "special.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "unit-name.h" +#include "unit-printf.h" +#include "unit.h" +#include "utf8.h" +#include "util.h" static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { [SERVICE_DEAD] = UNIT_INACTIVE, @@ -108,6 +115,7 @@ static void service_init(Unit *u) { s->type = _SERVICE_TYPE_INVALID; s->socket_fd = -1; s->bus_endpoint_fd = -1; + s->stdin_fd = s->stdout_fd = s->stderr_fd = -1; s->guess_main_pid = true; RATELIMIT_INIT(s->start_limit, u->manager->default_start_limit_interval, u->manager->default_start_limit_burst); @@ -167,7 +175,7 @@ static int service_set_main_pid(Service *s, pid_t pid) { s->main_pid = pid; s->main_pid_known = true; - if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) { + if (get_process_ppid(pid, &ppid) >= 0 && ppid != getpid()) { log_unit_warning(UNIT(s), "Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", pid); s->main_pid_alien = true; } else @@ -261,6 +269,7 @@ static void service_fd_store_unlink(ServiceFDStore *fs) { sd_event_source_unref(fs->event_source); } + free(fs->fdname); safe_close(fs->fd); free(fs); } @@ -270,11 +279,15 @@ static void service_release_resources(Unit *u) { assert(s); - if (!s->fd_store) + if (!s->fd_store && s->stdin_fd < 0 && s->stdout_fd < 0 && s->stderr_fd < 0) return; log_unit_debug(u, "Releasing all resources."); + s->stdin_fd = safe_close(s->stdin_fd); + s->stdout_fd = safe_close(s->stdout_fd); + s->stderr_fd = safe_close(s->stderr_fd); + while (s->fd_store) service_fd_store_unlink(s->fd_store); @@ -334,7 +347,7 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us return 0; } -static int service_add_fd_store(Service *s, int fd) { +static int service_add_fd_store(Service *s, int fd, const char *name) { ServiceFDStore *fs; int r; @@ -361,9 +374,15 @@ static int service_add_fd_store(Service *s, int fd) { fs->fd = fd; fs->service = s; + fs->fdname = strdup(name ?: "stored"); + if (!fs->fdname) { + free(fs); + return -ENOMEM; + } r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fd, 0, on_fd_store_io, fs); if (r < 0) { + free(fs->fdname); free(fs); return r; } @@ -376,7 +395,7 @@ static int service_add_fd_store(Service *s, int fd) { return 1; } -static int service_add_fd_store_set(Service *s, FDSet *fds) { +static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) { int r; assert(s); @@ -391,7 +410,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds) { if (fd < 0) break; - r = service_add_fd_store(s, fd); + r = service_add_fd_store(s, fd, name); if (r < 0) return log_unit_error_errno(UNIT(s), r, "Couldn't add fd to fd store: %m"); if (r > 0) { @@ -401,7 +420,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds) { } if (fdset_size(fds) > 0) - log_unit_warning(UNIT(s), "Tried to store more fds than FDStoreMax=%u allows, closing remaining.", s->n_fd_store_max); + log_unit_warning(UNIT(s), "Tried to store more fds than FileDescriptorStoreMax=%u allows, closing remaining.", s->n_fd_store_max); return 0; } @@ -482,6 +501,12 @@ static int service_verify(Service *s) { return -EINVAL; } + if (s->usb_function_descriptors && !s->usb_function_strings) + log_unit_warning(UNIT(s), "Service has USBFunctionDescriptors= setting, but no USBFunctionStrings=. Ignoring."); + + if (!s->usb_function_descriptors && s->usb_function_strings) + log_unit_warning(UNIT(s), "Service has USBFunctionStrings= setting, but no USBFunctionDescriptors=. Ignoring."); + return 0; } @@ -490,15 +515,38 @@ static int service_add_default_dependencies(Service *s) { assert(s); + if (!UNIT(s)->default_dependencies) + return 0; + /* Add a number of automatic dependencies useful for the * majority of services. */ - /* First, pull in base system */ - r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true); + if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { + /* First, pull in the really early boot stuff, and + * require it, so that we fail if we can't acquire + * it. */ + + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); + if (r < 0) + return r; + } else { + + /* In the --user instance there's no sysinit.target, + * in that case require basic.target instead. */ + + r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true); + if (r < 0) + return r; + } + + /* Second, if the rest of the base system is in the same + * transaction, order us after it, but do not pull it in or + * even require it. */ + r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_BASIC_TARGET, NULL, true); if (r < 0) return r; - /* Second, activate normal shutdown */ + /* Third, add us in for normal shutdown. */ return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); } @@ -520,6 +568,43 @@ static void service_fix_output(Service *s) { s->exec_context.std_output = UNIT(s)->manager->default_std_output; } +static int service_setup_bus_name(Service *s) { + int r; + + assert(s); + + if (!s->bus_name) + return 0; + + if (is_kdbus_available()) { + const char *n; + + n = strjoina(s->bus_name, ".busname"); + r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, n, NULL, true); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed to add dependency to .busname unit: %m"); + + } else { + /* If kdbus is not available, we know the dbus socket is required, hence pull it in, and require it */ + r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m"); + } + + /* Regardless if kdbus is used or not, we always want to be ordered against dbus.socket if both are in the transaction. */ + r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_DBUS_SOCKET, NULL, true); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m"); + + r = unit_watch_bus_name(UNIT(s), s->bus_name); + if (r == -EEXIST) + return log_unit_error_errno(UNIT(s), r, "Two services allocated for the same bus name %s, refusing operation.", s->bus_name); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Cannot watch bus name %s: %m", s->bus_name); + + return 0; +} + static int service_add_extras(Service *s) { int r; @@ -559,26 +644,13 @@ static int service_add_extras(Service *s) { if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE) s->notify_access = NOTIFY_MAIN; - if (s->bus_name) { - const char *n; - - n = strjoina(s->bus_name, ".busname"); - r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, n, NULL, true); - if (r < 0) - return r; - - r = unit_watch_bus_name(UNIT(s), s->bus_name); - if (r == -EEXIST) - return log_unit_error_errno(UNIT(s), r, "Two services allocated for the same bus name %s, refusing operation.", s->bus_name); - if (r < 0) - return log_unit_error_errno(UNIT(s), r, "Cannot watch bus name %s: %m", s->bus_name); - } + r = service_add_default_dependencies(s); + if (r < 0) + return r; - if (UNIT(s)->default_dependencies) { - r = service_add_default_dependencies(s); - if (r < 0) - return r; - } + r = service_setup_bus_name(s); + if (r < 0) + return r; return 0; } @@ -878,7 +950,6 @@ static void service_set_state(Service *s, ServiceState state) { log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state)); unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS); - s->reload_result = SERVICE_SUCCESS; } static int service_coldplug(Unit *u) { @@ -888,125 +959,143 @@ static int service_coldplug(Unit *u) { assert(s); assert(s->state == SERVICE_DEAD); - if (s->deserialized_state != s->state) { - - if (IN_SET(s->deserialized_state, - SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, - SERVICE_RELOAD, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) { - - usec_t k; - - k = IN_SET(s->deserialized_state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec; + if (s->deserialized_state == s->state) + return 0; - /* For the start/stop timeouts 0 means off */ - if (k > 0) { - r = service_arm_timer(s, k); - if (r < 0) - return r; - } - } + if (IN_SET(s->deserialized_state, + SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, + SERVICE_RELOAD, + SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) { - if (s->deserialized_state == SERVICE_AUTO_RESTART) { + usec_t k; - /* The restart timeouts 0 means immediately */ - r = service_arm_timer(s, s->restart_usec); - if (r < 0) - return r; - } + k = IN_SET(s->deserialized_state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec; - if (pid_is_unwaited(s->main_pid) && - ((s->deserialized_state == SERVICE_START && IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_ONESHOT, SERVICE_NOTIFY)) || - IN_SET(s->deserialized_state, - SERVICE_START, SERVICE_START_POST, - SERVICE_RUNNING, SERVICE_RELOAD, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) { - r = unit_watch_pid(UNIT(s), s->main_pid); + /* For the start/stop timeouts 0 means off */ + if (k > 0) { + r = service_arm_timer(s, k); if (r < 0) return r; } + } - if (pid_is_unwaited(s->control_pid) && - IN_SET(s->deserialized_state, - SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, - SERVICE_RELOAD, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) { - r = unit_watch_pid(UNIT(s), s->control_pid); - if (r < 0) - return r; - } + if (s->deserialized_state == SERVICE_AUTO_RESTART) { - if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) - unit_watch_all_pids(UNIT(s)); + /* The restart timeouts 0 means immediately */ + r = service_arm_timer(s, s->restart_usec); + if (r < 0) + return r; + } - if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) - service_start_watchdog(s); + if (s->main_pid > 0 && + pid_is_unwaited(s->main_pid) && + ((s->deserialized_state == SERVICE_START && IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_ONESHOT, SERVICE_NOTIFY)) || + IN_SET(s->deserialized_state, + SERVICE_START, SERVICE_START_POST, + SERVICE_RUNNING, SERVICE_RELOAD, + SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) { + r = unit_watch_pid(UNIT(s), s->main_pid); + if (r < 0) + return r; + } - service_set_state(s, s->deserialized_state); + if (s->control_pid > 0 && + pid_is_unwaited(s->control_pid) && + IN_SET(s->deserialized_state, + SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, + SERVICE_RELOAD, + SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) { + r = unit_watch_pid(UNIT(s), s->control_pid); + if (r < 0) + return r; } + if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) + unit_watch_all_pids(UNIT(s)); + + if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) + service_start_watchdog(s); + + service_set_state(s, s->deserialized_state); return 0; } -static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { +static int service_collect_fds(Service *s, int **fds, char ***fd_names) { + _cleanup_strv_free_ char **rfd_names = NULL; _cleanup_free_ int *rfds = NULL; - unsigned rn_fds = 0; - Iterator i; - int r; - Unit *u; + int rn_fds = 0, r; assert(s); assert(fds); - assert(n_fds); + assert(fd_names); - if (s->socket_fd >= 0) - return 0; + if (s->socket_fd >= 0) { - SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) { - int *cfds; - unsigned cn_fds; - Socket *sock; + /* Pass the per-connection socket */ - if (u->type != UNIT_SOCKET) - continue; + rfds = new(int, 1); + if (!rfds) + return -ENOMEM; + rfds[0] = s->socket_fd; - sock = SOCKET(u); + rfd_names = strv_new("connection", NULL); + if (!rfd_names) + return -ENOMEM; - r = socket_collect_fds(sock, &cfds, &cn_fds); - if (r < 0) - return r; + rn_fds = 1; + } else { + Iterator i; + Unit *u; - if (cn_fds <= 0) { - free(cfds); - continue; - } + /* Pass all our configured sockets for singleton services */ - if (!rfds) { - rfds = cfds; - rn_fds = cn_fds; - } else { - int *t; + SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) { + _cleanup_free_ int *cfds = NULL; + Socket *sock; + int cn_fds; - t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int)); - if (!t) { - free(cfds); - return -ENOMEM; - } + if (u->type != UNIT_SOCKET) + continue; + + sock = SOCKET(u); + + cn_fds = socket_collect_fds(sock, &cfds); + if (cn_fds < 0) + return cn_fds; - memcpy(t + rn_fds, cfds, cn_fds * sizeof(int)); - rfds = t; - rn_fds += cn_fds; + if (cn_fds <= 0) + continue; + + if (!rfds) { + rfds = cfds; + rn_fds = cn_fds; + + cfds = NULL; + } else { + int *t; + + t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int)); + if (!t) + return -ENOMEM; - free(cfds); + memcpy(t + rn_fds, cfds, cn_fds * sizeof(int)); + rfds = t; + rn_fds += cn_fds; + } + + r = strv_extend_n(&rfd_names, socket_fdname(sock), cn_fds); + if (r < 0) + return r; } } if (s->n_fd_store > 0) { ServiceFDStore *fs; + char **nl; int *t; t = realloc(rfds, (rn_fds + s->n_fd_store) * sizeof(int)); @@ -1014,15 +1103,32 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { return -ENOMEM; rfds = t; - LIST_FOREACH(fd_store, fs, s->fd_store) - rfds[rn_fds++] = fs->fd; + + nl = realloc(rfd_names, (rn_fds + s->n_fd_store + 1) * sizeof(char*)); + if (!nl) + return -ENOMEM; + + rfd_names = nl; + + LIST_FOREACH(fd_store, fs, s->fd_store) { + rfds[rn_fds] = fs->fd; + rfd_names[rn_fds] = strdup(strempty(fs->fdname)); + if (!rfd_names[rn_fds]) + return -ENOMEM; + + rn_fds++; + } + + rfd_names[rn_fds] = NULL; } *fds = rfds; - *n_fds = rn_fds; + *fd_names = rfd_names; rfds = NULL; - return 0; + rfd_names = NULL; + + return rn_fds; } static int service_spawn( @@ -1036,23 +1142,25 @@ static int service_spawn( bool is_control, pid_t *_pid) { - pid_t pid; - int r; - int *fds = NULL; - _cleanup_free_ int *fdsbuf = NULL; - unsigned n_fds = 0, n_env = 0; + _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL; _cleanup_free_ char *bus_endpoint_path = NULL; - _cleanup_strv_free_ char - **argv = NULL, **final_env = NULL, **our_env = NULL; + _cleanup_free_ int *fds = NULL; + unsigned n_fds = 0, n_env = 0; const char *path; + pid_t pid; + ExecParameters exec_params = { - .apply_permissions = apply_permissions, - .apply_chroot = apply_chroot, - .apply_tty_stdin = apply_tty_stdin, - .bus_endpoint_fd = -1, - .selinux_context_net = s->socket_fd_selinux_context_net + .apply_permissions = apply_permissions, + .apply_chroot = apply_chroot, + .apply_tty_stdin = apply_tty_stdin, + .bus_endpoint_fd = -1, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; + int r; + assert(s); assert(c); assert(_pid); @@ -1072,16 +1180,11 @@ static int service_spawn( s->exec_context.std_output == EXEC_OUTPUT_SOCKET || s->exec_context.std_error == EXEC_OUTPUT_SOCKET) { - if (s->socket_fd >= 0) { - fds = &s->socket_fd; - n_fds = 1; - } else { - r = service_collect_fds(s, &fdsbuf, &n_fds); - if (r < 0) - goto fail; + r = service_collect_fds(s, &fds, &fd_names); + if (r < 0) + goto fail; - fds = fdsbuf; - } + n_fds = r; } if (timeout > 0) { @@ -1119,7 +1222,7 @@ static int service_spawn( goto fail; } - if (UNIT_DEREF(s->accept_socket)) { + if (s->socket_fd >= 0) { union sockaddr_union sa; socklen_t salen = sizeof(sa); @@ -1167,7 +1270,7 @@ static int service_spawn( if (is_control && UNIT(s)->cgroup_path) { path = strjoina(UNIT(s)->cgroup_path, "/control"); - cg_create(SYSTEMD_CGROUP_CONTROLLER, path); + (void) cg_create(SYSTEMD_CGROUP_CONTROLLER, path); } else path = UNIT(s)->cgroup_path; @@ -1185,6 +1288,7 @@ static int service_spawn( exec_params.argv = argv; exec_params.fds = fds; + exec_params.fd_names = fd_names; exec_params.n_fds = n_fds; exec_params.environment = final_env; exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; @@ -1194,8 +1298,12 @@ static int service_spawn( exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); exec_params.watchdog_usec = s->watchdog_usec; exec_params.bus_endpoint_path = bus_endpoint_path; + exec_params.selinux_context_net = s->socket_fd_selinux_context_net; if (s->type == SERVICE_IDLE) exec_params.idle_pipe = UNIT(s)->manager->idle_pipe; + exec_params.stdin_fd = s->stdin_fd; + exec_params.stdout_fd = s->stdout_fd; + exec_params.stderr_fd = s->stderr_fd; r = exec_spawn(UNIT(s), c, @@ -1741,7 +1849,7 @@ static void service_enter_restart(Service *s) { * restarted. We use JOB_RESTART (instead of the more obvious * JOB_START) here so that those dependency jobs will be added * as well. */ - r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_FAIL, false, &error, NULL); + r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_FAIL, &error, NULL); if (r < 0) goto fail; @@ -1772,6 +1880,7 @@ static void service_enter_reload(Service *s) { assert(s); service_unwatch_control_pid(s); + s->reload_result = SERVICE_SUCCESS; s->control_command = s->exec_command[SERVICE_EXEC_RELOAD]; if (s->control_command) { @@ -1995,6 +2104,7 @@ _pure_ static bool service_can_reload(Unit *u) { static int service_serialize(Unit *u, FILE *f, FDSet *fds) { Service *s = SERVICE(u); ServiceFDStore *fs; + int r; assert(u); assert(f); @@ -2013,12 +2123,9 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known)); unit_serialize_item(u, f, "bus-name-good", yes_no(s->bus_name_good)); - if (s->status_text) { - _cleanup_free_ char *c = NULL; - - c = cescape(s->status_text); - unit_serialize_item(u, f, "status-text", strempty(c)); - } + r = unit_serialize_item_escaped(u, f, "status-text", s->status_text); + if (r < 0) + return r; /* FIXME: There's a minor uncleanliness here: if there are * multiple commands attached here, we will start from the @@ -2026,34 +2133,34 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { if (s->control_command_id >= 0) unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id)); - if (s->socket_fd >= 0) { - int copy; - - copy = fdset_put_dup(fds, s->socket_fd); - if (copy < 0) - return copy; - - unit_serialize_item_format(u, f, "socket-fd", "%i", copy); - } - - if (s->bus_endpoint_fd >= 0) { - int copy; - - copy = fdset_put_dup(fds, s->bus_endpoint_fd); - if (copy < 0) - return copy; + r = unit_serialize_item_fd(u, f, fds, "stdin-fd", s->stdin_fd); + if (r < 0) + return r; + r = unit_serialize_item_fd(u, f, fds, "stdout-fd", s->stdout_fd); + if (r < 0) + return r; + r = unit_serialize_item_fd(u, f, fds, "stderr-fd", s->stderr_fd); + if (r < 0) + return r; - unit_serialize_item_format(u, f, "endpoint-fd", "%i", copy); - } + r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_fd); + if (r < 0) + return r; + r = unit_serialize_item_fd(u, f, fds, "endpoint-fd", s->bus_endpoint_fd); + if (r < 0) + return r; LIST_FOREACH(fd_store, fs, s->fd_store) { + _cleanup_free_ char *c = NULL; int copy; copy = fdset_put_dup(fds, fs->fd); if (copy < 0) return copy; - unit_serialize_item_format(u, f, "fd-store-fd", "%i", copy); + c = cescape(fs->fdname); + + unit_serialize_item_format(u, f, "fd-store-fd", "%i %s", copy, strempty(c)); } if (s->main_exec_status.pid > 0) { @@ -2070,8 +2177,7 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { if (dual_timestamp_is_set(&s->watchdog_timestamp)) dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp); - if (s->forbid_restart) - unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart)); + unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart)); return 0; } @@ -2183,12 +2289,24 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, s->bus_endpoint_fd = fdset_remove(fds, fd); } } else if (streq(key, "fd-store-fd")) { + const char *fdv; + size_t pf; int fd; - if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + pf = strcspn(value, WHITESPACE); + fdv = strndupa(value, pf); + + if (safe_atoi(fdv, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse fd-store-fd value: %s", value); else { - r = service_add_fd_store(s, fd); + _cleanup_free_ char *t = NULL; + const char *fdn; + + fdn = value + pf; + fdn += strspn(fdn, WHITESPACE); + (void) cunescape(fdn, 0, &t); + + r = service_add_fd_store(s, fd, t); if (r < 0) log_unit_error_errno(u, r, "Failed to add fd to store: %m"); else if (r > 0) @@ -2230,6 +2348,33 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, log_unit_debug(u, "Failed to parse forbid-restart value: %s", value); else s->forbid_restart = b; + } else if (streq(key, "stdin-fd")) { + int fd; + + if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse stdin-fd value: %s", value); + else { + asynchronous_close(s->stdin_fd); + s->stdin_fd = fdset_remove(fds, fd); + } + } else if (streq(key, "stdout-fd")) { + int fd; + + if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse stdout-fd value: %s", value); + else { + asynchronous_close(s->stdout_fd); + s->stdout_fd = fdset_remove(fds, fd); + } + } else if (streq(key, "stderr-fd")) { + int fd; + + if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse stderr-fd value: %s", value); + else { + asynchronous_close(s->stderr_fd); + s->stderr_fd = fdset_remove(fds, fd); + } } else log_unit_debug(u, "Unknown serialization key: %s", key); @@ -2267,14 +2412,6 @@ static bool service_check_gc(Unit *u) { return false; } -_pure_ static bool service_check_snapshot(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - return s->socket_fd < 0; -} - static int service_retry_pid_file(Service *s) { int r; @@ -2737,6 +2874,8 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us case SERVICE_RELOAD: log_unit_warning(UNIT(s), "Reload operation timed out. Stopping."); + service_unwatch_control_pid(s); + service_kill_control_processes(s); s->reload_result = SERVICE_FAILURE_TIMEOUT; service_enter_running(s, SERVICE_SUCCESS); break; @@ -2940,8 +3079,17 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds) if (strv_find(tags, "WATCHDOG=1")) service_reset_watchdog(s); - if (strv_find(tags, "FDSTORE=1")) - service_add_fd_store_set(s, fds); + if (strv_find(tags, "FDSTORE=1")) { + const char *name; + + name = strv_find_startswith(tags, "FDNAME="); + if (name && !fdname_is_valid(name)) { + log_unit_warning(u, "Passed FDNAME= name is invalid, ignoring."); + name = NULL; + } + + service_add_fd_store_set(s, fds, name); + } /* Notify clients about changed status or main pid */ if (notify_dbus) @@ -3084,27 +3232,6 @@ static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error); } -static const char* const service_state_table[_SERVICE_STATE_MAX] = { - [SERVICE_DEAD] = "dead", - [SERVICE_START_PRE] = "start-pre", - [SERVICE_START] = "start", - [SERVICE_START_POST] = "start-post", - [SERVICE_RUNNING] = "running", - [SERVICE_EXITED] = "exited", - [SERVICE_RELOAD] = "reload", - [SERVICE_STOP] = "stop", - [SERVICE_STOP_SIGABRT] = "stop-sigabrt", - [SERVICE_STOP_SIGTERM] = "stop-sigterm", - [SERVICE_STOP_SIGKILL] = "stop-sigkill", - [SERVICE_STOP_POST] = "stop-post", - [SERVICE_FINAL_SIGTERM] = "final-sigterm", - [SERVICE_FINAL_SIGKILL] = "final-sigkill", - [SERVICE_FAILED] = "failed", - [SERVICE_AUTO_RESTART] = "auto-restart", -}; - -DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState); - static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { [SERVICE_RESTART_NO] = "no", [SERVICE_RESTART_ON_SUCCESS] = "on-success", @@ -3206,7 +3333,6 @@ const UnitVTable service_vtable = { .sub_state_to_string = service_sub_state_to_string, .check_gc = service_check_gc, - .check_snapshot = service_check_snapshot, .sigchld_event = service_sigchld_event, diff --git a/src/core/service.h b/src/core/service.h index 7da0a93961..d0faad88e0 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -24,31 +24,10 @@ typedef struct Service Service; typedef struct ServiceFDStore ServiceFDStore; +#include "exit-status.h" +#include "kill.h" #include "path.h" #include "ratelimit.h" -#include "kill.h" -#include "exit-status.h" - -typedef enum ServiceState { - SERVICE_DEAD, - SERVICE_START_PRE, - SERVICE_START, - SERVICE_START_POST, - SERVICE_RUNNING, - SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */ - SERVICE_RELOAD, - SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */ - SERVICE_STOP_SIGABRT, /* Watchdog timeout */ - SERVICE_STOP_SIGTERM, - SERVICE_STOP_SIGKILL, - SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */ - SERVICE_FINAL_SIGKILL, - SERVICE_FAILED, - SERVICE_AUTO_RESTART, - _SERVICE_STATE_MAX, - _SERVICE_STATE_INVALID = -1 -} ServiceState; typedef enum ServiceRestart { SERVICE_RESTART_NO, @@ -118,6 +97,7 @@ struct ServiceFDStore { Service *service; int fd; + char *fdname; sd_event_source *event_source; LIST_FIELDS(ServiceFDStore, fd_store); @@ -212,15 +192,19 @@ struct Service { ServiceFDStore *fd_store; unsigned n_fd_store; unsigned n_fd_store_max; + + char *usb_function_descriptors; + char *usb_function_strings; + + int stdin_fd; + int stdout_fd; + int stderr_fd; }; extern const UnitVTable service_vtable; int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net); -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) _const_; ServiceRestart service_restart_from_string(const char *s) _pure_; diff --git a/src/core/show-status.c b/src/core/show-status.c index 02b1be73e3..e4e12a3365 100644 --- a/src/core/show-status.c +++ b/src/core/show-status.c @@ -19,7 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "alloc-util.h" +#include "fd-util.h" +#include "io-util.h" +#include "parse-util.h" #include "show-status.h" +#include "string-util.h" +#include "terminal-util.h" #include "util.h" int parse_show_status(const char *v, ShowStatus *ret) { @@ -40,3 +46,81 @@ int parse_show_status(const char *v, ShowStatus *ret) { *ret = r ? SHOW_STATUS_YES : SHOW_STATUS_NO; return 0; } + +int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) { + static const char status_indent[] = " "; /* "[" STATUS "] " */ + _cleanup_free_ char *s = NULL; + _cleanup_close_ int fd = -1; + struct iovec iovec[6] = {}; + int n = 0; + static bool prev_ephemeral; + + assert(format); + + /* This is independent of logging, as status messages are + * optional and go exclusively to the console. */ + + if (vasprintf(&s, format, ap) < 0) + return log_oom(); + + fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return fd; + + if (ellipse) { + char *e; + size_t emax, sl; + int c; + + c = fd_columns(fd); + if (c <= 0) + c = 80; + + sl = status ? sizeof(status_indent)-1 : 0; + + emax = c - sl - 1; + if (emax < 3) + emax = 3; + + e = ellipsize(s, emax, 50); + if (e) { + free(s); + s = e; + } + } + + if (prev_ephemeral) + IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE); + prev_ephemeral = ephemeral; + + if (status) { + if (!isempty(status)) { + IOVEC_SET_STRING(iovec[n++], "["); + IOVEC_SET_STRING(iovec[n++], status); + IOVEC_SET_STRING(iovec[n++], "] "); + } else + IOVEC_SET_STRING(iovec[n++], status_indent); + } + + IOVEC_SET_STRING(iovec[n++], s); + if (!ephemeral) + IOVEC_SET_STRING(iovec[n++], "\n"); + + if (writev(fd, iovec, n) < 0) + return -errno; + + return 0; +} + +int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) { + va_list ap; + int r; + + assert(format); + + va_start(ap, format); + r = status_vprintf(status, ellipse, ephemeral, format, ap); + va_end(ap); + + return r; +} diff --git a/src/core/show-status.h b/src/core/show-status.h index a2b2153746..c79d4acb66 100644 --- a/src/core/show-status.h +++ b/src/core/show-status.h @@ -21,6 +21,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> + +#include "macro.h" + /* Manager status */ typedef enum ShowStatus { @@ -32,3 +36,6 @@ typedef enum ShowStatus { } ShowStatus; int parse_show_status(const char *v, ShowStatus *ret); + +int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0); +int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5); diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 8cc6efc5b8..3a95b5fd72 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -19,35 +19,39 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/mman.h> -#include <sys/reboot.h> -#include <linux/reboot.h> -#include <sys/stat.h> -#include <sys/mount.h> #include <errno.h> -#include <unistd.h> +#include <getopt.h> +#include <linux/reboot.h> #include <signal.h> #include <stdbool.h> #include <stdlib.h> -#include <getopt.h> +#include <sys/mman.h> +#include <sys/mount.h> +#include <sys/reboot.h> +#include <sys/stat.h> +#include <unistd.h> -#include "missing.h" -#include "log.h" +#include "alloc-util.h" +#include "cgroup-util.h" +#include "def.h" #include "fileio.h" +#include "killall.h" +#include "log.h" +#include "missing.h" +#include "parse-util.h" +#include "process-util.h" +#include "string-util.h" +#include "switch-root.h" +#include "terminal-util.h" #include "umount.h" #include "util.h" #include "virt.h" #include "watchdog.h" -#include "killall.h" -#include "cgroup-util.h" -#include "def.h" -#include "switch-root.h" -#include "process-util.h" -#include "terminal-util.h" #define FINALIZE_ATTEMPTS 50 static char* arg_verb; +static uint8_t arg_exit_code; static int parse_argv(int argc, char *argv[]) { enum { @@ -55,6 +59,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_LOG_TARGET, ARG_LOG_COLOR, ARG_LOG_LOCATION, + ARG_EXIT_CODE, }; static const struct option options[] = { @@ -62,6 +67,7 @@ static int parse_argv(int argc, char *argv[]) { { "log-target", required_argument, NULL, ARG_LOG_TARGET }, { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, + { "exit-code", required_argument, NULL, ARG_EXIT_CODE }, {} }; @@ -110,6 +116,13 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_EXIT_CODE: + r = safe_atou8(optarg, &arg_exit_code); + if (r < 0) + log_error("Failed to parse exit code %s, ignoring", optarg); + + break; + case '\001': if (!arg_verb) arg_verb = optarg; @@ -183,6 +196,8 @@ int main(int argc, char *argv[]) { cmd = RB_HALT_SYSTEM; else if (streq(arg_verb, "kexec")) cmd = LINUX_REBOOT_CMD_KEXEC; + else if (streq(arg_verb, "exit")) + cmd = 0; /* ignored, just checking that arg_verb is valid */ else { r = -EINVAL; log_error("Unknown action '%s'.", arg_verb); @@ -339,6 +354,16 @@ int main(int argc, char *argv[]) { if (!in_container) sync(); + if (streq(arg_verb, "exit")) { + if (in_container) + exit(arg_exit_code); + else { + /* We cannot exit() on the host, fallback on another + * method. */ + cmd = RB_POWER_OFF; + } + } + switch (cmd) { case LINUX_REBOOT_CMD_KEXEC: @@ -408,6 +433,5 @@ int main(int argc, char *argv[]) { error: log_emergency_errno(r, "Critical error while doing system shutdown: %m"); - freeze(); } diff --git a/src/core/slice.c b/src/core/slice.c index b414462066..06ac6f8450 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -21,13 +21,15 @@ #include <errno.h> +#include "alloc-util.h" +#include "dbus-slice.h" #include "log.h" -#include "strv.h" +#include "slice.h" #include "special.h" +#include "string-util.h" +#include "strv.h" #include "unit-name.h" #include "unit.h" -#include "slice.h" -#include "dbus-slice.h" static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = { [SLICE_DEAD] = UNIT_INACTIVE, @@ -83,6 +85,9 @@ static int slice_add_default_dependencies(Slice *s) { assert(s); + if (!UNIT(s)->default_dependencies) + return 0; + /* Make sure slices are unloaded on shutdown */ r = unit_add_two_dependencies_by_name( UNIT(s), @@ -94,7 +99,6 @@ static int slice_add_default_dependencies(Slice *s) { return 0; } - static int slice_verify(Slice *s) { _cleanup_free_ char *parent = NULL; int r; @@ -142,11 +146,9 @@ static int slice_load(Unit *u) { if (r < 0) return r; - if (u->default_dependencies) { - r = slice_add_default_dependencies(s); - if (r < 0) - return r; - } + r = slice_add_default_dependencies(s); + if (r < 0) + return r; } return slice_verify(s); @@ -253,7 +255,7 @@ _pure_ static const char *slice_sub_state_to_string(Unit *u) { return slice_state_to_string(SLICE(u)->state); } -static int slice_enumerate(Manager *m) { +static void slice_enumerate(Manager *m) { Unit *u; int r; @@ -262,18 +264,24 @@ static int slice_enumerate(Manager *m) { u = manager_get_unit(m, SPECIAL_ROOT_SLICE); if (!u) { u = unit_new(m, sizeof(Slice)); - if (!u) - return log_oom(); + if (!u) { + log_oom(); + return; + } r = unit_add_name(u, SPECIAL_ROOT_SLICE); if (r < 0) { unit_free(u); - return log_error_errno(r, "Failed to add -.slice name"); + log_error_errno(r, "Failed to add -.slice name"); + return; } } u->default_dependencies = false; u->no_gc = true; + u->ignore_on_isolate = true; + u->refuse_manual_start = true; + u->refuse_manual_stop = true; SLICE(u)->deserialized_state = SLICE_ACTIVE; if (!u->description) @@ -283,17 +291,8 @@ static int slice_enumerate(Manager *m) { unit_add_to_load_queue(u); unit_add_to_dbus_queue(u); - - return 0; } -static const char* const slice_state_table[_SLICE_STATE_MAX] = { - [SLICE_DEAD] = "dead", - [SLICE_ACTIVE] = "active" -}; - -DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState); - const UnitVTable slice_vtable = { .object_size = sizeof(Slice), .cgroup_context_offset = offsetof(Slice, cgroup_context), @@ -306,6 +305,7 @@ const UnitVTable slice_vtable = { .no_alias = true, .no_instances = true, + .can_transient = true, .load = slice_load, diff --git a/src/core/slice.h b/src/core/slice.h index ac648e56f8..0c356651e3 100644 --- a/src/core/slice.h +++ b/src/core/slice.h @@ -23,14 +23,6 @@ typedef struct Slice Slice; - -typedef enum SliceState { - SLICE_DEAD, - SLICE_ACTIVE, - _SLICE_STATE_MAX, - _SLICE_STATE_INVALID = -1 -} SliceState; - struct Slice { Unit meta; @@ -40,6 +32,3 @@ struct Slice { }; extern const UnitVTable slice_vtable; - -const char* slice_state_to_string(SliceState i) _const_; -SliceState slice_state_from_string(const char *s) _pure_; diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c index cbe7d0b4a9..0661ff9ecd 100644 --- a/src/core/smack-setup.c +++ b/src/core/smack-setup.c @@ -21,18 +21,22 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> +#include <dirent.h> #include <errno.h> -#include <string.h> -#include <stdlib.h> #include <fcntl.h> -#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "alloc-util.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "log.h" #include "macro.h" #include "smack-setup.h" +#include "string-util.h" #include "util.h" -#include "fileio.h" -#include "log.h" #ifdef HAVE_SMACK @@ -215,16 +219,14 @@ int mac_smack_setup(bool *loaded_policy) { log_info("Successfully loaded Smack policies."); break; default: - log_warning("Failed to load Smack access rules: %s, ignoring.", - strerror(abs(r))); + log_warning_errno(r, "Failed to load Smack access rules, ignoring: %m"); return 0; } #ifdef SMACK_RUN_LABEL r = write_string_file("/proc/self/attr/current", SMACK_RUN_LABEL, 0); if (r) - log_warning("Failed to set SMACK label \"%s\" on self: %s", - SMACK_RUN_LABEL, strerror(-r)); + log_warning_errno(r, "Failed to set SMACK label \"%s\" on self: %m", SMACK_RUN_LABEL); #endif r = write_cipso2_rules("/etc/smack/cipso.d/"); @@ -239,8 +241,7 @@ int mac_smack_setup(bool *loaded_policy) { log_info("Successfully loaded Smack/CIPSO policies."); break; default: - log_warning("Failed to load Smack/CIPSO access rules: %s, ignoring.", - strerror(abs(r))); + log_warning_errno(r, "Failed to load Smack/CIPSO access rules, ignoring: %m"); return 0; } diff --git a/src/core/snapshot.c b/src/core/snapshot.c deleted file mode 100644 index 336ff20f84..0000000000 --- a/src/core/snapshot.c +++ /dev/null @@ -1,306 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> - -#include "unit.h" -#include "snapshot.h" -#include "unit-name.h" -#include "dbus-snapshot.h" -#include "bus-common-errors.h" - -static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = { - [SNAPSHOT_DEAD] = UNIT_INACTIVE, - [SNAPSHOT_ACTIVE] = UNIT_ACTIVE -}; - -static void snapshot_init(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(UNIT(s)->load_state == UNIT_STUB); - - UNIT(s)->ignore_on_isolate = true; - UNIT(s)->ignore_on_snapshot = true; - UNIT(s)->allow_isolate = true; -} - -static void snapshot_set_state(Snapshot *s, SnapshotState state) { - SnapshotState old_state; - assert(s); - - old_state = s->state; - s->state = state; - - if (state != old_state) - log_unit_debug(UNIT(s), "Changed %s -> %s", snapshot_state_to_string(old_state), snapshot_state_to_string(state)); - - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); -} - -static int snapshot_load(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - /* Make sure that only snapshots created via snapshot_create() - * can be loaded */ - if (!u->transient && UNIT(s)->manager->n_reloading <= 0) - return -ENOENT; - - u->load_state = UNIT_LOADED; - return 0; -} - -static int snapshot_coldplug(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(s->state == SNAPSHOT_DEAD); - - if (s->deserialized_state != s->state) - snapshot_set_state(s, s->deserialized_state); - - return 0; -} - -static void snapshot_dump(Unit *u, FILE *f, const char *prefix) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(f); - - fprintf(f, - "%sSnapshot State: %s\n" - "%sClean Up: %s\n", - prefix, snapshot_state_to_string(s->state), - prefix, yes_no(s->cleanup)); -} - -static int snapshot_start(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(s->state == SNAPSHOT_DEAD); - - snapshot_set_state(s, SNAPSHOT_ACTIVE); - - if (s->cleanup) - unit_add_to_cleanup_queue(u); - - return 1; -} - -static int snapshot_stop(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(s->state == SNAPSHOT_ACTIVE); - - snapshot_set_state(s, SNAPSHOT_DEAD); - return 1; -} - -static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) { - Snapshot *s = SNAPSHOT(u); - Unit *other; - Iterator i; - - assert(s); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state)); - unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup)); - SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) - unit_serialize_item(u, f, "wants", other->id); - - return 0; -} - -static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Snapshot *s = SNAPSHOT(u); - int r; - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - SnapshotState state; - - state = snapshot_state_from_string(value); - if (state < 0) - log_unit_debug(u, "Failed to parse state value: %s", value); - else - s->deserialized_state = state; - - } else if (streq(key, "cleanup")) { - - r = parse_boolean(value); - if (r < 0) - log_unit_debug(u, "Failed to parse cleanup value: %s", value); - else - s->cleanup = r; - - } else if (streq(key, "wants")) { - - r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true); - if (r < 0) - return r; - } else - log_unit_debug(u, "Unknown serialization key: %s", key); - - return 0; -} - -_pure_ static UnitActiveState snapshot_active_state(Unit *u) { - assert(u); - - return state_translation_table[SNAPSHOT(u)->state]; -} - -_pure_ static const char *snapshot_sub_state_to_string(Unit *u) { - assert(u); - - return snapshot_state_to_string(SNAPSHOT(u)->state); -} - -int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, Snapshot **_s) { - _cleanup_free_ char *n = NULL; - Unit *other, *u = NULL; - Iterator i; - int r; - const char *k; - - assert(m); - assert(_s); - - if (name) { - if (!unit_name_is_valid(name, UNIT_NAME_PLAIN)) - return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name); - - if (!endswith(name, ".snapshot")) - return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s lacks snapshot suffix.", name); - - if (manager_get_unit(m, name)) - return sd_bus_error_setf(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name); - - } else { - - for (;;) { - if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0) - return -ENOMEM; - - if (!manager_get_unit(m, n)) { - name = n; - break; - } - - n = mfree(n); - } - } - - r = manager_load_unit_prepare(m, name, NULL, e, &u); - if (r < 0) - goto fail; - - u->transient = true; - manager_dispatch_load_queue(m); - assert(u->load_state == UNIT_LOADED); - - HASHMAP_FOREACH_KEY(other, k, m->units, i) { - - if (other->ignore_on_snapshot || - other->transient) - continue; - - if (k != other->id) - continue; - - if (UNIT_VTABLE(other)->check_snapshot) - if (!UNIT_VTABLE(other)->check_snapshot(other)) - continue; - - if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - continue; - - r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true); - if (r < 0) - goto fail; - } - - SNAPSHOT(u)->cleanup = cleanup; - *_s = SNAPSHOT(u); - - log_unit_info(u, "Created snapshot."); - - return 0; - -fail: - if (u) - unit_add_to_cleanup_queue(u); - - return r; -} - -void snapshot_remove(Snapshot *s) { - assert(s); - - log_unit_info(UNIT(s), "Removing snapshot."); - - unit_add_to_cleanup_queue(UNIT(s)); -} - -static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = { - [SNAPSHOT_DEAD] = "dead", - [SNAPSHOT_ACTIVE] = "active" -}; - -DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState); - -const UnitVTable snapshot_vtable = { - .object_size = sizeof(Snapshot), - - .no_alias = true, - .no_instances = true, - .no_gc = true, - - .init = snapshot_init, - .load = snapshot_load, - - .coldplug = snapshot_coldplug, - - .dump = snapshot_dump, - - .start = snapshot_start, - .stop = snapshot_stop, - - .serialize = snapshot_serialize, - .deserialize_item = snapshot_deserialize_item, - - .active_state = snapshot_active_state, - .sub_state_to_string = snapshot_sub_state_to_string, - - .bus_vtable = bus_snapshot_vtable -}; diff --git a/src/core/snapshot.h b/src/core/snapshot.h deleted file mode 100644 index f2451b1193..0000000000 --- a/src/core/snapshot.h +++ /dev/null @@ -1,48 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 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 Snapshot Snapshot; - - -typedef enum SnapshotState { - SNAPSHOT_DEAD, - SNAPSHOT_ACTIVE, - _SNAPSHOT_STATE_MAX, - _SNAPSHOT_STATE_INVALID = -1 -} SnapshotState; - -struct Snapshot { - Unit meta; - - SnapshotState state, deserialized_state; - - bool cleanup; -}; - -extern const UnitVTable snapshot_vtable; - -int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, Snapshot **s); -void snapshot_remove(Snapshot *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 9db42a0333..687675b24e 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -19,37 +19,46 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/stat.h> -#include <unistd.h> +#include <arpa/inet.h> #include <errno.h> #include <fcntl.h> -#include <sys/epoll.h> -#include <signal.h> -#include <arpa/inet.h> -#include <netinet/tcp.h> #include <mqueue.h> +#include <netinet/tcp.h> +#include <signal.h> +#include <sys/epoll.h> +#include <sys/stat.h> +#include <unistd.h> #include "sd-event.h" + +#include "alloc-util.h" +#include "bus-error.h" +#include "bus-util.h" +#include "copy.h" +#include "dbus-socket.h" +#include "def.h" +#include "exit-status.h" +#include "fd-util.h" +#include "formats-util.h" +#include "label.h" #include "log.h" -#include "strv.h" +#include "missing.h" #include "mkdir.h" +#include "parse-util.h" #include "path-util.h" -#include "unit-name.h" -#include "unit-printf.h" -#include "missing.h" -#include "special.h" -#include "label.h" -#include "exit-status.h" -#include "def.h" -#include "smack-util.h" -#include "bus-util.h" -#include "bus-error.h" +#include "process-util.h" #include "selinux-util.h" -#include "dbus-socket.h" -#include "unit.h" -#include "formats-util.h" #include "signal-util.h" +#include "smack-util.h" #include "socket.h" +#include "special.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "unit-name.h" +#include "unit-printf.h" +#include "unit.h" +#include "user-util.h" static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, @@ -104,6 +113,14 @@ static void socket_unwatch_control_pid(Socket *s) { s->control_pid = 0; } +static void socket_cleanup_fd_list(SocketPort *p) { + assert(p); + + close_many(p->auxiliary_fds, p->n_auxiliary_fds); + p->auxiliary_fds = mfree(p->auxiliary_fds); + p->n_auxiliary_fds = 0; +} + void socket_free_ports(Socket *s) { SocketPort *p; @@ -114,6 +131,7 @@ void socket_free_ports(Socket *s) { sd_event_source_unref(p->event_source); + socket_cleanup_fd_list(p); safe_close(p->fd); free(p->path); free(p); @@ -248,7 +266,7 @@ static int socket_add_mount_links(Socket *s) { if (p->type == SOCKET_SOCKET) path = socket_address_get_path(&p->address); - else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL) + else if (IN_SET(p->type, SOCKET_FIFO, SOCKET_SPECIAL, SOCKET_USB_FUNCTION)) path = p->path; if (!path) @@ -278,6 +296,9 @@ static int socket_add_default_dependencies(Socket *s) { int r; assert(s); + if (!UNIT(s)->default_dependencies) + return 0; + r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true); if (r < 0) return r; @@ -347,11 +368,9 @@ static int socket_add_extras(Socket *s) { return r; } - if (u->default_dependencies) { - r = socket_add_default_dependencies(s); - if (r < 0) - return r; - } + r = socket_add_default_dependencies(s); + if (r < 0) + return r; return 0; } @@ -494,6 +513,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sPassSecurity: %s\n" "%sTCPCongestion: %s\n" "%sRemoveOnStop: %s\n" + "%sWritable: %s\n" + "%sFDName: %s\n" "%sSELinuxContextFromNet: %s\n", prefix, socket_state_to_string(s->state), prefix, socket_result_to_string(s->result), @@ -510,6 +531,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(s->pass_sec), prefix, strna(s->tcp_congestion), prefix, yes_no(s->remove_on_stop), + prefix, yes_no(s->writable), + prefix, socket_fdname(s), prefix, yes_no(s->selinux_context_from_net)); if (s->control_pid > 0) @@ -630,7 +653,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { int r; char *k = NULL; - if ((r = socket_address_print(&p->address, &k)) < 0) + r = socket_address_print(&p->address, &k); + if (r < 0) t = strerror(-r); else t = k; @@ -639,6 +663,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { free(k); } else if (p->type == SOCKET_SPECIAL) fprintf(f, "%sListenSpecial: %s\n", prefix, p->path); + else if (p->type == SOCKET_USB_FUNCTION) + fprintf(f, "%sListenUSBFunction: %s\n", prefix, p->path); else if (p->type == SOCKET_MQUEUE) fprintf(f, "%sListenMessageQueue: %s\n", prefix, p->path); else @@ -775,6 +801,7 @@ static void socket_close_fds(Socket *s) { continue; p->fd = safe_close(p->fd); + socket_cleanup_fd_list(p); /* One little note: we should normally not delete any * sockets in the file system here! After all some @@ -940,50 +967,48 @@ static void socket_apply_fifo_options(Socket *s, int fd) { if (s->pipe_size > 0) if (fcntl(fd, F_SETPIPE_SZ, s->pipe_size) < 0) - log_unit_warning_errno(UNIT(s), errno, "F_SETPIPE_SZ: %m"); + log_unit_warning_errno(UNIT(s), errno, "Setting pipe size failed, ignoring: %m"); if (s->smack) { r = mac_smack_apply_fd(fd, SMACK_ATTR_ACCESS, s->smack); if (r < 0) - log_unit_error_errno(UNIT(s), r, "mac_smack_apply_fd: %m"); + log_unit_error_errno(UNIT(s), r, "SMACK relabelling failed, ignoring: %m"); } } static int fifo_address_create( const char *path, mode_t directory_mode, - mode_t socket_mode, - int *_fd) { + mode_t socket_mode) { - int fd = -1, r = 0; - struct stat st; + _cleanup_close_ int fd = -1; mode_t old_mask; + struct stat st; + int r; assert(path); - assert(_fd); mkdir_parents_label(path, directory_mode); r = mac_selinux_create_file_prepare(path, S_IFIFO); if (r < 0) - goto fail; + return r; /* Enforce the right access mode for the fifo */ old_mask = umask(~ socket_mode); /* Include the original umask in our mask */ - umask(~socket_mode | old_mask); + (void) umask(~socket_mode | old_mask); r = mkfifo(path, socket_mode); - umask(old_mask); + (void) umask(old_mask); if (r < 0 && errno != EEXIST) { r = -errno; goto fail; } - fd = open(path, - O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK | O_NOFOLLOW); + fd = open(path, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK | O_NOFOLLOW); if (fd < 0) { r = -errno; goto fail; @@ -1000,53 +1025,64 @@ static int fifo_address_create( (st.st_mode & 0777) != (socket_mode & ~old_mask) || st.st_uid != getuid() || st.st_gid != getgid()) { - r = -EEXIST; goto fail; } - *_fd = fd; - return 0; + r = fd; + fd = -1; + + return r; fail: mac_selinux_create_file_clear(); - safe_close(fd); - return r; } -static int special_address_create( - const char *path, - int *_fd) { - - int fd = -1, r = 0; +static int special_address_create(const char *path, bool writable) { + _cleanup_close_ int fd = -1; struct stat st; + int r; assert(path); - assert(_fd); - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW); - if (fd < 0) { - r = -errno; - goto fail; - } + fd = open(path, (writable ? O_RDWR : O_RDONLY)|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW); + if (fd < 0) + return -errno; - if (fstat(fd, &st) < 0) { - r = -errno; - goto fail; - } + if (fstat(fd, &st) < 0) + return -errno; /* Check whether this is a /proc, /sys or /dev file or char device */ - if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) { - r = -EEXIST; - goto fail; - } + if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) + return -EEXIST; - *_fd = fd; - return 0; + r = fd; + fd = -1; -fail: - safe_close(fd); + return r; +} + +static int usbffs_address_create(const char *path) { + _cleanup_close_ int fd = -1; + struct stat st; + int r; + + assert(path); + + fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW); + if (fd < 0) + return -errno; + + if (fstat(fd, &st) < 0) + return -errno; + + /* Check whether this is a regular file (ffs endpoint)*/ + if (!S_ISREG(st.st_mode)) + return -EEXIST; + + r = fd; + fd = -1; return r; } @@ -1055,22 +1091,22 @@ static int mq_address_create( const char *path, mode_t mq_mode, long maxmsg, - long msgsize, - int *_fd) { + long msgsize) { - int fd = -1, r = 0; + _cleanup_close_ int fd = -1; struct stat st; mode_t old_mask; struct mq_attr _attr, *attr = NULL; + int r; assert(path); - assert(_fd); if (maxmsg > 0 && msgsize > 0) { - zero(_attr); - _attr.mq_flags = O_NONBLOCK; - _attr.mq_maxmsg = maxmsg; - _attr.mq_msgsize = msgsize; + _attr = (struct mq_attr) { + .mq_flags = O_NONBLOCK, + .mq_maxmsg = maxmsg, + .mq_msgsize = msgsize, + }; attr = &_attr; } @@ -1078,33 +1114,24 @@ static int mq_address_create( old_mask = umask(~ mq_mode); /* Include the original umask in our mask */ - umask(~mq_mode | old_mask); + (void) umask(~mq_mode | old_mask); fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr); - umask(old_mask); + (void) umask(old_mask); - if (fd < 0) { - r = -errno; - goto fail; - } + if (fd < 0) + return -errno; - if (fstat(fd, &st) < 0) { - r = -errno; - goto fail; - } + if (fstat(fd, &st) < 0) + return -errno; if ((st.st_mode & 0777) != (mq_mode & ~old_mask) || st.st_uid != getuid() || - st.st_gid != getgid()) { + st.st_gid != getgid()) + return -EEXIST; - r = -EEXIST; - goto fail; - } + r = fd; + fd = -1; - *_fd = fd; - return 0; - -fail: - safe_close(fd); return r; } @@ -1124,11 +1151,78 @@ static int socket_symlink(Socket *s) { return 0; } +static int usbffs_write_descs(int fd, Service *s) { + int r; + + if (!s->usb_function_descriptors || !s->usb_function_strings) + return -EINVAL; + + r = copy_file_fd(s->usb_function_descriptors, fd, false); + if (r < 0) + return r; + + return copy_file_fd(s->usb_function_strings, fd, false); +} + +static int usbffs_select_ep(const struct dirent *d) { + return d->d_name[0] != '.' && !streq(d->d_name, "ep0"); +} + +static int usbffs_dispatch_eps(SocketPort *p) { + _cleanup_free_ struct dirent **ent = NULL; + _cleanup_free_ char *path = NULL; + int r, i, n, k; + + path = dirname_malloc(p->path); + if (!path) + return -ENOMEM; + + r = scandir(path, &ent, usbffs_select_ep, alphasort); + if (r < 0) + return -errno; + + n = r; + p->auxiliary_fds = new(int, n); + if (!p->auxiliary_fds) + return -ENOMEM; + + p->n_auxiliary_fds = n; + + k = 0; + for (i = 0; i < n; ++i) { + _cleanup_free_ char *ep = NULL; + + ep = path_make_absolute(ent[i]->d_name, path); + if (!ep) + return -ENOMEM; + + path_kill_slashes(ep); + + r = usbffs_address_create(ep); + if (r < 0) + goto fail; + + p->auxiliary_fds[k] = r; + + ++k; + free(ent[i]); + } + + return r; + +fail: + close_many(p->auxiliary_fds, k); + p->auxiliary_fds = mfree(p->auxiliary_fds); + p->n_auxiliary_fds = 0; + + return r; +} + static int socket_open_fds(Socket *s) { + _cleanup_(mac_selinux_freep) char *label = NULL; + bool know_label = false; SocketPort *p; int r; - char *label = NULL; - bool know_label = false; assert(s); @@ -1137,7 +1231,9 @@ static int socket_open_fds(Socket *s) { if (p->fd >= 0) continue; - if (p->type == SOCKET_SOCKET) { + switch (p->type) { + + case SOCKET_SOCKET: if (!know_label) { /* Figure out label, if we don't it know @@ -1170,6 +1266,19 @@ static int socket_open_fds(Socket *s) { know_label = true; } + /* Apply the socket protocol */ + switch(p->address.type) { + case SOCK_STREAM: + case SOCK_SEQPACKET: + if (p->socket->socket_protocol == IPPROTO_SCTP) + p->address.protocol = p->socket->socket_protocol; + break; + case SOCK_DGRAM: + if (p->socket->socket_protocol == IPPROTO_UDPLITE) + p->address.protocol = p->socket->socket_protocol; + break; + } + r = socket_address_listen( &p->address, SOCK_CLOEXEC|SOCK_NONBLOCK, @@ -1188,49 +1297,72 @@ static int socket_open_fds(Socket *s) { p->fd = r; socket_apply_socket_options(s, p->fd); socket_symlink(s); + break; - } else if (p->type == SOCKET_SPECIAL) { + case SOCKET_SPECIAL: - r = special_address_create( - p->path, - &p->fd); - if (r < 0) + p->fd = special_address_create(p->path, s->writable); + if (p->fd < 0) { + r = p->fd; goto rollback; + } + break; - } else if (p->type == SOCKET_FIFO) { + case SOCKET_FIFO: - r = fifo_address_create( + p->fd = fifo_address_create( p->path, s->directory_mode, - s->socket_mode, - &p->fd); - if (r < 0) + s->socket_mode); + if (p->fd < 0) { + r = p->fd; goto rollback; + } socket_apply_fifo_options(s, p->fd); socket_symlink(s); + break; - } else if (p->type == SOCKET_MQUEUE) { + case SOCKET_MQUEUE: - r = mq_address_create( + p->fd = mq_address_create( p->path, s->socket_mode, s->mq_maxmsg, - s->mq_msgsize, - &p->fd); + s->mq_msgsize); + if (p->fd < 0) { + r = p->fd; + goto rollback; + } + break; + + case SOCKET_USB_FUNCTION: + + p->fd = usbffs_address_create(p->path); + if (p->fd < 0) { + r = p->fd; + goto rollback; + } + + r = usbffs_write_descs(p->fd, SERVICE(UNIT_DEREF(s->service))); if (r < 0) goto rollback; - } else + + r = usbffs_dispatch_eps(p); + if (r < 0) + goto rollback; + + break; + + default: assert_not_reached("Unknown port type"); + } } - mac_selinux_free(label); return 0; rollback: socket_close_fds(s); - mac_selinux_free(label); - return r; } @@ -1337,7 +1469,9 @@ static int socket_coldplug(Unit *u) { if (s->deserialized_state == s->state) return 0; - if (IN_SET(s->deserialized_state, + if (s->control_pid > 0 && + pid_is_unwaited(s->control_pid) && + IN_SET(s->deserialized_state, SOCKET_START_PRE, SOCKET_START_CHOWN, SOCKET_START_POST, @@ -1348,9 +1482,6 @@ static int socket_coldplug(Unit *u) { SOCKET_FINAL_SIGTERM, SOCKET_FINAL_SIGKILL)) { - if (s->control_pid <= 0) - return -EBADMSG; - r = unit_watch_pid(UNIT(s), s->control_pid); if (r < 0) return r; @@ -1392,6 +1523,9 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { .apply_chroot = true, .apply_tty_stdin = true, .bus_endpoint_fd = -1, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(s); @@ -1800,7 +1934,7 @@ static void socket_enter_running(Socket *s, int cfd) { goto fail; } - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, &error, NULL); if (r < 0) goto fail; } @@ -1858,7 +1992,7 @@ static void socket_enter_running(Socket *s, int cfd) { cfd = -1; s->n_connections ++; - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); if (r < 0) goto fail; @@ -2035,6 +2169,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path); else if (p->type == SOCKET_MQUEUE) unit_serialize_item_format(u, f, "mqueue", "%i %s", copy, p->path); + else if (p->type == SOCKET_USB_FUNCTION) + unit_serialize_item_format(u, f, "ffs", "%i %s", copy, p->path); else { assert(p->type == SOCKET_FIFO); unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path); @@ -2184,13 +2320,33 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, p->fd = fdset_remove(fds, fd); } } + + } else if (streq(key, "ffs")) { + int fd, skip = 0; + SocketPort *p; + + if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse ffs value: %s", value); + else { + + LIST_FOREACH(port, p, s->ports) + if (p->type == SOCKET_USB_FUNCTION && + path_equal_or_files_same(p->path, value+skip)) + break; + + if (p) { + safe_close(p->fd); + p->fd = fdset_remove(fds, fd); + } + } + } else log_unit_debug(UNIT(s), "Unknown serialization key: %s", key); return 0; } -static int socket_distribute_fds(Unit *u, FDSet *fds) { +static void socket_distribute_fds(Unit *u, FDSet *fds) { Socket *s = SOCKET(u); SocketPort *p; @@ -2214,8 +2370,6 @@ static int socket_distribute_fds(Unit *u, FDSet *fds) { } } } - - return 0; } _pure_ static UnitActiveState socket_active_state(Unit *u) { @@ -2266,6 +2420,9 @@ const char* socket_port_type_to_string(SocketPort *p) { case SOCKET_FIFO: return "FIFO"; + case SOCKET_USB_FUNCTION: + return "USBFunction"; + default: return NULL; } @@ -2297,7 +2454,6 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, log_unit_error(UNIT(p->socket), "Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that."); else log_unit_error(UNIT(p->socket), "Got unexpected poll event (0x%x) on socket.", revents); - goto fail; } @@ -2493,43 +2649,43 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use return 0; } -int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) { - int *rfds; - unsigned rn_fds, k; +int socket_collect_fds(Socket *s, int **fds) { + int *rfds, k = 0, n = 0; SocketPort *p; assert(s); assert(fds); - assert(n_fds); /* Called from the service code for requesting our fds */ - rn_fds = 0; - LIST_FOREACH(port, p, s->ports) + LIST_FOREACH(port, p, s->ports) { if (p->fd >= 0) - rn_fds++; + n++; + n += p->n_auxiliary_fds; + } - if (rn_fds <= 0) { + if (n <= 0) { *fds = NULL; - *n_fds = 0; return 0; } - rfds = new(int, rn_fds); + rfds = new(int, n); if (!rfds) return -ENOMEM; - k = 0; - LIST_FOREACH(port, p, s->ports) + LIST_FOREACH(port, p, s->ports) { + int i; + if (p->fd >= 0) rfds[k++] = p->fd; + for (i = 0; i < p->n_auxiliary_fds; ++i) + rfds[k++] = p->auxiliary_fds[i]; + } - assert(k == rn_fds); + assert(k == n); *fds = rfds; - *n_fds = rn_fds; - - return 0; + return n; } static void socket_reset_failed(Unit *u) { @@ -2625,23 +2781,18 @@ static int socket_get_timeout(Unit *u, uint64_t *timeout) { return 1; } -static const char* const socket_state_table[_SOCKET_STATE_MAX] = { - [SOCKET_DEAD] = "dead", - [SOCKET_START_PRE] = "start-pre", - [SOCKET_START_CHOWN] = "start-chown", - [SOCKET_START_POST] = "start-post", - [SOCKET_LISTENING] = "listening", - [SOCKET_RUNNING] = "running", - [SOCKET_STOP_PRE] = "stop-pre", - [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm", - [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill", - [SOCKET_STOP_POST] = "stop-post", - [SOCKET_FINAL_SIGTERM] = "final-sigterm", - [SOCKET_FINAL_SIGKILL] = "final-sigkill", - [SOCKET_FAILED] = "failed" -}; +char *socket_fdname(Socket *s) { + assert(s); -DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState); + /* Returns the name to use for $LISTEN_NAMES. If the user + * didn't specify anything specifically, use the socket unit's + * name as fallback. */ + + if (s->fdname) + return s->fdname; + + return UNIT(s)->id; +} static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { [SOCKET_EXEC_START_PRE] = "StartPre", diff --git a/src/core/socket.h b/src/core/socket.h index fa3ebdafa0..08033287a6 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -23,27 +23,9 @@ typedef struct Socket Socket; -#include "socket-util.h" #include "mount.h" #include "service.h" - -typedef enum SocketState { - SOCKET_DEAD, - SOCKET_START_PRE, - SOCKET_START_CHOWN, - SOCKET_START_POST, - SOCKET_LISTENING, - SOCKET_RUNNING, - SOCKET_STOP_PRE, - SOCKET_STOP_PRE_SIGTERM, - SOCKET_STOP_PRE_SIGKILL, - SOCKET_STOP_POST, - SOCKET_FINAL_SIGTERM, - SOCKET_FINAL_SIGKILL, - SOCKET_FAILED, - _SOCKET_STATE_MAX, - _SOCKET_STATE_INVALID = -1 -} SocketState; +#include "socket-util.h" typedef enum SocketExecCommand { SOCKET_EXEC_START_PRE, @@ -60,6 +42,7 @@ typedef enum SocketType { SOCKET_FIFO, SOCKET_SPECIAL, SOCKET_MQUEUE, + SOCKET_USB_FUNCTION, _SOCKET_FIFO_MAX, _SOCKET_FIFO_INVALID = -1 } SocketType; @@ -81,6 +64,8 @@ typedef struct SocketPort { SocketType type; int fd; + int *auxiliary_fds; + int n_auxiliary_fds; SocketAddress address; char *path; @@ -133,6 +118,9 @@ struct Socket { bool accept; bool remove_on_stop; + bool writable; + + int socket_protocol; /* Socket options */ bool keep_alive; @@ -168,20 +156,23 @@ struct Socket { char *user, *group; bool reset_cpu_usage:1; + + char *fdname; }; /* Called from the service code when collecting fds */ -int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds); +int socket_collect_fds(Socket *s, int **fds); /* 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; +int socket_instantiate_service(Socket *s); + +char *socket_fdname(Socket *s); -const char* socket_state_to_string(SocketState i) _const_; -SocketState socket_state_from_string(const char *s) _pure_; +extern const UnitVTable socket_vtable; const char* socket_exec_command_to_string(SocketExecCommand i) _const_; SocketExecCommand socket_exec_command_from_string(const char *s) _pure_; @@ -190,5 +181,3 @@ const char* socket_result_to_string(SocketResult i) _const_; SocketResult socket_result_from_string(const char *s) _pure_; const char* socket_port_type_to_string(SocketPort *p) _pure_; - -int socket_instantiate_service(Socket *s); diff --git a/src/core/swap.c b/src/core/swap.c index 311ce7ee04..ee0838e676 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -20,22 +20,30 @@ ***/ #include <errno.h> -#include <unistd.h> #include <sys/epoll.h> #include <sys/stat.h> -#include <libudev.h> +#include <unistd.h> -#include "unit.h" -#include "swap.h" -#include "unit-name.h" +#include "libudev.h" + +#include "alloc-util.h" #include "dbus-swap.h" -#include "special.h" +#include "escape.h" #include "exit-status.h" +#include "fd-util.h" +#include "formats-util.h" +#include "fstab-util.h" +#include "parse-util.h" #include "path-util.h" -#include "virt.h" +#include "process-util.h" +#include "special.h" +#include "string-table.h" +#include "string-util.h" +#include "swap.h" #include "udev-util.h" -#include "fstab-util.h" -#include "formats-util.h" +#include "unit-name.h" +#include "unit.h" +#include "virt.h" static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = { [SWAP_DEAD] = UNIT_INACTIVE, @@ -205,6 +213,9 @@ static int swap_add_device_links(Swap *s) { static int swap_add_default_dependencies(Swap *s) { assert(s); + if (!UNIT(s)->default_dependencies) + return 0; + if (UNIT(s)->manager->running_as != MANAGER_SYSTEM) return 0; @@ -323,11 +334,9 @@ static int swap_load(Unit *u) { if (r < 0) return r; - if (UNIT(s)->default_dependencies) { - r = swap_add_default_dependencies(s); - if (r < 0) - return r; - } + r = swap_add_default_dependencies(s); + if (r < 0) + return r; } return swap_verify(s); @@ -520,16 +529,16 @@ static int swap_coldplug(Unit *u) { if (new_state == s->state) return 0; - if (new_state == SWAP_ACTIVATING || - new_state == SWAP_ACTIVATING_SIGTERM || - new_state == SWAP_ACTIVATING_SIGKILL || - new_state == SWAP_ACTIVATING_DONE || - new_state == SWAP_DEACTIVATING || - new_state == SWAP_DEACTIVATING_SIGTERM || - new_state == SWAP_DEACTIVATING_SIGKILL) { - - if (s->control_pid <= 0) - return -EBADMSG; + if (s->control_pid > 0 && + pid_is_unwaited(s->control_pid) && + IN_SET(new_state, + SWAP_ACTIVATING, + SWAP_ACTIVATING_SIGTERM, + SWAP_ACTIVATING_SIGKILL, + SWAP_ACTIVATING_DONE, + SWAP_DEACTIVATING, + SWAP_DEACTIVATING_SIGTERM, + SWAP_DEACTIVATING_SIGKILL)) { r = unit_watch_pid(UNIT(s), s->control_pid); if (r < 0) @@ -597,6 +606,9 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { .apply_chroot = true, .apply_tty_stdin = true, .bus_endpoint_fd = -1, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(s); @@ -1195,7 +1207,7 @@ static Unit *swap_following(Unit *u) { if (other->from_fragment) return UNIT(other); - /* Otherwise make everybody follow the unit that's named after + /* Otherwise, make everybody follow the unit that's named after * the swap device in the kernel */ if (streq_ptr(s->what, s->devnode)) @@ -1254,30 +1266,39 @@ static void swap_shutdown(Manager *m) { m->proc_swaps = safe_fclose(m->proc_swaps); - hashmap_free(m->swaps_by_devnode); - m->swaps_by_devnode = NULL; + m->swaps_by_devnode = hashmap_free(m->swaps_by_devnode); } -static int swap_enumerate(Manager *m) { +static void swap_enumerate(Manager *m) { int r; assert(m); if (!m->proc_swaps) { m->proc_swaps = fopen("/proc/swaps", "re"); - if (!m->proc_swaps) - return errno == ENOENT ? 0 : -errno; + if (!m->proc_swaps) { + if (errno == ENOENT) + log_debug("Not swap enabled, skipping enumeration"); + else + log_error_errno(errno, "Failed to open /proc/swaps: %m"); + + return; + } r = sd_event_add_io(m->event, &m->swap_event_source, fileno(m->proc_swaps), EPOLLPRI, swap_dispatch_io, m); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to watch /proc/swaps: %m"); goto fail; + } /* Dispatch this before we dispatch SIGCHLD, so that * we always get the events from /proc/swaps before * the SIGCHLD of /sbin/swapon. */ r = sd_event_source_set_priority(m->swap_event_source, -10); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to change /proc/swaps priority: %m"); goto fail; + } (void) sd_event_source_set_description(m->swap_event_source, "swap-proc"); } @@ -1286,11 +1307,10 @@ static int swap_enumerate(Manager *m) { if (r < 0) goto fail; - return 0; + return; fail: swap_shutdown(m); - return r; } int swap_process_device_new(Manager *m, struct udev_device *dev) { @@ -1399,21 +1419,6 @@ static bool swap_supported(void) { return supported; } -static const char* const swap_state_table[_SWAP_STATE_MAX] = { - [SWAP_DEAD] = "dead", - [SWAP_ACTIVATING] = "activating", - [SWAP_ACTIVATING_DONE] = "activating-done", - [SWAP_ACTIVE] = "active", - [SWAP_DEACTIVATING] = "deactivating", - [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm", - [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill", - [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm", - [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill", - [SWAP_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState); - static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = { [SWAP_EXEC_ACTIVATE] = "ExecActivate", [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate", diff --git a/src/core/swap.h b/src/core/swap.h index 9136b9abab..303b926568 100644 --- a/src/core/swap.h +++ b/src/core/swap.h @@ -22,26 +22,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <libudev.h> +#include "libudev.h" typedef struct Swap Swap; - -typedef enum SwapState { - SWAP_DEAD, - SWAP_ACTIVATING, /* /sbin/swapon is running, but the swap not yet enabled. */ - SWAP_ACTIVATING_DONE, /* /sbin/swapon is running, and the swap is done. */ - SWAP_ACTIVE, - SWAP_DEACTIVATING, - SWAP_ACTIVATING_SIGTERM, - SWAP_ACTIVATING_SIGKILL, - SWAP_DEACTIVATING_SIGTERM, - SWAP_DEACTIVATING_SIGKILL, - SWAP_FAILED, - _SWAP_STATE_MAX, - _SWAP_STATE_INVALID = -1 -} SwapState; - typedef enum SwapExecCommand { SWAP_EXEC_ACTIVATE, SWAP_EXEC_DEACTIVATE, @@ -120,9 +104,6 @@ extern const UnitVTable swap_vtable; int swap_process_device_new(Manager *m, struct udev_device *dev); int swap_process_device_remove(Manager *m, struct udev_device *dev); -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) _const_; SwapExecCommand swap_exec_command_from_string(const char *s) _pure_; diff --git a/src/core/system.conf b/src/core/system.conf index 231609033b..e2ded27333 100644 --- a/src/core/system.conf +++ b/src/core/system.conf @@ -17,9 +17,10 @@ #LogColor=yes #LogLocation=no #DumpCore=yes -#CrashShell=no #ShowStatus=yes -#CrashChVT=1 +#CrashChangeVT=no +#CrashShell=no +#CrashReboot=no #CPUAffinity=1 2 #JoinControllers=cpu,cpuacct net_cls,net_prio #RuntimeWatchdogSec=0 @@ -39,6 +40,8 @@ #DefaultCPUAccounting=no #DefaultBlockIOAccounting=no #DefaultMemoryAccounting=no +#DefaultTasksAccounting=yes +#DefaultTasksMax=512 #DefaultLimitCPU= #DefaultLimitFSIZE= #DefaultLimitDATA= diff --git a/src/core/target.c b/src/core/target.c index f714cb31c2..14f9b2e26a 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -19,13 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ - -#include "unit.h" -#include "target.h" -#include "log.h" #include "dbus-target.h" +#include "log.h" #include "special.h" +#include "string-util.h" #include "unit-name.h" +#include "unit.h" +#include "target.h" static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = { [TARGET_DEAD] = UNIT_INACTIVE, @@ -52,9 +52,7 @@ static int target_add_default_dependencies(Target *t) { static const UnitDependency deps[] = { UNIT_REQUIRES, - UNIT_REQUIRES_OVERRIDABLE, UNIT_REQUISITE, - UNIT_REQUISITE_OVERRIDABLE, UNIT_WANTS, UNIT_BINDS_TO, UNIT_PART_OF @@ -192,13 +190,6 @@ _pure_ static const char *target_sub_state_to_string(Unit *u) { return target_state_to_string(TARGET(u)->state); } -static const char* const target_state_table[_TARGET_STATE_MAX] = { - [TARGET_DEAD] = "dead", - [TARGET_ACTIVE] = "active" -}; - -DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState); - const UnitVTable target_vtable = { .object_size = sizeof(Target), diff --git a/src/core/target.h b/src/core/target.h index 0a25ef469a..3cc6c07bfa 100644 --- a/src/core/target.h +++ b/src/core/target.h @@ -23,14 +23,6 @@ typedef struct Target Target; - -typedef enum TargetState { - TARGET_DEAD, - TARGET_ACTIVE, - _TARGET_STATE_MAX, - _TARGET_STATE_INVALID = -1 -} TargetState; - struct Target { Unit meta; @@ -38,6 +30,3 @@ struct Target { }; extern const UnitVTable target_vtable; - -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 eb6567bbfa..6b0f8e8616 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -21,13 +21,21 @@ #include <errno.h> -#include "unit.h" -#include "unit-name.h" -#include "timer.h" +#include "alloc-util.h" +#include "bus-error.h" +#include "bus-util.h" #include "dbus-timer.h" +#include "fs-util.h" +#include "parse-util.h" +#include "random-util.h" #include "special.h" -#include "bus-util.h" -#include "bus-error.h" +#include "string-table.h" +#include "string-util.h" +#include "timer.h" +#include "unit-name.h" +#include "unit.h" +#include "user-util.h" +#include "virt.h" static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = { [TIMER_DEAD] = UNIT_INACTIVE, @@ -48,6 +56,7 @@ static void timer_init(Unit *u) { t->next_elapse_monotonic_or_boottime = USEC_INFINITY; t->next_elapse_realtime = USEC_INFINITY; t->accuracy_usec = u->manager->default_timer_accuracy_usec; + t->remain_after_elapse = true; } void timer_free_values(Timer *t) { @@ -95,6 +104,9 @@ static int timer_add_default_dependencies(Timer *t) { assert(t); + if (!UNIT(t)->default_dependencies) + return 0; + r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true); if (r < 0) return r; @@ -185,11 +197,9 @@ static int timer_load(Unit *u) { if (r < 0) return r; - if (u->default_dependencies) { - r = timer_add_default_dependencies(t); - if (r < 0) - return r; - } + r = timer_add_default_dependencies(t); + if (r < 0) + return r; } return timer_verify(t); @@ -209,13 +219,15 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) { "%sUnit: %s\n" "%sPersistent: %s\n" "%sWakeSystem: %s\n" - "%sAccuracy: %s\n", + "%sAccuracy: %s\n" + "%sRemainAfterElapse: %s\n", prefix, timer_state_to_string(t->state), prefix, timer_result_to_string(t->result), prefix, trigger ? trigger->id : "n/a", prefix, yes_no(t->persistent), prefix, yes_no(t->wake_system), - prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1)); + prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1), + prefix, yes_no(t->remain_after_elapse)); LIST_FOREACH(value, v, t->values) { @@ -267,13 +279,13 @@ static int timer_coldplug(Unit *u) { assert(t); assert(t->state == TIMER_DEAD); - if (t->deserialized_state != t->state) { + if (t->deserialized_state == t->state) + return 0; - if (t->deserialized_state == TIMER_WAITING) - timer_enter_waiting(t, false); - else - timer_set_state(t, t->deserialized_state); - } + if (t->deserialized_state == TIMER_WAITING) + timer_enter_waiting(t, false); + else + timer_set_state(t, t->deserialized_state); return 0; } @@ -287,6 +299,23 @@ static void timer_enter_dead(Timer *t, TimerResult f) { timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD); } +static void timer_enter_elapsed(Timer *t, bool leave_around) { + assert(t); + + /* If a unit is marked with RemainAfterElapse=yes we leave it + * around even after it elapsed once, so that starting it + * later again does not necessarily mean immediate + * retriggering. We unconditionally leave units with + * TIMER_UNIT_ACTIVE or TIMER_UNIT_INACTIVE triggers around, + * since they might be restarted automatically at any time + * later on. */ + + if (t->remain_after_elapse || leave_around) + timer_set_state(t, TIMER_ELAPSED); + else + timer_enter_dead(t, TIMER_SUCCESS); +} + static usec_t monotonic_to_boottime(usec_t t) { usec_t a, b; @@ -302,10 +331,33 @@ static usec_t monotonic_to_boottime(usec_t t) { return 0; } +static void add_random(Timer *t, usec_t *v) { + char s[FORMAT_TIMESPAN_MAX]; + usec_t add; + + assert(t); + assert(*v); + + if (t->random_usec == 0) + return; + if (*v == USEC_INFINITY) + return; + + add = random_u64() % t->random_usec; + + if (*v + add < *v) /* overflow */ + *v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */ + else + *v += add; + + log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0)); +} + static void timer_enter_waiting(Timer *t, bool initial) { bool found_monotonic = false, found_realtime = false; usec_t ts_realtime, ts_monotonic; usec_t base = 0; + bool leave_around = false; TimerValue *v; int r; @@ -353,16 +405,20 @@ static void timer_enter_waiting(Timer *t, bool initial) { break; case TIMER_BOOT: - /* CLOCK_MONOTONIC equals the uptime on Linux */ - base = 0; - break; - + if (detect_container() <= 0) { + /* CLOCK_MONOTONIC equals the uptime on Linux */ + base = 0; + break; + } + /* In a container we don't want to include the time the host + * was already up when the container started, so count from + * our own startup. Fall through. */ case TIMER_STARTUP: base = UNIT(t)->manager->userspace_timestamp.monotonic; break; case TIMER_UNIT_ACTIVE: - + leave_around = true; base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic; if (base <= 0) @@ -374,7 +430,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { break; case TIMER_UNIT_INACTIVE: - + leave_around = true; base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic; if (base <= 0) @@ -411,14 +467,18 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (!found_monotonic && !found_realtime) { log_unit_debug(UNIT(t), "Timer is elapsed."); - timer_set_state(t, TIMER_ELAPSED); + timer_enter_elapsed(t, leave_around); return; } if (found_monotonic) { char buf[FORMAT_TIMESPAN_MAX]; + usec_t left; - log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0)); + add_random(t, &t->next_elapse_monotonic_or_boottime); + + left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0; + log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0)); if (t->monotonic_event_source) { r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime); @@ -451,6 +511,9 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (found_realtime) { char buf[FORMAT_TIMESTAMP_MAX]; + + add_random(t, &t->next_elapse_realtime); + log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime)); if (t->realtime_event_source) { @@ -499,15 +562,14 @@ static void timer_enter_running(Timer *t) { if (unit_stop_pending(UNIT(t))) return; - r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)), - JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)), JOB_REPLACE, &error, NULL); if (r < 0) goto fail; dual_timestamp_get(&t->last_trigger); if (t->stamp_path) - touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, 0); + touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, MODE_INVALID); timer_set_state(t, TIMER_RUNNING); return; @@ -543,7 +605,7 @@ static int timer_start(Unit *u) { /* The timer has never run before, * make sure a stamp file exists. */ - touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0); + touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); } t->result = TIMER_SUCCESS; @@ -713,16 +775,6 @@ static void timer_time_change(Unit *u) { timer_enter_waiting(t, false); } -static const char* const timer_state_table[_TIMER_STATE_MAX] = { - [TIMER_DEAD] = "dead", - [TIMER_WAITING] = "waiting", - [TIMER_RUNNING] = "running", - [TIMER_ELAPSED] = "elapsed", - [TIMER_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState); - static const char* const timer_base_table[_TIMER_BASE_MAX] = { [TIMER_ACTIVE] = "OnActiveSec", [TIMER_BOOT] = "OnBootSec", diff --git a/src/core/timer.h b/src/core/timer.h index 9d919e4d3e..0599f07818 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -25,16 +25,6 @@ typedef struct Timer Timer; #include "calendarspec.h" -typedef enum TimerState { - TIMER_DEAD, - TIMER_WAITING, - TIMER_RUNNING, - TIMER_ELAPSED, - TIMER_FAILED, - _TIMER_STATE_MAX, - _TIMER_STATE_INVALID = -1 -} TimerState; - typedef enum TimerBase { TIMER_ACTIVE, TIMER_BOOT, @@ -68,6 +58,7 @@ struct Timer { Unit meta; usec_t accuracy_usec; + usec_t random_usec; LIST_HEAD(TimerValue, values); usec_t next_elapse_realtime; @@ -83,6 +74,7 @@ struct Timer { bool persistent; bool wake_system; + bool remain_after_elapse; char *stamp_path; }; @@ -91,9 +83,6 @@ void timer_free_values(Timer *t); extern const UnitVTable timer_vtable; -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) _const_; TimerBase timer_base_from_string(const char *s) _pure_; diff --git a/src/core/transaction.c b/src/core/transaction.c index b8f69ec6f3..15e79d00b3 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -19,13 +19,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> #include <fcntl.h> +#include <unistd.h> +#include "alloc-util.h" #include "bus-common-errors.h" #include "bus-error.h" -#include "transaction.h" #include "terminal-util.h" +#include "transaction.h" static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies); @@ -98,9 +99,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; /* Patch us in as new owner of the JobDependency objects */ @@ -401,7 +400,7 @@ 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)); - unit_status_printf(delete->unit, ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF, + unit_status_printf(delete->unit, ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL, "Ordering cycle found, skipping %s"); transaction_delete_unit(tr, delete->unit); return -EAGAIN; @@ -736,15 +735,15 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0 && m->idle_pipe[2] < 0 && m->idle_pipe[3] < 0) { - pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC); - pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC); + (void) pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC); + (void) pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC); } } return 0; } -static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) { +static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool *is_new) { Job *j, *f; assert(tr); @@ -773,7 +772,6 @@ static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, b j->generation = 0; j->marker = NULL; j->matters_to_anchor = false; - j->override = override; j->irreversible = tr->irreversible; LIST_PREPEND(transaction, f, j); @@ -832,7 +830,6 @@ int transaction_add_job_and_dependencies( Unit *unit, Job *by, bool matters, - bool override, bool conflicts, bool ignore_requirements, bool ignore_order, @@ -894,7 +891,7 @@ int transaction_add_job_and_dependencies( /* First add the job. */ - ret = transaction_add_one_job(tr, type, unit, override, &is_new); + ret = transaction_add_one_job(tr, type, unit, &is_new); if (!ret) return -ENOMEM; @@ -917,7 +914,7 @@ int transaction_add_job_and_dependencies( * add all dependencies of everybody following. */ if (unit_following_set(ret->unit, &following) > 0) { SET_FOREACH(dep, following, i) { - r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, false, false, ignore_order, e); if (r < 0) { log_unit_warning(dep, "Cannot add dependency job for, ignoring: %s", bus_error_message(e, r)); sd_bus_error_free(e); @@ -930,7 +927,7 @@ int transaction_add_job_and_dependencies( /* Finally, recursively add in all dependencies. */ if (type == JOB_START || type == JOB_RESTART) { SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -940,7 +937,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -949,19 +946,8 @@ int transaction_add_job_and_dependencies( } } - SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e); - if (r < 0) { - log_unit_full(dep, - r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r, - "Cannot add dependency job, ignoring: %s", - bus_error_message(e, r)); - sd_bus_error_free(e); - } - } - SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e); if (r < 0) { log_unit_full(dep, r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r, @@ -972,7 +958,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) { - r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -981,19 +967,8 @@ int transaction_add_job_and_dependencies( } } - SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) { - r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e); - if (r < 0) { - log_unit_full(dep, - r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r, - "Cannot add dependency job, ignoring: %s", - bus_error_message(e, r)); - sd_bus_error_free(e); - } - } - SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) { - r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -1003,7 +978,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) { - r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e); if (r < 0) { log_unit_warning(dep, "Cannot add dependency job, ignoring: %s", @@ -1038,7 +1013,7 @@ int transaction_add_job_and_dependencies( if (nt == JOB_NOP) continue; - r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -1051,7 +1026,7 @@ int transaction_add_job_and_dependencies( if (type == JOB_RELOAD) { SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) { - r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, false, false, ignore_order, e); if (r < 0) { log_unit_warning(dep, "Cannot add dependency reload job, ignoring: %s", @@ -1096,7 +1071,7 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) { if (hashmap_get(tr->jobs, u)) continue; - r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL); + r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, NULL); if (r < 0) log_unit_warning_errno(u, r, "Cannot add isolate job, ignoring: %m"); } diff --git a/src/core/transaction.h b/src/core/transaction.h index d949b21b8d..5c4a13edab 100644 --- a/src/core/transaction.h +++ b/src/core/transaction.h @@ -23,10 +23,10 @@ typedef struct Transaction Transaction; -#include "unit.h" -#include "manager.h" -#include "job.h" #include "hashmap.h" +#include "job.h" +#include "manager.h" +#include "unit.h" struct Transaction { /* Jobs to be added */ @@ -44,7 +44,6 @@ int transaction_add_job_and_dependencies( Unit *unit, Job *by, bool matters, - bool override, bool conflicts, bool ignore_requirements, bool ignore_order, diff --git a/src/core/triggers.systemd.in b/src/core/triggers.systemd.in new file mode 100644 index 0000000000..141f42dbcf --- /dev/null +++ b/src/core/triggers.systemd.in @@ -0,0 +1,26 @@ +# -*- Mode: rpm-spec; indent-tabs-mode: nil -*- */ +# +# This file is part of systemd. +# +# Copyright 2015 Zbigniew Jędrzejewski-Szmek +# +# 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/>. + +# The contents of this are an example to be copied into systemd.spec. + +%transfiletriggerin -- @systemunitdir@ /etc/systemd/system +systemctl daemon-reload &>/dev/null || : + +%transfiletriggerun -- @systemunitdir@ /etc/systemd/system +systemctl daemon-reload &>/dev/null || : diff --git a/src/core/umount.c b/src/core/umount.c index 22dbe67259..9d1f7660db 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -21,23 +21,30 @@ #include <errno.h> #include <fcntl.h> +#include <linux/dm-ioctl.h> +#include <linux/loop.h> #include <string.h> #include <sys/mount.h> #include <sys/swap.h> -#include <linux/loop.h> -#include <linux/dm-ioctl.h> +#include "libudev.h" + +#include "alloc-util.h" +#include "escape.h" +#include "fd-util.h" +#include "fstab-util.h" #include "list.h" #include "mount-setup.h" -#include "umount.h" #include "path-util.h" +#include "string-util.h" +#include "udev-util.h" +#include "umount.h" #include "util.h" #include "virt.h" -#include "libudev.h" -#include "udev-util.h" typedef struct MountPoint { char *path; + char *options; dev_t devnum; LIST_FIELDS(struct MountPoint, mount_point); } MountPoint; @@ -71,7 +78,7 @@ static int mount_points_list_get(MountPoint **head) { return -errno; for (i = 1;; i++) { - _cleanup_free_ char *path = NULL; + _cleanup_free_ char *path = NULL, *options = NULL; char *p = NULL; MountPoint *m; int k; @@ -82,15 +89,15 @@ static int mount_points_list_get(MountPoint **head) { "%*s " /* (3) major:minor */ "%*s " /* (4) root */ "%ms " /* (5) mount point */ - "%*s" /* (6) mount options */ + "%*s" /* (6) mount flags */ "%*[^-]" /* (7) optional fields */ "- " /* (8) separator */ "%*s " /* (9) file system type */ "%*s" /* (10) mount source */ - "%*s" /* (11) mount options 2 */ + "%ms" /* (11) mount options */ "%*[^\n]", /* some rubbish at the end */ - &path); - if (k != 1) { + &path, &options); + if (k != 2) { if (k == EOF) break; @@ -125,6 +132,9 @@ static int mount_points_list_get(MountPoint **head) { } m->path = p; + m->options = options; + options = NULL; + LIST_PREPEND(mount_point, *head, m); } @@ -369,6 +379,14 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e benefits, but might confuse the host, as we remount the superblock here, not the bind mound. */ if (detect_container() <= 0) { + _cleanup_free_ char *options = NULL; + /* MS_REMOUNT requires that the data parameter + * should be the same from the original mount + * except for the desired changes. Since we want + * to remount read-only, we should filter out + * rw (and ro too, because it confuses the kernel) */ + (void) fstab_filter_options(m->options, "rw\0ro\0", NULL, NULL, &options); + /* We always try to remount directories * read-only first, before we go on and umount * them. @@ -385,7 +403,8 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e * alias read-only we hence should be * relatively safe regarding keeping the fs we * can otherwise not see dirty. */ - (void) mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL); + log_info("Remounting '%s' read-only with options '%s'.", m->path, options); + (void) mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, options); } /* Skip / and /usr since we cannot unmount that diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index 0889769d03..f587a5a141 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -19,14 +19,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" +#include "alloc-util.h" +#include "cgroup-util.h" +#include "formats-util.h" +#include "macro.h" #include "specifier.h" +#include "string-util.h" #include "strv.h" #include "unit-name.h" #include "unit-printf.h" -#include "macro.h" -#include "cgroup-util.h" -#include "formats-util.h" +#include "unit.h" +#include "user-util.h" static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) { Unit *u = userdata; @@ -63,10 +66,7 @@ static int specifier_instance_unescaped(char specifier, void *data, void *userda assert(u); - if (!u->instance) - return -EINVAL; - - return unit_name_unescape(u->instance, ret); + return unit_name_unescape(strempty(u->instance), ret); } static int specifier_filename(char specifier, void *data, void *userdata, char **ret) { @@ -128,6 +128,8 @@ static int specifier_cgroup_slice(char specifier, void *data, void *userdata, ch n = unit_default_cgroup_path(slice); } else n = strdup(u->manager->cgroup_root); + if (!n) + return -ENOMEM; *ret = n; return 0; @@ -157,162 +159,43 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char ** } static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { - char *printed = NULL; - Unit *u = userdata; - ExecContext *c; - int r = 0; - - assert(u); - - c = unit_get_exec_context(u); - if (!c) - return -EINVAL; - - if (u->manager->running_as == MANAGER_SYSTEM) { - - /* We cannot use NSS from PID 1, hence try to make the - * best of it in that case, and fail if we can't help - * it */ + char *t; - if (!c->user || streq(c->user, "root") || streq(c->user, "0")) - printed = strdup(specifier == 'u' ? "root" : "0"); - else { - if (specifier == 'u') - printed = strdup(c->user); - else { - uid_t uid; + /* If we are UID 0 (root), this will not result in NSS, + * otherwise it might. This is good, as we want to be able to + * run this in PID 1, where our user ID is 0, but where NSS + * lookups are not allowed. */ - r = parse_uid(c->user, &uid); - if (r < 0) - return -ENODATA; - - r = asprintf(&printed, UID_FMT, uid); - } - } - - } else { - _cleanup_free_ char *tmp = NULL; - const char *username = NULL; - uid_t uid; - - if (c->user) - username = c->user; - else - /* get USER env from env or our own uid */ - username = tmp = getusername_malloc(); - - /* fish username from passwd */ - r = get_user_creds(&username, &uid, NULL, NULL, NULL); - if (r < 0) - return r; - - if (specifier == 'u') - printed = strdup(username); - else - r = asprintf(&printed, UID_FMT, uid); - } - - if (r < 0 || !printed) + t = getusername_malloc(); + if (!t) return -ENOMEM; - *ret = printed; + *ret = t; return 0; } -static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - ExecContext *c; - char *n; - int r; - - assert(u); - - c = unit_get_exec_context(u); - if (!c) - return -EOPNOTSUPP; - - if (u->manager->running_as == MANAGER_SYSTEM) { +static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) { - /* We cannot use NSS from PID 1, hence try to make the - * best of it if we can, but fail if we can't */ - - if (!c->user || streq(c->user, "root") || streq(c->user, "0")) - n = strdup("/root"); - else - return -EOPNOTSUPP; - - } else { - - /* return HOME if set, otherwise from passwd */ - if (!c || !c->user) { - r = get_home_dir(&n); - if (r < 0) - return r; - } else { - const char *username, *home; - - username = c->user; - r = get_user_creds(&username, NULL, NULL, &home, NULL); - if (r < 0) - return r; - - n = strdup(home); - } - } - - if (!n) + if (asprintf(ret, UID_FMT, getuid()) < 0) return -ENOMEM; - *ret = n; return 0; } -static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - ExecContext *c; - char *n; - int r; - - assert(u); - - c = unit_get_exec_context(u); - if (!c) - return -EOPNOTSUPP; - - if (u->manager->running_as == MANAGER_SYSTEM) { - - /* We cannot use NSS from PID 1, hence try to make the - * best of it if we can, but fail if we can't */ - - if (!c->user || streq(c->user, "root") || streq(c->user, "0")) - n = strdup("/bin/sh"); - else - return -EOPNOTSUPP; - - } else { +static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) { - /* return /bin/sh for root, otherwise the value from passwd */ - if (!c->user) { - r = get_shell(&n); - if (r < 0) - return r; - } else { - const char *username, *shell; + /* On PID 1 (which runs as root) this will not result in NSS, + * which is good. See above */ - username = c->user; - r = get_user_creds(&username, NULL, NULL, NULL, &shell); - if (r < 0) - return r; + return get_home_dir(ret); +} - n = strdup(shell); - } - } +static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) { - if (!n) - return -ENOMEM; + /* On PID 1 (which runs as root) this will not result in NSS, + * which is good. See above */ - *ret = n; - return 0; + return get_shell(ret); } int unit_name_printf(Unit *u, const char* format, char **ret) { @@ -352,10 +235,10 @@ int unit_full_printf(Unit *u, const char *format, char **ret) { * %r where units in this slice are placed in the cgroup tree * %R the root of this systemd's instance tree * %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 + * %U the UID of the running user + * %u the username of the running user + * %h the homedir of the running user + * %s the shell of the running user * %m the machine ID of the running system * %H the host name of the running system * %b the boot ID of the running system @@ -375,7 +258,8 @@ int unit_full_printf(Unit *u, const char *format, char **ret) { { 'r', specifier_cgroup_slice, NULL }, { 'R', specifier_cgroup_root, NULL }, { 't', specifier_runtime, NULL }, - { 'U', specifier_user_name, NULL }, + + { 'U', specifier_user_id, NULL }, { 'u', specifier_user_name, NULL }, { 'h', specifier_user_home, NULL }, { 's', specifier_user_shell, NULL }, diff --git a/src/core/unit.c b/src/core/unit.c index 24a6747b10..0a02e38aa8 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -20,43 +20,49 @@ ***/ #include <errno.h> -#include <string.h> #include <stdlib.h> -#include <unistd.h> +#include <string.h> #include <sys/stat.h> +#include <unistd.h> #include "sd-id128.h" #include "sd-messages.h" -#include "set.h" -#include "macro.h" -#include "strv.h" -#include "path-util.h" -#include "log.h" + +#include "alloc-util.h" +#include "bus-common-errors.h" +#include "bus-util.h" #include "cgroup-util.h" -#include "missing.h" -#include "mkdir.h" +#include "dbus-unit.h" +#include "dbus.h" +#include "dropin.h" +#include "escape.h" +#include "execute.h" #include "fileio-label.h" #include "formats-util.h" +#include "load-dropin.h" +#include "load-fragment.h" +#include "log.h" +#include "macro.h" +#include "missing.h" +#include "mkdir.h" +#include "parse-util.h" +#include "path-util.h" #include "process-util.h" -#include "virt.h" -#include "bus-common-errors.h" -#include "bus-util.h" -#include "dropin.h" -#include "unit-name.h" +#include "set.h" #include "special.h" +#include "stat-util.h" +#include "string-util.h" +#include "strv.h" +#include "unit-name.h" #include "unit.h" -#include "load-fragment.h" -#include "load-dropin.h" -#include "dbus.h" -#include "dbus-unit.h" -#include "execute.h" +#include "user-util.h" +#include "virt.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, [UNIT_SOCKET] = &socket_vtable, [UNIT_BUSNAME] = &busname_vtable, [UNIT_TARGET] = &target_vtable, - [UNIT_SNAPSHOT] = &snapshot_vtable, [UNIT_DEVICE] = &device_vtable, [UNIT_MOUNT] = &mount_vtable, [UNIT_AUTOMOUNT] = &automount_vtable, @@ -125,6 +131,10 @@ static void unit_init(Unit *u) { cc->cpu_accounting = u->manager->default_cpu_accounting; cc->blockio_accounting = u->manager->default_blockio_accounting; cc->memory_accounting = u->manager->default_memory_accounting; + cc->tasks_accounting = u->manager->default_tasks_accounting; + + if (u->type != UNIT_SLICE) + cc->tasks_max = u->manager->default_tasks_max; } ec = unit_get_exec_context(u); @@ -307,9 +317,6 @@ bool unit_check_gc(Unit *u) { if (state != UNIT_INACTIVE) return true; - if (UNIT_VTABLE(u)->no_gc) - return true; - if (u->no_gc) return true; @@ -411,12 +418,11 @@ static void unit_remove_transient(Unit *u) { STRV_FOREACH(i, u->dropin_paths) { _cleanup_free_ char *p = NULL; - int r; (void) unlink(*i); - r = path_get_parent(*i, &p); - if (r >= 0) + p = dirname_malloc(*i); + if (p) (void) rmdir(p); } } @@ -445,13 +451,13 @@ static void unit_free_requires_mounts_for(Unit *u) { } } - strv_free(u->requires_mounts_for); - u->requires_mounts_for = NULL; + u->requires_mounts_for = strv_free(u->requires_mounts_for); } static void unit_done(Unit *u) { ExecContext *ec; CGroupContext *cc; + int r; assert(u); @@ -468,6 +474,10 @@ static void unit_done(Unit *u) { cc = unit_get_cgroup_context(u); if (cc) cgroup_context_done(cc); + + r = unit_remove_from_netclass_cgroup(u); + if (r < 0) + log_warning_errno(r, "Unable to remove unit from netclass group: %m"); } void unit_free(Unit *u) { @@ -528,7 +538,7 @@ void unit_free(Unit *u) { unit_release_cgroup(u); - manager_update_failed_units(u->manager, u, false); + (void) manager_update_failed_units(u->manager, u, false); set_remove(u->manager->startup_units, u); free(u->description); @@ -674,8 +684,7 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD /* The move cannot fail. The caller must have performed a reservation. */ assert_se(complete_move(&u->dependencies[d], &other->dependencies[d]) == 0); - set_free(other->dependencies[d]); - other->dependencies[d] = NULL; + other->dependencies[d] = set_free(other->dependencies[d]); } int unit_merge(Unit *u, Unit *other) { @@ -988,15 +997,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tRefuseManualStop: %s\n" "%s\tDefaultDependencies: %s\n" "%s\tOnFailureJobMode: %s\n" - "%s\tIgnoreOnIsolate: %s\n" - "%s\tIgnoreOnSnapshot: %s\n", + "%s\tIgnoreOnIsolate: %s\n", prefix, yes_no(u->stop_when_unneeded), prefix, yes_no(u->refuse_manual_start), prefix, yes_no(u->refuse_manual_stop), prefix, yes_no(u->default_dependencies), prefix, job_mode_to_string(u->on_failure_job_mode), - prefix, yes_no(u->ignore_on_isolate), - prefix, yes_no(u->ignore_on_snapshot)); + prefix, yes_no(u->ignore_on_isolate)); if (UNIT_VTABLE(u)->dump) UNIT_VTABLE(u)->dump(u, f, prefix2); @@ -1094,9 +1101,7 @@ static int unit_add_target_dependencies(Unit *u) { static const UnitDependency deps[] = { UNIT_REQUIRED_BY, - UNIT_REQUIRED_BY_OVERRIDABLE, UNIT_REQUISITE_OF, - UNIT_REQUISITE_OF_OVERRIDABLE, UNIT_WANTED_BY, UNIT_BOUND_BY }; @@ -1125,12 +1130,12 @@ static int unit_add_slice_dependencies(Unit *u) { return 0; if (UNIT_ISSET(u->slice)) - return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, UNIT_DEREF(u->slice), true); + return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT_DEREF(u->slice), true); - if (streq(u->id, SPECIAL_ROOT_SLICE)) + if (unit_has_name(u, SPECIAL_ROOT_SLICE)) return 0; - return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, SPECIAL_ROOT_SLICE, NULL, true); + return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_ROOT_SLICE, NULL, true); } static int unit_add_mount_dependencies(Unit *u) { @@ -1143,13 +1148,23 @@ static int unit_add_mount_dependencies(Unit *u) { char prefix[strlen(*i) + 1]; PATH_FOREACH_PREFIX_MORE(prefix, *i) { + _cleanup_free_ char *p = NULL; Unit *m; - r = manager_get_unit_by_path(u->manager, prefix, ".mount", &m); + r = unit_name_from_path(prefix, ".mount", &p); if (r < 0) return r; - if (r == 0) + + m = manager_get_unit(u->manager, p); + if (!m) { + /* Make sure to load the mount unit if + * it exists. If so the dependencies + * on this unit will be added later + * during the loading of the mount + * unit. */ + (void) manager_load_unit_prepare(u->manager, p, NULL, NULL, &m); continue; + } if (m == u) continue; @@ -1173,15 +1188,20 @@ static int unit_add_mount_dependencies(Unit *u) { static int unit_add_startup_units(Unit *u) { CGroupContext *c; + int r; c = unit_get_cgroup_context(u); if (!c) return 0; - if (c->startup_cpu_shares == (unsigned long) -1 && - c->startup_blockio_weight == (unsigned long) -1) + if (c->startup_cpu_shares == CGROUP_CPU_SHARES_INVALID && + c->startup_blockio_weight == CGROUP_BLKIO_WEIGHT_INVALID) return 0; + r = set_ensure_allocated(&u->manager->startup_units, NULL); + if (r < 0) + return r; + return set_put(u->manager->startup_units, u); } @@ -1237,6 +1257,14 @@ int unit_load(Unit *u) { } unit_update_cgroup_members_masks(u); + + /* If we are reloading, we need to wait for the deserializer + * to restore the net_cls ids that have been set previously */ + if (u->manager->n_reloading <= 0) { + r = unit_add_to_netclass_cgroup(u); + if (r < 0) + return r; + } } assert((u->load_state != UNIT_MERGED) == !u->merged_into); @@ -1319,12 +1347,18 @@ static bool unit_assert_test(Unit *u) { return u->assert_result; } +void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) { + DISABLE_WARNING_FORMAT_NONLITERAL; + manager_status_printf(u->manager, STATUS_TYPE_NORMAL, status, unit_status_msg_format, unit_description(u)); + REENABLE_WARNING; +} + _pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) { const char *format; const UnitStatusMessageFormats *format_table; assert(u); - assert(t == JOB_START || t == JOB_STOP || t == JOB_RELOAD); + assert(IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)); if (t != JOB_RELOAD) { format_table = &UNIT_VTABLE(u)->status_message_formats; @@ -1349,6 +1383,10 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) { assert(u); + /* Reload status messages have traditionally not been printed to console. */ + if (!IN_SET(t, JOB_START, JOB_STOP)) + return; + format = unit_get_status_message_format(u, t); DISABLE_WARNING_FORMAT_NONLITERAL; @@ -1363,7 +1401,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { assert(u); - if (t != JOB_START && t != JOB_STOP && t != JOB_RELOAD) + if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)) return; if (log_on_console()) @@ -1395,12 +1433,12 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { } void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) { + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); unit_status_log_starting_stopping_reloading(u, t); - - /* Reload status messages have traditionally not been printed to console. */ - if (t != JOB_RELOAD) - unit_status_print_starting_stopping(u, t); + unit_status_print_starting_stopping(u, t); } /* Errors: @@ -1575,11 +1613,11 @@ bool unit_can_reload(Unit *u) { static void unit_check_unneeded(Unit *u) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + static const UnitDependency needed_dependencies[] = { UNIT_REQUIRED_BY, - UNIT_REQUIRED_BY_OVERRIDABLE, UNIT_REQUISITE_OF, - UNIT_REQUISITE_OF_OVERRIDABLE, UNIT_WANTED_BY, UNIT_BOUND_BY, }; @@ -1616,12 +1654,13 @@ static void unit_check_unneeded(Unit *u) { log_unit_info(u, "Unit not needed anymore. Stopping."); /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ - r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); + r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL); if (r < 0) - log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m"); + log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r)); } static void unit_check_binds_to(Unit *u) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; bool stop = false; Unit *other; Iterator i; @@ -1661,9 +1700,9 @@ static void unit_check_binds_to(Unit *u) { log_unit_info(u, "Unit is bound to inactive unit %s. Stopping, too.", other->id); /* A unit we need to run is gone. Sniff. Let's stop this. */ - r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); + r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL); if (r < 0) - log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m"); + log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r)); } static void retroactively_start_dependencies(Unit *u) { @@ -1676,30 +1715,25 @@ static void retroactively_start_dependencies(Unit *u) { SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], 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); + manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL); SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], 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_REQUIRES_OVERRIDABLE], 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_FAIL, false, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, 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))) - manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_FAIL, NULL, NULL); SET_FOREACH(other, u->dependencies[UNIT_CONFLICTS], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); } static void retroactively_stop_dependencies(Unit *u) { @@ -1712,7 +1746,7 @@ static void retroactively_stop_dependencies(Unit *u) { /* Pull down units which are bound to us recursively if enabled */ SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); } static void check_unneeded_dependencies(Unit *u) { @@ -1726,18 +1760,12 @@ static void check_unneeded_dependencies(Unit *u) { SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); - SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_unneeded(other); SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); - SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_unneeded(other); SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); @@ -1757,7 +1785,7 @@ void unit_start_on_failure(Unit *u) { SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) { int r; - r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, true, NULL, NULL); + r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, NULL); if (r < 0) log_unit_error_errno(u, r, "Failed to enqueue OnFailure= job: %m"); } @@ -1808,7 +1836,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } /* Keep track of failed units */ - manager_update_failed_units(u->manager, u, ns == UNIT_FAILED); + (void) manager_update_failed_units(u->manager, u, ns == UNIT_FAILED); /* Make sure the cgroup is always removed when we become inactive */ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) @@ -2105,16 +2133,12 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_REQUIRES] = UNIT_REQUIRED_BY, - [UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE, [UNIT_WANTS] = UNIT_WANTED_BY, [UNIT_REQUISITE] = UNIT_REQUISITE_OF, - [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUISITE_OF_OVERRIDABLE, [UNIT_BINDS_TO] = UNIT_BOUND_BY, [UNIT_PART_OF] = UNIT_CONSISTS_OF, [UNIT_REQUIRED_BY] = UNIT_REQUIRES, - [UNIT_REQUIRED_BY_OVERRIDABLE] = UNIT_REQUIRES_OVERRIDABLE, [UNIT_REQUISITE_OF] = UNIT_REQUISITE, - [UNIT_REQUISITE_OF_OVERRIDABLE] = UNIT_REQUISITE_OVERRIDABLE, [UNIT_WANTED_BY] = UNIT_WANTS, [UNIT_BOUND_BY] = UNIT_BINDS_TO, [UNIT_CONSISTS_OF] = UNIT_PART_OF, @@ -2293,47 +2317,9 @@ int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency return unit_add_two_dependencies(u, d, e, other, add_reference); } -int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { - _cleanup_free_ char *buf = NULL; - Unit *other; - int r; - - assert(u); - assert(name || path); - - r = resolve_template(u, name, path, &buf, &name); - if (r < 0) - return r; - - r = manager_load_unit(u->manager, name, path, NULL, &other); - if (r < 0) - return r; - - return unit_add_dependency(other, d, u, add_reference); -} - -int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) { - _cleanup_free_ char *buf = NULL; - Unit *other; - int r; - - assert(u); - assert(name || path); - - r = resolve_template(u, name, path, &buf, &name); - if (r < 0) - return r; - - r = manager_load_unit(u->manager, name, path, NULL, &other); - if (r < 0) - return r; - - return unit_add_two_dependencies(other, d, e, u, add_reference); -} - int set_unit_path(const char *p) { /* This is mostly for debug purposes */ - if (setenv("SYSTEMD_UNIT_PATH", p, 0) < 0) + if (setenv("SYSTEMD_UNIT_PATH", p, 1) < 0) return -errno; return 0; @@ -2481,26 +2467,23 @@ static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd return 0; } -int unit_install_bus_match(sd_bus *bus, Unit *u, const char *name) { - _cleanup_free_ char *match = NULL; - Manager *m = u->manager; +int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) { + const char *match; - assert(m); + assert(u); + assert(bus); + assert(name); if (u->match_bus_slot) return -EBUSY; - match = strjoin("type='signal'," + match = strjoina("type='signal'," "sender='org.freedesktop.DBus'," "path='/org/freedesktop/DBus'," "interface='org.freedesktop.DBus'," "member='NameOwnerChanged'," - "arg0='", - name, - "'", + "arg0='", name, "'", NULL); - if (!match) - return -ENOMEM; return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u); } @@ -2517,9 +2500,9 @@ int unit_watch_bus_name(Unit *u, const char *name) { if (u->manager->api_bus) { /* If the bus is already available, install the match directly. * Otherwise, just put the name in the list. bus_setup_api() will take care later. */ - r = unit_install_bus_match(u->manager->api_bus, u, name); + r = unit_install_bus_match(u, u->manager->api_bus, name); if (r < 0) - return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m"); + return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name); } r = hashmap_put(u->manager->watch_bus, name, u); @@ -2587,6 +2570,9 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { unit_serialize_item(u, f, "cgroup", u->cgroup_path); unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized)); + if (u->cgroup_netclass_id) + unit_serialize_item_format(u, f, "netclass-id", "%" PRIu32, u->cgroup_netclass_id); + if (serialize_jobs) { if (u->job) { fprintf(f, "job\n"); @@ -2604,6 +2590,62 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { return 0; } +int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) { + assert(u); + assert(f); + assert(key); + + if (!value) + return 0; + + fputs(key, f); + fputc('=', f); + fputs(value, f); + fputc('\n', f); + + return 1; +} + +int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *value) { + _cleanup_free_ char *c = NULL; + + assert(u); + assert(f); + assert(key); + + if (!value) + return 0; + + c = cescape(value); + if (!c) + return -ENOMEM; + + fputs(key, f); + fputc('=', f); + fputs(c, f); + fputc('\n', f); + + return 1; +} + +int unit_serialize_item_fd(Unit *u, FILE *f, FDSet *fds, const char *key, int fd) { + int copy; + + assert(u); + assert(f); + assert(key); + + if (fd < 0) + return 0; + + copy = fdset_put_dup(fds, fd); + if (copy < 0) + return copy; + + fprintf(f, "%s=%i\n", key, copy); + return 1; +} + void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) { va_list ap; @@ -2622,15 +2664,6 @@ void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *f fputc('\n', f); } -void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) { - assert(u); - assert(f); - assert(key); - assert(value); - - fprintf(f, "%s=%s\n", key, value); -} - int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { ExecRuntime **rt = NULL; size_t offset; @@ -2774,6 +2807,17 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { u->cgroup_realized = b; continue; + } else if (streq(l, "netclass-id")) { + r = safe_atou32(v, &u->cgroup_netclass_id); + if (r < 0) + log_unit_debug(u, "Failed to parse netclass ID %s, ignoring.", v); + else { + r = unit_add_to_netclass_cgroup(u); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to add unit to netclass cgroup, ignoring: %m"); + } + + continue; } if (unit_can_serialize(u)) { @@ -2837,7 +2881,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) { } int unit_coldplug(Unit *u) { - int r; + int r = 0, q = 0; assert(u); @@ -2848,26 +2892,18 @@ int unit_coldplug(Unit *u) { u->coldplugged = true; - if (UNIT_VTABLE(u)->coldplug) { + if (UNIT_VTABLE(u)->coldplug) r = UNIT_VTABLE(u)->coldplug(u); - if (r < 0) - return r; - } - if (u->job) { - r = job_coldplug(u->job); - if (r < 0) - return r; - } + if (u->job) + q = job_coldplug(u->job); - return 0; -} + if (r < 0) + return r; + if (q < 0) + return q; -void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) { - DISABLE_WARNING_FORMAT_NONLITERAL; - manager_status_printf(u->manager, STATUS_TYPE_NORMAL, - status, unit_status_msg_format, unit_description(u)); - REENABLE_WARNING; + return 0; } bool unit_need_daemon_reload(Unit *u) { @@ -3033,32 +3069,39 @@ int unit_kill_common( sd_bus_error *error) { int r = 0; + bool killed = false; - if (who == KILL_MAIN) { + if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL)) { if (main_pid < 0) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type)); else if (main_pid == 0) return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); } - if (who == KILL_CONTROL) { + if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL)) { if (control_pid < 0) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type)); else if (control_pid == 0) return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); } - if (who == KILL_CONTROL || who == KILL_ALL) - if (control_pid > 0) + if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL, KILL_ALL, KILL_ALL_FAIL)) + if (control_pid > 0) { if (kill(control_pid, signo) < 0) r = -errno; + else + killed = true; + } - if (who == KILL_MAIN || who == KILL_ALL) - if (main_pid > 0) + if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL, KILL_ALL, KILL_ALL_FAIL)) + if (main_pid > 0) { if (kill(main_pid, signo) < 0) r = -errno; + else + killed = true; + } - if (who == KILL_ALL && u->cgroup_path) { + if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path) { _cleanup_set_free_ Set *pid_set = NULL; int q; @@ -3070,8 +3113,13 @@ int unit_kill_common( q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, false, false, pid_set); if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) r = q; + else + killed = true; } + if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL, KILL_ALL_FAIL)) + return -ESRCH; + return r; } @@ -3087,12 +3135,19 @@ int unit_following_set(Unit *u, Set **s) { } UnitFileState unit_get_unit_file_state(Unit *u) { + int r; + assert(u); - if (u->unit_file_state < 0 && u->fragment_path) - u->unit_file_state = unit_file_get_state( + if (u->unit_file_state < 0 && u->fragment_path) { + r = unit_file_get_state( u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, - NULL, basename(u->fragment_path)); + NULL, + basename(u->fragment_path), + &u->unit_file_state); + if (r < 0) + u->unit_file_state = UNIT_FILE_BAD; + } return u->unit_file_state; } @@ -3103,7 +3158,8 @@ int unit_get_unit_file_preset(Unit *u) { if (u->unit_file_preset < 0 && u->fragment_path) u->unit_file_preset = unit_file_query_preset( u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, - NULL, basename(u->fragment_path)); + NULL, + basename(u->fragment_path)); return u->unit_file_preset; } @@ -3268,19 +3324,6 @@ static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, return 0; } -static int unit_drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **p, char **q) { - _cleanup_free_ char *dir = NULL; - int r; - - assert(u); - - r = unit_drop_in_dir(u, mode, u->transient, &dir); - if (r < 0) - return r; - - return drop_in_file(dir, u->id, 50, name, p, q); -} - int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { _cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL; @@ -3379,28 +3422,6 @@ int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const return unit_write_drop_in_private(u, mode, name, p); } -int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) { - _cleanup_free_ char *p = NULL, *q = NULL; - int r; - - assert(u); - - if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) - return 0; - - r = unit_drop_in_file(u, mode, name, &p, &q); - if (r < 0) - return r; - - if (unlink(q) < 0) - r = errno == ENOENT ? 0 : -errno; - else - r = 1; - - rmdir(p); - return r; -} - int unit_make_transient(Unit *u) { assert(u); @@ -3410,7 +3431,15 @@ int unit_make_transient(Unit *u) { u->load_state = UNIT_STUB; u->load_error = 0; u->transient = true; + u->fragment_path = mfree(u->fragment_path); + u->source_path = mfree(u->source_path); + u->dropin_paths = strv_free(u->dropin_paths); + u->fragment_mtime = u->source_mtime = u->dropin_mtime = 0; + + unit_add_to_dbus_queue(u); + unit_add_to_gc_queue(u); + unit_add_to_load_queue(u); return 0; } @@ -3687,13 +3716,20 @@ int unit_fail_if_symlink(Unit *u, const char* where) { return -ELOOP; } -static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { - [UNIT_ACTIVE] = "active", - [UNIT_RELOADING] = "reloading", - [UNIT_INACTIVE] = "inactive", - [UNIT_FAILED] = "failed", - [UNIT_ACTIVATING] = "activating", - [UNIT_DEACTIVATING] = "deactivating" -}; +bool unit_is_pristine(Unit *u) { + assert(u); + + /* Check if the unit already exists or is already around, + * in a number of different ways. Note that to cater for unit + * types such as slice, we are generally fine with units that + * are marked UNIT_LOADED even even though nothing was + * actually loaded, as those unit types don't require a file + * on disk to validly load. */ -DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState); + return !(!IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_LOADED) || + u->fragment_path || + u->source_path || + !strv_isempty(u->dropin_paths) || + u->job || + u->merged_into); +} diff --git a/src/core/unit.h b/src/core/unit.h index 3c7684411b..1681bbf53b 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -27,26 +27,14 @@ typedef struct Unit Unit; typedef struct UnitVTable UnitVTable; -typedef enum UnitActiveState UnitActiveState; typedef struct UnitRef UnitRef; typedef struct UnitStatusMessageFormats UnitStatusMessageFormats; -#include "list.h" #include "condition.h" +#include "failure-action.h" #include "install.h" +#include "list.h" #include "unit-name.h" -#include "failure-action.h" - -enum UnitActiveState { - UNIT_ACTIVE, - UNIT_RELOADING, - UNIT_INACTIVE, - UNIT_FAILED, - UNIT_ACTIVATING, - UNIT_DEACTIVATING, - _UNIT_ACTIVE_STATE_MAX, - _UNIT_ACTIVE_STATE_INVALID = -1 -}; typedef enum KillOperation { KILL_TERMINATE, @@ -161,6 +149,9 @@ struct Unit { /* CGroup realize members queue */ LIST_FIELDS(Unit, cgroup_queue); + /* Units with the same CGroup netclass */ + LIST_FIELDS(Unit, cgroup_netclass); + /* PIDs we keep an eye on. Note that a unit might have many * more, but these are the ones we care enough about to * process SIGCHLD for */ @@ -189,6 +180,8 @@ struct Unit { CGroupMask cgroup_members_mask; int cgroup_inotify_wd; + uint32_t cgroup_netclass_id; + /* How to start OnFailure units */ JobMode on_failure_job_mode; @@ -210,9 +203,6 @@ struct Unit { /* Ignore this unit when isolating */ bool ignore_on_isolate; - /* Ignore this unit when snapshotting */ - bool ignore_on_snapshot; - /* Did the last condition check succeed? */ bool condition_result; bool assert_result; @@ -252,17 +242,16 @@ typedef enum UnitSetPropertiesMode { UNIT_PERSISTENT = 2, } UnitSetPropertiesMode; -#include "socket.h" +#include "automount.h" #include "busname.h" -#include "target.h" -#include "snapshot.h" #include "device.h" -#include "automount.h" -#include "swap.h" -#include "timer.h" -#include "slice.h" #include "path.h" #include "scope.h" +#include "slice.h" +#include "socket.h" +#include "swap.h" +#include "target.h" +#include "timer.h" struct UnitVTable { /* How much memory does an object of this unit type need */ @@ -329,7 +318,7 @@ struct UnitVTable { int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds); /* Try to match up fds with what we need for this unit */ - int (*distribute_fds)(Unit *u, FDSet *fds); + void (*distribute_fds)(Unit *u, FDSet *fds); /* Boils down the more complex internal state of this unit to * a simpler one that the engine can understand */ @@ -350,9 +339,6 @@ struct UnitVTable { * shall release its runtime resources */ void (*release_resources)(Unit *u); - /* Return true when this unit is suitable for snapshotting */ - bool (*check_snapshot)(Unit *u); - /* Invoked on every child that died */ void (*sigchld_event)(Unit *u, pid_t pid, int code, int status); @@ -396,7 +382,7 @@ struct UnitVTable { * everything that is loaded here should still stay in * inactive state. It is the job of the coldplug() call above * to put the units into the initial state. */ - int (*enumerate)(Manager *m); + void (*enumerate)(Manager *m); /* Type specific cleanups. */ void (*shutdown)(Manager *m); @@ -417,9 +403,6 @@ struct UnitVTable { /* Instances make no sense for this type */ bool no_instances:1; - /* Exclude from automatic gc */ - bool no_gc:1; - /* True if transient units of this type are OK */ bool can_transient:1; }; @@ -450,7 +433,6 @@ DEFINE_CAST(SERVICE, Service); DEFINE_CAST(SOCKET, Socket); DEFINE_CAST(BUSNAME, BusName); DEFINE_CAST(TARGET, Target); -DEFINE_CAST(SNAPSHOT, Snapshot); DEFINE_CAST(DEVICE, Device); DEFINE_CAST(MOUNT, Mount); DEFINE_CAST(AUTOMOUNT, Automount); @@ -471,9 +453,6 @@ int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference); int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference); -int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference); -int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference); - int unit_add_exec_dependencies(Unit *u, ExecContext *c); int unit_choose_id(Unit *u, const char *name); @@ -527,7 +506,7 @@ void unit_unwatch_all_pids(Unit *u); void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2); -int unit_install_bus_match(sd_bus *bus, Unit *u, const char *name); +int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name); int unit_watch_bus_name(Unit *u, const char *name); void unit_unwatch_bus_name(Unit *u, const char *name); @@ -540,11 +519,15 @@ char *unit_dbus_path(Unit *u); int unit_load_related_unit(Unit *u, const char *type, Unit **_found); 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_(4,5); -void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value); int unit_deserialize(Unit *u, FILE *f, FDSet *fds); +int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value); +int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *value); +int unit_serialize_item_fd(Unit *u, FILE *f, FDSet *fds, const char *key, int fd); +void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_(4,5); + int unit_add_node_link(Unit *u, const char *what, bool wants); int unit_coldplug(Unit *u); @@ -595,8 +578,6 @@ int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *n int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data); int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) _printf_(4,5); -int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name); - int unit_kill_context(Unit *u, KillContext *c, KillOperation k, pid_t main_pid, pid_t control_pid, bool main_pid_alien); int unit_make_transient(Unit *u); @@ -605,6 +586,8 @@ int unit_require_mounts_for(Unit *u, const char *path); bool unit_type_supported(UnitType t); +bool unit_is_pristine(Unit *u); + static inline bool unit_supported(Unit *u) { return unit_type_supported(u->type); } @@ -612,9 +595,6 @@ static inline bool unit_supported(Unit *u) { void unit_warn_if_dir_nonempty(Unit *u, const char* where); int unit_fail_if_symlink(Unit *u, const char* where); -const char *unit_active_state_to_string(UnitActiveState i) _const_; -UnitActiveState unit_active_state_from_string(const char *s) _pure_; - /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full(unit, level, error, ...) \ |