+
+Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
+ char *p;
+ Unit *u;
+
+ assert(m);
+ assert(cgroup);
+
+ u = hashmap_get(m->cgroup_unit, cgroup);
+ if (u)
+ return u;
+
+ p = strdupa(cgroup);
+ for (;;) {
+ char *e;
+
+ e = strrchr(p, '/');
+ if (e == p || !e)
+ return NULL;
+
+ *e = 0;
+
+ u = hashmap_get(m->cgroup_unit, p);
+ if (u)
+ return u;
+ }
+}
+
+Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(m);
+
+ if (pid <= 1)
+ return NULL;
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
+ if (r < 0)
+ return NULL;
+
+ return manager_get_unit_by_cgroup(m, cgroup);
+}
+
+int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
+ Unit *u;
+ int r;
+
+ assert(m);
+ assert(cgroup);
+
+ u = manager_get_unit_by_cgroup(m, cgroup);
+ if (!u)
+ return 0;
+
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
+ if (r <= 0)
+ return r;
+
+ if (UNIT_VTABLE(u)->notify_cgroup_empty)
+ UNIT_VTABLE(u)->notify_cgroup_empty(u);
+
+ unit_add_to_gc_queue(u);
+ return 0;
+}
+
+int unit_get_memory_current(Unit *u, uint64_t *ret) {
+ _cleanup_free_ char *v = NULL;
+ int r;
+
+ assert(u);
+ assert(ret);
+
+ if (!u->cgroup_path)
+ return -ENODATA;
+
+ if ((u->cgroup_realized_mask & CGROUP_MEMORY) == 0)
+ return -ENODATA;
+
+ r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+
+ return safe_atou64(v, ret);
+}
+
+static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
+ _cleanup_free_ char *v = NULL;
+ uint64_t ns;
+ int r;
+
+ assert(u);
+ assert(ret);
+
+ if (!u->cgroup_path)
+ return -ENODATA;
+
+ if ((u->cgroup_realized_mask & CGROUP_CPUACCT) == 0)
+ return -ENODATA;
+
+ r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(v, &ns);
+ if (r < 0)
+ return r;
+
+ *ret = ns;
+ return 0;
+}
+
+int unit_get_cpu_usage(Unit *u, nsec_t *ret) {
+ nsec_t ns;
+ int r;
+
+ r = unit_get_cpu_usage_raw(u, &ns);
+ if (r < 0)
+ return r;
+
+ if (ns > u->cpuacct_usage_base)
+ ns -= u->cpuacct_usage_base;
+ else
+ ns = 0;
+
+ *ret = ns;
+ return 0;
+}
+
+int unit_reset_cpu_usage(Unit *u) {
+ nsec_t ns;
+ int r;
+
+ assert(u);
+
+ r = unit_get_cpu_usage_raw(u, &ns);
+ if (r < 0) {
+ u->cpuacct_usage_base = 0;
+ return r;
+ }
+
+ u->cpuacct_usage_base = ns;
+ return 0;
+}
+
+static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
+ [CGROUP_AUTO] = "auto",
+ [CGROUP_CLOSED] = "closed",
+ [CGROUP_STRICT] = "strict",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);