]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/unit.c
core: unified cgroup hierarchy support
[thirdparty/systemd.git] / src / core / unit.c
index 7bb2afc9f2a965810e594fed6af55efed0556a5f..8c07c6140dbea58084a7489f211d33afb71b266c 100644 (file)
 #include "sd-id128.h"
 #include "sd-messages.h"
 #include "set.h"
-#include "unit.h"
 #include "macro.h"
 #include "strv.h"
 #include "path-util.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
 #include "log.h"
-#include "unit-name.h"
-#include "dbus-unit.h"
-#include "special.h"
 #include "cgroup-util.h"
 #include "missing.h"
 #include "mkdir.h"
 #include "fileio-label.h"
+#include "formats-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 "special.h"
+#include "unit.h"
+#include "load-fragment.h"
+#include "load-dropin.h"
 #include "dbus.h"
+#include "dbus-unit.h"
 #include "execute.h"
-#include "dropin.h"
-#include "formats-util.h"
-#include "process-util.h"
 
 const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
         [UNIT_SERVICE] = &service_vtable,
@@ -89,6 +91,7 @@ Unit *unit_new(Manager *m, size_t size) {
         u->unit_file_state = _UNIT_FILE_STATE_INVALID;
         u->unit_file_preset = -1;
         u->on_failure_job_mode = JOB_REPLACE;
+        u->cgroup_inotify_wd = -1;
 
         RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
 
@@ -404,17 +407,17 @@ static void unit_remove_transient(Unit *u) {
                 return;
 
         if (u->fragment_path)
-                unlink(u->fragment_path);
+                (void) unlink(u->fragment_path);
 
         STRV_FOREACH(i, u->dropin_paths) {
                 _cleanup_free_ char *p = NULL;
                 int r;
 
-                unlink(*i);
+                (void) unlink(*i);
 
                 r = path_get_parent(*i, &p);
                 if (r >= 0)
-                        rmdir(p);
+                        (void) rmdir(p);
         }
 }
 
@@ -481,6 +484,8 @@ void unit_free(Unit *u) {
 
         unit_done(u);
 
+        sd_bus_slot_unref(u->match_bus_slot);
+
         unit_free_requires_mounts_for(u);
 
         SET_FOREACH(t, u->names, i)
@@ -521,10 +526,7 @@ void unit_free(Unit *u) {
         if (u->in_cgroup_queue)
                 LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u);
 
-        if (u->cgroup_path) {
-                hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
-                free(u->cgroup_path);
-        }
+        unit_release_cgroup(u);
 
         manager_update_failed_units(u->manager, u, false);
         set_remove(u->manager->startup_units, u);
@@ -1119,7 +1121,7 @@ static int unit_add_target_dependencies(Unit *u) {
 static int unit_add_slice_dependencies(Unit *u) {
         assert(u);
 
-        if (!unit_get_cgroup_context(u))
+        if (!UNIT_HAS_CGROUP_CONTEXT(u))
                 return 0;
 
         if (UNIT_ISSET(u->slice))
@@ -1318,42 +1320,28 @@ static bool unit_assert_test(Unit *u) {
 }
 
 _pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) {
-        const UnitStatusMessageFormats *format_table;
-
-        assert(u);
-        assert(t >= 0);
-        assert(t < _JOB_TYPE_MAX);
-
-        if (t != JOB_START && t != JOB_STOP)
-                return NULL;
-
-        format_table = &UNIT_VTABLE(u)->status_message_formats;
-        if (!format_table)
-                return NULL;
-
-        return format_table->starting_stopping[t == JOB_STOP];
-}
-
-_pure_ static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) {
         const char *format;
+        const UnitStatusMessageFormats *format_table;
 
         assert(u);
-        assert(t >= 0);
-        assert(t < _JOB_TYPE_MAX);
+        assert(t == JOB_START || t == JOB_STOP || t == JOB_RELOAD);
 
-        format = unit_get_status_message_format(u, t);
-        if (format)
-                return format;
+        if (t != JOB_RELOAD) {
+                format_table = &UNIT_VTABLE(u)->status_message_formats;
+                if (format_table) {
+                        format = format_table->starting_stopping[t == JOB_STOP];
+                        if (format)
+                                return format;
+                }
+        }
 
         /* Return generic strings */
         if (t == JOB_START)
                 return "Starting %s.";
         else if (t == JOB_STOP)
                 return "Stopping %s.";
-        else if (t == JOB_RELOAD)
+        else
                 return "Reloading %s.";
-
-        return NULL;
 }
 
 static void unit_status_print_starting_stopping(Unit *u, JobType t) {
@@ -1361,12 +1349,7 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) {
 
         assert(u);
 
-        /* We only print status messages for selected units on
-         * selected operations. */
-
         format = unit_get_status_message_format(u, t);
-        if (!format)
-                return;
 
         DISABLE_WARNING_FORMAT_NONLITERAL;
         unit_status_printf(u, "", format);
@@ -1388,9 +1371,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
 
         /* We log status messages for all units and all operations. */
 
-        format = unit_get_status_message_format_try_harder(u, t);
-        if (!format)
-                return;
+        format = unit_get_status_message_format(u, t);
 
         DISABLE_WARNING_FORMAT_NONLITERAL;
         snprintf(buf, sizeof(buf), format, unit_description(u));
@@ -1413,6 +1394,15 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
                    NULL);
 }
 
+void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) {
+
+        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);
+}
+
 /* Errors:
  *         -EBADR:     This unit type does not support starting.
  *         -EALREADY:  Unit is already started.
@@ -1423,7 +1413,6 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
 int unit_start(Unit *u) {
         UnitActiveState state;
         Unit *following;
-        int r;
 
         assert(u);
 
@@ -1477,14 +1466,7 @@ int unit_start(Unit *u) {
 
         unit_add_to_dbus_queue(u);
 
-        r = UNIT_VTABLE(u)->start(u);
-        if (r <= 0)
-                return r;
-
-        /* Log if the start function actually did something */
-        unit_status_log_starting_stopping_reloading(u, JOB_START);
-        unit_status_print_starting_stopping(u, JOB_START);
-        return r;
+        return UNIT_VTABLE(u)->start(u);
 }
 
 bool unit_can_start(Unit *u) {
@@ -1508,7 +1490,6 @@ bool unit_can_isolate(Unit *u) {
 int unit_stop(Unit *u) {
         UnitActiveState state;
         Unit *following;
-        int r;
 
         assert(u);
 
@@ -1527,13 +1508,7 @@ int unit_stop(Unit *u) {
 
         unit_add_to_dbus_queue(u);
 
-        r = UNIT_VTABLE(u)->stop(u);
-        if (r <= 0)
-                return r;
-
-        unit_status_log_starting_stopping_reloading(u, JOB_STOP);
-        unit_status_print_starting_stopping(u, JOB_STOP);
-        return r;
+        return UNIT_VTABLE(u)->stop(u);
 }
 
 /* Errors:
@@ -1544,7 +1519,6 @@ int unit_stop(Unit *u) {
 int unit_reload(Unit *u) {
         UnitActiveState state;
         Unit *following;
-        int r;
 
         assert(u);
 
@@ -1571,12 +1545,7 @@ int unit_reload(Unit *u) {
 
         unit_add_to_dbus_queue(u);
 
-        r = UNIT_VTABLE(u)->reload(u);
-        if (r <= 0)
-                return r;
-
-        unit_status_log_starting_stopping_reloading(u, JOB_RELOAD);
-        return r;
+        return UNIT_VTABLE(u)->reload(u);
 }
 
 bool unit_can_reload(Unit *u) {
@@ -1596,7 +1565,7 @@ static void unit_check_unneeded(Unit *u) {
         static const UnitDependency needed_dependencies[] = {
                 UNIT_REQUIRED_BY,
                 UNIT_REQUIRED_BY_OVERRIDABLE,
-                UNIT_REQUISITE,
+                UNIT_REQUISITE_OF,
                 UNIT_REQUISITE_OF_OVERRIDABLE,
                 UNIT_WANTED_BY,
                 UNIT_BOUND_BY,
@@ -1830,7 +1799,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
 
         /* Make sure the cgroup is always removed when we become inactive */
         if (UNIT_IS_INACTIVE_OR_FAILED(ns))
-                unit_destroy_cgroup_if_empty(u);
+                unit_prune_cgroup(u);
 
         /* Note that this doesn't apply to RemainAfterExit services exiting
          * successfully, since there's no change of state in that case. Which is
@@ -2046,9 +2015,9 @@ void unit_unwatch_pid(Unit *u, pid_t pid) {
         assert(u);
         assert(pid >= 1);
 
-        hashmap_remove_value(u->manager->watch_pids1, LONG_TO_PTR(pid), u);
-        hashmap_remove_value(u->manager->watch_pids2, LONG_TO_PTR(pid), u);
-        set_remove(u->pids, LONG_TO_PTR(pid));
+        (void) hashmap_remove_value(u->manager->watch_pids1, LONG_TO_PTR(pid), u);
+        (void) hashmap_remove_value(u->manager->watch_pids2, LONG_TO_PTR(pid), u);
+        (void) set_remove(u->pids, LONG_TO_PTR(pid));
 }
 
 void unit_unwatch_all_pids(Unit *u) {
@@ -2057,70 +2026,7 @@ void unit_unwatch_all_pids(Unit *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;
-
-        assert(u);
-        assert(path);
-
-        /* Adds all PIDs from a specific cgroup path to the set of PIDs we watch. */
-
-        r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f);
-        if (r >= 0) {
-                pid_t pid;
-
-                while ((r = cg_read_pid(f, &pid)) > 0) {
-                        r = unit_watch_pid(u, pid);
-                        if (r < 0 && ret >= 0)
-                                ret = r;
-                }
-                if (r < 0 && ret >= 0)
-                        ret = r;
-
-        } else if (ret >= 0)
-                ret = r;
-
-        r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d);
-        if (r >= 0) {
-                char *fn;
-
-                while ((r = cg_read_subgroup(d, &fn)) > 0) {
-                        _cleanup_free_ char *p = NULL;
-
-                        p = strjoin(path, "/", fn, NULL);
-                        free(fn);
-
-                        if (!p)
-                                return -ENOMEM;
-
-                        r = unit_watch_pids_in_path(u, p);
-                        if (r < 0 && ret >= 0)
-                                ret = r;
-                }
-                if (r < 0 && ret >= 0)
-                        ret = r;
-
-        } else if (ret >= 0)
-                ret = r;
-
-        return ret;
-}
-
-int unit_watch_all_pids(Unit *u) {
-        assert(u);
-
-        /* Adds all PIDs from our cgroup to the set of PIDs we watch */
-
-        if (!u->cgroup_path)
-                return -ENOENT;
-
-        return unit_watch_pids_in_path(u, u->cgroup_path);
+        u->pids = set_free(u->pids);
 }
 
 void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) {
@@ -2429,39 +2335,49 @@ char *unit_dbus_path(Unit *u) {
         return unit_dbus_path_from_name(u->id);
 }
 
-char *unit_default_cgroup_path(Unit *u) {
-        _cleanup_free_ char *escaped = NULL, *slice = NULL;
-        int r;
-
+int unit_set_slice(Unit *u, Unit *slice) {
         assert(u);
+        assert(slice);
 
-        if (unit_has_name(u, SPECIAL_ROOT_SLICE))
-                return strdup(u->manager->cgroup_root);
+        /* Sets the unit slice if it has not been set before. Is extra
+         * careful, to only allow this for units that actually have a
+         * cgroup context. Also, we don't allow to set this for slices
+         * (since the parent slice is derived from the name). Make
+         * sure the unit we set is actually a slice. */
 
-        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;
-        }
+        if (!UNIT_HAS_CGROUP_CONTEXT(u))
+                return -EOPNOTSUPP;
 
-        escaped = cg_escape(u->id);
-        if (!escaped)
-                return NULL;
+        if (u->type == UNIT_SLICE)
+                return -EINVAL;
 
-        if (slice)
-                return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL);
-        else
-                return strjoin(u->manager->cgroup_root, "/", escaped, NULL);
+        if (unit_active_state(u) != UNIT_INACTIVE)
+                return -EBUSY;
+
+        if (slice->type != UNIT_SLICE)
+                return -EINVAL;
+
+        if (unit_has_name(u, SPECIAL_INIT_SCOPE) &&
+            !unit_has_name(slice, SPECIAL_ROOT_SLICE))
+                return -EPERM;
+
+        if (UNIT_DEREF(u->slice) == slice)
+                return 0;
+
+        if (UNIT_ISSET(u->slice))
+                return -EBUSY;
+
+        unit_ref_set(&u->slice, slice);
+        return 1;
 }
 
