]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/unit.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / core / unit.c
index dd5e80128512766ce2b6c133bcd1127155e49d08..a054cc79b06c9d4fffe76655e3eb83e634918a60 100644 (file)
 ***/
 
 #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 "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 "bus-common-errors.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "dbus-unit.h"
 #include "dbus.h"
-#include "execute.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 "path-util.h"
 #include "process-util.h"
+#include "set.h"
+#include "special.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "virt.h"
+#include "unit.h"
 
 const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
         [UNIT_SERVICE] = &service_vtable,
@@ -89,6 +94,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);
 
@@ -122,6 +128,7 @@ 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;
         }
 
         ec = unit_get_exec_context(u);
@@ -404,17 +411,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);
         }
 }
 
@@ -442,13 +449,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);
 
@@ -465,6 +472,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) {
@@ -481,6 +492,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,12 +534,9 @@ 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);
+        (void) manager_update_failed_units(u->manager, u, false);
         set_remove(u->manager->startup_units, u);
 
         free(u->description);
@@ -672,8 +682,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) {
@@ -1119,16 +1128,16 @@ 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))
-                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) {
@@ -1141,13 +1150,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;
 
@@ -1171,15 +1190,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);
 }
 
@@ -1235,6 +1259,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);
@@ -1414,6 +1446,7 @@ int unit_start(Unit *u) {
 
         assert(u);
 
+        /* Units that aren't loaded cannot be started */
         if (u->load_state != UNIT_LOADED)
                 return -EINVAL;
 
@@ -1442,6 +1475,15 @@ int unit_start(Unit *u) {
                 return -EPROTO;
         }
 
+        /* Units of types that aren't supported cannot be
+         * started. Note that we do this test only after the condition
+         * checks, so that we rather return condition check errors
+         * (which are usually not considered a true failure) than "not
+         * supported" errors (which are considered a failure).
+         */
+        if (!unit_supported(u))
+                return -EOPNOTSUPP;
+
         /* Forward to the main object, if we aren't it. */
         following = unit_following(u);
         if (following) {
@@ -1449,9 +1491,6 @@ int unit_start(Unit *u) {
                 return unit_start(following);
         }
 
-        if (!unit_supported(u))
-                return -EOPNOTSUPP;
-
         /* If it is stopped, but we cannot start it, then fail */
         if (!UNIT_VTABLE(u)->start)
                 return -EBADR;
@@ -1470,6 +1509,12 @@ int unit_start(Unit *u) {
 bool unit_can_start(Unit *u) {
         assert(u);
 
+        if (u->load_state != UNIT_LOADED)
+                return false;
+
+        if (!unit_supported(u))
+                return false;
+
         return !!UNIT_VTABLE(u)->start;
 }
 
@@ -1793,11 +1838,11 @@ 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))
-                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
@@ -1993,16 +2038,16 @@ int unit_watch_pid(Unit *u, pid_t pid) {
         if (r < 0)
                 return r;
 
-        r = hashmap_put(u->manager->watch_pids1, LONG_TO_PTR(pid), u);
+        r = hashmap_put(u->manager->watch_pids1, PID_TO_PTR(pid), u);
         if (r == -EEXIST) {
                 r = hashmap_ensure_allocated(&u->manager->watch_pids2, NULL);
                 if (r < 0)
                         return r;
 
-                r = hashmap_put(u->manager->watch_pids2, LONG_TO_PTR(pid), u);
+                r = hashmap_put(u->manager->watch_pids2, PID_TO_PTR(pid), u);
         }
 
-        q = set_put(u->pids, LONG_TO_PTR(pid));
+        q = set_put(u->pids, PID_TO_PTR(pid));
         if (q < 0)
                 return q;
 
@@ -2013,81 +2058,18 @@ 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, PID_TO_PTR(pid), u);
+        (void) hashmap_remove_value(u->manager->watch_pids2, PID_TO_PTR(pid), u);
+        (void) set_remove(u->pids, PID_TO_PTR(pid));
 }
 
 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)));
