packages feed

cgroup-rts-threads-0.2.0.0: src/System/CGroup/V2/CGroup.hs

{-# LANGUAGE TemplateHaskell #-}

-- | Common types and operations for cgroups (v2)
module System.CGroup.V2.CGroup (
  CGroup (..),
  resolveCGroup,
  resolveCGroup',
) where

import Data.Foldable (find)
import qualified Data.Text as Text
import Path
import System.CGroup.Types (Mount (..), RawCGroup (..), parseCGroups, parseFile, parseMountInfo)

-- | A cgroup (under cgroups v2)
data CGroup = CGroup
  { -- | The root of the cgroup hierarchy
    cgroupRoot :: Path Abs Dir
  , -- | A specific cgroup's relative path from the cgroup hierarchy root
    cgroupLeaf :: Path Rel Dir
  }
  deriving (Eq, Ord, Show)

-- | Resolve the cgroup (v2) used for 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 cgroup is unable to be found, or when the
-- current process is not running under cgroups v2
resolveCGroup :: IO CGroup
resolveCGroup = do
  cgroupPath <- parseAbsFile "/proc/self/cgroup"
  mountinfoPath <- parseAbsFile "/proc/self/mountinfo"
  resolveCGroup' cgroupPath mountinfoPath

-- | Resolve a cgroup (v2) from the given cgroup and mountinfo files
--
-- Throws an Exception when the cgroup is unable to be found, or when the
-- provided paths do not construct a valid cgroup
resolveCGroup' :: Path Abs File -> Path Abs File -> IO CGroup
resolveCGroup' cgroupPath mountinfoPath = do
  cgroups <- parseFile parseCGroups cgroupPath
  case cgroups of
    -- expect to find a cgroup with hierarchy ID 0 and an empty list of controllers
    [RawCGroup "0" [] cgroupLeafAbs] -> do
      mounts <- parseFile parseMountInfo mountinfoPath
      cgroupRootMount <- maybe (fail "Couldn't find cgroup hierarchy root mount") pure (findCGroupHierarchyRootMount mounts)
      mountPointAsPath <- parseAbsDir (Text.unpack (mountPoint cgroupRootMount))

      case fromAbsDir cgroupLeafAbs of
        "/" ->
          pure
            ( CGroup
                { cgroupRoot = mountPointAsPath
                , cgroupLeaf = $(mkRelDir ".")
                }
            )
        _ -> do
          -- Drop the leading '/' from the cgroup path
          cgroupLeafRel <- parseRelDir (drop 1 (fromAbsDir cgroupLeafAbs))
          pure
            ( CGroup
                { cgroupRoot = mountPointAsPath
                , cgroupLeaf = cgroupLeafRel
                }
            )
    _ -> fail ("Found incompatible cgroups: " <> show cgroups)

-- | Find the cgroups v2 hierarchy root.
--
-- We expect to find a mount with the filesystem type "cgroup2"
findCGroupHierarchyRootMount :: [Mount] -> Maybe Mount
findCGroupHierarchyRootMount = find ((== "cgroup2") . mountFilesystemType)