-int unit_add_default_slice(Unit *u, CGroupContext *c) {
+int unit_set_default_slice(Unit *u) {
         _cleanup_free_ char *b = NULL;
         const char *slice_name;
         Unit *slice;
         int r;
 
         assert(u);
-        assert(c);
 
         if (UNIT_ISSET(u->slice))
                 return 0;
@@ -2493,7 +2409,7 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) {
                 slice_name = b;
         } else
                 slice_name =
-                        u->manager->running_as == MANAGER_SYSTEM
+                        u->manager->running_as == MANAGER_SYSTEM && !unit_has_name(u, SPECIAL_INIT_SCOPE)
                         ? SPECIAL_SYSTEM_SLICE
                         : SPECIAL_ROOT_SLICE;
 
@@ -2501,8 +2417,7 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) {
         if (r < 0)
                 return r;
 
-        unit_ref_set(&u->slice, slice);
-        return 0;
+        return unit_set_slice(u, slice);
 }
 
 const char *unit_slice_name(Unit *u) {
@@ -2533,14 +2448,74 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
         return r;
 }
 
+static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        const char *name, *old_owner, *new_owner;
+        Unit *u = userdata;
+        int r;
+
+        assert(message);
+        assert(u);
+
+        r = sd_bus_message_read(message, "sss", &name, &old_owner, &new_owner);
+        if (r < 0) {
+                bus_log_parse_error(r);
+                return 0;
+        }
+
+        if (UNIT_VTABLE(u)->bus_name_owner_change)
+                UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner);
+
+        return 0;
+}
+
+int unit_install_bus_match(sd_bus *bus, Unit *u, const char *name) {
+        _cleanup_free_ char *match = NULL;
+        Manager *m = u->manager;
+
+        assert(m);
+
+        if (u->match_bus_slot)
+                return -EBUSY;
+
+        match = strjoin("type='signal',"
+                        "sender='org.freedesktop.DBus',"
+                        "path='/org/freedesktop/DBus',"
+                        "interface='org.freedesktop.DBus',"
+                        "member='NameOwnerChanged',"
+                        "arg0='",
+                        name,
+                        "'",
+                        NULL);
+        if (!match)
+                return -ENOMEM;
+
+        return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u);
+}
+
 int unit_watch_bus_name(Unit *u, const char *name) {
+        int r;
+
         assert(u);
         assert(name);
 
         /* Watch a specific name on the bus. We only support one unit
          * watching each name for now. */
 
-        return hashmap_put(u->manager->watch_bus, name, u);
+        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);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m");
+        }
+
+        r = hashmap_put(u->manager->watch_bus, name, u);
+        if (r < 0) {
+                u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+                return log_warning_errno(r, "Failed to put bus name to hashmap: %m");
+        }
+
+        return 0;
 }
 
 void unit_unwatch_bus_name(Unit *u, const char *name) {
@@ -2548,6 +2523,7 @@ void unit_unwatch_bus_name(Unit *u, const char *name) {
         assert(name);
 
         hashmap_remove_value(u->manager->watch_bus, name, u);
+        u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
 }
 
 bool unit_can_serialize(Unit *u) {
@@ -2642,40 +2618,6 @@ void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
         fprintf(f, "%s=%s\n", key, value);
 }
 
-static int unit_set_cgroup_path(Unit *u, const char *path) {
-        _cleanup_free_ char *p = NULL;
-        int r;
-
-        assert(u);
-
-        if (path) {
-                p = strdup(path);
-                if (!p)
-                        return -ENOMEM;
-        } else
-                p = NULL;
-
-        if (streq_ptr(u->cgroup_path, p))
-                return 0;
-
-        if (p) {
-                r = hashmap_put(u->manager->cgroup_unit, p, u);
-                if (r < 0)
-                        return r;
-        }
-
-        if (u->cgroup_path) {
-                log_unit_debug(u, "Changing cgroup path from %s to %s.", u->cgroup_path, strna(p));
-                hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
-                free(u->cgroup_path);
-        }
-
-        u->cgroup_path = p;
-        p = NULL;
-
-        return 0;
-}
-
 int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
         ExecRuntime **rt = NULL;
         size_t offset;
@@ -2806,6 +2748,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                         if (r < 0)
                                 log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v);
 
