+ return cg_path_get_slice(cgroup, slice);
+}
+
+char *cg_escape(const char *p) {
+ bool need_prefix = false;
+
+ /* This implements very minimal escaping for names to be used
+ * as file names in the cgroup tree: any name which might
+ * conflict with a kernel name or is prefixed with '_' is
+ * prefixed with a '_'. That way, when reading cgroup names it
+ * is sufficient to remove a single prefixing underscore if
+ * there is one. */
+
+ /* The return value of this function (unlike cg_unescape())
+ * needs free()! */
+
+ if (p[0] == 0 ||
+ p[0] == '_' ||
+ p[0] == '.' ||
+ streq(p, "notify_on_release") ||
+ streq(p, "release_agent") ||
+ streq(p, "tasks"))
+ need_prefix = true;
+ else {
+ const char *dot;
+
+ dot = strrchr(p, '.');
+ if (dot) {
+
+ if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
+ need_prefix = true;
+ else {
+ char *n;
+
+ n = strndupa(p, dot - p);
+
+ if (check_hierarchy(n) >= 0)
+ need_prefix = true;
+ }
+ }
+ }
+
+ if (need_prefix)
+ return strappend("_", p);
+ else
+ return strdup(p);
+}
+
+char *cg_unescape(const char *p) {
+ assert(p);
+
+ /* The return value of this function (unlike cg_escape())
+ * doesn't need free()! */
+
+ if (p[0] == '_')
+ return (char*) p+1;
+
+ return (char*) p;
+}
+
+#define CONTROLLER_VALID \
+ DIGITS LETTERS \
+ "_"
+
+bool cg_controller_is_valid(const char *p, bool allow_named) {
+ const char *t, *s;
+
+ if (!p)
+ return false;
+
+ if (allow_named) {
+ s = startswith(p, "name=");
+ if (s)
+ p = s;
+ }
+
+ if (*p == 0 || *p == '_')
+ return false;
+
+ for (t = p; *t; t++)
+ if (!strchr(CONTROLLER_VALID, *t))
+ return false;
+
+ if (t - p > FILENAME_MAX)
+ return false;
+
+ return true;
+}
+
+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, TEMPLATE_INVALID))
+ return -EINVAL;