cgroup-rts-threads-0.2.0.0: src/System/CGroup/V1/Controller.hs
-- | Common types and operations for cgroup controllers.
module System.CGroup.V1.Controller (
-- * cgroup controllers
Controller (..),
resolveCGroupController,
resolveCGroupController',
) where
import Control.Monad (guard)
import Data.Foldable (find)
import Data.Maybe (listToMaybe, mapMaybe)
import Data.Text (Text)
import qualified Data.Text as Text
import Path
import System.CGroup.Types (Mount (..), RawCGroup (..), parseCGroups, parseFile, parseMountInfo)
-- | A cgroup (v1) controller path for a specific subsystem
newtype Controller a = Controller {unController :: Path Abs Dir}
deriving (Eq, Ord, Show)
-- | Resolve a cgroup (v1) controller by name, as viewed by the current process
--
-- see cgroups(7): \/proc\/self\/cgroup is a file that contains information about
-- control groups applied to this process
--
-- see proc(5): \/proc\/self\/mountinfo is a file that contains information about
-- mounts available to this process
--
-- Throws an Exception when the controller is not able to be found, or when
-- running outside of a cgroup
resolveCGroupController :: Text -> IO (Controller a)
resolveCGroupController controller = do
cgroupPath <- parseAbsFile "/proc/self/cgroup"
mountinfoPath <- parseAbsFile "/proc/self/mountinfo"
resolveCGroupController' cgroupPath mountinfoPath controller
-- | Resolve a cgroup controller by name, under the given cgroup and
-- mountinfo paths
--
-- Throws an Exception when the controller is not able to be found, or when
-- running outside of a cgroup
resolveCGroupController' :: Path Abs File -> Path Abs File -> Text -> IO (Controller a)
resolveCGroupController' cgroupPath mountinfoPath controllerName = do
cgroups <- parseFile parseCGroups cgroupPath
mounts <- parseFile parseMountInfo mountinfoPath
cgroup <- maybe (fail "Couldn't find cgroup for controller") pure (findMatchingCGroup controllerName cgroups)
resolved <- maybe (fail "Couldn't find mount for cgroup") pure (resolveControllerMountPath controllerName cgroup mounts)
pure (Controller resolved)
-- | Find a cgroup for a specific controller (cgroups v1)
findMatchingCGroup :: Text -> [RawCGroup] -> Maybe RawCGroup
findMatchingCGroup controllerName = find containsController
where
containsController :: RawCGroup -> Bool
containsController = (controllerName `elem`) . rawCGroupControllers
-- | Find a Mount matching a controller name and cgroup, returning the absolute
-- resolved path of a controller
resolveControllerMountPath :: Text -> RawCGroup -> [Mount] -> Maybe (Path Abs Dir)
resolveControllerMountPath controllerName cgroup = firstMaybe (tryResolveMount controllerName cgroup)
firstMaybe :: (a -> Maybe b) -> [a] -> Maybe b
firstMaybe f = listToMaybe . mapMaybe f
-- | Attempt to match a cgroup controller to a mount, returning the absolute
-- resolved path of the controller
--
-- Returns Nothing if the mount does not match the cgroup controller
--
-- A matching mount must have a filesystem type of "cgroup" and contain the
-- controller name within its "super options".
--
-- Per cgroups(7), the cgroup path is relative to a mount root in the process's
-- mount hierarchy. Notably, a mount root /is not the same as its mount point/.
-- A mount point is the path at which the mount is visible to the process.
--
-- As such, we need to look for a mount whose mount root either..
--
-- - ..exactly matches our cgroup's path, in which case we directly return the
-- mount's mount path; OR
--
-- - ..is a prefix of our cgroup's path, in which case we return the relative
-- path from the mount root appended to the mount's mount path
tryResolveMount :: Text -> RawCGroup -> Mount -> Maybe (Path Abs Dir)
tryResolveMount controllerName cgroup mount = do
guard ("cgroup" == mountFilesystemType mount)
guard (controllerName `elem` mountSuperOptions mount)
mountRootAsPath <- parseAbsDir (Text.unpack (mountRoot mount))
mountPointAsPath <- parseAbsDir (Text.unpack (mountPoint mount))
if rawCGroupPath cgroup == mountRootAsPath
then Just mountPointAsPath
else do
rel <- stripProperPrefix mountRootAsPath (rawCGroupPath cgroup)
Just (mountPointAsPath </> rel)