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:
Joey Hess 2025-01-03 14:33:24 -04:00
parent e8e36a90c1
commit a19a3076b5
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
5 changed files with 79 additions and 38 deletions

View file

@ -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
* Added config `url.<base>.annexInsteadOf` corresponding to git's

View file

@ -255,9 +255,9 @@ coreBare = "core.bare"
{- Runs a command to get the configuration of a repo,
- and returns a repo populated with the configuration, as well as the raw
- output and the standard error of the command. -}
fromPipe :: Repo -> String -> [CommandParam] -> ConfigStyle -> IO (Either SomeException (Repo, S.ByteString, String))
fromPipe r cmd params st = tryNonAsync $ withCreateProcess p go
- output and the exit status and standard error of the command. -}
fromPipe :: Repo -> String -> [CommandParam] -> ConfigStyle -> IO (Repo, S.ByteString, ExitCode, String)
fromPipe r cmd params st = withCreateProcess p go
where
p = (proc cmd $ toCommand params)
{ std_out = CreatePipe
@ -267,9 +267,13 @@ fromPipe r cmd params st = tryNonAsync $ withCreateProcess p go
withAsync (getstderr pid herr []) $ \errreader -> do
val <- S.hGetContents hout
err <- wait errreader
forceSuccessProcess p pid
exitcode <- waitForProcess pid
case exitcode of
ExitSuccess -> do
r' <- store val st r
return (r', val, err)
return (r', val, exitcode, err)
ExitFailure _ ->
return (r, val, exitcode, err)
go _ _ _ _ = error "internal"
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
- 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"
[ Param "config"
, Param "--file"

View file

@ -22,7 +22,6 @@ import qualified Data.Map as M
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
import qualified System.FilePath.ByteString as P
import Control.Exception
import Data.Default
import Annex.Common
@ -59,7 +58,6 @@ import Utility.Tmp
import Logs.Remote
import Utility.Gpg
import Utility.SshHost
import Utility.Tuple
import Utility.Directory.Create
import Messages.Progress
import Types.ProposedAccepted
@ -508,16 +506,25 @@ getGCryptId :: Bool -> Git.Repo -> RemoteGitConfig -> Annex (Maybe Git.GCrypt.GC
getGCryptId fast r gc
| Git.repoIsLocal r || Git.repoIsLocalUnknown r = extract <$>
liftIO (catchMaybeIO $ Git.Config.read r)
| not fast = extract . liftM fst3 <$> getM (eitherToMaybe <$>)
[ Ssh.onRemote NoConsumeStdin r (\f p -> liftIO (Git.Config.fromPipe r f p Git.Config.ConfigList), return (Left $ giveup "configlist failed")) "configlist" [] []
, getConfigViaRsync r gc
| not fast = extract <$> getM id
[ Ssh.onRemote NoConsumeStdin r (configpipe, return Nothing) "configlist" [] []
, getconfig $ getConfigViaRsync r gc
]
| otherwise = return (Nothing, r)
where
extract Nothing = (Nothing, 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
let (rsynctransport, rsyncurl, _) = rsyncTransport r gc
opts <- rsynctransport

View file

@ -278,14 +278,25 @@ tryGitConfigRead autoinit r hasuuid
| Git.repoIsSsh r = storeUpdatedRemote $ do
v <- Ssh.onRemote NoConsumeStdin r
( pipedconfig Git.Config.ConfigList autoinit (Git.repoDescribe r)
, return (Left "configlist failed")
, error "internal"
)
"configlist" [] configlistfields
case v of
Right r'
| haveconfig r' -> return r'
| otherwise -> configlist_failed
Left _ -> configlist_failed
| otherwise -> do
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.GCrypt.isEncrypted r = handlegcrypt =<< getConfigMaybe (remoteAnnexConfig r "uuid")
| Git.repoIsUrl r = do
@ -298,31 +309,35 @@ tryGitConfigRead autoinit r hasuuid
haveconfig = not . M.null . Git.config
pipedconfig st mustincludeuuuid configloc cmd params = do
v <- liftIO $ Git.Config.fromPipe r cmd params st
case v of
Right (r', val, _err) -> do
(r', val, exitcode, _err) <- liftIO $
Git.Config.fromPipe r cmd params st
if exitcode == ExitSuccess
then do
unless (isUUIDConfigured r' || val == mempty || not mustincludeuuuid) $ do
warning $ UnquotedString $ "Failed to get annex.uuid configuration of repository " ++ Git.repoDescribe r
warning $ UnquotedString $ "Instead, got: " ++ show val
warning "This is unexpected; please check the network transport!"
return $ Right r'
Left l -> do
else do
warning $ UnquotedString $ "Unable to parse git config from " ++ configloc
return $ Left (show l)
return $ Left exitcode
geturlconfig = Url.withUrlOptionsPromptingCreds $ \uo -> do
let url = Git.repoLocation r ++ "/config"
v <- withTmpFile "git-annex.tmp" $ \tmpfile h -> do
liftIO $ hClose h
Url.download' nullMeterUpdate Nothing url tmpfile uo >>= \case
Right () -> pipedconfig Git.Config.ConfigNullList
Right () ->
pipedconfig Git.Config.ConfigNullList
False url "git"
[ Param "config"
, Param "--null"
, Param "--list"
, Param "--file"
, File tmpfile
]
] >>= return . \case
Right r' -> Right r'
Left exitcode -> Left $ "git config exited " ++ show exitcode
Left err -> return (Left err)
case v of
Right r' -> do
@ -342,15 +357,7 @@ tryGitConfigRead autoinit r hasuuid
warning $ UnquotedString $ url ++ " " ++ err
return r
{- Is this remote just not available, or does
- 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
configlist_failed = set_ignore "does not have git-annex installed" True
set_ignore msg longmessage = do
case Git.remoteName r of

View file

@ -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.
"""]]