+
+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_no_create(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, cg_migrate_callback_t path_callback, void *userdata) {
+ 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) {
+ const char *p = NULL;
+
+ if (path_callback)
+ p = path_callback(bit, userdata);
+
+ if (!p)
+ p = path;
+
+ cg_attach_fallback(n, path, pid);
+ }
+
+ bit <<= 1;
+ }
+
+ return 0;
+}
+
+int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
+ 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, path_callback, userdata);
+ if (q < 0)
+ r = q;
+ }
+
+ return r;
+}
+
+int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
+ 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) {
+ const char *p = NULL;
+
+ if (to_callback)
+ p = to_callback(bit, userdata);
+
+ if (!p)
+ p = to;
+
+ cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, 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;
+}
+
+int cg_kernel_controllers(Set *controllers) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char buf[LINE_MAX];
+ int r;
+
+ assert(controllers);
+
+ f = fopen("/proc/cgroups", "re");
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
+ return -errno;
+ }
+
+ /* Ignore the header line */
+ (void) fgets(buf, sizeof(buf), f);
+
+ for (;;) {
+ char *controller;
+ int enabled = 0;
+
+ errno = 0;
+ if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
+
+ if (feof(f))
+ break;
+
+ if (ferror(f) && errno)
+ return -errno;
+
+ return -EBADMSG;
+ }
+
+ if (!enabled) {
+ free(controller);
+ continue;
+ }
+
+ if (!filename_is_valid(controller)) {
+ free(controller);
+ return -EBADMSG;
+ }
+
+ r = set_consume(controllers, controller);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}