diff options
-rw-r--r-- | Annex/Index.hs | 37 | ||||
-rw-r--r-- | Annex/Ssh.hs | 82 | ||||
-rw-r--r-- | CmdLine/GitAnnex.hs | 7 | ||||
-rw-r--r-- | Command/Sync.hs | 15 | ||||
-rw-r--r-- | RemoteDaemon/Common.hs | 2 | ||||
-rw-r--r-- | RemoteDaemon/Transport/Ssh.hs | 10 | ||||
-rw-r--r-- | Utility/Process.hs | 4 | ||||
-rw-r--r-- | debian/changelog | 2 |
8 files changed, 121 insertions, 38 deletions
diff --git a/Annex/Index.hs b/Annex/Index.hs index a1b2442fc..af0cab45e 100644 --- a/Annex/Index.hs +++ b/Annex/Index.hs @@ -9,6 +9,7 @@ module Annex.Index ( withIndexFile, + addGitEnv, ) where import qualified Control.Exception as E @@ -23,24 +24,30 @@ import Annex.Exception withIndexFile :: FilePath -> Annex a -> Annex a withIndexFile f a = do g <- gitRepo -#ifdef __ANDROID__ - {- This should not be necessary on Android, but there is some - - weird getEnvironment breakage. See - - https://github.com/neurocyte/ghc-android/issues/7 - - Use getEnv to get some key environment variables that - - git expects to have. -} - let keyenv = words "USER PATH GIT_EXEC_PATH HOSTNAME HOME" - let getEnvPair k = maybe Nothing (\v -> Just (k, v)) <$> getEnv k - e <- liftIO $ catMaybes <$> forM keyenv getEnvPair - let e' = ("GIT_INDEX_FILE", f):e -#else - e <- liftIO getEnvironment - let e' = addEntry "GIT_INDEX_FILE" f e -#endif - let g' = g { gitEnv = Just e' } + g' <- liftIO $ addGitEnv g "GIT_INDEX_FILE" f r <- tryAnnex $ do Annex.changeState $ \s -> s { Annex.repo = g' } a Annex.changeState $ \s -> s { Annex.repo = (Annex.repo s) { gitEnv = gitEnv g} } either E.throw return r + +addGitEnv :: Repo -> String -> String -> IO Repo +addGitEnv g var val = do + e <- maybe copyenv return (gitEnv g) + let e' = addEntry var val e + return $ g { gitEnv = Just e' } + where + copyenv = do +#ifdef __ANDROID__ + {- This should not be necessary on Android, but there is some + - weird getEnvironment breakage. See + - https://github.com/neurocyte/ghc-android/issues/7 + - Use getEnv to get some key environment variables that + - git expects to have. -} + let keyenv = words "USER PATH GIT_EXEC_PATH HOSTNAME HOME" + let getEnvPair k = maybe Nothing (\v -> Just (k, v)) <$> getEnv k + liftIO $ catMaybes <$> forM keyenv getEnvPair +#else + liftIO getEnvironment +#endif diff --git a/Annex/Ssh.hs b/Annex/Ssh.hs index bd10a40d4..fab25c462 100644 --- a/Annex/Ssh.hs +++ b/Annex/Ssh.hs @@ -1,6 +1,6 @@ {- git-annex ssh interface, with connection caching - - - Copyright 2012,2013 Joey Hess <joey@kitenet.net> + - Copyright 2012-2014 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -} @@ -11,19 +11,28 @@ module Annex.Ssh ( sshCachingOptions, sshCacheDir, sshReadPort, + sshCachingEnv, + sshCachingTo, + inRepoWithSshCachingTo, + runSshCaching, ) where import qualified Data.Map as M import Data.Hash.MD5 import System.Process (cwd) +import System.Exit import Common.Annex import Annex.LockPool import qualified Build.SysConfig as SysConfig import qualified Annex +import qualified Git +import qualified Git.Url import Config +import Config.Files import Utility.Env import Types.CleanupActions +import Annex.Index (addGitEnv) #ifndef mingw32_HOST_OS import Annex.Perms #endif @@ -31,22 +40,13 @@ import Annex.Perms {- Generates parameters to ssh to a given host (or user@host) on a given - port, with connection caching. -} sshCachingOptions :: (String, Maybe Integer) -> [CommandParam] -> Annex [CommandParam] -sshCachingOptions (host, port) opts = do - Annex.addCleanup SshCachingCleanup sshCleanup - go =<< sshInfo (host, port) +sshCachingOptions (host, port) opts = go =<< sshInfo (host, port) where go (Nothing, params) = ret params go (Just socketfile, params) = do - cleanstale - liftIO $ createDirectoryIfMissing True $ parentDir socketfile - lockFile $ socket2lock socketfile + prepSocket socketfile ret params ret ps = return $ ps ++ opts ++ portParams port ++ [Param "-T"] - -- 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 (not . any isLock . M.keys <$> getPool) - sshCleanup {- Returns a filename to use for a ssh connection caching socket, and - parameters to enable ssh connection caching. -} @@ -109,6 +109,21 @@ portParams :: Maybe Integer -> [CommandParam] portParams Nothing = [] portParams (Just port) = [Param "-p", Param $ show port] +{- Prepare to use a socket file. Locks a lock file to prevent + - other git-annex processes from stopping the ssh on this socket. -} +prepSocket :: FilePath -> Annex () +prepSocket socketfile = do + -- 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. + whenM (not . any isLock . M.keys <$> getPool) + sshCleanup + -- Cleanup at end of this run. + Annex.addCleanup SshCachingCleanup sshCleanup + + liftIO $ createDirectoryIfMissing True $ parentDir socketfile + lockFile $ socket2lock socketfile + {- Stop any unused ssh processes. -} sshCleanup :: Annex () sshCleanup = go =<< sshCacheDir @@ -199,3 +214,46 @@ sshReadPort params = (port, reverse args) aux (p,ps) (q:rest) | "-p" `isPrefixOf` q = aux (readPort $ drop 2 q, ps) rest | otherwise = aux (p,q:ps) rest readPort p = fmap fst $ listToMaybe $ reads p + +{- When this env var is set, git-annex runs ssh with parameters + - to use the socket file that the env var contains. + - + - This is a workaround for GiT_SSH not being able to contain + - additional parameters to pass to ssh. -} +sshCachingEnv :: String +sshCachingEnv = "GIT_ANNEX_SSHCACHING" + +{- Enables ssh caching for git push/pull to a particular + - remote git repo. (Can safely be used on non-ssh remotes.) + - + - Like inRepo, the action is run with the local git repo. + - But here it's a modified version, with gitEnv to set GIT_SSH=git-annex, + - and sshCachingEnv set so that git-annex will know what socket + - file to use. -} +inRepoWithSshCachingTo :: Git.Repo -> (Git.Repo -> IO a) -> Annex a +inRepoWithSshCachingTo remote a = + liftIO . a =<< sshCachingTo remote =<< gitRepo + +{- To make any git commands be run with ssh caching enabled, + - alters the local Git.Repo's gitEnv to set GIT_SSH=git-annex, + - and set sshCachingEnv so that git-annex will know what socket + - file to use. -} +sshCachingTo :: Git.Repo -> Git.Repo -> Annex Git.Repo +sshCachingTo remote g = case Git.Url.hostuser remote of + Nothing -> return g + Just host -> do + (msockfile, _) <- sshInfo (host, Git.Url.port remote) + case msockfile of + Nothing -> return g + Just sockfile -> do + command <- liftIO readProgramFile + prepSocket sockfile + liftIO $ do + g' <- addGitEnv g sshCachingEnv sockfile + addGitEnv g' "GIT_SSH" command + +runSshCaching :: [String] -> String -> IO () +runSshCaching args sockfile = do + let args' = toCommand (sshConnectionCachingParams sockfile) ++ args + let p = proc "ssh" args' + exitWith =<< waitForProcess . processHandle =<< createProcess p diff --git a/CmdLine/GitAnnex.hs b/CmdLine/GitAnnex.hs index 9f6eb5ff0..7fdad4dae 100644 --- a/CmdLine/GitAnnex.hs +++ b/CmdLine/GitAnnex.hs @@ -1,6 +1,6 @@ {- git-annex main program - - - Copyright 2010-2013 Joey Hess <joey@kitenet.net> + - Copyright 2010-2014 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -} @@ -12,6 +12,8 @@ module CmdLine.GitAnnex where import qualified Git.CurrentRepo import CmdLine import Command +import Utility.Env +import Annex.Ssh import qualified Command.Add import qualified Command.Unannex @@ -193,4 +195,5 @@ run args = do #ifdef WITH_EKG _ <- forkServer "localhost" 4242 #endif - dispatch True args cmds gitAnnexOptions [] header Git.CurrentRepo.get + maybe (dispatch True args cmds gitAnnexOptions [] header Git.CurrentRepo.get) + (runSshCaching args) =<< getEnv sshCachingEnv diff --git a/Command/Sync.hs b/Command/Sync.hs index a4004736a..dfcb0d22a 100644 --- a/Command/Sync.hs +++ b/Command/Sync.hs @@ -21,7 +21,6 @@ import qualified Git.LsFiles as LsFiles import qualified Git.Branch import qualified Git.Ref import qualified Git -import qualified Types.Remote import qualified Remote.Git import Config import Annex.Wanted @@ -32,6 +31,7 @@ import Logs.Location import Annex.Drop import Annex.UUID import Annex.AutoMerge +import Annex.Ssh import Control.Concurrent.MVar @@ -113,11 +113,11 @@ syncRemotes rs = ifM (Annex.getState Annex.fast) ( nub <$> pickfast , wanted ) | null rs = filterM good =<< concat . Remote.byCost <$> available | otherwise = listed listed = catMaybes <$> mapM (Remote.byName . Just) rs - available = filter (remoteAnnexSync . Types.Remote.gitconfig) + available = filter (remoteAnnexSync . Remote.gitconfig) . filter (not . Remote.isXMPPRemote) <$> Remote.remoteList good r - | Remote.gitSyncableRemote r = Remote.Git.repoAvail $ Types.Remote.repo r + | Remote.gitSyncableRemote r = Remote.Git.repoAvail $ Remote.repo r | otherwise = return True fastest = fromMaybe [] . headMaybe . Remote.byCost @@ -201,7 +201,7 @@ pullRemote remote branch = do stopUnless fetch $ next $ mergeRemote remote branch where - fetch = inRepo $ Git.Command.runBool + fetch = inRepoWithSshCachingTo (Remote.repo remote) $ Git.Command.runBool [Param "fetch", Param $ Remote.name remote] {- The remote probably has both a master and a synced/master branch. @@ -227,14 +227,15 @@ pushRemote _remote Nothing = stop pushRemote remote (Just branch) = go =<< needpush where needpush - | remoteAnnexReadOnly (Types.Remote.gitconfig remote) = return False + | remoteAnnexReadOnly (Remote.gitconfig remote) = return False | otherwise = anyM (newer remote) [syncBranch branch, Annex.Branch.name] go False = stop go True = do showStart "push" (Remote.name remote) next $ next $ do showOutput - ok <- inRepo $ pushBranch remote branch + ok <- inRepoWithSshCachingTo (Remote.repo remote) $ + pushBranch remote branch unless ok $ do warning $ unwords [ "Pushing to " ++ Remote.name remote ++ " failed." ] showLongNote "(non-fast-forward problems can be solved by setting receive.denyNonFastforwards to false in the remote's git config)" @@ -367,7 +368,7 @@ syncFile rs f (k, _) = do next $ next $ getViaTmp k $ \dest -> getKeyFile' k (Just f) dest have wantput r - | Remote.readonly r || remoteAnnexReadOnly (Types.Remote.gitconfig r) = return False + | Remote.readonly r || remoteAnnexReadOnly (Remote.gitconfig r) = return False | otherwise = wantSend True (Just k) (Just f) (Remote.uuid r) handleput lack = ifM (inAnnex k) ( map put <$> filterM wantput lack diff --git a/RemoteDaemon/Common.hs b/RemoteDaemon/Common.hs index 29aeb00d3..e844e2c88 100644 --- a/RemoteDaemon/Common.hs +++ b/RemoteDaemon/Common.hs @@ -20,7 +20,7 @@ import Annex.CatFile import Control.Concurrent -- Runs an Annex action. Long-running actions should be avoided, --- since only one liftAnnex can be running at a time, amoung all +-- since only one liftAnnex can be running at a time, across all -- transports. liftAnnex :: TransportHandle -> Annex a -> IO a liftAnnex (TransportHandle _ annexstate) a = do diff --git a/RemoteDaemon/Transport/Ssh.hs b/RemoteDaemon/Transport/Ssh.hs index 87fcf6f8c..d6150bbce 100644 --- a/RemoteDaemon/Transport/Ssh.hs +++ b/RemoteDaemon/Transport/Ssh.hs @@ -8,6 +8,7 @@ module RemoteDaemon.Transport.Ssh (transport) where import Common.Annex +import Annex.Ssh import RemoteDaemon.Types import RemoteDaemon.Common import Remote.Helper.Ssh @@ -22,7 +23,14 @@ import Control.Concurrent.Async import System.Process (std_in, std_out, std_err) transport :: Transport -transport r url transporthandle ichan ochan = do +transport r url h@(TransportHandle g s) ichan ochan = do + -- enable ssh connection caching wherever inLocalRepo is called + g' <- liftAnnex h $ sshCachingTo r g + transport' r url (TransportHandle g' s) ichan ochan + +transport' :: Transport +transport' r url transporthandle ichan ochan = do + v <- liftAnnex transporthandle $ git_annex_shell r "notifychanges" [] [] case v of Nothing -> noop diff --git a/Utility/Process.hs b/Utility/Process.hs index 1945e4b9d..3f93dc2fc 100644 --- a/Utility/Process.hs +++ b/Utility/Process.hs @@ -31,6 +31,7 @@ module Utility.Process ( stdinHandle, stdoutHandle, stderrHandle, + processHandle, devNull, ) where @@ -313,6 +314,9 @@ bothHandles :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> (Han bothHandles (Just hin, Just hout, _, _) = (hin, hout) bothHandles _ = error "expected bothHandles" +processHandle :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> ProcessHandle +processHandle (_, _, _, pid) = pid + {- Debugging trace for a CreateProcess. -} debugProcess :: CreateProcess -> IO () debugProcess p = do diff --git a/debian/changelog b/debian/changelog index 1ec8ba622..1b5b39de8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -8,6 +8,8 @@ git-annex (5.20140413) UNRELEASED; urgency=medium it's currently connected with. * webapp: Rework xmpp nudge to prompt for either xmpp or a ssh remote be set up. + * sync, assistant, remotedaemon: Use ssh connection caching for git pushes + and pulls. * Improve handling on monthly/yearly scheduling. -- Joey Hess <joeyh@debian.org> Fri, 11 Apr 2014 21:33:35 -0400 |