2014-04-06 23:06:03 +00:00
|
|
|
{- git-remote-daemon core
|
|
|
|
-
|
|
|
|
- Copyright 2014 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
module RemoteDaemon.Core (runForeground) where
|
|
|
|
|
|
|
|
import qualified Annex
|
|
|
|
import Common
|
|
|
|
import Types.GitConfig
|
2014-04-08 17:41:36 +00:00
|
|
|
import RemoteDaemon.Common
|
2014-04-06 23:06:03 +00:00
|
|
|
import RemoteDaemon.Types
|
|
|
|
import RemoteDaemon.Transport
|
|
|
|
import qualified Git
|
|
|
|
import qualified Git.Types as Git
|
|
|
|
import qualified Git.CurrentRepo
|
|
|
|
import Utility.SimpleProtocol
|
2014-04-08 17:41:36 +00:00
|
|
|
import Config
|
2014-04-12 20:32:59 +00:00
|
|
|
import Annex.Ssh
|
2014-04-06 23:06:03 +00:00
|
|
|
|
|
|
|
import Control.Concurrent.Async
|
2014-04-08 17:41:36 +00:00
|
|
|
import Control.Concurrent
|
2014-04-06 23:06:03 +00:00
|
|
|
import Network.URI
|
|
|
|
import qualified Data.Map as M
|
|
|
|
|
|
|
|
runForeground :: IO ()
|
|
|
|
runForeground = do
|
2014-04-08 18:02:25 +00:00
|
|
|
(readh, writeh) <- ioHandles
|
2014-04-06 23:06:03 +00:00
|
|
|
ichan <- newChan :: IO (Chan Consumed)
|
|
|
|
ochan <- newChan :: IO (Chan Emitted)
|
|
|
|
|
|
|
|
let reader = forever $ do
|
2014-04-08 18:02:25 +00:00
|
|
|
l <- hGetLine readh
|
2014-04-06 23:06:03 +00:00
|
|
|
case parseMessage l of
|
|
|
|
Nothing -> error $ "protocol error: " ++ l
|
|
|
|
Just cmd -> writeChan ichan cmd
|
|
|
|
let writer = forever $ do
|
|
|
|
msg <- readChan ochan
|
2014-04-08 18:02:25 +00:00
|
|
|
hPutStrLn writeh $ unwords $ formatMessage msg
|
|
|
|
hFlush writeh
|
2014-04-08 17:51:49 +00:00
|
|
|
let controller = runController ichan ochan
|
2014-04-06 23:06:03 +00:00
|
|
|
|
2014-04-08 17:51:49 +00:00
|
|
|
-- If any thread fails, the rest will be killed.
|
|
|
|
void $ tryIO $
|
|
|
|
reader `concurrently` writer `concurrently` controller
|
2014-04-06 23:06:03 +00:00
|
|
|
|
|
|
|
type RemoteMap = M.Map Git.Repo (IO (), Chan Consumed)
|
|
|
|
|
|
|
|
-- Runs the transports, dispatching messages to them, and handling
|
|
|
|
-- the main control messages.
|
2014-04-08 17:51:49 +00:00
|
|
|
runController :: Chan Consumed -> Chan Emitted -> IO ()
|
|
|
|
runController ichan ochan = do
|
2014-04-08 17:41:36 +00:00
|
|
|
h <- genTransportHandle
|
|
|
|
m <- genRemoteMap h ochan
|
2014-04-06 23:06:03 +00:00
|
|
|
startrunning m
|
2014-04-08 17:41:36 +00:00
|
|
|
go h False m
|
2014-04-06 23:06:03 +00:00
|
|
|
where
|
2014-04-08 17:41:36 +00:00
|
|
|
go h paused m = do
|
2014-04-06 23:06:03 +00:00
|
|
|
cmd <- readChan ichan
|
|
|
|
case cmd of
|
|
|
|
RELOAD -> do
|
2014-04-20 19:45:14 +00:00
|
|
|
h' <- updateTransportHandle h
|
|
|
|
m' <- genRemoteMap h' ochan
|
2014-04-06 23:06:03 +00:00
|
|
|
let common = M.intersection m m'
|
|
|
|
let new = M.difference m' m
|
|
|
|
let old = M.difference m m'
|
2014-04-12 20:32:59 +00:00
|
|
|
broadcast STOP old
|
2014-04-06 23:06:03 +00:00
|
|
|
unless paused $
|
|
|
|
startrunning new
|
2014-04-20 19:45:14 +00:00
|
|
|
go h' paused (M.union common new)
|
2014-04-12 20:32:59 +00:00
|
|
|
LOSTNET -> do
|
|
|
|
-- force close all cached ssh connections
|
|
|
|
-- (done here so that if there are multiple
|
|
|
|
-- ssh remotes, it's only done once)
|
|
|
|
liftAnnex h forceSshCleanup
|
|
|
|
broadcast LOSTNET m
|
2014-04-14 18:20:10 +00:00
|
|
|
go h True m
|
2014-04-06 23:06:03 +00:00
|
|
|
PAUSE -> do
|
2014-04-12 20:32:59 +00:00
|
|
|
broadcast STOP m
|
2014-04-14 18:20:10 +00:00
|
|
|
go h True m
|
2014-04-06 23:06:03 +00:00
|
|
|
RESUME -> do
|
|
|
|
when paused $
|
|
|
|
startrunning m
|
2014-04-08 17:41:36 +00:00
|
|
|
go h False m
|
2014-04-06 23:06:03 +00:00
|
|
|
STOP -> exitSuccess
|
|
|
|
-- All remaining messages are sent to
|
|
|
|
-- all Transports.
|
|
|
|
msg -> do
|
|
|
|
unless paused $
|
|
|
|
forM_ chans (`writeChan` msg)
|
2014-04-08 17:41:36 +00:00
|
|
|
go h paused m
|
2014-04-06 23:06:03 +00:00
|
|
|
where
|
|
|
|
chans = map snd (M.elems m)
|
|
|
|
|
|
|
|
startrunning m = forM_ (M.elems m) startrunning'
|
|
|
|
startrunning' (transport, _) = void $ async transport
|
|
|
|
|
2014-04-12 20:32:59 +00:00
|
|
|
broadcast msg m = forM_ (M.elems m) send
|
|
|
|
where
|
|
|
|
send (_, c) = writeChan c msg
|
2014-04-06 23:06:03 +00:00
|
|
|
|
|
|
|
-- Generates a map with a transport for each supported remote in the git repo,
|
|
|
|
-- except those that have annex.sync = false
|
2014-04-08 17:41:36 +00:00
|
|
|
genRemoteMap :: TransportHandle -> Chan Emitted -> IO RemoteMap
|
2014-04-20 19:45:14 +00:00
|
|
|
genRemoteMap h@(TransportHandle g _) ochan =
|
2014-04-08 17:41:36 +00:00
|
|
|
M.fromList . catMaybes <$> mapM gen (Git.remotes g)
|
2014-04-06 23:06:03 +00:00
|
|
|
where
|
|
|
|
gen r = case Git.location r of
|
|
|
|
Git.Url u -> case M.lookup (uriScheme u) remoteTransports of
|
|
|
|
Just transport
|
|
|
|
| remoteAnnexSync (extractRemoteGitConfig r (Git.repoDescribe r)) -> do
|
|
|
|
ichan <- newChan :: IO (Chan Consumed)
|
|
|
|
return $ Just
|
|
|
|
( r
|
2014-04-09 18:10:29 +00:00
|
|
|
, (transport r (RemoteURI u) h ichan ochan, ichan)
|
2014-04-06 23:06:03 +00:00
|
|
|
)
|
|
|
|
_ -> return Nothing
|
|
|
|
_ -> return Nothing
|
2014-04-08 17:41:36 +00:00
|
|
|
|
|
|
|
genTransportHandle :: IO TransportHandle
|
|
|
|
genTransportHandle = do
|
|
|
|
annexstate <- newMVar =<< Annex.new =<< Git.CurrentRepo.get
|
|
|
|
g <- Annex.repo <$> readMVar annexstate
|
|
|
|
return $ TransportHandle g annexstate
|
2014-04-20 19:45:14 +00:00
|
|
|
|
|
|
|
updateTransportHandle :: TransportHandle -> IO TransportHandle
|
|
|
|
updateTransportHandle h@(TransportHandle _g annexstate) = do
|
|
|
|
g' <- liftAnnex h $ do
|
|
|
|
reloadConfig
|
|
|
|
Annex.fromRepo id
|
|
|
|
return (TransportHandle g' annexstate)
|