+ p += n;
+ p += strspn(p, "/");
+
+ return p;
+}
+
+int cg_path_get_user_unit(const char *path, char **unit) {
+ const char *e;
+
+ assert(path);
+ assert(unit);
+
+ /* We always have to parse the path from the beginning as unit
+ * cgroups might have arbitrary child cgroups and we shouldn't get
+ * confused by those */
+
+ /* Skip slices, if there are any */
+ e = skip_slices(path);
+
+ /* Skip the session scope, require that there is one */
+ e = skip_session(e);
+ if (!e)
+ return -ENOENT;
+
+ /* And skip more slices */
+ e = skip_slices(e);
+
+ return cg_path_decode_unit(e, unit);
+}
+
+int cg_pid_get_user_unit(pid_t pid, char **unit) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(unit);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_user_unit(cgroup, unit);
+}
+
+int cg_path_get_machine_name(const char *path, char **machine) {
+ const char *e, *n, *x;
+ char *s, *r;
+ size_t l;
+
+ assert(path);
+ assert(machine);
+
+ /* Skip slices, if there are any */
+ e = skip_slices(path);
+
+ n = strchrnul(e, '/');
+ if (e == n)
+ return -ENOENT;
+
+ s = strndupa(e, n - e);
+ s = cg_unescape(s);
+
+ x = startswith(s, "machine-");
+ if (!x)
+ return -ENOENT;
+ if (!endswith(x, ".scope"))
+ return -ENOENT;
+
+ l = strlen(x);
+ if (l <= 6)
+ return -ENOENT;
+
+ r = strndup(x, l - 6);
+ if (!r)
+ return -ENOMEM;
+
+ *machine = r;
+ return 0;
+}
+
+int cg_pid_get_machine_name(pid_t pid, char **machine) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(machine);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_machine_name(cgroup, machine);
+}
+
+int cg_path_get_session(const char *path, char **session) {
+ const char *e, *n, *x;
+ char *s, *r;
+ size_t l;
+
+ assert(path);
+ assert(session);
+
+ /* Skip slices, if there are any */
+ e = skip_slices(path);
+
+ n = strchrnul(e, '/');
+ if (e == n)
+ return -ENOENT;
+
+ s = strndupa(e, n - e);
+ s = cg_unescape(s);
+
+ x = startswith(s, "session-");
+ if (!x)
+ return -ENOENT;
+ if (!endswith(x, ".scope"))
+ return -ENOENT;
+
+ l = strlen(x);
+ if (l <= 6)
+ return -ENOENT;
+
+ r = strndup(x, l - 6);
+ if (!r)
+ return -ENOMEM;
+
+ *session = r;
+ return 0;
+}
+
+int cg_pid_get_session(pid_t pid, char **session) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(session);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_session(cgroup, session);
+}
+
+int cg_path_get_owner_uid(const char *path, uid_t *uid) {
+ _cleanup_free_ char *slice = NULL;
+ const char *e;
+ char *s;
+ int r;
+
+ assert(path);
+ assert(uid);
+
+ r = cg_path_get_slice(path, &slice);
+ if (r < 0)
+ return r;
+
+ e = startswith(slice, "user-");
+ if (!e)
+ return -ENOENT;
+ if (!endswith(slice, ".slice"))
+ return -ENOENT;
+
+ s = strndupa(e, strlen(e) - 6);
+ if (!s)
+ return -ENOMEM;
+
+ return parse_uid(s, uid);
+}
+
+int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(uid);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_owner_uid(cgroup, uid);
+}
+
+int cg_path_get_slice(const char *p, char **slice) {
+ const char *e = NULL;
+ size_t m = 0;
+
+ assert(p);
+ assert(slice);
+
+ for (;;) {
+ size_t n;
+
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
+ char *s;
+
+ if (!e)
+ return -ENOENT;
+
+ s = strndup(e, m);
+ if (!s)
+ return -ENOMEM;
+
+ *slice = s;
+ return 0;
+ }
+
+ e = p;
+ m = n;
+
+ p += n;
+ }
+}
+
+int cg_pid_get_slice(pid_t pid, char **slice) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(slice);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_slice(cgroup, slice);
+}
+
+int cg_controller_from_attr(const char *attr, char **controller) {
+ const char *dot;
+ char *c;
+
+ assert(attr);
+ assert(controller);
+
+ if (!filename_is_safe(attr))
+ return -EINVAL;
+
+ dot = strchr(attr, '.');
+ if (!dot) {
+ *controller = NULL;
+ return 0;
+ }
+
+ c = strndup(attr, dot - attr);
+ if (!c)
+ return -ENOMEM;
+
+ if (!cg_controller_is_valid(c, false)) {
+ free(c);
+ return -EINVAL;
+ }
+
+ *controller = c;
+ return 1;
+}
+
+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;