+
+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_with_mask(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 (bit & mask)
+ cg_create(n, path);
+ else
+ cg_trim(n, path, true);
+
+ bit <<= 1;
+ }
+
+ return r;
+}
+
+int cg_attach_with_mask(CGroupControllerMask mask, const char *path, pid_t pid) {
+ CGroupControllerMask bit = 1;
+ const char *n;
+ int r;
+
+ r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
+
+ NULSTR_FOREACH(n, mask_names) {
+ if (bit & mask)
+ cg_attach(n, path, pid);
+ else {
+ char prefix[strlen(path) + 1], *slash;
+
+ /* OK, this one is a bit harder... Now we need
+ * to add to the closest parent cgroup we
+ * can find */
+ strcpy(prefix, path);
+ while ((slash = strrchr(prefix, '/'))) {
+ int q;
+ *slash = 0;
+
+ q = cg_attach(n, prefix, pid);
+ if (q >= 0)
+ break;
+ }
+ }
+
+ bit <<= 1;
+ }
+
+ return r;
+}
+
+int cg_attach_many_with_mask(CGroupControllerMask mask, 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 k;
+
+ k = cg_attach_with_mask(mask, path, pid);
+ if (k < 0)
+ r = k;
+ }
+
+ return r;
+}
+
+int cg_migrate_with_mask(CGroupControllerMask mask, const char *from, const char *to) {
+ CGroupControllerMask bit = 1;
+ const char *n;
+ int r;
+
+ if (path_equal(from, to))
+ return 0;
+
+ r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
+
+ NULSTR_FOREACH(n, mask_names) {
+ if (bit & mask)
+ cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, to, n, to, false, false);
+ else {
+ char prefix[strlen(to) + 1], *slash;
+
+ strcpy(prefix, to);
+ while ((slash = strrchr(prefix, '/'))) {
+ int q;
+
+ *slash = 0;
+
+ q = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, to, n, prefix, false, false);
+ if (q >= 0)
+ break;
+ }
+ }
+
+ bit <<= 1;
+ }
+
+ return r;
+}
+
+int cg_trim_with_mask(CGroupControllerMask mask, 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 (bit & mask)
+ cg_trim(n, path, delete_root);
+
+ bit <<= 1;
+ }
+
+ return r;
+}
+
+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;
+}