X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Funit.c;h=c4ed9233731a88756e15b855b338bfdaa11a3a12;hp=b91fcf195748383fd04da356d69f41a8aebfc170;hb=4e595329a93ed190795c2e24bf132d5028ec6a72;hpb=bc432dc7eb62c5671f2b741a86a66393adb350dc;ds=sidebyside diff --git a/src/core/unit.c b/src/core/unit.c index b91fcf195..c4ed92337 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -101,104 +101,123 @@ bool unit_has_name(Unit *u, const char *name) { return !!set_get(u->names, (char*) name); } +static void unit_init(Unit *u) { + CGroupContext *cc; + ExecContext *ec; + KillContext *kc; + + assert(u); + assert(u->manager); + assert(u->type >= 0); + + cc = unit_get_cgroup_context(u); + if (cc) { + cgroup_context_init(cc); + + /* Copy in the manager defaults into the cgroup + * context, _before_ the rest of the settings have + * been initialized */ + + 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->cpu_quota_period_usec = u->manager->default_cpu_quota_period_usec; + } + + ec = unit_get_exec_context(u); + if (ec) + exec_context_init(ec); + + kc = unit_get_kill_context(u); + if (kc) + kill_context_init(kc); + + if (UNIT_VTABLE(u)->init) + UNIT_VTABLE(u)->init(u); +} + int unit_add_name(Unit *u, const char *text) { + _cleanup_free_ char *s = NULL, *i = NULL; UnitType t; - char *s, *i = NULL; int r; assert(u); assert(text); if (unit_name_is_template(text)) { + if (!u->instance) return -EINVAL; s = unit_name_replace_instance(text, u->instance); } else s = strdup(text); - if (!s) return -ENOMEM; - if (!unit_name_is_valid(s, TEMPLATE_INVALID)) { - r = -EINVAL; - goto fail; - } + if (!unit_name_is_valid(s, TEMPLATE_INVALID)) + return -EINVAL; assert_se((t = unit_name_to_type(s)) >= 0); - if (u->type != _UNIT_TYPE_INVALID && t != u->type) { - r = -EINVAL; - goto fail; - } + if (u->type != _UNIT_TYPE_INVALID && t != u->type) + return -EINVAL; r = unit_name_to_instance(s, &i); if (r < 0) - goto fail; + return r; - if (i && unit_vtable[t]->no_instances) { - r = -EINVAL; - goto fail; - } + if (i && unit_vtable[t]->no_instances) + return -EINVAL; /* Ensure that this unit is either instanced or not instanced, * but not both. */ - if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) { - r = -EINVAL; - goto fail; - } + if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) + return -EINVAL; if (unit_vtable[t]->no_alias && !set_isempty(u->names) && - !set_get(u->names, s)) { - r = -EEXIST; - goto fail; - } + !set_get(u->names, s)) + return -EEXIST; - if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) { - r = -E2BIG; - goto fail; - } + if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) + return -E2BIG; r = set_put(u->names, s); if (r < 0) { if (r == -EEXIST) - r = 0; - goto fail; + return 0; + + return r; } r = hashmap_put(u->manager->units, s, u); if (r < 0) { set_remove(u->names, s); - goto fail; + return r; } if (u->type == _UNIT_TYPE_INVALID) { - u->type = t; u->id = s; u->instance = i; LIST_PREPEND(units_by_type, u->manager->units_by_type[t], u); - if (UNIT_VTABLE(u)->init) - UNIT_VTABLE(u)->init(u); - } else - free(i); + unit_init(u); - unit_add_to_dbus_queue(u); - return 0; + i = NULL; + } -fail: - free(s); - free(i); + s = NULL; - return r; + unit_add_to_dbus_queue(u); + return 0; } int unit_choose_id(Unit *u, const char *name) { - char *s, *i; _cleanup_free_ char *t = NULL; + char *s, *i; int r; assert(u); @@ -218,7 +237,6 @@ int unit_choose_id(Unit *u, const char *name) { /* Selects one of the names of this unit as the id */ s = set_get(u->names, (char*) name); - if (!s) return -ENOENT; @@ -259,9 +277,6 @@ int unit_set_description(Unit *u, const char *description) { bool unit_check_gc(Unit *u) { assert(u); - if (u->load_state == UNIT_STUB) - return true; - if (UNIT_VTABLE(u)->no_gc) return true; @@ -331,7 +346,8 @@ void unit_add_to_dbus_queue(Unit *u) { return; /* Shortcut things if nobody cares */ - if (set_isempty(u->manager->subscribed)) { + if (sd_bus_track_count(u->manager->subscribed) <= 0 && + set_isempty(u->manager->private_buses)) { u->sent_dbus_new_signal = true; return; } @@ -412,6 +428,27 @@ static void unit_free_requires_mounts_for(Unit *u) { u->requires_mounts_for = NULL; } +static void unit_done(Unit *u) { + ExecContext *ec; + CGroupContext *cc; + + assert(u); + + if (u->type < 0) + return; + + if (UNIT_VTABLE(u)->done) + UNIT_VTABLE(u)->done(u); + + ec = unit_get_exec_context(u); + if (ec) + exec_context_done(ec); + + cc = unit_get_cgroup_context(u); + if (cc) + cgroup_context_done(cc); +} + void unit_free(Unit *u) { UnitDependency d; Iterator i; @@ -424,9 +461,7 @@ void unit_free(Unit *u) { bus_unit_send_removed_signal(u); - if (u->load_state != UNIT_STUB) - if (UNIT_VTABLE(u)->done) - UNIT_VTABLE(u)->done(u); + unit_done(u); unit_free_requires_mounts_for(u); @@ -473,6 +508,8 @@ void unit_free(Unit *u) { free(u->cgroup_path); } + set_remove(u->manager->failed_units, u); + free(u->description); strv_free(u->documentation); free(u->fragment_path); @@ -686,6 +723,19 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { return r; } + if (u->manager->running_as != SYSTEMD_SYSTEM) + return 0; + + if (c->private_tmp) { + r = unit_require_mounts_for(u, "/tmp"); + if (r < 0) + return r; + + r = unit_require_mounts_for(u, "/var/tmp"); + if (r < 0) + return r; + } + if (c->std_output != EXEC_OUTPUT_KMSG && c->std_output != EXEC_OUTPUT_SYSLOG && c->std_output != EXEC_OUTPUT_JOURNAL && @@ -703,11 +753,9 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { /* If syslog or kernel logging is requested, make sure our own * logging daemon is run first. */ - if (u->manager->running_as == SYSTEMD_SYSTEM) { - r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true); - if (r < 0) - return r; - } + r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true); + if (r < 0) + return r; return 0; } @@ -947,7 +995,7 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) { return unit_add_dependency(target, UNIT_AFTER, u, true); } -static int unit_add_default_dependencies(Unit *u) { +static int unit_add_target_dependencies(Unit *u) { static const UnitDependency deps[] = { UNIT_REQUIRED_BY, @@ -958,8 +1006,8 @@ static int unit_add_default_dependencies(Unit *u) { Unit *target; Iterator i; - int r; unsigned k; + int r = 0; assert(u); @@ -970,20 +1018,22 @@ static int unit_add_default_dependencies(Unit *u) { return r; } - if (u->default_dependencies && unit_get_cgroup_context(u)) { - if (UNIT_ISSET(u->slice)) - r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, UNIT_DEREF(u->slice), true); - else - r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, SPECIAL_ROOT_SLICE, NULL, true); + return r; +} - if (r < 0) - return r; - } +static int unit_add_slice_dependencies(Unit *u) { + assert(u); - return 0; + if (!unit_get_cgroup_context(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_by_name(u, UNIT_AFTER, UNIT_WANTS, SPECIAL_ROOT_SLICE, NULL, true); } -static int unit_add_mount_links(Unit *u) { +static int unit_add_mount_dependencies(Unit *u) { char **i; int r; @@ -1050,13 +1100,15 @@ int unit_load(Unit *u) { if (u->load_state == UNIT_LOADED) { - if (u->default_dependencies) { - r = unit_add_default_dependencies(u); - if (r < 0) - goto fail; - } + r = unit_add_target_dependencies(u); + if (r < 0) + goto fail; + + r = unit_add_slice_dependencies(u); + if (r < 0) + goto fail; - r = unit_add_mount_links(u); + r = unit_add_mount_dependencies(u); if (r < 0) goto fail; @@ -1136,8 +1188,6 @@ _pure_ static const char *unit_get_status_message_format_try_harder(Unit *u, Job return NULL; } -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" static void unit_status_print_starting_stopping(Unit *u, JobType t) { const char *format; @@ -1150,12 +1200,11 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) { if (!format) return; + DISABLE_WARNING_FORMAT_NONLITERAL; unit_status_printf(u, "", format); + REENABLE_WARNING; } -#pragma GCC diagnostic pop -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { const char *format; char buf[LINE_MAX]; @@ -1175,8 +1224,10 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { if (!format) return; + DISABLE_WARNING_FORMAT_NONLITERAL; snprintf(buf, sizeof(buf), format, unit_description(u)); char_array_0(buf); + REENABLE_WARNING; mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING : t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING : @@ -1188,7 +1239,6 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { "MESSAGE=%s", buf, NULL); } -#pragma GCC diagnostic pop /* Errors: * -EBADR: This unit type does not support starting. @@ -1507,6 +1557,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su m = u->manager; + /* Update timestamps for state changes */ if (m->n_reloading <= 0) { dual_timestamp ts; @@ -1523,11 +1574,18 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su u->active_exit_timestamp = ts; } + /* Keep track of failed of units */ + if (ns == UNIT_FAILED && os != UNIT_FAILED) + set_put(u->manager->failed_units, u); + else if (os == UNIT_FAILED && ns != UNIT_FAILED) + set_remove(u->manager->failed_units, u); + + /* Make sure the cgroup is always removed when we become inactive */ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) unit_destroy_cgroup(u); /* Note that this doesn't apply to RemainAfterExit services exiting - * sucessfully, since there's no change of state in that case. Which is + * successfully, since there's no change of state in that case. Which is * why it is handled in service_set_state() */ if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) { ExecContext *ec; @@ -1704,11 +1762,11 @@ int unit_watch_pid(Unit *u, pid_t pid) { /* Watch a specific PID. We only support one or two units * watching each PID for now, not more. */ - r = hashmap_ensure_allocated(&u->manager->watch_pids1, trivial_hash_func, trivial_compare_func); + r = set_ensure_allocated(&u->pids, trivial_hash_func, trivial_compare_func); if (r < 0) return r; - r = set_ensure_allocated(&u->pids, trivial_hash_func, trivial_compare_func); + r = hashmap_ensure_allocated(&u->manager->watch_pids1, trivial_hash_func, trivial_compare_func); if (r < 0) return r; @@ -1737,7 +1795,17 @@ void unit_unwatch_pid(Unit *u, pid_t pid) { set_remove(u->pids, LONG_TO_PTR(pid)); } -static int watch_pids_in_path(Unit *u, const char *path) { +void unit_unwatch_all_pids(Unit *u) { + assert(u); + + while (!set_isempty(u->pids)) + unit_unwatch_pid(u, PTR_TO_LONG(set_first(u->pids))); + + set_free(u->pids); + u->pids = NULL; +} + +static int unit_watch_pids_in_path(Unit *u, const char *path) { _cleanup_closedir_ DIR *d = NULL; _cleanup_fclose_ FILE *f = NULL; int ret = 0, r; @@ -1775,7 +1843,7 @@ static int watch_pids_in_path(Unit *u, const char *path) { if (!p) return -ENOMEM; - r = watch_pids_in_path(u, p); + r = unit_watch_pids_in_path(u, p); if (r < 0 && ret >= 0) ret = r; } @@ -1788,31 +1856,15 @@ static int watch_pids_in_path(Unit *u, const char *path) { return ret; } - int unit_watch_all_pids(Unit *u) { assert(u); - if (!u->cgroup_path) - return -ENOENT; - /* Adds all PIDs from our cgroup to the set of PIDs we watch */ - return watch_pids_in_path(u, u->cgroup_path); -} - -void unit_unwatch_all_pids(Unit *u) { - Iterator i; - void *e; - - assert(u); - - SET_FOREACH(e, u->pids, i) { - hashmap_remove_value(u->manager->watch_pids1, e, u); - hashmap_remove_value(u->manager->watch_pids2, e, u); - } + if (!u->cgroup_path) + return -ENOENT; - set_free(u->pids); - u->pids = NULL; + return unit_watch_pids_in_path(u, u->cgroup_path); } void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) { @@ -1829,8 +1881,8 @@ void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) { if (pid == except1 || pid == except2) continue; - if (kill(pid, 0) < 0 && errno == ESRCH) - set_remove(u->pids, e); + if (!pid_is_unwaited(pid)) + unit_unwatch_pid(u, pid); } } @@ -2133,20 +2185,18 @@ char *unit_default_cgroup_path(Unit *u) { return strjoin(u->manager->cgroup_root, "/", escaped, NULL); } -int unit_add_default_slice(Unit *u) { +int unit_add_default_slice(Unit *u, CGroupContext *c) { _cleanup_free_ char *b = NULL; const char *slice_name; Unit *slice; int r; assert(u); + assert(c); if (UNIT_ISSET(u->slice)) return 0; - if (!unit_get_cgroup_context(u)) - return 0; - if (u->instance) { _cleanup_free_ char *prefix = NULL, *escaped = NULL; @@ -2438,10 +2488,18 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { if (!s) return -ENOMEM; - free(u->cgroup_path); - u->cgroup_path = s; + if (u->cgroup_path) { + void *p; + p = hashmap_remove(u->manager->cgroup_unit, u->cgroup_path); + log_info("Removing cgroup_path %s from hashmap (%p)", + u->cgroup_path, p); + free(u->cgroup_path); + } + + u->cgroup_path = s; assert(hashmap_put(u->manager->cgroup_unit, s, u) == 1); + continue; } @@ -2521,12 +2579,11 @@ int unit_coldplug(Unit *u) { return 0; } -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) { + DISABLE_WARNING_FORMAT_NONLITERAL; manager_status_printf(u->manager, false, status, unit_status_msg_format, unit_description(u)); + REENABLE_WARNING; } -#pragma GCC diagnostic pop bool unit_need_daemon_reload(Unit *u) { _cleanup_strv_free_ char **t = NULL; @@ -2779,27 +2836,55 @@ void unit_ref_unset(UnitRef *ref) { ref->unit = NULL; } -int unit_exec_context_defaults(Unit *u, ExecContext *c) { +int unit_patch_contexts(Unit *u) { + CGroupContext *cc; + ExecContext *ec; unsigned i; int r; assert(u); - assert(c); - /* This only copies in the ones that need memory */ - for (i = 0; i < RLIMIT_NLIMITS; i++) - if (u->manager->rlimit[i] && !c->rlimit[i]) { - c->rlimit[i] = newdup(struct rlimit, u->manager->rlimit[i], 1); - if (!c->rlimit[i]) - return -ENOMEM; + /* Patch in the manager defaults into the exec and cgroup + * contexts, _after_ the rest of the settings have been + * initialized */ + + ec = unit_get_exec_context(u); + if (ec) { + /* This only copies in the ones that need memory */ + for (i = 0; i < _RLIMIT_MAX; i++) + if (u->manager->rlimit[i] && !ec->rlimit[i]) { + ec->rlimit[i] = newdup(struct rlimit, u->manager->rlimit[i], 1); + if (!ec->rlimit[i]) + return -ENOMEM; + } + + if (u->manager->running_as == SYSTEMD_USER && + !ec->working_directory) { + + r = get_home_dir(&ec->working_directory); + if (r < 0) + return r; } - if (u->manager->running_as == SYSTEMD_USER && - !c->working_directory) { + if (u->manager->running_as == SYSTEMD_USER && + (ec->syscall_whitelist || + !set_isempty(ec->syscall_filter) || + !set_isempty(ec->syscall_archs) || + ec->address_families_whitelist || + !set_isempty(ec->address_families))) + ec->no_new_privileges = true; - r = get_home_dir(&c->working_directory); - if (r < 0) - return r; + if (ec->private_devices) + ec->capability_bounding_set_drop |= (uint64_t) 1ULL << (uint64_t) CAP_MKNOD; + } + + cc = unit_get_cgroup_context(u); + if (cc) { + + if (ec && + ec->private_devices && + cc->device_policy == CGROUP_AUTO) + cc->device_policy = CGROUP_CLOSED; } return 0; @@ -2809,6 +2894,9 @@ ExecContext *unit_get_exec_context(Unit *u) { size_t offset; assert(u); + if (u->type < 0) + return NULL; + offset = UNIT_VTABLE(u)->exec_context_offset; if (offset <= 0) return NULL; @@ -2820,6 +2908,9 @@ KillContext *unit_get_kill_context(Unit *u) { size_t offset; assert(u); + if (u->type < 0) + return NULL; + offset = UNIT_VTABLE(u)->kill_context_offset; if (offset <= 0) return NULL; @@ -2830,6 +2921,9 @@ KillContext *unit_get_kill_context(Unit *u) { CGroupContext *unit_get_cgroup_context(Unit *u) { size_t offset; + if (u->type < 0) + return NULL; + offset = UNIT_VTABLE(u)->cgroup_context_offset; if (offset <= 0) return NULL; @@ -2840,6 +2934,9 @@ CGroupContext *unit_get_cgroup_context(Unit *u) { ExecRuntime *unit_get_exec_runtime(Unit *u) { size_t offset; + if (u->type < 0) + return NULL; + offset = UNIT_VTABLE(u)->exec_runtime_offset; if (offset <= 0) return NULL; @@ -2856,7 +2953,6 @@ static int drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, c assert(name); assert(_p); assert(_q); - assert(mode & (UNIT_PERSISTENT|UNIT_RUNTIME)); b = xescape(name, "/."); if (!b) @@ -2875,7 +2971,7 @@ static int drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, c return -ENOENT; p = strjoin(c, "/", u->id, ".d", NULL); - } else if (mode & UNIT_PERSISTENT) + } else if (mode == UNIT_PERSISTENT && !u->transient) p = strjoin("/etc/systemd/system/", u->id, ".d", NULL); else p = strjoin("/run/systemd/system/", u->id, ".d", NULL); @@ -2901,7 +2997,7 @@ int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, co assert(name); assert(data); - if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; r = drop_in_file(u, mode, name, &p, &q); @@ -2921,7 +3017,7 @@ int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *n assert(name); assert(format); - if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; va_start(ap, format); @@ -2944,7 +3040,7 @@ int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char * if (!UNIT_VTABLE(u)->private_section) return -EINVAL; - if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; ndata = strjoin("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL); @@ -2963,7 +3059,7 @@ int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const assert(name); assert(format); - if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; va_start(ap, format); @@ -2982,7 +3078,7 @@ int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) { assert(u); - if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; r = drop_in_file(u, mode, name, &p, &q); @@ -3158,11 +3254,9 @@ int unit_require_mounts_for(Unit *u, const char *path) { return 0; } - r = strv_push(&u->requires_mounts_for, p); - if (r < 0) { - free(p); + r = strv_consume(&u->requires_mounts_for, p); + if (r < 0) return r; - } PATH_FOREACH_PREFIX_MORE(prefix, p) { Set *x; @@ -3263,9 +3357,9 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_TRIGGERED_BY] = "TriggeredBy", [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo", [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom", + [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf", [UNIT_REFERENCES] = "References", [UNIT_REFERENCED_BY] = "ReferencedBy", - [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf", }; DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);