keter/Keter/Process.hs
2012-05-17 10:59:17 +03:00

68 lines
2.4 KiB
Haskell

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TemplateHaskell #-}
module Keter.Process
( run
, terminate
, Process
) where
import Keter.Prelude
import Keter.Logger
import qualified System.Process as SP
import Data.Time (diffUTCTime)
data Status = NeedsRestart | NoRestart | Running SP.ProcessHandle
-- | Run the given command, restarting if the process dies.
run :: FilePath -- ^ executable
-> FilePath -- ^ working directory
-> [String] -- ^ command line parameter
-> [(String, String)] -- ^ environment
-> Logger
-> KIO Process
run exec dir args env logger = do
mstatus <- newMVar NeedsRestart
let loop mlast = do
next <- modifyMVar mstatus $ \status ->
case status of
NoRestart -> return (NoRestart, return ())
_ -> do
now <- getCurrentTime
case mlast of
Just last | diffUTCTime now last < 5 -> do
log $ ProcessWaiting exec
threadDelay $ 5 * 1000 * 1000
_ -> return ()
res <- liftIO $ SP.createProcess cp
case res of
Left e -> do
$logEx e
return (NeedsRestart, return ())
Right (hin, hout, herr, ph) -> do
attach logger $ Handles hin hout herr
log $ ProcessCreated exec
return (Running ph, liftIO (SP.waitForProcess ph) >> loop (Just now))
next
forkKIO $ loop Nothing
return $ Process mstatus
where
cp = (SP.proc (toString exec) $ map toString args)
{ SP.cwd = Just $ toString dir
, SP.env = Just $ map (toString *** toString) env
, SP.std_in = SP.CreatePipe
, SP.std_out = SP.CreatePipe
, SP.std_err = SP.CreatePipe
, SP.close_fds = True
}
-- | Abstract type containing information on a process which will be restarted.
newtype Process = Process (MVar Status)
-- | Terminate the process and prevent it from being restarted.
terminate :: Process -> KIO ()
terminate (Process mstatus) = do
status <- swapMVar mstatus NoRestart
case status of
Running ph -> void $ liftIO $ SP.terminateProcess ph
_ -> return ()