X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Funit.c;h=70cdd3d943dbcabc91e38d63343793566fd1f77c;hp=4b9abf32dc04379cc210b79cc4211276d778c16c;hb=6fa4853328e3d78d092172fa54effb7e785d0a85;hpb=3ecaa09bccd8a59c9f1e06756a1334a162206dc4 diff --git a/src/core/unit.c b/src/core/unit.c index 4b9abf32d..70cdd3d94 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -44,7 +44,6 @@ #include "special.h" #include "cgroup-util.h" #include "missing.h" -#include "cgroup-attr.h" #include "mkdir.h" #include "label.h" #include "fileio-label.h" @@ -60,7 +59,9 @@ const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_AUTOMOUNT] = &automount_vtable, [UNIT_SNAPSHOT] = &snapshot_vtable, [UNIT_SWAP] = &swap_vtable, - [UNIT_PATH] = &path_vtable + [UNIT_PATH] = &path_vtable, + [UNIT_SLICE] = &slice_vtable, + [UNIT_SCOPE] = &scope_vtable }; Unit *unit_new(Manager *m, size_t size) { @@ -230,8 +231,13 @@ int unit_set_description(Unit *u, const char *description) { assert(u); - if (!(s = strdup(description))) - return -ENOMEM; + if (isempty(description)) + s = NULL; + else { + s = strdup(description); + if (!s) + return -ENOMEM; + } free(u->description); u->description = s; @@ -305,9 +311,6 @@ void unit_add_to_gc_queue(Unit *u) { u->in_gc_queue = true; u->manager->n_in_gc_queue ++; - - if (u->manager->gc_queue_timestamp <= 0) - u->manager->gc_queue_timestamp = now(CLOCK_MONOTONIC); } void unit_add_to_dbus_queue(Unit *u) { @@ -348,6 +351,29 @@ static void bidi_set_free(Unit *u, Set *s) { set_free(s); } +static void unit_remove_transient(Unit *u) { + char **i; + + assert(u); + + if (!u->transient) + return; + + if (u->fragment_path) + unlink(u->fragment_path); + + STRV_FOREACH(i, u->dropin_paths) { + _cleanup_free_ char *p = NULL; + int r; + + unlink(*i); + + r = path_get_parent(*i, &p); + if (r >= 0) + rmdir(p); + } +} + void unit_free(Unit *u) { UnitDependency d; Iterator i; @@ -355,6 +381,9 @@ void unit_free(Unit *u) { assert(u); + if (u->manager->n_reloading <= 0) + unit_remove_transient(u); + bus_unit_send_removed_signal(u); if (u->load_state != UNIT_STUB) @@ -401,9 +430,10 @@ void unit_free(Unit *u) { u->manager->n_in_gc_queue--; } - cgroup_bonding_free_list(u->cgroup_bondings, u->manager->n_reloading <= 0); - cgroup_attribute_free_list(u->cgroup_attributes); + if (u->in_cgroup_queue) + LIST_REMOVE(Unit, cgroup_queue, u->manager->cgroup_queue, u); + free(u->cgroup_path); free(u->description); strv_free(u->documentation); free(u->fragment_path); @@ -415,6 +445,8 @@ void unit_free(Unit *u) { condition_free_list(u->conditions); + unit_ref_unset(&u->slice); + while (u->refs) unit_ref_unset(u->refs); @@ -521,7 +553,7 @@ int unit_merge(Unit *u, Unit *other) { return -EINVAL; if (other->load_state != UNIT_STUB && - other->load_state != UNIT_ERROR) + other->load_state != UNIT_NOT_FOUND) return -EEXIST; if (other->job) @@ -577,7 +609,8 @@ int unit_merge_by_name(Unit *u, const char *name) { name = s; } - if (!(other = manager_get_unit(u->manager, name))) + other = manager_get_unit(u->manager, name); + if (!other) r = unit_add_name(u, name); else r = unit_merge(u, other); @@ -669,7 +702,12 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tActive Exit Timestamp: %s\n" "%s\tInactive Enter Timestamp: %s\n" "%s\tGC Check Good: %s\n" - "%s\tNeed Daemon Reload: %s\n", + "%s\tNeed Daemon Reload: %s\n" + "%s\tTransient: %s\n" + "%s\tSlice: %s\n" + "%s\tCGroup: %s\n" + "%s\tCGroup realized: %s\n" + "%s\tCGroup mask: 0x%x\n", prefix, u->id, prefix, unit_description(u), prefix, strna(u->instance), @@ -680,7 +718,12 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)), prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)), prefix, yes_no(unit_check_gc(u)), - prefix, yes_no(unit_need_daemon_reload(u))); + prefix, yes_no(unit_need_daemon_reload(u)), + prefix, yes_no(u->transient), + prefix, strna(unit_slice_name(u)), + prefix, strna(u->cgroup_path), + prefix, yes_no(u->cgroup_realized), + prefix, u->cgroup_mask); SET_FOREACH(t, u->names, i) fprintf(f, "%s\tName: %s\n", prefix, t); @@ -730,8 +773,6 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { } if (u->load_state == UNIT_LOADED) { - CGroupBonding *b; - CGroupAttribute *a; fprintf(f, "%s\tStopWhenUnneeded: %s\n" @@ -749,20 +790,6 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(u->ignore_on_isolate), prefix, yes_no(u->ignore_on_snapshot)); - LIST_FOREACH(by_unit, b, u->cgroup_bondings) - fprintf(f, "%s\tControlGroup: %s:%s\n", - prefix, b->controller, b->path); - - LIST_FOREACH(by_unit, a, u->cgroup_attributes) { - _cleanup_free_ char *v = NULL; - - if (a->semantics && a->semantics->map_write) - a->semantics->map_write(a->semantics, a->value, &v); - - fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n", - prefix, a->controller, a->name, v ? v : a->value); - } - if (UNIT_VTABLE(u)->dump) UNIT_VTABLE(u)->dump(u, f, prefix2); @@ -790,14 +817,16 @@ int unit_load_fragment_and_dropin(Unit *u) { assert(u); /* Load a .service file */ - if ((r = unit_load_fragment(u)) < 0) + r = unit_load_fragment(u); + if (r < 0) return r; if (u->load_state == UNIT_STUB) return -ENOENT; /* Load drop-in directory data */ - if ((r = unit_load_dropin(unit_follow_merge(u))) < 0) + r = unit_load_dropin(unit_follow_merge(u)); + if (r < 0) return r; return 0; @@ -813,14 +842,16 @@ int unit_load_fragment_and_dropin_optional(Unit *u) { * something can be loaded or not doesn't matter. */ /* Load a .service file */ - if ((r = unit_load_fragment(u)) < 0) + r = unit_load_fragment(u); + if (r < 0) return r; if (u->load_state == UNIT_STUB) u->load_state = UNIT_LOADED; /* Load drop-in directory data */ - if ((r = unit_load_dropin(unit_follow_merge(u))) < 0) + r = unit_load_dropin(unit_follow_merge(u)); + if (r < 0) return r; return 0; @@ -853,6 +884,7 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) { } static int unit_add_default_dependencies(Unit *u) { + static const UnitDependency deps[] = { UNIT_REQUIRED_BY, UNIT_REQUIRED_BY_OVERRIDABLE, @@ -868,9 +900,21 @@ static int unit_add_default_dependencies(Unit *u) { assert(u); for (k = 0; k < ELEMENTSOF(deps); k++) - SET_FOREACH(target, u->dependencies[deps[k]], i) - if ((r = unit_add_default_target_dependency(u, target)) < 0) + SET_FOREACH(target, u->dependencies[deps[k]], i) { + r = unit_add_default_target_dependency(u, target); + if (r < 0) 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); + + if (r < 0) + return r; + } return 0; } @@ -891,34 +935,38 @@ int unit_load(Unit *u) { if (u->load_state != UNIT_STUB) return 0; - if (UNIT_VTABLE(u)->load) - if ((r = UNIT_VTABLE(u)->load(u)) < 0) + if (UNIT_VTABLE(u)->load) { + r = UNIT_VTABLE(u)->load(u); + if (r < 0) goto fail; + } if (u->load_state == UNIT_STUB) { r = -ENOENT; goto fail; } - if (u->load_state == UNIT_LOADED && - u->default_dependencies) - if ((r = unit_add_default_dependencies(u)) < 0) - goto fail; - if (u->load_state == UNIT_LOADED) { + + if (u->default_dependencies) { + r = unit_add_default_dependencies(u); + if (r < 0) + goto fail; + } + r = unit_add_mount_links(u); if (r < 0) - return r; - } + goto fail; - if (u->on_failure_isolate && - set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { + if (u->on_failure_isolate && + set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { - log_error_unit(u->id, - "More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", u->id); + log_error_unit(u->id, + "More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", u->id); - r = -EINVAL; - goto fail; + r = -EINVAL; + goto fail; + } } assert((u->load_state != UNIT_MERGED) == !u->merged_into); @@ -929,7 +977,7 @@ int unit_load(Unit *u) { return 0; fail: - u->load_state = UNIT_ERROR; + u->load_state = u->load_state == UNIT_STUB ? UNIT_NOT_FOUND : UNIT_ERROR; u->load_error = r; unit_add_to_dbus_queue(u); unit_add_to_gc_queue(u); @@ -949,7 +997,7 @@ bool unit_condition_test(Unit *u) { return u->condition_result; } -static const char* unit_get_status_message_format(Unit *u, JobType t) { +_pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) { const UnitStatusMessageFormats *format_table; assert(u); @@ -966,7 +1014,7 @@ static const char* unit_get_status_message_format(Unit *u, JobType t) { return format_table->starting_stopping[t == JOB_STOP]; } -static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) { +_pure_ static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) { const char *format; assert(u); @@ -1206,19 +1254,19 @@ static void unit_check_unneeded(Unit *u) { return; SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) - if (unit_pending_active(other)) + if (unit_active_or_pending(other)) return; log_info_unit(u->id, "Service %s is not needed anymore. Stopping.", u->id); @@ -1368,7 +1416,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - cgroup_bonding_trim_list(u->cgroup_bondings, true); + unit_destroy_cgroup(u); if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) { ExecContext *ec = unit_get_exec_context(u); @@ -1468,7 +1516,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (ns != os && ns == UNIT_FAILED) { log_notice_unit(u->id, - "MESSAGE=Unit %s entered failed state.", u->id); + "Unit %s entered failed state.", u->id); unit_start_on_failure(u); } } @@ -1938,49 +1986,27 @@ char *unit_dbus_path(Unit *u) { return unit_dbus_path_from_name(u->id); } -int unit_add_cgroup(Unit *u, CGroupBonding *b) { +char *unit_default_cgroup_path(Unit *u) { + _cleanup_free_ char *escaped_instance = NULL, *slice = NULL; int r; assert(u); - assert(b); - - assert(b->path); - if (!b->controller) { - b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER); - if (!b->controller) - return log_oom(); - - b->ours = true; - } + if (unit_has_name(u, SPECIAL_ROOT_SLICE)) + return strdup(u->manager->cgroup_root); - /* Ensure this hasn't been added yet */ - assert(!b->unit); - - if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) { - CGroupBonding *l; - - l = hashmap_get(u->manager->cgroup_bondings, b->path); - LIST_PREPEND(CGroupBonding, by_path, l, b); - - r = hashmap_replace(u->manager->cgroup_bondings, b->path, l); - if (r < 0) { - LIST_REMOVE(CGroupBonding, by_path, l, b); - return r; - } + if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) { + r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice); + if (r < 0) + return NULL; } - LIST_PREPEND(CGroupBonding, by_unit, u->cgroup_bondings, b); - b->unit = u; - - return 0; -} - -char *unit_default_cgroup_path(Unit *u) { - assert(u); + escaped_instance = cg_escape(u->id); + if (!escaped_instance) + return NULL; if (u->instance) { - _cleanup_free_ char *t = NULL, *escaped_template = NULL, *escaped_instance = NULL; + _cleanup_free_ char *t = NULL, *escaped_template = NULL; t = unit_name_template(u->id); if (!t) @@ -1990,328 +2016,78 @@ char *unit_default_cgroup_path(Unit *u) { if (!escaped_template) return NULL; - escaped_instance = cg_escape(u->id); - if (!escaped_instance) - return NULL; - - return strjoin(u->manager->cgroup_hierarchy, "/", escaped_template, "/", escaped_instance, NULL); - } else { - _cleanup_free_ char *escaped = NULL; - - escaped = cg_escape(u->id); - if (!escaped) - return NULL; - - return strjoin(u->manager->cgroup_hierarchy, "/", escaped, NULL); - } + return strjoin(u->manager->cgroup_root, "/", + slice ? slice : "", slice ? "/" : "", + escaped_template, "/", escaped_instance, NULL); + } else + return strjoin(u->manager->cgroup_root, "/", + slice ? slice : "", slice ? "/" : "", + escaped_instance, NULL); } -int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret) { - char *controller = NULL, *path = NULL; - CGroupBonding *b = NULL; - bool ours = false; +int unit_add_default_slice(Unit *u) { + Unit *slice; int r; assert(u); - assert(name); - - r = cg_split_spec(name, &controller, &path); - if (r < 0) - return r; - - if (!path) { - path = unit_default_cgroup_path(u); - ours = true; - } - - if (!controller) { - controller = strdup(SYSTEMD_CGROUP_CONTROLLER); - ours = true; - } - - if (!path || !controller) { - free(path); - free(controller); - return log_oom(); - } - - b = cgroup_bonding_find_list(u->cgroup_bondings, controller); - if (b) { - if (streq(path, b->path)) { - free(path); - free(controller); - - if (ret) - *ret = b; - return 0; - } - - if (overwrite && !b->essential) { - free(controller); - - free(b->path); - b->path = path; - - b->ours = ours; - b->realized = false; - - if (ret) - *ret = b; - - return 1; - } - - r = -EEXIST; - b = NULL; - goto fail; - } - b = new0(CGroupBonding, 1); - if (!b) { - r = -ENOMEM; - goto fail; - } - - b->controller = controller; - b->path = path; - b->ours = ours; - b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER); - - r = unit_add_cgroup(u, b); - if (r < 0) - goto fail; - - if (ret) - *ret = b; - - return 1; - -fail: - free(path); - free(controller); - free(b); - - return r; -} - -static int unit_add_one_default_cgroup(Unit *u, const char *controller) { - CGroupBonding *b = NULL; - int r = -ENOMEM; - - assert(u); - - if (!controller) - controller = SYSTEMD_CGROUP_CONTROLLER; - - if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) + if (UNIT_ISSET(u->slice)) return 0; - b = new0(CGroupBonding, 1); - if (!b) - return -ENOMEM; - - b->controller = strdup(controller); - if (!b->controller) - goto fail; - - b->path = unit_default_cgroup_path(u); - if (!b->path) - goto fail; - - b->ours = true; - b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER); - - r = unit_add_cgroup(u, b); - if (r < 0) - goto fail; - - return 1; - -fail: - free(b->path); - free(b->controller); - free(b); - - return r; -} - -int unit_add_default_cgroups(Unit *u) { - CGroupAttribute *a; - char **c; - int r; - - assert(u); - - /* Adds in the default cgroups, if they weren't specified - * otherwise. */ - - if (!u->manager->cgroup_hierarchy) + if (!unit_get_cgroup_context(u)) return 0; - r = unit_add_one_default_cgroup(u, NULL); + r = manager_load_unit(u->manager, u->manager->running_as == SYSTEMD_SYSTEM ? SPECIAL_SYSTEM_SLICE : SPECIAL_ROOT_SLICE, NULL, NULL, &slice); if (r < 0) return r; - STRV_FOREACH(c, u->manager->default_controllers) - unit_add_one_default_cgroup(u, *c); - - LIST_FOREACH(by_unit, a, u->cgroup_attributes) - unit_add_one_default_cgroup(u, a->controller); - + unit_ref_set(&u->slice, slice); return 0; } -CGroupBonding* unit_get_default_cgroup(Unit *u) { - assert(u); - - return cgroup_bonding_find_list(u->cgroup_bondings, NULL); -} - -int unit_add_cgroup_attribute( - Unit *u, - const CGroupSemantics *semantics, - const char *controller, - const char *name, - const char *value, - CGroupAttribute **ret) { - - _cleanup_free_ char *c = NULL; - CGroupAttribute *a; - int r; - +const char *unit_slice_name(Unit *u) { assert(u); - assert(value); - - if (semantics) { - /* Semantics always take precedence */ - if (semantics->name) - name = semantics->name; - - if (semantics->controller) - controller = semantics->controller; - } - - if (!name) - return -EINVAL; - - if (!controller) { - r = cg_controller_from_attr(name, &c); - if (r < 0) - return -EINVAL; - - controller = c; - } - if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER)) - return -EINVAL; - - if (!filename_is_safe(name)) - return -EINVAL; - - if (!filename_is_safe(controller)) - return -EINVAL; - - /* Check if this attribute already exists. Note that we will - * explicitly check for the value here too, as there are - * attributes which accept multiple values. */ - a = cgroup_attribute_find_list(u->cgroup_attributes, controller, name); - if (a) { - if (streq(value, a->value)) { - /* Exactly the same value is always OK, let's ignore this */ - if (ret) - *ret = a; - - return 0; - } - - if (semantics && !semantics->multiple) { - char *v; - - /* If this is a single-item entry, we can - * simply patch the existing attribute */ - - v = strdup(value); - if (!v) - return -ENOMEM; - - free(a->value); - a->value = v; - - if (ret) - *ret = a; - return 1; - } - } - - a = new0(CGroupAttribute, 1); - if (!a) - return -ENOMEM; - - if (c) { - a->controller = c; - c = NULL; - } else - a->controller = strdup(controller); - - a->name = strdup(name); - a->value = strdup(value); - - if (!a->controller || !a->name || !a->value) { - free(a->controller); - free(a->name); - free(a->value); - free(a); - return -ENOMEM; - } - - a->semantics = semantics; - a->unit = u; - - LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a); - - if (ret) - *ret = a; + if (!UNIT_ISSET(u->slice)) + return NULL; - return 1; + return UNIT_DEREF(u->slice)->id; } int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { - char *t; + _cleanup_free_ char *t = NULL; int r; assert(u); assert(type); assert(_found); - if (!(t = unit_name_change_suffix(u->id, type))) + t = unit_name_change_suffix(u->id, type); + if (!t) return -ENOMEM; assert(!unit_has_name(u, t)); r = manager_load_unit(u->manager, t, NULL, NULL, _found); - free(t); - assert(r < 0 || *_found != u); - return r; } int unit_get_related_unit(Unit *u, const char *type, Unit **_found) { + _cleanup_free_ char *t = NULL; Unit *found; - char *t; assert(u); assert(type); assert(_found); - if (!(t = unit_name_change_suffix(u->id, type))) + t = unit_name_change_suffix(u->id, type); + if (!t) return -ENOMEM; assert(!unit_has_name(u, t)); found = manager_get_unit(u->manager, t); - free(t); - if (!found) return -ENOENT; @@ -2352,7 +2128,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { if (!unit_can_serialize(u)) return 0; - if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0) + r = UNIT_VTABLE(u)->serialize(u, f, fds); + if (r < 0) return r; @@ -2377,6 +2154,11 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { if (dual_timestamp_is_set(&u->condition_timestamp)) unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result)); + unit_serialize_item(u, f, "transient", yes_no(u->transient)); + + if (u->cgroup_path) + unit_serialize_item(u, f, "cgroup", u->cgroup_path); + /* End marker */ fputc('\n', f); return 0; @@ -2499,15 +2281,38 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { } else if (streq(l, "condition-result")) { int b; - if ((b = parse_boolean(v)) < 0) + b = parse_boolean(v); + if (b < 0) log_debug("Failed to parse condition result value %s", v); else u->condition_result = b; continue; + + } else if (streq(l, "transient")) { + int b; + + b = parse_boolean(v); + if (b < 0) + log_debug("Failed to parse transient bool %s", v); + else + u->transient = b; + + continue; + } else if (streq(l, "cgroup")) { + char *s; + + s = strdup(v); + if (!v) + return -ENOMEM; + + free(u->cgroup_path); + u->cgroup_path = s; + continue; } - if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0) + r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds); + if (r < 0) return r; } } @@ -2574,9 +2379,12 @@ 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) { manager_status_printf(u->manager, false, status, unit_status_msg_format, unit_description(u)); } +#pragma GCC diagnostic pop bool unit_need_daemon_reload(Unit *u) { _cleanup_strv_free_ char **t = NULL; @@ -2649,7 +2457,19 @@ Unit *unit_following(Unit *u) { return NULL; } -bool unit_pending_inactive(Unit *u) { +bool unit_stop_pending(Unit *u) { + assert(u); + + /* This call does check the current state of the unit. It's + * hence useful to be called from state change calls of the + * unit itself, where the state isn't updated yet. This is + * different from unit_inactive_or_pending() which checks both + * the current state and for a queued job. */ + + return u->job && u->job->type == JOB_STOP; +} + +bool unit_inactive_or_pending(Unit *u) { assert(u); /* Returns true if the unit is inactive or going down */ @@ -2657,13 +2477,13 @@ bool unit_pending_inactive(Unit *u) { if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))) return true; - if (u->job && u->job->type == JOB_STOP) + if (unit_stop_pending(u)) return true; return false; } -bool unit_pending_active(Unit *u) { +bool unit_active_or_pending(Unit *u) { assert(u); /* Returns true if the unit is active or going up */ @@ -2728,7 +2548,7 @@ int unit_kill_common( if (kill(main_pid, signo) < 0) r = -errno; - if (who == KILL_ALL) { + if (who == KILL_ALL && u->cgroup_path) { _cleanup_set_free_ Set *pid_set = NULL; int q; @@ -2749,7 +2569,7 @@ int unit_kill_common( return q; } - q = cgroup_bonding_kill_list(u->cgroup_bondings, signo, false, false, pid_set, NULL); + q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set); if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) r = q; } @@ -2848,7 +2668,6 @@ int unit_exec_context_defaults(Unit *u, ExecContext *c) { 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); @@ -2878,7 +2697,17 @@ ExecContext *unit_get_exec_context(Unit *u) { return (ExecContext*) ((uint8_t*) u + offset); } -static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char **_q) { +CGroupContext *unit_get_cgroup_context(Unit *u) { + size_t offset; + + offset = UNIT_VTABLE(u)->cgroup_context_offset; + if (offset <= 0) + return NULL; + + return (CGroupContext*) ((uint8_t*) u + offset); +} + +static int drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **_p, char **_q) { char *p, *q; int r; @@ -2886,9 +2715,7 @@ static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char assert(name); assert(_p); assert(_q); - - if (u->manager->running_as == SYSTEMD_USER && runtime) - return -ENOTSUP; + assert(mode & (UNIT_PERSISTENT|UNIT_RUNTIME)); if (!filename_is_safe(name)) return -EINVAL; @@ -2903,14 +2730,14 @@ static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char return -ENOENT; p = strjoin(c, "/", u->id, ".d", NULL); - } else if (runtime) - p = strjoin("/run/systemd/system/", u->id, ".d", NULL); - else + } else if (mode & UNIT_PERSISTENT) p = strjoin("/etc/systemd/system/", u->id, ".d", NULL); + else + p = strjoin("/run/systemd/system/", u->id, ".d", NULL); if (!p) return -ENOMEM; - q = strjoin(p, "/50-", name, ".conf", NULL); + q = strjoin(p, "/90-", name, ".conf", NULL); if (!q) { free(p); return -ENOMEM; @@ -2921,13 +2748,18 @@ static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char return 0; } -int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) { +int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { _cleanup_free_ char *p = NULL, *q = NULL; int r; assert(u); + assert(name); + assert(data); - r = drop_in_file(u, runtime, name, &p, &q); + if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + return 0; + + r = drop_in_file(u, mode, name, &p, &q); if (r < 0) return r; @@ -2935,22 +2767,79 @@ int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data return write_string_file_atomic_label(q, data); } -int unit_remove_drop_in(Unit *u, bool runtime, const char *name) { +int unit_write_drop_in_private_section(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { + _cleanup_free_ char *ndata = NULL; + + assert(u); + assert(name); + assert(data); + + if (!UNIT_VTABLE(u)->private_section) + return -EINVAL; + + ndata = strjoin("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL); + if (!ndata) + return -ENOMEM; + + return unit_write_drop_in(u, mode, name, ndata); +} + +int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) { _cleanup_free_ char *p = NULL, *q = NULL; int r; assert(u); - r = drop_in_file(u, runtime, name, &p, &q); + if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + return 0; + + r = drop_in_file(u, mode, name, &p, &q); if (unlink(q) < 0) - r = -errno; + r = errno == ENOENT ? 0 : -errno; else - r = 0; + r = 1; rmdir(p); return r; } +int unit_make_transient(Unit *u) { + int r; + + assert(u); + + u->load_state = UNIT_STUB; + u->load_error = 0; + u->transient = true; + + free(u->fragment_path); + u->fragment_path = NULL; + + if (u->manager->running_as == SYSTEMD_USER) { + _cleanup_free_ char *c = NULL; + + r = user_config_home(&c); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + u->fragment_path = strjoin(c, "/", u->id, NULL); + if (!u->fragment_path) + return -ENOMEM; + + mkdir_p(c, 0755); + } else { + u->fragment_path = strappend("/run/systemd/system/", u->id); + if (!u->fragment_path) + return -ENOMEM; + + mkdir_p("/run/systemd/system", 0755); + } + + return write_string_file_atomic_label(u->fragment_path, "# Transient stub"); +} + int unit_kill_context( Unit *u, KillContext *c, @@ -2996,7 +2885,7 @@ int unit_kill_context( wait_for_exit = true; } - if (c->kill_mode == KILL_CONTROL_GROUP) { + if (c->kill_mode == KILL_CONTROL_GROUP && u->cgroup_path) { _cleanup_set_free_ Set *pid_set = NULL; pid_set = set_new(trivial_hash_func, trivial_compare_func); @@ -3016,7 +2905,7 @@ int unit_kill_context( return r; } - r = cgroup_bonding_kill_list(u->cgroup_bondings, sig, true, false, pid_set, NULL); + r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, true, false, pid_set); if (r < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) log_warning_unit(u->id, "Failed to kill control group: %s", strerror(-r));