@@ -10,15 +10,20 @@ import System.IO
10
10
( hFlush
11
11
, stdout
12
12
, stdin
13
+ , stderr
13
14
, hSetBinaryMode
15
+ , hClose
14
16
)
15
17
import 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 )
19
21
import Control.Applicative ((<$>) , (<*>) , (<|>) )
20
22
import Control.Monad.IO.Class (liftIO )
21
23
24
+ -- Posix
25
+ import System.Posix.Process (forkProcess , createSession )
26
+
22
27
-- SSH
23
28
import qualified Network.SSH.Client.LibSSH2.Foreign as SSH
24
29
( initialize
@@ -64,7 +69,7 @@ import Control.Distributed.Process.Backend.Azure
64
69
, cloudServices
65
70
, CloudService (cloudServiceName , cloudServiceVMs )
66
71
, VirtualMachine (vmName )
67
- , Backend (copyToVM , checkMD5 , callOnVM )
72
+ , Backend (copyToVM , checkMD5 , callOnVM , spawnOnVM )
68
73
)
69
74
import qualified Control.Distributed.Process.Backend.Azure as Azure (remoteTable )
70
75
@@ -78,10 +83,11 @@ data ProcessPair b = forall a. Serializable a => ProcessPair {
78
83
, ppairDict :: Static (SerializableDict a )
79
84
}
80
85
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@
83
89
-> IO ()
84
- genericMain remoteTable cmds = do
90
+ genericMain remoteTable callable spawnable = do
85
91
_ <- SSH. initialize True
86
92
cmd <- execParser opts
87
93
case cmd of
@@ -110,20 +116,31 @@ genericMain remoteTable cmds = do
110
116
if and matches
111
117
then exitSuccess
112
118
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)
115
130
params <- azureParameters (azureOptions cmd) (Just (sshOptions cmd))
116
131
backend <- initializeBackend params
117
132
css <- cloudServices backend
118
133
forM_ (findTarget (target cmd) css) $ \ vm -> do
119
134
putStr (vmName vm ++ " : " ) >> hFlush stdout
120
135
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
123
139
OnVmCommand (vmCmd@ OnVmRun {}) ->
124
140
onVmRun (remoteTable . Azure. remoteTable $ initRemoteTable)
125
141
(onVmIP vmCmd)
126
142
(onVmPort vmCmd)
143
+ (onVmBackground vmCmd)
127
144
SSH. exit
128
145
where
129
146
opts = info (helper <*> commandParser)
@@ -152,19 +169,35 @@ azureParameters opts (Just sshOpts) = do
152
169
azureSshUserName = remoteUser sshOpts
153
170
}
154
171
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))
168
201
169
202
--------------------------------------------------------------------------------
170
203
-- Command line options --
@@ -217,8 +250,9 @@ data Command =
217
250
218
251
data OnVmCommand =
219
252
OnVmRun {
220
- onVmIP :: String
221
- , onVmPort :: String
253
+ onVmIP :: String
254
+ , onVmPort :: String
255
+ , onVmBackground :: Bool
222
256
}
223
257
deriving Show
224
258
@@ -317,6 +351,9 @@ onVmRunParser = OnVmRun
317
351
& metavar " PORT"
318
352
& help " port number"
319
353
)
354
+ <*> switch ( long " background"
355
+ & help " Run the process in the background"
356
+ )
320
357
321
358
onVmCommandParser :: Parser Command
322
359
onVmCommandParser = OnVmCommand <$> subparser
0 commit comments