+                        (void) unit_watch_cgroup(u);
+
                         continue;
                 } else if (streq(l, "cgroup-realized")) {
                         int b;
@@ -3077,17 +3021,17 @@ int unit_kill_common(
 
         int r = 0;
 
-        if (who == KILL_MAIN && main_pid <= 0) {
+        if (who == KILL_MAIN) {
                 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
+                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 && control_pid <= 0) {
+        if (who == KILL_CONTROL) {
                 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
+                else if (control_pid == 0)
                         return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
         }
 
@@ -3110,7 +3054,7 @@ int unit_kill_common(
                 if (!pid_set)
                         return -ENOMEM;
 
-                q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
+                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;
         }
@@ -3286,6 +3230,8 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) {
 }
 
 static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) {
+        assert(u);
+
         if (u->manager->running_as == MANAGER_USER) {
                 int r;
 
@@ -3293,9 +3239,9 @@ static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient,
                         r = user_config_home(dir);
                 else
                         r = user_runtime_dir(dir);
-
                 if (r == 0)
                         return -ENOENT;
+
                 return r;
         }
 
@@ -3309,8 +3255,7 @@ 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) {
+static int unit_drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **p, char **q) {
         _cleanup_free_ char *dir = NULL;
         int r;
 
@@ -3444,40 +3389,17 @@ int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) {
 }
 
 int unit_make_transient(Unit *u) {
-        int r;
-
         assert(u);
 
+        if (!UNIT_VTABLE(u)->can_transient)
+                return -EOPNOTSUPP;
+
         u->load_state = UNIT_STUB;
         u->load_error = 0;
         u->transient = true;
+        u->fragment_path = mfree(u->fragment_path);
 
-        free(u->fragment_path);
-        u->fragment_path = NULL;
-
-        if (u->manager->running_as == MANAGER_USER) {
-                _cleanup_free_ char *c = NULL;
-
-                r = user_runtime_dir(&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");
+        return 0;
 }
 
 int unit_kill_context(
@@ -3488,7 +3410,8 @@ int unit_kill_context(
                 pid_t control_pid,
                 bool main_pid_alien) {
 
-        int sig, wait_for_exit = false, r;
+        bool wait_for_exit = false;
+        int sig, r;
 
         assert(u);
         assert(c);
@@ -3517,13 +3440,13 @@ int unit_kill_context(
                         _cleanup_free_ char *comm = NULL;
                         get_process_comm(main_pid, &comm);
 
-                        log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s): %m", main_pid, strna(comm));
+                        log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s), ignoring: %m", main_pid, strna(comm));
                 } else {
                         if (!main_pid_alien)
                                 wait_for_exit = true;
 
-                        if (c->send_sighup && k != KILL_KILL)
-                                kill(main_pid, SIGHUP);
+                        if (c->send_sighup && k == KILL_TERMINATE)
+                                (void) kill(main_pid, SIGHUP);
                 }
         }
 
@@ -3534,16 +3457,17 @@ int unit_kill_context(
                         _cleanup_free_ char *comm = NULL;
                         get_process_comm(control_pid, &comm);
 
-                        log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s): %m", control_pid, strna(comm));
+                        log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s), ignoring: %m", control_pid, strna(comm));
                 } else {
                         wait_for_exit = true;
 
-                        if (c->send_sighup && k != KILL_KILL)
-                                kill(control_pid, SIGHUP);
+                        if (c->send_sighup && k == KILL_TERMINATE)
+                                (void) kill(control_pid, SIGHUP);
                 }
         }
 
