2014-04-01 06:37:48 +00:00
|
|
|
-- | Simple server, using a named pipe. Client connects, sends a command,
|
|
|
|
-- and gets back all the output from the command, in a stream.
|
|
|
|
--
|
|
|
|
-- This is useful for eg, docker.
|
|
|
|
|
|
|
|
module Propellor.SimpleSh where
|
|
|
|
|
|
|
|
import Network.Socket
|
2014-05-11 13:30:45 +00:00
|
|
|
import Control.Concurrent
|
2014-04-01 06:37:48 +00:00
|
|
|
import Control.Concurrent.Async
|
|
|
|
import System.Process (std_in, std_out, std_err)
|
|
|
|
|
|
|
|
import Propellor
|
2014-04-01 17:51:58 +00:00
|
|
|
import Utility.FileMode
|
|
|
|
import Utility.ThreadScheduler
|
2014-04-01 06:37:48 +00:00
|
|
|
|
|
|
|
data Cmd = Cmd String [String]
|
|
|
|
deriving (Read, Show)
|
|
|
|
|
2014-04-04 22:21:54 +00:00
|
|
|
data Resp = StdoutLine String | StderrLine String | Done
|
2014-04-01 06:37:48 +00:00
|
|
|
deriving (Read, Show)
|
|
|
|
|
|
|
|
simpleSh :: FilePath -> IO ()
|
|
|
|
simpleSh namedpipe = do
|
|
|
|
nukeFile namedpipe
|
2014-04-01 17:51:58 +00:00
|
|
|
let dir = takeDirectory namedpipe
|
|
|
|
createDirectoryIfMissing True dir
|
|
|
|
modifyFileMode dir (removeModes otherGroupModes)
|
2014-04-01 06:37:48 +00:00
|
|
|
s <- socket AF_UNIX Stream defaultProtocol
|
2014-04-10 04:51:12 +00:00
|
|
|
bindSocket s (SockAddrUnix namedpipe)
|
2014-04-01 06:37:48 +00:00
|
|
|
listen s 2
|
|
|
|
forever $ do
|
|
|
|
(client, _addr) <- accept s
|
2014-05-11 13:30:45 +00:00
|
|
|
forkIO $ do
|
|
|
|
h <- socketToHandle client ReadWriteMode
|
|
|
|
maybe noop (run h) . readish =<< hGetLine h
|
2014-04-01 06:37:48 +00:00
|
|
|
where
|
|
|
|
run h (Cmd cmd params) = do
|
2014-05-11 13:22:59 +00:00
|
|
|
debug ["simplesh run", cmd, show params]
|
2014-04-01 06:37:48 +00:00
|
|
|
chan <- newChan
|
|
|
|
let runwriter = do
|
|
|
|
v <- readChan chan
|
2014-05-11 13:22:59 +00:00
|
|
|
debug ["simplesh run", cmd, show params, "writer got:", show v]
|
2014-04-01 06:37:48 +00:00
|
|
|
hPutStrLn h (show v)
|
2014-04-20 15:38:50 +00:00
|
|
|
hFlush h
|
2014-04-01 06:37:48 +00:00
|
|
|
case v of
|
2014-04-04 22:21:54 +00:00
|
|
|
Done -> noop
|
2014-04-01 06:37:48 +00:00
|
|
|
_ -> runwriter
|
|
|
|
writer <- async runwriter
|
|
|
|
|
2014-05-09 15:02:05 +00:00
|
|
|
flip catchIO (\_e -> writeChan chan Done) $ do
|
|
|
|
let p = (proc cmd params)
|
|
|
|
{ std_in = Inherit
|
|
|
|
, std_out = CreatePipe
|
|
|
|
, std_err = CreatePipe
|
|
|
|
}
|
|
|
|
(Nothing, Just outh, Just errh, pid) <- createProcess p
|
2014-05-11 13:22:59 +00:00
|
|
|
debug ["simplesh run", cmd, show params, "started"]
|
2014-05-09 15:02:05 +00:00
|
|
|
|
|
|
|
let mkreader t from = maybe noop (const $ mkreader t from)
|
|
|
|
=<< catchMaybeIO (writeChan chan . t =<< hGetLine from)
|
|
|
|
void $ concurrently
|
|
|
|
(mkreader StdoutLine outh)
|
|
|
|
(mkreader StderrLine errh)
|
2014-04-04 22:21:54 +00:00
|
|
|
|
2014-05-11 13:22:59 +00:00
|
|
|
debug ["simplesh run", cmd, show params, "waiting for process"]
|
2014-05-09 15:02:05 +00:00
|
|
|
void $ tryIO $ waitForProcess pid
|
2014-04-01 06:37:48 +00:00
|
|
|
|
2014-05-11 13:22:59 +00:00
|
|
|
debug ["simplesh run", cmd, show params, "sending Done"]
|
2014-05-09 15:02:05 +00:00
|
|
|
writeChan chan Done
|
2014-04-01 06:37:48 +00:00
|
|
|
|
2014-05-09 15:02:05 +00:00
|
|
|
hClose outh
|
|
|
|
hClose errh
|
2014-04-01 06:37:48 +00:00
|
|
|
|
2014-05-11 13:22:59 +00:00
|
|
|
debug ["simplesh run", cmd, show params, "wait writer"]
|
2014-05-09 15:02:05 +00:00
|
|
|
wait writer
|
2014-05-11 13:34:10 +00:00
|
|
|
debug ["simplesh run", cmd, show params, "wait writer complete"]
|
2014-04-01 06:37:48 +00:00
|
|
|
hClose h
|
2014-05-11 13:22:59 +00:00
|
|
|
debug ["simplesh run", cmd, show params, "fully done"]
|
2014-04-01 06:37:48 +00:00
|
|
|
|
|
|
|
simpleShClient :: FilePath -> String -> [String] -> ([Resp] -> IO a) -> IO a
|
|
|
|
simpleShClient namedpipe cmd params handler = do
|
2014-04-30 00:05:39 +00:00
|
|
|
debug ["simplesh connecting"]
|
2014-04-01 06:37:48 +00:00
|
|
|
s <- socket AF_UNIX Stream defaultProtocol
|
|
|
|
connect s (SockAddrUnix namedpipe)
|
|
|
|
h <- socketToHandle s ReadWriteMode
|
|
|
|
hPutStrLn h $ show $ Cmd cmd params
|
2014-04-20 15:38:50 +00:00
|
|
|
hFlush h
|
2014-04-30 00:05:39 +00:00
|
|
|
debug ["simplesh sent command"]
|
2014-04-01 06:37:48 +00:00
|
|
|
resps <- catMaybes . map readish . lines <$> hGetContents h
|
2014-04-30 00:05:39 +00:00
|
|
|
v <- hClose h `after` handler resps
|
|
|
|
debug ["simplesh processed response"]
|
|
|
|
return v
|
2014-04-01 07:48:45 +00:00
|
|
|
|
2014-04-01 17:51:58 +00:00
|
|
|
simpleShClientRetry :: Int -> FilePath -> String -> [String] -> ([Resp] -> IO a) -> IO a
|
|
|
|
simpleShClientRetry retries namedpipe cmd params handler = go retries
|
|
|
|
where
|
|
|
|
run = simpleShClient namedpipe cmd params handler
|
|
|
|
go n
|
|
|
|
| n < 1 = run
|
|
|
|
| otherwise = do
|
|
|
|
v <- tryIO run
|
|
|
|
case v of
|
|
|
|
Right r -> return r
|
2014-04-30 00:05:39 +00:00
|
|
|
Left e -> do
|
|
|
|
debug ["simplesh connection retry", show e]
|
2014-04-01 17:51:58 +00:00
|
|
|
threadDelaySeconds (Seconds 1)
|
|
|
|
go (n - 1)
|
|
|
|
|
2014-04-01 07:48:45 +00:00
|
|
|
getStdout :: Resp -> Maybe String
|
|
|
|
getStdout (StdoutLine s) = Just s
|
|
|
|
getStdout _ = Nothing
|