@@ -10,15 +10,20 @@ import System.IO
13 , hSetBinaryMode
14 , hSetBinaryMode
15import Data.Binary (decode)
17import Data.Binary (decode)
16−import qualified Data.ByteString.Lazy as BSL (ByteString, getContents)
18+import qualified Data.ByteString.Lazy as BSL (getContents, length)
17−import Control.Monad (unless, forM, forM_, join)
19+import Control.Monad (unless, forM, forM_, join, void)
18−import Control.Exception (throwIO, SomeException)
20+import Control.Exception (throwIO, SomeException, evaluate)
19import Control.Applicative ((<$>), (<*>), (<|>))
21import Control.Applicative ((<$>), (<*>), (<|>))
20import Control.Monad.IO.Class (liftIO)
22import Control.Monad.IO.Class (liftIO)
25+import System.Posix.Process (forkProcess, createSession)
23import qualified Network.SSH.Client.LibSSH2.Foreign as SSH
28import qualified Network.SSH.Client.LibSSH2.Foreign as SSH
24 ( initialize
29 ( initialize
@@ -64,7 +69,7 @@ import Control.Distributed.Process.Backend.Azure
64 , cloudServices
69 , cloudServices
65 , CloudService(cloudServiceName, cloudServiceVMs)
70 , CloudService(cloudServiceName, cloudServiceVMs)
66 , VirtualMachine(vmName)
71 , VirtualMachine(vmName)
67− , Backend(copyToVM, checkMD5, callOnVM)
72+ , Backend(copyToVM, checkMD5, callOnVM, spawnOnVM)
69import qualified Control.Distributed.Process.Backend.Azure as Azure (remoteTable)
74import qualified Control.Distributed.Process.Backend.Azure as Azure (remoteTable)
@@ -78,10 +83,11 @@ data ProcessPair b = forall a. Serializable a => ProcessPair {
78 , ppairDict :: Static (SerializableDict a)
83 , ppairDict :: Static (SerializableDict a)
81−genericMain :: (RemoteTable -> RemoteTable) -- ^ Standard CH remote table
86+genericMain :: (RemoteTable -> RemoteTable) -- ^ Standard CH remote table
82− -> (String -> IO (ProcessPair ())) -- ^ Closures to support in 'run'
87+ -> (String -> IO (ProcessPair ())) -- ^ Closures to support in 'run'
88+ -> (String -> IO (Closure (Process ()))) -- ^ Closures to support in @run --background@
84−genericMain remoteTable cmds = do
90+genericMain remoteTable callable spawnable = do
85 _ <- SSH.initialize True
91 _ <- SSH.initialize True
86 cmd <- execParser opts
92 cmd <- execParser opts
87 case cmd of
93 case cmd of
@@ -110,20 +116,31 @@ genericMain remoteTable cmds = do
110 if and matches
116 if and matches
111 then exitSuccess
117 then exitSuccess
112 else exitFailure
118 else exitFailure
113− RunOn {} -> do
119+ RunOn {} | background cmd -> do
120+ rProc <- spawnable (closureId cmd)
121+ params <- azureParameters (azureOptions cmd) (Just (sshOptions cmd))
122+ backend <- initializeBackend params
123+ css <- cloudServices backend
124+ forM_ (findTarget (target cmd) css) $ \vm -> do
125+ putStr (vmName vm ++ ": ") >> hFlush stdout
126+ spawnOnVM backend vm (remotePort cmd) rProc
128+ RunOn {} {- not (background cmd) -} -> do
114− procPair <- cmds (closureId cmd)
129+ procPair <- callable (closureId cmd)
115 params <- azureParameters (azureOptions cmd) (Just (sshOptions cmd))
130 params <- azureParameters (azureOptions cmd) (Just (sshOptions cmd))
116 backend <- initializeBackend params
131 backend <- initializeBackend params
117 css <- cloudServices backend
132 css <- cloudServices backend
118 forM_ (findTarget (target cmd) css) $ \vm -> do
133 forM_ (findTarget (target cmd) css) $ \vm -> do
119 putStr (vmName vm ++ ": ") >> hFlush stdout
134 putStr (vmName vm ++ ": ") >> hFlush stdout
120 case procPair of
135 case procPair of
121− ProcessPair rProc lProc dict ->
136+ ProcessPair rProc lProc dict -> do
122− callOnVM backend dict vm (remotePort cmd) rProc >>= lProc
137+ result <- callOnVM backend dict vm (remotePort cmd) rProc
123 OnVmCommand (vmCmd@OnVmRun {}) ->
139 OnVmCommand (vmCmd@OnVmRun {}) ->
124 onVmRun (remoteTable . Azure.remoteTable $ initRemoteTable)
140 onVmRun (remoteTable . Azure.remoteTable $ initRemoteTable)
125 (onVmIP vmCmd)
141 (onVmIP vmCmd)
126 (onVmPort vmCmd)
142 (onVmPort vmCmd)
143+ (onVmBackground vmCmd)
129 opts = info (helper <*> commandParser)
146 opts = info (helper <*> commandParser)
@@ -152,19 +169,35 @@ azureParameters opts (Just sshOpts) = do
152 azureSshUserName = remoteUser sshOpts
169 azureSshUserName = remoteUser sshOpts
155−onVmRun :: RemoteTable -> String -> String -> IO ()
172+onVmRun :: RemoteTable -> String -> String -> Bool -> IO ()
156−onVmRun rtable host port = do
173+onVmRun rtable host port bg = do
157− hSetBinaryMode stdin True
174+ hSetBinaryMode stdin True
158− hSetBinaryMode stdout True
175+ hSetBinaryMode stdout True
159− proc <- BSL.getContents :: IO BSL.ByteString
176+ procEnc <- BSL.getContents
177+ -- Force evaluation (so that we can safely close stdin)
178+ _length <- evaluate (BSL.length procEnc)
179+ let proc = decode procEnc
185+ void . forkProcess $ do
191+ startCH :: Closure (Process ()) -> IO ()
160− mTransport <- createTransport host port defaultTCPParameters
193+ mTransport <- createTransport host port defaultTCPParameters
161− case mTransport of
194+ case mTransport of
162− Left err -> throwIO err
195+ Left err -> throwIO err
163− Right transport -> do
196+ Right transport -> do
164− node <- newLocalNode transport rtable
197+ node <- newLocalNode transport rtable
165− runProcess node $
198+ runProcess node $
166− catch (join . unClosure . decode $ proc)
199+ catch (join . unClosure $ proc)
167− (\e -> liftIO (print (e :: SomeException) >> throwIO e))
200+ (\e -> liftIO (print (e :: SomeException) >> throwIO e))
169--------------------------------------------------------------------------------
202--------------------------------------------------------------------------------
170-- Command line options --
203-- Command line options --
@@ -217,8 +250,9 @@ data Command =
218data OnVmCommand =
251data OnVmCommand =
219 OnVmRun {
252 OnVmRun {
220− onVmIP :: String
253+ onVmIP :: String
221− , onVmPort :: String
254+ , onVmPort :: String
255+ , onVmBackground :: Bool
223 deriving Show
257 deriving Show
@@ -317,6 +351,9 @@ onVmRunParser = OnVmRun
317 & metavar "PORT"
351 & metavar "PORT"
318 & help "port number"
352 & help "port number"
354+ <*> switch ( long "background"
355+ & help "Run the process in the background"
321onVmCommandParser :: Parser Command
358onVmCommandParser :: Parser Command
322onVmCommandParser = OnVmCommand <$> subparser
359onVmCommandParser = OnVmCommand <$> subparser