+
+int cg_slice_to_path(const char *unit, char **ret) {
+ _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
+ const char *dash;
+
+ assert(unit);
+ assert(ret);
+
+ if (!unit_name_is_valid(unit, false))
+ return -EINVAL;
+
+ if (!endswith(unit, ".slice"))
+ return -EINVAL;
+
+ p = unit_name_to_prefix(unit);
+ if (!p)
+ return -ENOMEM;
+
+ dash = strchr(p, '-');
+ while (dash) {
+ _cleanup_free_ char *escaped = NULL;
+ char n[dash - p + sizeof(".slice")];
+
+ strcpy(stpncpy(n, p, dash - p), ".slice");
+
+ if (!unit_name_is_valid(n, false))
+ return -EINVAL;
+
+ escaped = cg_escape(n);
+ if (!escaped)
+ return -ENOMEM;
+
+ if (!strextend(&s, escaped, "/", NULL))
+ return -ENOMEM;
+
+ dash = strchr(dash+1, '-');
+ }
+
+ e = cg_escape(unit);
+ if (!e)
+ return -ENOMEM;
+
+ if (!strextend(&s, e, NULL))
+ return -ENOMEM;
+
+ *ret = s;
+ s = NULL;
+
+ return 0;
+}
+
+int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ r = cg_get_path(controller, path, attribute, &p);
+ if (r < 0)
+ return r;
+
+ return write_string_file(p, value);
+}
+
+static const char mask_names[] =
+ "cpu\0"
+ "cpuacct\0"
+ "blkio\0"
+ "memory\0"
+ "devices\0";
+
+int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
+ CGroupControllerMask bit = 1;
+ const char *n;
+ int r;
+
+ /* This one will create a cgroup in our private tree, but also
+ * duplicate it in the trees specified in mask, and remove it
+ * in all others */
+
+ /* First create the cgroup in our own hierarchy. */
+ r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
+ if (r < 0)
+ return r;
+
+ /* Then, do the same in the other hierarchies */
+ NULSTR_FOREACH(n, mask_names) {
+ if (mask & bit)
+ cg_create(n, path);
+ else if (supported & bit)
+ cg_trim(n, path, true);
+
+ bit <<= 1;
+ }
+
+ return 0;
+}
+
+int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid) {
+ CGroupControllerMask bit = 1;
+ const char *n;
+ int r;
+
+ r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
+ if (r < 0)
+ return r;
+
+ NULSTR_FOREACH(n, mask_names) {
+ if (supported & bit)
+ cg_attach_fallback(n, path, pid);
+
+ bit <<= 1;
+ }
+
+ return 0;
+}
+
+int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids) {
+ Iterator i;
+ void *pidp;
+ int r = 0;
+
+ SET_FOREACH(pidp, pids, i) {
+ pid_t pid = PTR_TO_LONG(pidp);
+ int q;
+
+ q = cg_attach_everywhere(supported, path, pid);
+ if (q < 0)
+ r = q;
+ }
+
+ return r;
+}
+
+int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to) {
+ CGroupControllerMask bit = 1;
+ const char *n;
+ int r;
+
+ if (!path_equal(from, to)) {
+ r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
+ if (r < 0)
+ return r;
+ }
+
+ NULSTR_FOREACH(n, mask_names) {
+ if (supported & bit)
+ cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, to, false, false);
+
+ bit <<= 1;
+ }
+
+ return 0;
+}
+
+int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
+ CGroupControllerMask bit = 1;
+ const char *n;
+ int r;
+
+ r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
+ if (r < 0)
+ return r;
+
+ NULSTR_FOREACH(n, mask_names) {
+ if (supported & bit)
+ cg_trim(n, path, delete_root);
+
+ bit <<= 1;
+ }
+
+ return 0;
+}
+
+CGroupControllerMask cg_mask_supported(void) {
+ CGroupControllerMask bit = 1, mask = 0;
+ const char *n;
+
+ NULSTR_FOREACH(n, mask_names) {
+ if (check_hierarchy(n) >= 0)
+ mask |= bit;
+
+ bit <<= 1;
+ }
+
+ return mask;
+}