{-# LANGUAGE GeneralizedNewtypeDeriving #-} module Angel.Data ( GroupConfig(..) , SpecKey , RunKey , ProgramId , FileRequest , Program(..) , RunState(..) , Spec , KillDirective(..) , Verbosity(..) , Options(..) , AngelM(..) , defaultProgram , defaultDelay , defaultStdout , defaultStderr , runAngelM ) where import qualified Data.Map as M import System.Process ( ProcessHandle ) import Control.Applicative import Control.Monad.IO.Class import Control.Monad.Reader import Control.Concurrent.STM.TChan (TChan) import System.IO (Handle) -- |the whole shared state of the program; spec is what _should_ -- |be running, while running is what actually _is_ running_ currently data GroupConfig = GroupConfig { spec :: SpecKey, running :: RunKey, fileRequest :: TChan FileRequest } -- |map program ids to relevant structure type SpecKey = M.Map ProgramId Program type RunKey = M.Map ProgramId RunState data RunState = RunState { rsProgram :: Program, rsHandle :: Maybe ProcessHandle, rsLogHandle :: Maybe ProcessHandle } type ProgramId = String type FileRequest = (String, TChan (Maybe Handle)) -- |the representation of a program is these 6 values, -- |read from the config file data Program = Program { name :: String, exec :: Maybe String, user :: Maybe String, delay :: Maybe Int, stdout :: Maybe String, stderr :: Maybe String, workingDir :: Maybe FilePath, logExec :: Maybe String, pidFile :: Maybe FilePath, env :: [(String, String)], termGrace :: Maybe Int -- ^ How long to wait after sending a SIGTERM before SIGKILL. Nothing = never SIGKILL. Default Nothing } deriving (Show, Eq, Ord) -- |represents all the data needed to handle terminating a process data KillDirective = SoftKill String ProcessHandle (Maybe ProcessHandle) | HardKill String ProcessHandle (Maybe ProcessHandle) Int -- instance Show KillDirective where -- show (SoftKill _) = "SoftKill" -- show (HardKill _ grace) = "HardKill after " ++ show grace ++ "s" -- |Lower-level atoms in the configuration process type Spec = [Program] data Verbosity = V0 -- ^ Failures only | V1 -- ^ Failures + program starts/stops | V2 -- ^ Max verbosity. Default. Logs all of the above as well as state changes and other debugging info. deriving (Show, Eq, Ord) data Options = Options { configFile :: FilePath , verbosity :: Verbosity } newtype AngelM a = AngelM { unAngelM :: ReaderT Options IO a } deriving (Functor, Applicative, Monad, MonadReader Options, MonadIO) runAngelM :: Options -> AngelM a -> IO a runAngelM o (AngelM f) = runReaderT f o -- |a template for an empty program; the variable set to "" -- |are required, and must be overridden in the config file defaultProgram :: Program defaultProgram = Program "" Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing [] Nothing defaultDelay :: Int defaultDelay = 5 defaultStdout :: FilePath defaultStdout = "/dev/null" defaultStderr :: FilePath defaultStderr = "/dev/null"