+
+int cg_get_machine_path(char **path) {
+ _cleanup_free_ char *root = NULL;
+ char *p;
+
+ assert(path);
+
+ if (cg_get_root_path(&root) < 0 || streq(root, "/"))
+ p = strdup("/machine");
+ else
+ p = strappend(root, "/machine");
+
+ if (!p)
+ return -ENOMEM;
+
+ *path = p;
+ return 0;
+}
+
+char **cg_shorten_controllers(char **controllers) {
+ char **f, **t;
+
+ if (!controllers)
+ return controllers;
+
+ for (f = controllers, t = controllers; *f; f++) {
+ const char *p;
+ int r;
+
+ p = normalize_controller(*f);
+
+ if (streq(*f, "systemd")) {
+ free(*f);
+ continue;
+ }
+
+ r = check_hierarchy(p);
+ if (r < 0) {
+ log_debug("Controller %s is not available, removing from controllers list.", *f);
+ free(*f);
+ continue;
+ }
+
+ *(t++) = *f;
+ }
+
+ *t = NULL;
+ return strv_uniq(controllers);
+}
+
+int cg_pid_get_path_shifted(pid_t pid, char **root, char **cgroup) {
+ _cleanup_free_ char *cg_root = NULL;
+ char *cg_process, *p;
+ int r;
+
+ r = cg_get_root_path(&cg_root);
+ if (r < 0)
+ return r;
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process);
+ if (r < 0)
+ return r;
+
+ p = path_startswith(cg_process, cg_root);
+ if (p)
+ p--;
+ else
+ p = cg_process;
+
+ if (cgroup) {
+ char* c;
+
+ c = strdup(p);
+ if (!c) {
+ free(cg_process);
+ return -ENOMEM;
+ }
+
+ *cgroup = c;
+ }
+
+ if (root) {
+ cg_process[p-cg_process] = 0;
+ *root = cg_process;
+ } else
+ free(cg_process);
+
+ return 0;
+}
+
+/* non-static only for testing purposes */
+int cg_path_decode_unit(const char *cgroup, char **unit){
+ char *p, *e, *c, *s, *k;
+
+ assert(cgroup);
+ assert(unit);
+
+ e = strchrnul(cgroup, '/');
+ c = strndupa(cgroup, e - cgroup);
+
+ /* Could this be a valid unit name? */
+ if (!unit_name_is_valid(c, true))
+ return -EINVAL;
+
+ if (!unit_name_is_template(c))
+ s = strdup(c);
+ else {
+ if (*e != '/')
+ return -EINVAL;
+
+ e += strspn(e, "/");
+ p = strchrnul(e, '/');
+
+ /* Don't allow empty instance strings */
+ if (p == e)
+ return -EINVAL;
+
+ k = strndupa(e, p - e);
+
+ s = unit_name_replace_instance(c, k);
+ }
+
+ if (!s)
+ return -ENOMEM;
+
+ *unit = s;
+ return 0;
+}
+
+int cg_path_get_unit(const char *path, char **unit) {
+ const char *e;
+
+ assert(path);
+ assert(unit);
+
+ e = path_startswith(path, "/system/");
+ if (!e)
+ return -ENOENT;
+
+ return cg_path_decode_unit(e, unit);
+}
+
+int cg_pid_get_unit(pid_t pid, char **unit) {
+ char _cleanup_free_ *cgroup = NULL;
+ int r;
+
+ assert(unit);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_unit(cgroup, unit);
+}
+
+static const char *skip_label(const char *e) {
+ assert(e);
+
+ e = strchr(e, '/');
+ if (!e)
+ return NULL;
+
+ e += strspn(e, "/");
+ return e;
+}
+
+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 */
+
+ e = path_startswith(path, "/user/");
+ if (!e)
+ return -ENOENT;
+
+ /* Skip the user name */
+ e = skip_label(e);
+ if (!e)
+ return -ENOENT;
+
+ /* Skip the session ID */
+ e = skip_label(e);
+ if (!e)
+ return -ENOENT;
+
+ /* Skip the systemd cgroup */
+ e = skip_label(e);
+ if (!e)
+ return -ENOENT;
+
+ return cg_path_decode_unit(e, unit);
+}
+
+int cg_pid_get_user_unit(pid_t pid, char **unit) {
+ char _cleanup_free_ *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;
+ char *s;
+
+ assert(path);
+ assert(machine);
+
+ e = path_startswith(path, "/machine/");
+ if (!e)
+ return -ENOENT;
+
+ n = strchrnul(e, '/');
+ if (e == n)
+ return -ENOENT;
+
+ s = strndup(e, n - e);
+ if (!s)
+ return -ENOMEM;
+
+ *machine = s;
+ return 0;
+}
+
+int cg_pid_get_machine_name(pid_t pid, char **machine) {
+ char _cleanup_free_ *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;
+ char *s;
+
+ assert(path);
+ assert(session);
+
+ e = path_startswith(path, "/user/");
+ if (!e)
+ return -ENOENT;
+
+ /* Skip the user name */
+ e = skip_label(e);
+ if (!e)
+ return -ENOENT;
+
+ n = strchrnul(e, '/');
+ if (e == n)
+ return -ENOENT;
+
+ if (n - e == 6 && memcmp(e, "shared", 6) == 0)
+ return -ENOENT;
+
+ s = strndup(e, n - e);
+ if (!s)
+ return -ENOMEM;
+
+ *session = s;
+ return 0;
+}
+
+int cg_pid_get_session(pid_t pid, char **session) {
+ char _cleanup_free_ *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_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 (!filename_is_safe(c)) {
+ free(c);
+ return -EINVAL;
+ }
+
+ *controller = c;
+ return 1;
+}