+                unit_unwatch_pid(u, PTR_TO_PID(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) {
@@ -2099,7 +2081,7 @@ void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) {
         /* Cleans dead PIDs from our list */
 
         SET_FOREACH(e, u->pids, i) {
-                pid_t pid = PTR_TO_LONG(e);
+                pid_t pid = PTR_TO_PID(e);
 
                 if (pid == except1 || pid == except2)
                         continue;
@@ -2341,44 +2323,6 @@ 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)
@@ -2396,39 +2340,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;
@@ -2460,7 +2414,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;
 
@@ -2468,8 +2422,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) {
@@ -2500,14 +2453,71 @@ 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(Unit *u, sd_bus *bus, const char *name) {
+        const char *match;
+
+        assert(u);
+        assert(bus);
+        assert(name);
+
+        if (u->match_bus_slot)
+                return -EBUSY;
+
+        match = strjoina("type='signal',"
+                        "sender='org.freedesktop.DBus',"
+                        "path='/org/freedesktop/DBus',"
+                        "interface='org.freedesktop.DBus',"
+                        "member='NameOwnerChanged',"
+                        "arg0='", name, "'",
+                        NULL);
+
+        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, u->manager->api_bus, 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) {
@@ -2515,6 +2525,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) {
@@ -2565,6 +2576,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");
@@ -2582,65 +2596,78 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
         return 0;
 }
 
-void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
-        va_list ap;
-
+int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
         assert(u);
         assert(f);
         assert(key);
-        assert(format);
+
+        if (!value)
+                return 0;
 
         fputs(key, f);
         fputc('=', f);
-
-        va_start(ap, format);
-        vfprintf(f, format, ap);
-        va_end(ap);
-
+        fputs(value, f);
         fputc('\n', f);
+
+        return 1;
 }
 
-void 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) {
+        _cleanup_free_ char *c = NULL;
+
         assert(u);
         assert(f);
         assert(key);
-        assert(value);
 
-        fprintf(f, "%s=%s\n", key, value);
+        if (!value)
+                return 0;
+
+        c = cescape(value);
+        if (!c)
+                return -ENOMEM;
+
+        fputs(key, f);
+        fputc('=', f);
+        fputs(c, f);
+        fputc('\n', f);
+
+        return 1;
 }
 
-static int unit_set_cgroup_path(Unit *u, const char *path) {
-        _cleanup_free_ char *p = NULL;
-        int r;
+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 (path) {
-                p = strdup(path);
-                if (!p)
-                        return -ENOMEM;
-        } else
-                p = NULL;
-
-        if (streq_ptr(u->cgroup_path, p))
+        if (fd < 0)
                 return 0;
 
-        if (p) {
-                r = hashmap_put(u->manager->cgroup_unit, p, u);
-                if (r < 0)
-                        return r;
-        }
+        copy = fdset_put_dup(fds, fd);
+        if (copy < 0)
+                return copy;
 
-        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);
-        }
+        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;
 
-        u->cgroup_path = p;
-        p = NULL;
+        assert(u);
+        assert(f);
+        assert(key);
+        assert(format);
 
-        return 0;
+        fputs(key, f);
+        fputc('=', f);
+
+        va_start(ap, format);
+        vfprintf(f, format, ap);
+        va_end(ap);
+
+        fputc('\n', f);
 }
 
 int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
@@ -2773,6 +2800,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;
@@ -2783,6 +2812,17 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                         else
                                 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;
                 }
 
@@ -3016,13 +3056,13 @@ static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) {
 
         /* Exclude the main/control pids from being killed via the cgroup */
         if (main_pid > 0) {
-                r = set_put(pid_set, LONG_TO_PTR(main_pid));
+                r = set_put(pid_set, PID_TO_PTR(main_pid));
                 if (r < 0)
                         goto fail;
         }
 
         if (control_pid > 0) {
-                r = set_put(pid_set, LONG_TO_PTR(control_pid));
+                r = set_put(pid_set, PID_TO_PTR(control_pid));
                 if (r < 0)
                         goto fail;
         }
@@ -3043,32 +3083,39 @@ int unit_kill_common(
                 sd_bus_error *error) {
 
         int r = 0;
+        bool killed = false;
 
-        if (who == KILL_MAIN && main_pid <= 0) {
+        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
+                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 (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
+                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;
 
@@ -3077,11 +3124,16 @@ 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;
+                else
+                        killed = true;
         }
 
+        if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL, KILL_ALL_FAIL))
+                return -ESRCH;
+
         return r;
 }
 
@@ -3253,6 +3305,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;
 
@@ -3260,9 +3314,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;
         }
 
@@ -3276,20 +3330,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;
@@ -3388,63 +3428,18 @@ 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) {
-        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(
@@ -3455,7 +3450,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);
@@ -3484,13 +3480,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);
                 }
         }
 
@@ -3501,16 +3497,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 */
@@ -3518,21 +3515,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() == 0 && !unit_cgroup_delegate(u)))
+                                wait_for_exit = true;
 
                         if (c->send_sighup && k != KILL_KILL) {
                                 set_free(pid_set);
@@ -3707,14 +3713,3 @@ 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"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);