content: module Mueval.Resources (limitResources) where
  
  import Control.Monad (when)
  import System.Posix.Process (nice)
  import System.Posix.Resource -- (Resource(..), ResourceLimits, setResourceLimit)
  
  {- | Pull together several methods of reducing priority and easy access to resources:
    'nice', and the rlimit bindings,
    If called with False, 'limitResources' will not use POSIX rlimits.
  -}
  limitResources :: Bool -> IO ()
  limitResources rlimit = do
      nice 20 -- Set our process priority way down
      when rlimit $ mapM_ (uncurry setResourceLimit) limits
  
  {- | Set all the available rlimits.
     These values have been determined through trial-and-error
  -}
  stackSizeLimitSoft
      , stackSizeLimitHard
      , totalMemoryLimitSoft
      , totalMemoryLimitHard
      , dataSizeLimitSoft
      , openFilesLimitSoft
      , openFilesLimitHard
      , fileSizeLimitSoft
      , fileSizeLimitHard
      , dataSizeLimitHard
      , cpuTimeLimitSoft
      , cpuTimeLimitHard
      , coreSizeLimitSoft
      , coreSizeLimitHard
      , zero ::
          ResourceLimit
  totalMemoryLimitSoft = dataSizeLimitSoft
  totalMemoryLimitHard = dataSizeLimitHard
  -- These limits seem to be useless?
  stackSizeLimitSoft = zero
  stackSizeLimitHard = zero
  -- We allow a few files to be opened, such as package.conf, because they are necessary. This
  -- doesn't seem to be security problem because it'll be opened at the module
  -- stage, before code ever evaluates. I hope.
  openFilesLimitSoft = openFilesLimitHard
  openFilesLimitHard = ResourceLimit 7
  -- TODO: It would be nice to set these to zero, but right now Hint gets around the
  -- insecurity of the GHC API by writing stuff out to a file in /tmp, so we need
  -- to allow our compiled binary to do file I/O... :( But at least we can still limit
  -- how much we write out!
  fileSizeLimitSoft = fileSizeLimitHard
  fileSizeLimitHard = ResourceLimit 10800
  dataSizeLimitSoft = dataSizeLimitHard
  dataSizeLimitHard = ResourceLimit $ 6 ^ (12 :: Int)
  -- These should not be identical, to give the XCPU handler time to trigger
  cpuTimeLimitSoft = ResourceLimit 4
  cpuTimeLimitHard = ResourceLimit 5
  coreSizeLimitSoft = coreSizeLimitHard
  coreSizeLimitHard = zero
  -- convenience
  zero = ResourceLimit 0
  
  limits :: [(Resource, ResourceLimits)]
  limits =
      [ (ResourceStackSize, ResourceLimits stackSizeLimitSoft stackSizeLimitHard)
      , (ResourceTotalMemory, ResourceLimits totalMemoryLimitSoft totalMemoryLimitHard)
      , (ResourceOpenFiles, ResourceLimits openFilesLimitSoft openFilesLimitHard)
      , (ResourceFileSize, ResourceLimits fileSizeLimitSoft fileSizeLimitHard)
      , (ResourceDataSize, ResourceLimits dataSizeLimitSoft dataSizeLimitHard)
      , (ResourceCoreFileSize, ResourceLimits coreSizeLimitSoft coreSizeLimitHard)
      , (ResourceCPUTime, ResourceLimits cpuTimeLimitSoft cpuTimeLimitHard)
      ]
  