bein-0.3: Bein/Minion/Commands.hs
module Bein.Minion.Commands (
module Bein.Commands,
ifVerbose, verboseMsg, verboseMsgLn,
scratchDir, execution, minionSocket,
minionPid, writeMinionPid,
dieWith,
query, update,
cleanUp,
receiveOneRow,
ignoreSignal
) where
import Database.HDBC
( SqlValue, toSql, IConnection(commit, run), quickQuery' )
import Control.Monad.Trans ()
import System.IO ()
import Control.Monad.Reader
( MonadReader(ask), MonadIO(..), ReaderT )
import System.Posix.Types ( ProcessID )
import Control.Concurrent.STM ( atomically, readTVar, writeTVar )
import System.Exit ( ExitCode(ExitFailure) )
import System.IO ()
import System.Environment ()
import System.Console.GetOpt ()
import System.FilePath.Posix ( joinPath )
import System.Directory
( removeDirectoryRecursive, setCurrentDirectory )
import System.Posix.Signals
( Signal, Handler(Ignore), installHandler, sigKILL, signalProcess )
import System.Posix.Process ( exitImmediately )
import System.IO ( stdout, hPutStrLn, hFlush )
import Bein.Commands ( encodeResponse, forkR, catchR, database, maybeRowQuery )
import Bein.Minion.Types
( Configuration(minion_port, scratch_directory),
BeinM,
ExecutionID,
configField,
State(stMinionPid, stSettings),
Settings(setExecution, setScratchDir, setVerbose) )
import Bein.Minion.Arguments ()
ifVerbose :: ReaderT State IO () -> ReaderT State IO ()
ifVerbose act = ask >>= \st -> if setVerbose (stSettings st) then act else return ()
verboseMsg :: String -> ReaderT State IO ()
verboseMsg m = ifVerbose $ liftIO $ putStr m >> hFlush stdout
verboseMsgLn :: String -> BeinM State ()
verboseMsgLn m = ifVerbose $ liftIO $ putStrLn m >> hFlush stdout
scratchDir :: ReaderT State IO FilePath
scratchDir = do
st <- ask
baseDir <- configField scratch_directory
let scrDir = setScratchDir $ stSettings st
return $ joinPath [baseDir, scrDir]
execution :: ReaderT State IO ExecutionID
execution = ask >>= return.stSettings >>= return.setExecution
minionSocket :: ReaderT State IO FilePath
minionSocket = do
dir <- scratchDir
minionSocketName <- configField minion_port
return $ joinPath [dir,minionSocketName]
minionPid :: ReaderT State IO (Maybe ProcessID)
minionPid = ask >>= return.stMinionPid >>= liftIO.atomically.readTVar
writeMinionPid :: ProcessID -> ReaderT State IO ()
writeMinionPid pid = ask >>= return.stMinionPid >>= \v ->
liftIO $ atomically $ writeTVar v (Just pid)
dieWith :: String -> BeinM State a
dieWith m = (liftIO $ hPutStrLn stdout m) >> cleanUp (ExitFailure 1)
query :: String -> [SqlValue] -> BeinM State [[SqlValue]]
query q vs = database >>= \conn -> liftIO $ quickQuery' conn q vs
update :: String -> [SqlValue] -> BeinM State ()
update q vs = database >>= (\conn -> liftIO $ run conn q vs >> commit conn)
cleanUp :: ExitCode -> BeinM State a
cleanUp term = do
minionPid >>= \p -> liftIO $ (case p of
Nothing -> return ()
Just pid -> signalProcess sigKILL pid) `catch` (const $ return ())
configField scratch_directory >>= liftIO.setCurrentDirectory
scratchDir >>= \d -> liftIO $ (removeDirectoryRecursive d `catch` (const $ return ()))
execution >>= \ex -> update updateString [toSql ex]
liftIO $ exitImmediately term
return undefined
where updateString = "update executions set status = 'failed' " ++
"where id=? and status='running'"
receiveOneRow :: BeinM State [[a]] -> BeinM State [a]
receiveOneRow act = act >>= \r -> case r of
[] -> dieWith "Received no rows from database."
[a] -> return a
_ -> dieWith "Received multiple rows from database."
ignoreSignal :: Signal -> BeinM s Handler
ignoreSignal sig = liftIO $ installHandler sig Ignore Nothing