stop cleanly when there's a IO error accessing the Handle

All other exceptions are let through, but IO errors accessing the handle
are to be expected, so quietly ignore.
This commit is contained in:
Joey Hess 2016-11-21 21:22:58 -04:00
parent ae69ebfc7c
commit 483dbcdbef
No known key found for this signature in database
GPG key ID: C910D9222512E3C7
3 changed files with 70 additions and 60 deletions

View file

@ -32,7 +32,8 @@ run (_remotename:address:[]) = forever $ do
| otherwise = parseAddressPort address | otherwise = parseAddressPort address
go service = do go service = do
ready ready
connectService onionaddress onionport service >>= exitWith res <- connectService onionaddress onionport service
exitWith (fromMaybe (ExitFailure 1) res)
ready = do ready = do
putStrLn "" putStrLn ""
hFlush stdout hFlush stdout
@ -47,7 +48,7 @@ parseAddressPort s =
Nothing -> giveup "onion address must include port number" Nothing -> giveup "onion address must include port number"
Just p -> (OnionAddress a, p) Just p -> (OnionAddress a, p)
connectService :: OnionAddress -> OnionPort -> Service -> IO ExitCode connectService :: OnionAddress -> OnionPort -> Service -> IO (Maybe ExitCode)
connectService address port service = do connectService address port service = do
state <- Annex.new =<< Git.CurrentRepo.get state <- Annex.new =<< Git.CurrentRepo.get
Annex.eval state $ do Annex.eval state $ do

View file

