/* Error codes: * * invalid input parameters → -EINVAL * invalid fd → -EBADF * process does not exist → -ESRCH * cgroup does not exist → -ENOENT * machine, session does not exist → -ENXIO * requested metadata on object is missing → -ENODATA */ The format of a systemd cgroup path is: /prefix.../slice/slice/slice/unit/extra... Where - there may be 0 or more slices - `prefix...` may be an arbitrary number/arrangement of path segments - `extra...` may be an arbitrary number/arrangement of path segments If there is more than one slice in a path, then the rightmost slice is the one that we mean when we say "the slice". We will refer to everything under `prefix...` as a "tree" (my term). Because `prefix...` and `extra...` may be arbitrary, we can have multiple of trees nested inside eachother. Because `prefix...` may be arbitrary, we need to know how to skip over it; how to get to "our" tree. For the system cgroup tree, we do this by looking at the cgroup of PID 1 and then trimming a designated suffix from it to get back to the root of the tree. For user cgroup trees, `prefix...` is a *unit* under the system cgroup tree, where the unit matches either `user@UID.service` or `session-SESSION.scope`. A container may nest its cgroup tree inside of a unit also. Because the container will have its own PID namespace, it will have its own PID 1, and be able to inspect the cgroup of PID 1, just as the host system does (though it is probably wiser to use cgroup namespaces to make it appear that the prefix is the root). | Thing | | | |-----------+------------------------------------------------+-------| | Cgroup | - | | |-----------+------------------------------------------------+-------| | Unit | | | | Slice | (/$X.slice){0,} or "-.slice" | | |-----------+------------------------------------------------+-------| | UserUnit | SkipUserPrefix.GetUnit | | | UserSlice | SkipUserPrefix.GetSlice | | |-----------+------------------------------------------------+-------| | Session | scanf("session-${SessionName}.scope") | Unit | | Owner | scanf("user-${UserID}.slice") | Slice | | Machine | readlink("/run/systemd/machines/unit:${Unit}") | Unit | * PID ** get_... *** session *** owner_uid *** unit *** user_unit *** slice *** user_slice *** machine_name *** cgroup * peer ** get_... *** session *** owner_uid *** unit *** user_unit *** slice *** user_slice *** machine_name *** cgroup * UID ** is_on_seat ** get_... *** state *** display *** sessions *** seats * session ** is_active ** is_remote ** get_... *** state *** uid *** seat *** service *** type *** class *** desktop *** display *** remote_host *** remote_user *** tty *** vt * seat ** can_multi_session ** can_tty ** can_graphical ** get_... *** active *** sessions * machine ** get_... *** class *** ifindices * top level ** get_... *** seats *** sessions *** uids *** machine_names * login monitor ** new ** unref ** flush ** get_... *** fd *** events *** timeout