@@ -10,15 +10,20 @@ import System.IO
1010 ( hFlush
1111 , stdout
1212 , stdin
13+ , stderr
1314 , hSetBinaryMode
15+ , hClose
1416 )
1517import Data.Binary (decode)
16−import qualified Data.ByteString.Lazy as BSL (ByteString, getContents)
17−import Control.Monad (unless, forM, forM_, join)
18−import Control.Exception (throwIO, SomeException)
18+import qualified Data.ByteString.Lazy as BSL (getContents, length)
19+import Control.Monad (unless, forM, forM_, join, void)
20+import Control.Exception (throwIO, SomeException, evaluate)
1921import Control.Applicative ((<$>), (<*>), (<|>))
2022import Control.Monad.IO.Class (liftIO)
2123
24+-- Posix
25+import System.Posix.Process (forkProcess, createSession)
26+
2227-- SSH
2328import qualified Network.SSH.Client.LibSSH2.Foreign as SSH
2429 ( initialize
@@ -64,7 +69,7 @@ import Control.Distributed.Process.Backend.Azure
6469 , cloudServices
6570 , CloudService(cloudServiceName, cloudServiceVMs)
6671 , VirtualMachine(vmName)
67− , Backend(copyToVM, checkMD5, callOnVM)
72+ , Backend(copyToVM, checkMD5, callOnVM, spawnOnVM)
6873 )
6974import qualified Control.Distributed.Process.Backend.Azure as Azure (remoteTable)
7075
@@ -78,10 +83,11 @@ data ProcessPair b = forall a. Serializable a => ProcessPair {
7883 , ppairDict :: Static (SerializableDict a)
7984 }
8085
81−genericMain :: (RemoteTable -> RemoteTable) -- ^ Standard CH remote table
82− -> (String -> IO (ProcessPair ())) -- ^ Closures to support in 'run'
86+genericMain :: (RemoteTable -> RemoteTable) -- ^ Standard CH remote table
87+ -> (String -> IO (ProcessPair ())) -- ^ Closures to support in 'run'
88+ -> (String -> IO (Closure (Process ()))) -- ^ Closures to support in @run --background@
8389 -> IO ()
84−genericMain remoteTable cmds = do
90+genericMain remoteTable callable spawnable = do
8591 _ <- SSH.initialize True
8692 cmd <- execParser opts
8793 case cmd of
@@ -110,20 +116,31 @@ genericMain remoteTable cmds = do
110116 if and matches
111117 then exitSuccess
112118 else exitFailure
113− RunOn {} -> do
114− procPair <- cmds (closureId cmd)
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
127+ putStrLn "OK"
128+ RunOn {} {- not (background cmd) -} -> do
129+ procPair <- callable (closureId cmd)
115130 params <- azureParameters (azureOptions cmd) (Just (sshOptions cmd))
116131 backend <- initializeBackend params
117132 css <- cloudServices backend
118133 forM_ (findTarget (target cmd) css) $ \vm -> do
119134 putStr (vmName vm ++ ": ") >> hFlush stdout
120135 case procPair of
121− ProcessPair rProc lProc dict ->
122− callOnVM backend dict vm (remotePort cmd) rProc >>= lProc
136+ ProcessPair rProc lProc dict -> do
137+ result <- callOnVM backend dict vm (remotePort cmd) rProc
138+ lProc result
123139 OnVmCommand (vmCmd@OnVmRun {}) ->
124140 onVmRun (remoteTable . Azure.remoteTable $ initRemoteTable)
125141 (onVmIP vmCmd)
126142 (onVmPort vmCmd)
143+ (onVmBackground vmCmd)
127144 SSH.exit
128145 where
129146 opts = info (helper <*> commandParser)
@@ -152,19 +169,35 @@ azureParameters opts (Just sshOpts) = do
152169 azureSshUserName = remoteUser sshOpts
153170 }
154171
155−onVmRun :: RemoteTable -> String -> String -> IO ()
156−onVmRun rtable host port = do
157− hSetBinaryMode stdin True
158− hSetBinaryMode stdout True
159− proc <- BSL.getContents :: IO BSL.ByteString
160− mTransport <- createTransport host port defaultTCPParameters
161− case mTransport of
162− Left err -> throwIO err
163− Right transport -> do
164− node <- newLocalNode transport rtable
165− runProcess node $
166− catch (join . unClosure . decode $ proc)
167− (\e -> liftIO (print (e :: SomeException) >> throwIO e))
172+onVmRun :: RemoteTable -> String -> String -> Bool -> IO ()
173+onVmRun rtable host port bg = do
174+ hSetBinaryMode stdin True
175+ hSetBinaryMode stdout True
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
180+ if bg
181+ then do
182+ hClose stdin
183+ hClose stdout
184+ hClose stderr
185+ void . forkProcess $ do
186+ void createSession
187+ startCH proc
188+ else
189+ startCH proc
190+ where
191+ startCH :: Closure (Process ()) -> IO ()
192+ startCH proc = do
193+ mTransport <- createTransport host port defaultTCPParameters
194+ case mTransport of
195+ Left err -> throwIO err
196+ Right transport -> do
197+ node <- newLocalNode transport rtable
198+ runProcess node $
199+ catch (join . unClosure $ proc)
200+ (\e -> liftIO (print (e :: SomeException) >> throwIO e))
168201
169202--------------------------------------------------------------------------------
170203-- Command line options --
@@ -217,8 +250,9 @@ data Command =
217250
218251data OnVmCommand =
219252 OnVmRun {
220− onVmIP :: String
221− , onVmPort :: String
253+ onVmIP :: String
254+ , onVmPort :: String
255+ , onVmBackground :: Bool
222256 }
223257 deriving Show
224258
@@ -317,6 +351,9 @@ onVmRunParser = OnVmRun
317351 & metavar "PORT"
318352 & help "port number"
319353 )
354+ <*> switch ( long "background"
355+ & help "Run the process in the background"
356+ )
320357
321358onVmCommandParser :: Parser Command
322359onVmCommandParser = OnVmCommand <$> subparser