-        if ((c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL)) && u->cgroup_path) {
+        if (u->cgroup_path &&
+            (c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL))) {
                 _cleanup_set_free_ Set *pid_set = NULL;
 
                 /* Exclude the main/control pids from being killed via the cgroup */
@@ -3551,21 +3475,30 @@ int unit_kill_context(
                 if (!pid_set)
                         return -ENOMEM;
 
-                r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, true, false, pid_set);
+                r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, k != KILL_TERMINATE, false, pid_set);
                 if (r < 0) {
                         if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
-                                log_unit_warning_errno(u, r, "Failed to kill control group: %m");
-                } else if (r > 0) {
+                                log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", u->cgroup_path);
 
-                        /* FIXME: For now, we will not wait for the
-                         * cgroup members to die, simply because
-                         * cgroup notification is unreliable. It
-                         * doesn't work at all in containers, and
-                         * outside of containers it can be confused
-                         * easily by leaving directories in the
-                         * cgroup. */
+                } else if (r > 0) {
 
-                        /* wait_for_exit = true; */
+                        /* FIXME: For now, on the legacy hierarchy, we
+                         * will not wait for the cgroup members to die
+                         * if we are running in a container or if this
+                         * is a delegation unit, simply because cgroup
+                         * notification is unreliable in these
+                         * cases. It doesn't work at all in
+                         * containers, and outside of containers it
+                         * can be confused easily by left-over
+                         * directories in the cgroup -- which however
+                         * should not exist in non-delegated units. On
+                         * the unified hierarchy that's different,
+                         * there we get proper events. Hence rely on
+                         * them.*/
+
+                        if  (cg_unified() > 0 ||
+                             (detect_container(NULL) == 0 && !unit_cgroup_delegate(u)))
+                                wait_for_exit = true;
 
                         if (c->send_sighup && k != KILL_KILL) {
                                 set_free(pid_set);