diff options
author | Joey Hess <joey@kitenet.net> | 2012-01-20 15:34:52 -0400 |
---|---|---|
committer | Joey Hess <joey@kitenet.net> | 2012-01-20 17:14:56 -0400 |
commit | 47250a153a6c5a2864fec15fb136290683aeb1c6 (patch) | |
tree | 5c17ef6c035d6c919403e52e61ed0d37c2bfd824 /Annex/Ssh.hs | |
parent | 25f998679cd68cd4bb9b320998253f1b2ae23315 (diff) |
ssh connection caching
Ssh connection caching is now enabled automatically by git-annex. Only one
ssh connection is made to each host per git-annex run, which can speed some
things up a lot, as well as avoiding repeated password prompts. Concurrent
git-annex processes also share ssh connections. Cached ssh connections are
shut down when git-annex exits.
Note: The rsync special remote does not yet participate in the ssh
connection caching.
Diffstat (limited to 'Annex/Ssh.hs')
-rw-r--r-- | Annex/Ssh.hs | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/Annex/Ssh.hs b/Annex/Ssh.hs new file mode 100644 index 000000000..cd832a373 --- /dev/null +++ b/Annex/Ssh.hs @@ -0,0 +1,107 @@ +{- git-annex ssh interface, with connection caching + - + - Copyright 2012 Joey Hess <joey@kitenet.net> + - + - Licensed under the GNU GPL version 3 or higher. + -} + +module Annex.Ssh ( + sshParams, + sshCleanup, +) where + +import qualified Data.Map as M +import System.IO.Error (try) + +import Common.Annex +import Annex.LockPool + +{- Generates parameters to ssh to a given host (or user@host) on a given + - port, with connection caching. -} +sshParams :: (String, Maybe Integer) -> Annex [CommandParam] +sshParams (host, port) = do + cleanstale + (socketfile, params) <- sshInfo (host, port) + liftIO $ createDirectoryIfMissing True $ parentDir socketfile + lockFile $ socket2lock socketfile + return params + where + -- If the lock pool is empty, this is the first ssh of this + -- run. There could be stale ssh connections hanging around + -- from a previous git-annex run that was interrupted. + cleanstale = whenM (null . filter isLock . M.keys <$> getPool) $ + sshCleanup + +sshInfo :: (String, Maybe Integer) -> Annex (FilePath, [CommandParam]) +sshInfo (host, port) = do + dir <- fromRepo $ gitAnnexSshDir + let socketfile = dir </> hostport2socket host port + return $ (socketfile, cacheParams socketfile ++ portParams port ++ [Param host]) + +cacheParams :: FilePath -> [CommandParam] +cacheParams socketfile = + [ Param "-S", Param socketfile + , Params "-o ControlMaster=auto -o ControlPersist=yes" + ] + +portParams :: Maybe Integer -> [CommandParam] +portParams Nothing = [] +portParams (Just port) = [Param "-p", Param $ show port] + +{- Stop any unused ssh processes. -} +sshCleanup :: Annex () +sshCleanup = do + dir <- fromRepo $ gitAnnexSshDir + liftIO $ createDirectoryIfMissing True dir + sockets <- filter (not . isLock) <$> liftIO (dirContents dir) + forM_ sockets cleanup + where + cleanup socketfile = do + -- Drop any shared lock we have, and take an + -- exclusive lock, without blocking. If the lock + -- succeeds, nothing is using this ssh, and it can + -- be stopped. + let lockfile = socket2lock socketfile + unlockFile lockfile + fd <- liftIO $ openFd lockfile ReadWrite (Just stdFileMode) defaultFileFlags + v <- liftIO $ try $ setLock fd (WriteLock, AbsoluteSeek, 0, 0) + case v of + Left _ -> return () + Right _ -> stopssh socketfile + liftIO $ closeFd fd + stopssh socketfile = do + (_, params) <- sshInfo $ socket2hostport socketfile + _ <- liftIO $ do + -- "ssh -O stop" is noisy on stderr even with -q + let cmd = unwords $ toCommand $ + [ Params "-O stop" + ] ++ params + _ <- boolSystem "sh" + [ Param "-c" + , Param $ "ssh " ++ cmd ++ " >/dev/null 2>/dev/null" + ] + --try $ removeFile socketfile + return () + -- Cannot remove the lock file; other processes may + -- be waiting on our exclusive lock to use it. + return () + +hostport2socket :: String -> Maybe Integer -> FilePath +hostport2socket host Nothing = host +hostport2socket host (Just port) = host ++ "!" ++ show port + +socket2hostport :: FilePath -> (String, Maybe Integer) +socket2hostport socket + | null p = (h, Nothing) + | otherwise = (h, readMaybe p) + where + (h, p) = separate (== '!') $ takeFileName socket + +socket2lock :: FilePath -> FilePath +socket2lock socket = socket ++ lockExt + +isLock :: FilePath -> Bool +isLock f = lockExt `isSuffixOf` f + +lockExt :: String +lockExt = ".lock" |