2012-02-20 19:20:36 +00:00
|
|
|
{- Interface for running a shell command as a coprocess,
|
|
|
|
- sending it queries and getting back results.
|
|
|
|
-
|
2023-12-06 20:27:12 +00:00
|
|
|
- Copyright 2012-2023 Joey Hess <id@joeyh.name>
|
2012-02-20 19:20:36 +00:00
|
|
|
-
|
2014-05-10 14:01:27 +00:00
|
|
|
- License: BSD-2-clause
|
2012-02-20 19:20:36 +00:00
|
|
|
-}
|
|
|
|
|
2013-05-12 03:11:56 +00:00
|
|
|
{-# LANGUAGE CPP #-}
|
|
|
|
|
2012-02-20 19:20:36 +00:00
|
|
|
module Utility.CoProcess (
|
2020-05-04 16:44:26 +00:00
|
|
|
CoProcessHandle,
|
2020-04-20 17:53:27 +00:00
|
|
|
CoProcessState(..),
|
2012-02-20 19:20:36 +00:00
|
|
|
start,
|
|
|
|
stop,
|
2013-05-12 03:11:56 +00:00
|
|
|
query,
|
2023-12-06 20:27:12 +00:00
|
|
|
send,
|
|
|
|
receive,
|
2012-02-20 19:20:36 +00:00
|
|
|
) where
|
|
|
|
|
|
|
|
import Common
|
|
|
|
|
2013-05-31 16:20:17 +00:00
|
|
|
import Control.Concurrent.MVar
|
2023-12-06 19:38:01 +00:00
|
|
|
import Control.Monad.IO.Class (MonadIO)
|
2012-02-20 19:20:36 +00:00
|
|
|
|
2013-05-31 16:20:17 +00:00
|
|
|
type CoProcessHandle = MVar CoProcessState
|
|
|
|
|
|
|
|
data CoProcessState = CoProcessState
|
|
|
|
{ coProcessPid :: ProcessHandle
|
|
|
|
, coProcessTo :: Handle
|
|
|
|
, coProcessFrom :: Handle
|
|
|
|
, coProcessSpec :: CoProcessSpec
|
|
|
|
}
|
|
|
|
|
|
|
|
data CoProcessSpec = CoProcessSpec
|
2014-01-02 01:42:25 +00:00
|
|
|
{ coProcessNumRestarts :: Int
|
2013-05-31 16:20:17 +00:00
|
|
|
, coProcessCmd :: FilePath
|
|
|
|
, coProcessParams :: [String]
|
|
|
|
, coProcessEnv :: Maybe [(String, String)]
|
|
|
|
}
|
|
|
|
|
2014-01-02 01:42:25 +00:00
|
|
|
start :: Int -> FilePath -> [String] -> Maybe [(String, String)] -> IO CoProcessHandle
|
2014-06-10 23:20:14 +00:00
|
|
|
start numrestarts cmd params environ = do
|
|
|
|
s <- start' $ CoProcessSpec numrestarts cmd params environ
|
2013-05-31 16:20:17 +00:00
|
|
|
newMVar s
|
|
|
|
|
|
|
|
start' :: CoProcessSpec -> IO CoProcessState
|
|
|
|
start' s = do
|
2013-06-14 21:59:22 +00:00
|
|
|
(pid, from, to) <- startInteractiveProcess (coProcessCmd s) (coProcessParams s) (coProcessEnv s)
|
2016-11-01 18:03:55 +00:00
|
|
|
rawMode from
|
|
|
|
rawMode to
|
2013-05-31 16:20:17 +00:00
|
|
|
return $ CoProcessState pid to from s
|
2016-11-01 18:03:55 +00:00
|
|
|
where
|
|
|
|
#ifdef mingw32_HOST_OS
|
2016-12-24 18:46:31 +00:00
|
|
|
rawMode h = hSetNewlineMode h noNewlineTranslation
|
|
|
|
#else
|
|
|
|
rawMode _ = return ()
|
2016-11-01 18:03:55 +00:00
|
|
|
#endif
|
2012-02-20 19:20:36 +00:00
|
|
|
|
|
|
|
stop :: CoProcessHandle -> IO ()
|
2013-05-31 16:20:17 +00:00
|
|
|
stop ch = do
|
|
|
|
s <- readMVar ch
|
|
|
|
hClose $ coProcessTo s
|
|
|
|
hClose $ coProcessFrom s
|
|
|
|
let p = proc (coProcessCmd $ coProcessSpec s) (coProcessParams $ coProcessSpec s)
|
|
|
|
forceSuccessProcess p (coProcessPid s)
|
2012-02-20 19:20:36 +00:00
|
|
|
|
2020-07-06 19:11:28 +00:00
|
|
|
{- To handle a restartable process, any IO exception thrown by the send and
|
2013-05-31 16:20:17 +00:00
|
|
|
- receive actions are assumed to mean communication with the process
|
2020-07-06 19:11:28 +00:00
|
|
|
- failed, and the failed action is re-run with a new process. -}
|
2023-12-06 19:38:01 +00:00
|
|
|
query :: (MonadIO m, MonadCatch m) => CoProcessHandle -> (Handle -> m a) -> (Handle -> m b) -> m b
|
2023-12-06 20:27:12 +00:00
|
|
|
query ch sender receiver = do
|
2023-12-06 19:38:01 +00:00
|
|
|
s <- liftIO $ readMVar ch
|
2023-12-06 20:27:12 +00:00
|
|
|
restartable s (sender $ coProcessTo s) $ const $
|
2023-12-06 19:38:01 +00:00
|
|
|
restartable s (liftIO $ hFlush $ coProcessTo s) $ const $
|
2023-12-06 20:27:12 +00:00
|
|
|
restartable s (receiver $ coProcessFrom s)
|
2020-07-06 19:11:28 +00:00
|
|
|
return
|
2013-05-31 16:20:17 +00:00
|
|
|
where
|
2020-07-06 19:11:28 +00:00
|
|
|
restartable s a cont
|
2014-01-02 01:42:25 +00:00
|
|
|
| coProcessNumRestarts (coProcessSpec s) > 0 =
|
2020-07-06 19:11:28 +00:00
|
|
|
maybe restart cont =<< catchMaybeIO a
|
|
|
|
| otherwise = cont =<< a
|
|
|
|
restart = do
|
2023-12-06 19:38:01 +00:00
|
|
|
s <- liftIO $ takeMVar ch
|
|
|
|
void $ liftIO $ catchMaybeIO $ do
|
2013-05-31 16:20:17 +00:00
|
|
|
hClose $ coProcessTo s
|
|
|
|
hClose $ coProcessFrom s
|
2023-12-06 19:38:01 +00:00
|
|
|
void $ liftIO $ waitForProcess $ coProcessPid s
|
|
|
|
s' <- liftIO $ start' $ (coProcessSpec s)
|
2020-07-06 19:11:28 +00:00
|
|
|
{ coProcessNumRestarts = coProcessNumRestarts (coProcessSpec s) - 1 }
|
2023-12-06 19:38:01 +00:00
|
|
|
liftIO $ putMVar ch s'
|
2023-12-06 20:27:12 +00:00
|
|
|
query ch sender receiver
|
|
|
|
|
|
|
|
send :: MonadIO m => CoProcessHandle -> (Handle -> m a) -> m a
|
|
|
|
send ch a = do
|
|
|
|
s <- liftIO $ readMVar ch
|
|
|
|
a (coProcessTo s)
|
|
|
|
|
|
|
|
receive :: MonadIO m => CoProcessHandle -> (Handle -> m a) -> m a
|
|
|
|
receive ch a = do
|
|
|
|
s <- liftIO $ readMVar ch
|
|
|
|
liftIO $ hFlush $ coProcessTo s
|
|
|
|
a (coProcessFrom s)
|