ssh exit status 255 is a connection problem
Previously, when the git config was unable to be read from a ssh remote, it would try to git fetch from it to determine if the remote was otherwise accessible. That was unnessary work, since exit status 255 indicates a connection problem. As well as avoiding the extra work of the fetch, this also improves things when a ssh remote cannot be connected to due to a problem with the git-annex ssh control socket. In that situation, ssh will also exit 255. Before, the git fetch was tried in that situation, and would succeed, since it does not use the git-annex ssh control socket. git-annex would conclude that git-annex-shell was not installed on the remote, which could be wrong. I suppose it also used to be possible for the user to need to enter a ssh password on each connection to the remote. If they entered the wrong password for the git-annex-shell call, but then the right password for the git fetch, it would also incorrectly set annex-ignore, and that situation is also now fixed.
This commit is contained in:
parent
e8e36a90c1
commit
a19a3076b5
5 changed files with 79 additions and 38 deletions
|
@ -1,3 +1,10 @@
|
||||||
|
git-annex (10.20250103) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* Improve handing of ssh connection problems during
|
||||||
|
remote annex.uuid discovery.
|
||||||
|
|
||||||
|
-- Joey Hess <id@joeyh.name> Fri, 03 Jan 2025 14:30:38 -0400
|
||||||
|
|
||||||
git-annex (10.20250102) upstream; urgency=medium
|
git-annex (10.20250102) upstream; urgency=medium
|
||||||
|
|
||||||
* Added config `url.<base>.annexInsteadOf` corresponding to git's
|
* Added config `url.<base>.annexInsteadOf` corresponding to git's
|
||||||
|
|
|
@ -255,9 +255,9 @@ coreBare = "core.bare"
|
||||||
|
|
||||||
{- Runs a command to get the configuration of a repo,
|
{- Runs a command to get the configuration of a repo,
|
||||||
- and returns a repo populated with the configuration, as well as the raw
|
- and returns a repo populated with the configuration, as well as the raw
|
||||||
- output and the standard error of the command. -}
|
- output and the exit status and standard error of the command. -}
|
||||||
fromPipe :: Repo -> String -> [CommandParam] -> ConfigStyle -> IO (Either SomeException (Repo, S.ByteString, String))
|
fromPipe :: Repo -> String -> [CommandParam] -> ConfigStyle -> IO (Repo, S.ByteString, ExitCode, String)
|
||||||
fromPipe r cmd params st = tryNonAsync $ withCreateProcess p go
|
fromPipe r cmd params st = withCreateProcess p go
|
||||||
where
|
where
|
||||||
p = (proc cmd $ toCommand params)
|
p = (proc cmd $ toCommand params)
|
||||||
{ std_out = CreatePipe
|
{ std_out = CreatePipe
|
||||||
|
@ -267,9 +267,13 @@ fromPipe r cmd params st = tryNonAsync $ withCreateProcess p go
|
||||||
withAsync (getstderr pid herr []) $ \errreader -> do
|
withAsync (getstderr pid herr []) $ \errreader -> do
|
||||||
val <- S.hGetContents hout
|
val <- S.hGetContents hout
|
||||||
err <- wait errreader
|
err <- wait errreader
|
||||||
forceSuccessProcess p pid
|
exitcode <- waitForProcess pid
|
||||||
r' <- store val st r
|
case exitcode of
|
||||||
return (r', val, err)
|
ExitSuccess -> do
|
||||||
|
r' <- store val st r
|
||||||
|
return (r', val, exitcode, err)
|
||||||
|
ExitFailure _ ->
|
||||||
|
return (r, val, exitcode, err)
|
||||||
go _ _ _ _ = error "internal"
|
go _ _ _ _ = error "internal"
|
||||||
|
|
||||||
getstderr pid herr c = hGetLineUntilExitOrEOF pid herr >>= \case
|
getstderr pid herr c = hGetLineUntilExitOrEOF pid herr >>= \case
|
||||||
|
@ -278,7 +282,7 @@ fromPipe r cmd params st = tryNonAsync $ withCreateProcess p go
|
||||||
|
|
||||||
{- Reads git config from a specified file and returns the repo populated
|
{- Reads git config from a specified file and returns the repo populated
|
||||||
- with the configuration. -}
|
- with the configuration. -}
|
||||||
fromFile :: Repo -> FilePath -> IO (Either SomeException (Repo, S.ByteString, String))
|
fromFile :: Repo -> FilePath -> IO (Repo, S.ByteString, ExitCode, String)
|
||||||
fromFile r f = fromPipe r "git"
|
fromFile r f = fromPipe r "git"
|
||||||
[ Param "config"
|
[ Param "config"
|
||||||
, Param "--file"
|
, Param "--file"
|
||||||
|
|
|
@ -22,7 +22,6 @@ import qualified Data.Map as M
|
||||||
import qualified Data.ByteString as S
|
import qualified Data.ByteString as S
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
import qualified System.FilePath.ByteString as P
|
import qualified System.FilePath.ByteString as P
|
||||||
import Control.Exception
|
|
||||||
import Data.Default
|
import Data.Default
|
||||||
|
|
||||||
import Annex.Common
|
import Annex.Common
|
||||||
|
@ -59,7 +58,6 @@ import Utility.Tmp
|
||||||
import Logs.Remote
|
import Logs.Remote
|
||||||
import Utility.Gpg
|
import Utility.Gpg
|
||||||
import Utility.SshHost
|
import Utility.SshHost
|
||||||
import Utility.Tuple
|
|
||||||
import Utility.Directory.Create
|
import Utility.Directory.Create
|
||||||
import Messages.Progress
|
import Messages.Progress
|
||||||
import Types.ProposedAccepted
|
import Types.ProposedAccepted
|
||||||
|
@ -508,16 +506,25 @@ getGCryptId :: Bool -> Git.Repo -> RemoteGitConfig -> Annex (Maybe Git.GCrypt.GC
|
||||||
getGCryptId fast r gc
|
getGCryptId fast r gc
|
||||||
| Git.repoIsLocal r || Git.repoIsLocalUnknown r = extract <$>
|
| Git.repoIsLocal r || Git.repoIsLocalUnknown r = extract <$>
|
||||||
liftIO (catchMaybeIO $ Git.Config.read r)
|
liftIO (catchMaybeIO $ Git.Config.read r)
|
||||||
| not fast = extract . liftM fst3 <$> getM (eitherToMaybe <$>)
|
| not fast = extract <$> getM id
|
||||||
[ Ssh.onRemote NoConsumeStdin r (\f p -> liftIO (Git.Config.fromPipe r f p Git.Config.ConfigList), return (Left $ giveup "configlist failed")) "configlist" [] []
|
[ Ssh.onRemote NoConsumeStdin r (configpipe, return Nothing) "configlist" [] []
|
||||||
, getConfigViaRsync r gc
|
, getconfig $ getConfigViaRsync r gc
|
||||||
]
|
]
|
||||||
| otherwise = return (Nothing, r)
|
| otherwise = return (Nothing, r)
|
||||||
where
|
where
|
||||||
extract Nothing = (Nothing, r)
|
extract Nothing = (Nothing, r)
|
||||||
extract (Just r') = (fromConfigValue <$> Git.Config.getMaybe coreGCryptId r', r')
|
extract (Just r') = (fromConfigValue <$> Git.Config.getMaybe coreGCryptId r', r')
|
||||||
|
|
||||||
getConfigViaRsync :: Git.Repo -> RemoteGitConfig -> Annex (Either SomeException (Git.Repo, S.ByteString, String))
|
configpipe f p = getconfig $ liftIO $
|
||||||
|
Git.Config.fromPipe r f p Git.Config.ConfigList
|
||||||
|
|
||||||
|
getconfig a = do
|
||||||
|
(r', _, exitcode, _) <- a
|
||||||
|
if exitcode == ExitSuccess
|
||||||
|
then return (Just r')
|
||||||
|
else return Nothing
|
||||||
|
|
||||||
|
getConfigViaRsync :: Git.Repo -> RemoteGitConfig -> Annex (Git.Repo, S.ByteString, ExitCode, String)
|
||||||
getConfigViaRsync r gc = do
|
getConfigViaRsync r gc = do
|
||||||
let (rsynctransport, rsyncurl, _) = rsyncTransport r gc
|
let (rsynctransport, rsyncurl, _) = rsyncTransport r gc
|
||||||
opts <- rsynctransport
|
opts <- rsynctransport
|
||||||
|
|
|
@ -278,14 +278,25 @@ tryGitConfigRead autoinit r hasuuid
|
||||||
| Git.repoIsSsh r = storeUpdatedRemote $ do
|
| Git.repoIsSsh r = storeUpdatedRemote $ do
|
||||||
v <- Ssh.onRemote NoConsumeStdin r
|
v <- Ssh.onRemote NoConsumeStdin r
|
||||||
( pipedconfig Git.Config.ConfigList autoinit (Git.repoDescribe r)
|
( pipedconfig Git.Config.ConfigList autoinit (Git.repoDescribe r)
|
||||||
, return (Left "configlist failed")
|
, error "internal"
|
||||||
)
|
)
|
||||||
"configlist" [] configlistfields
|
"configlist" [] configlistfields
|
||||||
case v of
|
case v of
|
||||||
Right r'
|
Right r'
|
||||||
| haveconfig r' -> return r'
|
| haveconfig r' -> return r'
|
||||||
| otherwise -> configlist_failed
|
| otherwise -> do
|
||||||
Left _ -> configlist_failed
|
configlist_failed
|
||||||
|
return r
|
||||||
|
Left exitcode -> do
|
||||||
|
-- ssh exits 255 when there was an error
|
||||||
|
-- connecting to the remote server.
|
||||||
|
if exitcode /= ExitFailure 255
|
||||||
|
then do
|
||||||
|
configlist_failed
|
||||||
|
return r
|
||||||
|
else do
|
||||||
|
warning $ UnquotedString $ "Unable to connect to repository " ++ Git.repoDescribe r ++ " to get its annex.uuid configuration."
|
||||||
|
return r
|
||||||
| Git.repoIsHttp r = storeUpdatedRemote geturlconfig
|
| Git.repoIsHttp r = storeUpdatedRemote geturlconfig
|
||||||
| Git.GCrypt.isEncrypted r = handlegcrypt =<< getConfigMaybe (remoteAnnexConfig r "uuid")
|
| Git.GCrypt.isEncrypted r = handlegcrypt =<< getConfigMaybe (remoteAnnexConfig r "uuid")
|
||||||
| Git.repoIsUrl r = do
|
| Git.repoIsUrl r = do
|
||||||
|
@ -298,31 +309,35 @@ tryGitConfigRead autoinit r hasuuid
|
||||||
haveconfig = not . M.null . Git.config
|
haveconfig = not . M.null . Git.config
|
||||||
|
|
||||||
pipedconfig st mustincludeuuuid configloc cmd params = do
|
pipedconfig st mustincludeuuuid configloc cmd params = do
|
||||||
v <- liftIO $ Git.Config.fromPipe r cmd params st
|
(r', val, exitcode, _err) <- liftIO $
|
||||||
case v of
|
Git.Config.fromPipe r cmd params st
|
||||||
Right (r', val, _err) -> do
|
if exitcode == ExitSuccess
|
||||||
|
then do
|
||||||
unless (isUUIDConfigured r' || val == mempty || not mustincludeuuuid) $ do
|
unless (isUUIDConfigured r' || val == mempty || not mustincludeuuuid) $ do
|
||||||
warning $ UnquotedString $ "Failed to get annex.uuid configuration of repository " ++ Git.repoDescribe r
|
warning $ UnquotedString $ "Failed to get annex.uuid configuration of repository " ++ Git.repoDescribe r
|
||||||
warning $ UnquotedString $ "Instead, got: " ++ show val
|
warning $ UnquotedString $ "Instead, got: " ++ show val
|
||||||
warning "This is unexpected; please check the network transport!"
|
warning "This is unexpected; please check the network transport!"
|
||||||
return $ Right r'
|
return $ Right r'
|
||||||
Left l -> do
|
else do
|
||||||
warning $ UnquotedString $ "Unable to parse git config from " ++ configloc
|
warning $ UnquotedString $ "Unable to parse git config from " ++ configloc
|
||||||
return $ Left (show l)
|
return $ Left exitcode
|
||||||
|
|
||||||
geturlconfig = Url.withUrlOptionsPromptingCreds $ \uo -> do
|
geturlconfig = Url.withUrlOptionsPromptingCreds $ \uo -> do
|
||||||
let url = Git.repoLocation r ++ "/config"
|
let url = Git.repoLocation r ++ "/config"
|
||||||
v <- withTmpFile "git-annex.tmp" $ \tmpfile h -> do
|
v <- withTmpFile "git-annex.tmp" $ \tmpfile h -> do
|
||||||
liftIO $ hClose h
|
liftIO $ hClose h
|
||||||
Url.download' nullMeterUpdate Nothing url tmpfile uo >>= \case
|
Url.download' nullMeterUpdate Nothing url tmpfile uo >>= \case
|
||||||
Right () -> pipedconfig Git.Config.ConfigNullList
|
Right () ->
|
||||||
False url "git"
|
pipedconfig Git.Config.ConfigNullList
|
||||||
[ Param "config"
|
False url "git"
|
||||||
, Param "--null"
|
[ Param "config"
|
||||||
, Param "--list"
|
, Param "--null"
|
||||||
, Param "--file"
|
, Param "--list"
|
||||||
, File tmpfile
|
, Param "--file"
|
||||||
]
|
, File tmpfile
|
||||||
|
] >>= return . \case
|
||||||
|
Right r' -> Right r'
|
||||||
|
Left exitcode -> Left $ "git config exited " ++ show exitcode
|
||||||
Left err -> return (Left err)
|
Left err -> return (Left err)
|
||||||
case v of
|
case v of
|
||||||
Right r' -> do
|
Right r' -> do
|
||||||
|
@ -342,15 +357,7 @@ tryGitConfigRead autoinit r hasuuid
|
||||||
warning $ UnquotedString $ url ++ " " ++ err
|
warning $ UnquotedString $ url ++ " " ++ err
|
||||||
return r
|
return r
|
||||||
|
|
||||||
{- Is this remote just not available, or does
|
configlist_failed = set_ignore "does not have git-annex installed" True
|
||||||
- it not have git-annex-shell?
|
|
||||||
- Find out by trying to fetch from the remote. -}
|
|
||||||
configlist_failed = case Git.remoteName r of
|
|
||||||
Nothing -> return r
|
|
||||||
Just n -> do
|
|
||||||
whenM (inRepo $ Git.Command.runBool [Param "fetch", Param "--quiet", Param n]) $ do
|
|
||||||
set_ignore "does not have git-annex installed" True
|
|
||||||
return r
|
|
||||||
|
|
||||||
set_ignore msg longmessage = do
|
set_ignore msg longmessage = do
|
||||||
case Git.remoteName r of
|
case Git.remoteName r of
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
[[!comment format=mdwn
|
||||||
|
username="joey"
|
||||||
|
subject="""comment 1"""
|
||||||
|
date="2025-01-03T17:33:05Z"
|
||||||
|
content="""
|
||||||
|
I don't know why ssh is failing to connect via the socket file like that.
|
||||||
|
Generally when a socket file like this exists, there is a background ssh
|
||||||
|
process that is servicing connections to it. It may be there was some
|
||||||
|
problem with that process.
|
||||||
|
|
||||||
|
git-annex interprets ssh failing as the ssh server not having git-annex
|
||||||
|
installed, even though in this case it's some other problem. I have made
|
||||||
|
some changes that improve this, so in this situation it should not set
|
||||||
|
annex-ignore again, but will instead complain that it cannot connect to the
|
||||||
|
server.
|
||||||
|
"""]]
|
Loading…
Add table
Add a link
Reference in a new issue