@ -30,7 +30,7 @@ import Control.Concurrent.Async
import qualified Data.ByteString as B import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L
type RunProto = forall a m. (MonadIO m, MonadMask m) => Proto a -> m a type RunProto = forall a m. (MonadIO m, MonadMask m) => Proto a -> m (Maybe a)
data S = S data S = S
{ repo :: Repo { repo :: Repo
@ -40,58 +40,66 @@ data S = S
-- Implementation of the protocol, communicating with a peer -- Implementation of the protocol, communicating with a peer
-- over a Handle. No Local actions will be run. -- over a Handle. No Local actions will be run.
runNetProtoHandle :: (MonadIO m, MonadMask m) => Handle -> Handle -> Repo -> Proto a -> m a runNetProtoHandle :: (MonadIO m, MonadMask m) => Handle -> Handle -> Repo -> Proto a -> m (Maybe a)
runNetProtoHandle i o r = go runNetProtoHandle i o r = go
where where
go :: RunProto go :: RunProto
go (Pure a) = pure a go (Pure v) = pure (Just v)
go (Free (Net n)) = runNetHandle (S r i o) go n go (Free (Net n)) = runNetHandle (S r i o) go n
go (Free (Local _)) = error "local actions not allowed" go (Free (Local _)) = return Nothing
runNetHandle :: (MonadIO m, MonadMask m) => S -> RunProto -> NetF (Proto a) -> m a runNetHandle :: (MonadIO m, MonadMask m) => S -> RunProto -> NetF (Proto a) -> m (Maybe a)
runNetHandle s runner f = case f of runNetHandle s runner f = case f of
SendMessage m next -> do SendMessage m next -> do
liftIO $ do v <- liftIO $ tryIO $ do
hPutStrLn (ohdl s) (unwords (formatMessage m)) hPutStrLn (ohdl s) (unwords (formatMessage m))
hFlush (ohdl s) hFlush (ohdl s)
runner next case v of
Left _e -> return Nothing
Right () -> runner next
ReceiveMessage next -> do ReceiveMessage next -> do
l <- liftIO $ hGetLine (ihdl s) v <- liftIO $ tryIO $ hGetLine (ihdl s)
case parseMessage l of case v of
Just m -> runner (next m) Left _e -> return Nothing
Nothing -> runner $ do Right l -> case parseMessage l of
let e = ERROR $ "protocol parse error: " ++ show l Just m -> runner (next m)
net $ sendMessage e Nothing -> runner $ do
next e let e = ERROR $ "protocol parse error: " ++ show l
net $ sendMessage e
next e
SendBytes _len b next -> do SendBytes _len b next -> do
liftIO $ do v <- liftIO $ tryIO $ do
L.hPut (ohdl s) b L.hPut (ohdl s) b
hFlush (ohdl s) hFlush (ohdl s)
runner next case v of
Left _e -> return Nothing
Right () -> runner next
ReceiveBytes (Len n) next -> do ReceiveBytes (Len n) next -> do
b <- liftIO $ L.hGet (ihdl s) (fromIntegral n) v <- liftIO $ tryIO $ L.hGet (ihdl s) (fromIntegral n)
runner (next b) case v of
Left _e -> return Nothing
Right b -> runner (next b)
CheckAuthToken u t next -> do CheckAuthToken u t next -> do
authed <- return True -- TODO XXX FIXME really check authed <- return True -- TODO XXX FIXME really check
runner (next authed) runner (next authed)
Relay hin hout next -> Relay hin hout next -> do
runRelay runner hin hout >>= runner . next v <- liftIO $ runRelay runner hin hout
RelayService service next -> case v of
runRelayService s runner service >> runner next Nothing -> return Nothing
Just exitcode -> runner (next exitcode)
RelayService service next -> do
v <- liftIO $ runRelayService s runner service
case v of
Nothing -> return Nothing
Just () -> runner next
runRelay runRelay :: RunProto -> RelayHandle -> RelayHandle -> IO (Maybe ExitCode)
:: MonadIO m runRelay runner (RelayHandle hout) (RelayHandle hin) = bracket setup cleanup go
=> RunProto
-> RelayHandle
-> RelayHandle
-> m ExitCode
runRelay runner (RelayHandle hout) (RelayHandle hin) = liftIO $
bracket setup cleanup go
where where
setup = do setup = do
v <- newEmptyMVar v <- newEmptyMVar
void $ forkIO $ relayFeeder runner v void $ async $ relayFeeder runner v
void $ forkIO $ relayReader v hout void $ async $ relayReader v hout
return v return v
cleanup _ = do cleanup _ = do
@ -100,13 +108,8 @@ runRelay runner (RelayHandle hout) (RelayHandle hin) = liftIO $
go v = relayHelper runner v hin go v = relayHelper runner v hin
runRelayService runRelayService :: S -> RunProto -> Service -> IO (Maybe ())
:: MonadIO m runRelayService s runner service = bracket setup cleanup go
=> S
-> RunProto
-> Service
-> m ()
runRelayService s runner service = liftIO $ bracket setup cleanup go
where where
cmd = case service of cmd = case service of
UploadPack -> "upload-pack" UploadPack -> "upload-pack"
@ -123,28 +126,29 @@ runRelayService s runner service = liftIO $ bracket setup cleanup go
, std_in = CreatePipe , std_in = CreatePipe
} }
v <- newEmptyMVar v <- newEmptyMVar
feeder <- async $ relayFeeder runner v void $ async $ relayFeeder runner v
reader <- async $ relayReader v hout void $ async $ relayReader v hout
waiter <- async $ waitexit v pid waiter <- async $ waitexit v pid
return (v, feeder, reader, waiter, hin, hout, pid) return (v, waiter, hin, hout, pid)
cleanup (_, feeder, reader, waiter, hin, hout, pid) = do cleanup (_, waiter, hin, hout, pid) = do
hPutStrLn stderr "!!!!\n\nIN CLEANUP" hPutStrLn stderr "!!!!\n\nIN CLEANUP"
hFlush stderr hFlush stderr
hClose hin hClose hin
hClose hout hClose hout
cancel reader
cancel waiter cancel waiter
void $ waitForProcess pid void $ waitForProcess pid
go (v, _, _, _, hin, _, _) = do go (v, _, hin, _, _) = do
exitcode <- relayHelper runner v hin r <- relayHelper runner v hin
runner $ net $ relayToPeer (RelayDone exitcode) case r of
Nothing -> return Nothing
Just exitcode -> runner $ net $ relayToPeer (RelayDone exitcode)
waitexit v pid = putMVar v . RelayDone =<< waitForProcess pid waitexit v pid = putMVar v . RelayDone =<< waitForProcess pid
-- Processes RelayData as it is put into the MVar. -- Processes RelayData as it is put into the MVar.
relayHelper :: RunProto -> MVar RelayData -> Handle -> IO ExitCode relayHelper :: RunProto -> MVar RelayData -> Handle -> IO (Maybe ExitCode)
relayHelper runner v hin = loop relayHelper runner v hin = loop
where where
loop = do loop = do
@ -155,11 +159,13 @@ relayHelper runner v hin = loop
hFlush hin hFlush hin
loop loop
RelayToPeer b -> do RelayToPeer b -> do
runner $ net $ relayToPeer (RelayToPeer b) r <- runner $ net $ relayToPeer (RelayToPeer b)
loop case r of
Nothing -> return Nothing
Just () -> loop
RelayDone exitcode -> do RelayDone exitcode -> do
runner $ net $ relayToPeer (RelayDone exitcode) _ <- runner $ net $ relayToPeer (RelayDone exitcode)
return exitcode return (Just exitcode)
-- Takes input from the peer, and puts it into the MVar for processing. -- Takes input from the peer, and puts it into the MVar for processing.
-- Repeats until the peer tells it it's done. -- Repeats until the peer tells it it's done.
@ -167,11 +173,14 @@ relayFeeder :: RunProto -> MVar RelayData -> IO ()
relayFeeder runner v = loop relayFeeder runner v = loop
where where
loop = do loop = do
rd <- runner $ net relayFromPeer mrd <- runner $ net relayFromPeer
putMVar v rd case mrd of
case rd of Nothing -> return ()
RelayDone _ -> return () Just rd -> do
_ -> loop putMVar v rd
case rd of
RelayDone _ -> return ()
_ -> loop
-- Reads input from the Handle and puts it into the MVar for relaying to -- Reads input from the Handle and puts it into the MVar for relaying to
-- the peer. Continues until EOF on the Handle. -- the peer. Continues until EOF on the Handle.

View file

@ -45,5 +45,5 @@ server th@(TransportHandle (LocalRepo r) _) = do
forkIO $ do forkIO $ do
debugM "remotedaemon" "handling a connection" debugM "remotedaemon" "handling a connection"
h <- torHandle conn h <- torHandle conn
runNetProtoHandle h h r (serve u) _ <- runNetProtoHandle h h r (serve u)
hClose h hClose h