2013-09-24 17:37:41 +00:00
|
|
|
{- git-annex remote access with ssh and git-annex-shell
|
2011-03-05 19:47:00 +00:00
|
|
|
-
|
2018-03-08 18:02:18 +00:00
|
|
|
- Copyright 2011-2018 Joey Hess <id@joeyh.name>
|
2011-03-05 19:47:00 +00:00
|
|
|
-
|
2018-03-09 17:48:10 +00:00
|
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
2011-03-05 19:47:00 +00:00
|
|
|
-}
|
|
|
|
|
2012-01-10 19:29:10 +00:00
|
|
|
module Remote.Helper.Ssh where
|
2011-03-05 19:47:00 +00:00
|
|
|
|
2016-01-20 20:36:33 +00:00
|
|
|
import Annex.Common
|
2014-05-16 20:08:20 +00:00
|
|
|
import qualified Annex
|
2011-06-30 17:16:57 +00:00
|
|
|
import qualified Git
|
2011-12-14 19:30:14 +00:00
|
|
|
import qualified Git.Url
|
2011-10-15 21:47:03 +00:00
|
|
|
import Annex.UUID
|
2012-01-20 19:34:52 +00:00
|
|
|
import Annex.Ssh
|
2014-01-26 20:32:55 +00:00
|
|
|
import CmdLine.GitAnnexShell.Fields (Field, fieldName)
|
|
|
|
import qualified CmdLine.GitAnnexShell.Fields as Fields
|
2013-09-24 17:37:41 +00:00
|
|
|
import Remote.Helper.Messages
|
|
|
|
import Utility.Metered
|
|
|
|
import Utility.Rsync
|
2017-08-18 02:11:31 +00:00
|
|
|
import Utility.SshHost
|
2013-09-24 17:37:41 +00:00
|
|
|
import Types.Remote
|
2016-08-03 16:37:12 +00:00
|
|
|
import Types.Transfer
|
2014-04-17 18:31:42 +00:00
|
|
|
import Config
|
2018-03-08 18:02:18 +00:00
|
|
|
import qualified P2P.Protocol as P2P
|
|
|
|
import qualified P2P.IO as P2P
|
2018-03-09 17:48:10 +00:00
|
|
|
import qualified P2P.Annex as P2P
|
2018-03-08 18:02:18 +00:00
|
|
|
|
|
|
|
import Control.Concurrent.STM
|
display p2pstdio stderr after auth
Display error messages that come from git-annex-shell when the p2p protocol
is used, so that diskreserve messages, IO errors, etc from the remote side
are visible again.
Felt like it should perhaps use outputError, so --json-error-messages would
include these, but as an async IO action, it can't, and this would need
MessageState to be converted to a tvar. Anyway, when not using p2pstdio,
that's not done; nor is it done for stderr from external special remotes
or other commands, so punted on the idea for now.
This commit was sponsored by mo on Patreon.
2018-06-12 18:57:45 +00:00
|
|
|
import Control.Concurrent.Async
|
2011-03-05 19:47:00 +00:00
|
|
|
|
2017-03-17 20:02:47 +00:00
|
|
|
toRepo :: ConsumeStdin -> Git.Repo -> RemoteGitConfig -> SshCommand -> Annex (FilePath, [CommandParam])
|
|
|
|
toRepo cs r gc remotecmd = do
|
2017-08-18 02:11:31 +00:00
|
|
|
let host = maybe
|
|
|
|
(giveup "bad ssh url")
|
|
|
|
(either error id . mkSshHost)
|
|
|
|
(Git.Url.hostuser r)
|
2017-03-17 20:02:47 +00:00
|
|
|
sshCommand cs (host, Git.Url.port r) gc remotecmd
|
2011-04-09 18:26:32 +00:00
|
|
|
|
|
|
|
{- Generates parameters to run a git-annex-shell command on a remote
|
|
|
|
- repository. -}
|
2017-02-15 19:08:46 +00:00
|
|
|
git_annex_shell :: ConsumeStdin -> Git.Repo -> String -> [CommandParam] -> [(Field, String)] -> Annex (Maybe (FilePath, [CommandParam]))
|
|
|
|
git_annex_shell cs r command params fields
|
2015-08-13 19:05:39 +00:00
|
|
|
| not $ Git.repoIsUrl r = do
|
|
|
|
shellopts <- getshellopts
|
|
|
|
return $ Just (shellcmd, shellopts ++ fieldopts)
|
2011-04-09 18:26:32 +00:00
|
|
|
| Git.repoIsSsh r = do
|
2014-05-16 20:08:20 +00:00
|
|
|
gc <- Annex.getRemoteGitConfig r
|
2013-09-24 17:37:41 +00:00
|
|
|
u <- getRepoUUID r
|
2015-08-13 19:05:39 +00:00
|
|
|
shellopts <- getshellopts
|
|
|
|
let sshcmd = unwords $
|
|
|
|
fromMaybe shellcmd (remoteAnnexShell gc)
|
|
|
|
: map shellEscape (toCommand shellopts) ++
|
|
|
|
uuidcheck u ++
|
|
|
|
map shellEscape (toCommand fieldopts)
|
2017-03-17 20:02:47 +00:00
|
|
|
Just <$> toRepo cs r gc sshcmd
|
2011-04-09 18:26:32 +00:00
|
|
|
| otherwise = return Nothing
|
2012-11-11 04:51:07 +00:00
|
|
|
where
|
|
|
|
dir = Git.repoPath r
|
|
|
|
shellcmd = "git-annex-shell"
|
2015-08-13 19:05:39 +00:00
|
|
|
getshellopts = do
|
|
|
|
debug <- liftIO debugEnabled
|
|
|
|
let params' = if debug
|
|
|
|
then Param "--debug" : params
|
|
|
|
else params
|
2019-12-09 17:49:05 +00:00
|
|
|
return (Param command : File (fromRawFilePath dir) : params')
|
2012-11-11 04:51:07 +00:00
|
|
|
uuidcheck NoUUID = []
|
2019-01-01 17:49:19 +00:00
|
|
|
uuidcheck u@(UUID _) = ["--uuid", fromUUID u]
|
2012-11-11 04:51:07 +00:00
|
|
|
fieldopts
|
|
|
|
| null fields = []
|
|
|
|
| otherwise = fieldsep : map fieldopt fields ++ [fieldsep]
|
|
|
|
fieldsep = Param "--"
|
|
|
|
fieldopt (field, value) = Param $
|
|
|
|
fieldName field ++ "=" ++ value
|
2011-04-09 18:26:32 +00:00
|
|
|
|
|
|
|
{- Uses a supplied function (such as boolSystem) to run a git-annex-shell
|
|
|
|
- command on a remote.
|
|
|
|
-
|
|
|
|
- Or, if the remote does not support running remote commands, returns
|
|
|
|
- a specified error value. -}
|
|
|
|
onRemote
|
2017-02-15 19:08:46 +00:00
|
|
|
:: ConsumeStdin
|
|
|
|
-> Git.Repo
|
2019-11-12 14:07:27 +00:00
|
|
|
-> (FilePath -> [CommandParam] -> Annex a, Annex a)
|
2011-04-09 18:26:32 +00:00
|
|
|
-> String
|
|
|
|
-> [CommandParam]
|
2012-07-02 14:57:51 +00:00
|
|
|
-> [(Field, String)]
|
2011-04-09 18:26:32 +00:00
|
|
|
-> Annex a
|
2017-02-15 19:08:46 +00:00
|
|
|
onRemote cs r (with, errorval) command params fields = do
|
|
|
|
s <- git_annex_shell cs r command params fields
|
2011-04-09 18:26:32 +00:00
|
|
|
case s of
|
2019-11-12 14:07:27 +00:00
|
|
|
Just (c, ps) -> with c ps
|
2014-08-10 18:52:58 +00:00
|
|
|
Nothing -> errorval
|
2013-09-24 17:37:41 +00:00
|
|
|
|
|
|
|
{- Checks if a remote contains a key. -}
|
2014-08-06 17:45:19 +00:00
|
|
|
inAnnex :: Git.Repo -> Key -> Annex Bool
|
2013-09-24 17:37:41 +00:00
|
|
|
inAnnex r k = do
|
|
|
|
showChecking r
|
2019-01-14 17:03:35 +00:00
|
|
|
onRemote NoConsumeStdin r (runcheck, cantCheck r) "inannex" [Param $ serializeKey k] []
|
2013-09-24 17:37:41 +00:00
|
|
|
where
|
2019-11-12 14:07:27 +00:00
|
|
|
runcheck c p = liftIO $ dispatch =<< safeSystem c p
|
2014-08-10 18:52:58 +00:00
|
|
|
dispatch ExitSuccess = return True
|
|
|
|
dispatch (ExitFailure 1) = return False
|
2013-09-24 17:37:41 +00:00
|
|
|
dispatch _ = cantCheck r
|
|
|
|
|
|
|
|
{- Removes a key from a remote. -}
|
2020-05-14 18:08:09 +00:00
|
|
|
dropKey :: Git.Repo -> Key -> Annex ()
|
|
|
|
dropKey r key = unlessM (dropKey' r key) $
|
|
|
|
giveup "unable to remove key from remote"
|
|
|
|
|
|
|
|
dropKey' :: Git.Repo -> Key -> Annex Bool
|
|
|
|
dropKey' r key = onRemote NoConsumeStdin r (\f p -> liftIO (boolSystem f p), return False) "dropkey"
|
2015-06-01 17:52:23 +00:00
|
|
|
[ Param "--quiet", Param "--force"
|
2019-01-14 17:03:35 +00:00
|
|
|
, Param $ serializeKey key
|
2013-09-24 17:37:41 +00:00
|
|
|
]
|
|
|
|
[]
|
|
|
|
|
2018-03-12 22:36:07 +00:00
|
|
|
rsyncHelper :: OutputHandler -> Maybe MeterUpdate -> [CommandParam] -> Annex Bool
|
|
|
|
rsyncHelper oh m params = do
|
2018-03-12 23:10:22 +00:00
|
|
|
unless (quietMode oh) $
|
|
|
|
showOutput -- make way for progress bar
|
2015-04-03 20:48:30 +00:00
|
|
|
a <- case m of
|
2018-03-12 22:36:07 +00:00
|
|
|
Nothing -> return $ rsync params
|
|
|
|
Just meter -> return $ rsyncProgress oh meter params
|
2015-04-03 20:48:30 +00:00
|
|
|
ifM (liftIO a)
|
2013-09-24 17:37:41 +00:00
|
|
|
( return True
|
|
|
|
, do
|
|
|
|
showLongNote "rsync failed -- run git annex again to resume file transfer"
|
|
|
|
return False
|
|
|
|
)
|
|
|
|
|
|
|
|
{- Generates rsync parameters that ssh to the remote and asks it
|
|
|
|
- to either receive or send the key's content. -}
|
2013-10-01 18:10:45 +00:00
|
|
|
rsyncParamsRemote :: Bool -> Remote -> Direction -> Key -> FilePath -> AssociatedFile -> Annex [CommandParam]
|
2017-03-10 17:12:24 +00:00
|
|
|
rsyncParamsRemote unlocked r direction key file (AssociatedFile afile) = do
|
2013-09-24 17:37:41 +00:00
|
|
|
u <- getUUID
|
|
|
|
let fields = (Fields.remoteUUID, fromUUID u)
|
2015-12-26 17:59:27 +00:00
|
|
|
: (Fields.unlocked, if unlocked then "1" else "")
|
|
|
|
-- Send direct field for unlocked content, for backwards
|
|
|
|
-- compatability.
|
|
|
|
: (Fields.direct, if unlocked then "1" else "")
|
2019-11-26 19:27:22 +00:00
|
|
|
: maybe [] (\f -> [(Fields.associatedFile, fromRawFilePath f)]) afile
|
2018-06-04 18:31:55 +00:00
|
|
|
repo <- getRepo r
|
|
|
|
Just (shellcmd, shellparams) <- git_annex_shell ConsumeStdin repo
|
2013-09-24 17:37:41 +00:00
|
|
|
(if direction == Download then "sendkey" else "recvkey")
|
2019-01-14 17:03:35 +00:00
|
|
|
[ Param $ serializeKey key ]
|
2013-09-24 17:37:41 +00:00
|
|
|
fields
|
|
|
|
-- Convert the ssh command into rsync command line.
|
|
|
|
let eparam = rsyncShell (Param shellcmd:shellparams)
|
2014-04-17 18:31:42 +00:00
|
|
|
o <- rsyncParams r direction
|
2013-09-26 03:19:01 +00:00
|
|
|
return $ if direction == Download
|
|
|
|
then o ++ rsyncopts eparam dummy (File file)
|
|
|
|
else o ++ rsyncopts eparam (File file) dummy
|
2013-09-24 17:37:41 +00:00
|
|
|
where
|
|
|
|
rsyncopts ps source dest
|
|
|
|
| end ps == [dashdash] = ps ++ [source, dest]
|
|
|
|
| otherwise = ps ++ [dashdash, source, dest]
|
|
|
|
dashdash = Param "--"
|
|
|
|
{- The rsync shell parameter controls where rsync
|
|
|
|
- goes, so the source/dest parameter can be a dummy value,
|
|
|
|
- that just enables remote rsync mode.
|
|
|
|
- For maximum compatability with some patched rsyncs,
|
|
|
|
- the dummy value needs to still contain a hostname,
|
|
|
|
- even though this hostname will never be used. -}
|
|
|
|
dummy = Param "dummy:"
|
|
|
|
|
|
|
|
-- --inplace to resume partial files
|
2014-04-17 18:31:42 +00:00
|
|
|
--
|
|
|
|
-- Only use --perms when not on a crippled file system, as rsync
|
|
|
|
-- will fail trying to restore file perms onto a filesystem that does not
|
|
|
|
-- support them.
|
|
|
|
rsyncParams :: Remote -> Direction -> Annex [CommandParam]
|
|
|
|
rsyncParams r direction = do
|
|
|
|
crippled <- crippledFileSystem
|
|
|
|
return $ map Param $ catMaybes
|
|
|
|
[ Just "--progress"
|
|
|
|
, Just "--inplace"
|
|
|
|
, if crippled then Nothing else Just "--perms"
|
|
|
|
]
|
|
|
|
++ remoteAnnexRsyncOptions gc ++ dps
|
2014-02-02 20:06:34 +00:00
|
|
|
where
|
|
|
|
dps
|
|
|
|
| direction == Download = remoteAnnexRsyncDownloadOptions gc
|
|
|
|
| otherwise = remoteAnnexRsyncUploadOptions gc
|
|
|
|
gc = gitconfig r
|
2015-10-09 20:55:41 +00:00
|
|
|
|
|
|
|
-- Used by git-annex-shell lockcontent to indicate the content is
|
|
|
|
-- successfully locked.
|
|
|
|
contentLockedMarker :: String
|
|
|
|
contentLockedMarker = "OK"
|
2018-03-08 18:02:18 +00:00
|
|
|
|
|
|
|
-- A connection over ssh to git-annex shell speaking the P2P protocol.
|
2018-03-12 17:43:19 +00:00
|
|
|
type P2PSshConnection = P2P.ClosableConnection
|
display p2pstdio stderr after auth
Display error messages that come from git-annex-shell when the p2p protocol
is used, so that diskreserve messages, IO errors, etc from the remote side
are visible again.
Felt like it should perhaps use outputError, so --json-error-messages would
include these, but as an async IO action, it can't, and this would need
MessageState to be converted to a tvar. Anyway, when not using p2pstdio,
that's not done; nor is it done for stderr from external special remotes
or other commands, so punted on the idea for now.
This commit was sponsored by mo on Patreon.
2018-06-12 18:57:45 +00:00
|
|
|
(P2P.RunState, P2P.P2PConnection, ProcessHandle, TVar StderrHandlerState)
|
|
|
|
|
|
|
|
data StderrHandlerState = DiscardStderr | DisplayStderr | EndStderrHandler
|
2018-03-08 18:02:18 +00:00
|
|
|
|
2018-03-12 20:50:21 +00:00
|
|
|
closeP2PSshConnection :: P2PSshConnection -> IO (P2PSshConnection, Maybe ExitCode)
|
|
|
|
closeP2PSshConnection P2P.ClosedConnection = return (P2P.ClosedConnection, Nothing)
|
async exception safety
Masking ensures that EndStderrHandler gets written, so the helper
threads shut down.
However, nothing currently guarantees that calls to closeP2PSshConnection
are async exception safe, so made a note about it.
At this point, I've audited all calls to async, and made them all async
exception safe, except for ones in the assistant, and a few in leaf
commands (remotedaemon, enable-tor, multicast, p2p) which don't need to
be.
2020-06-05 18:56:41 +00:00
|
|
|
closeP2PSshConnection (P2P.OpenConnection (_st, conn, pid, stderrhandlerst)) =
|
|
|
|
-- mask async exceptions, avoid cleanup being interrupted
|
2020-06-09 17:48:48 +00:00
|
|
|
uninterruptibleMask_ $ do
|
async exception safety
Masking ensures that EndStderrHandler gets written, so the helper
threads shut down.
However, nothing currently guarantees that calls to closeP2PSshConnection
are async exception safe, so made a note about it.
At this point, I've audited all calls to async, and made them all async
exception safe, except for ones in the assistant, and a few in leaf
commands (remotedaemon, enable-tor, multicast, p2p) which don't need to
be.
2020-06-05 18:56:41 +00:00
|
|
|
P2P.closeConnection conn
|
|
|
|
atomically $ writeTVar stderrhandlerst EndStderrHandler
|
|
|
|
exitcode <- waitForProcess pid
|
|
|
|
return (P2P.ClosedConnection, Just exitcode)
|
2018-03-08 18:02:18 +00:00
|
|
|
|
|
|
|
-- Pool of connections over ssh to git-annex-shell p2pstdio.
|
|
|
|
type P2PSshConnectionPool = TVar (Maybe P2PSshConnectionPoolState)
|
|
|
|
|
|
|
|
data P2PSshConnectionPoolState
|
|
|
|
= P2PSshConnections [P2PSshConnection]
|
|
|
|
-- Remotes using an old version of git-annex-shell don't support P2P
|
|
|
|
| P2PSshUnsupported
|
|
|
|
|
|
|
|
mkP2PSshConnectionPool :: Annex P2PSshConnectionPool
|
|
|
|
mkP2PSshConnectionPool = liftIO $ newTVarIO Nothing
|
|
|
|
|
|
|
|
-- Takes a connection from the pool, if any are available, otherwise
|
|
|
|
-- tries to open a new one.
|
|
|
|
getP2PSshConnection :: Remote -> P2PSshConnectionPool -> Annex (Maybe P2PSshConnection)
|
|
|
|
getP2PSshConnection r connpool = getexistingconn >>= \case
|
|
|
|
Nothing -> return Nothing
|
|
|
|
Just Nothing -> openP2PSshConnection r connpool
|
|
|
|
Just (Just c) -> return (Just c)
|
|
|
|
where
|
|
|
|
getexistingconn = liftIO $ atomically $ readTVar connpool >>= \case
|
|
|
|
Just P2PSshUnsupported -> return Nothing
|
|
|
|
Just (P2PSshConnections (c:cs)) -> do
|
|
|
|
writeTVar connpool (Just (P2PSshConnections cs))
|
|
|
|
return (Just (Just c))
|
|
|
|
Just (P2PSshConnections []) -> return (Just Nothing)
|
|
|
|
Nothing -> return (Just Nothing)
|
|
|
|
|
|
|
|
-- Add a connection to the pool, unless it's closed.
|
|
|
|
storeP2PSshConnection :: P2PSshConnectionPool -> P2PSshConnection -> IO ()
|
|
|
|
storeP2PSshConnection _ P2P.ClosedConnection = return ()
|
|
|
|
storeP2PSshConnection connpool conn = atomically $ modifyTVar' connpool $ \case
|
|
|
|
Just (P2PSshConnections cs) -> Just (P2PSshConnections (conn:cs))
|
|
|
|
_ -> Just (P2PSshConnections [conn])
|
|
|
|
|
|
|
|
-- Try to open a P2PSshConnection.
|
|
|
|
-- The new connection is not added to the pool, so it's available
|
|
|
|
-- for the caller to use.
|
|
|
|
-- If the remote does not support the P2P protocol, that's remembered in
|
|
|
|
-- the connection pool.
|
|
|
|
openP2PSshConnection :: Remote -> P2PSshConnectionPool -> Annex (Maybe P2PSshConnection)
|
2018-03-08 20:21:16 +00:00
|
|
|
openP2PSshConnection r connpool = do
|
|
|
|
u <- getUUID
|
|
|
|
let ps = [Param (fromUUID u)]
|
2018-06-04 18:31:55 +00:00
|
|
|
repo <- getRepo r
|
|
|
|
git_annex_shell ConsumeStdin repo "p2pstdio" ps [] >>= \case
|
2018-03-08 18:02:18 +00:00
|
|
|
Nothing -> do
|
|
|
|
liftIO $ rememberunsupported
|
|
|
|
return Nothing
|
2018-06-04 18:31:55 +00:00
|
|
|
Just (cmd, params) -> start cmd params =<< getRepo r
|
2018-03-08 18:02:18 +00:00
|
|
|
where
|
display p2pstdio stderr after auth
Display error messages that come from git-annex-shell when the p2p protocol
is used, so that diskreserve messages, IO errors, etc from the remote side
are visible again.
Felt like it should perhaps use outputError, so --json-error-messages would
include these, but as an async IO action, it can't, and this would need
MessageState to be converted to a tvar. Anyway, when not using p2pstdio,
that's not done; nor is it done for stderr from external special remotes
or other commands, so punted on the idea for now.
This commit was sponsored by mo on Patreon.
2018-06-12 18:57:45 +00:00
|
|
|
start cmd params repo = liftIO $ do
|
|
|
|
(Just from, Just to, Just err, pid) <- createProcess $
|
2018-03-12 19:19:40 +00:00
|
|
|
(proc cmd (toCommand params))
|
|
|
|
{ std_in = CreatePipe
|
|
|
|
, std_out = CreatePipe
|
display p2pstdio stderr after auth
Display error messages that come from git-annex-shell when the p2p protocol
is used, so that diskreserve messages, IO errors, etc from the remote side
are visible again.
Felt like it should perhaps use outputError, so --json-error-messages would
include these, but as an async IO action, it can't, and this would need
MessageState to be converted to a tvar. Anyway, when not using p2pstdio,
that's not done; nor is it done for stderr from external special remotes
or other commands, so punted on the idea for now.
This commit was sponsored by mo on Patreon.
2018-06-12 18:57:45 +00:00
|
|
|
, std_err = CreatePipe
|
2018-03-12 19:19:40 +00:00
|
|
|
}
|
2020-06-03 20:03:08 +00:00
|
|
|
pidnum <- getPid pid
|
2018-03-08 18:02:18 +00:00
|
|
|
let conn = P2P.P2PConnection
|
2018-06-04 18:31:55 +00:00
|
|
|
{ P2P.connRepo = repo
|
2018-03-08 18:02:18 +00:00
|
|
|
, P2P.connCheckAuth = const False
|
2018-03-08 20:21:16 +00:00
|
|
|
, P2P.connIhdl = to
|
|
|
|
, P2P.connOhdl = from
|
2018-10-22 19:52:11 +00:00
|
|
|
, P2P.connIdent = P2P.ConnIdent $
|
2020-06-03 20:03:08 +00:00
|
|
|
Just $ "ssh connection " ++ show pidnum
|
2018-03-08 18:02:18 +00:00
|
|
|
}
|
2020-11-19 20:13:31 +00:00
|
|
|
stderrhandlerst <- newStderrHandler err pid
|
2018-03-12 19:19:40 +00:00
|
|
|
runst <- P2P.mkRunState P2P.Client
|
display p2pstdio stderr after auth
Display error messages that come from git-annex-shell when the p2p protocol
is used, so that diskreserve messages, IO errors, etc from the remote side
are visible again.
Felt like it should perhaps use outputError, so --json-error-messages would
include these, but as an async IO action, it can't, and this would need
MessageState to be converted to a tvar. Anyway, when not using p2pstdio,
that's not done; nor is it done for stderr from external special remotes
or other commands, so punted on the idea for now.
This commit was sponsored by mo on Patreon.
2018-06-12 18:57:45 +00:00
|
|
|
let c = P2P.OpenConnection (runst, conn, pid, stderrhandlerst)
|
2018-03-08 20:21:16 +00:00
|
|
|
-- When the connection is successful, the remote
|
2018-03-08 18:02:18 +00:00
|
|
|
-- will send an AUTH_SUCCESS with its uuid.
|
2018-03-12 17:43:19 +00:00
|
|
|
let proto = P2P.postAuth $
|
|
|
|
P2P.negotiateProtocolVersion P2P.maxProtocolVersion
|
2018-03-12 19:19:40 +00:00
|
|
|
tryNonAsync (P2P.runNetProto runst conn proto) >>= \case
|
display p2pstdio stderr after auth
Display error messages that come from git-annex-shell when the p2p protocol
is used, so that diskreserve messages, IO errors, etc from the remote side
are visible again.
Felt like it should perhaps use outputError, so --json-error-messages would
include these, but as an async IO action, it can't, and this would need
MessageState to be converted to a tvar. Anyway, when not using p2pstdio,
that's not done; nor is it done for stderr from external special remotes
or other commands, so punted on the idea for now.
This commit was sponsored by mo on Patreon.
2018-06-12 18:57:45 +00:00
|
|
|
Right (Right (Just theiruuid)) | theiruuid == uuid r -> do
|
|
|
|
atomically $
|
|
|
|
writeTVar stderrhandlerst DisplayStderr
|
2018-03-08 18:02:18 +00:00
|
|
|
return $ Just c
|
|
|
|
_ -> do
|
2018-03-12 20:50:21 +00:00
|
|
|
(cclosed, exitcode) <- closeP2PSshConnection c
|
|
|
|
-- ssh exits 255 when unable to connect to
|
|
|
|
-- server. Return a closed connection in
|
|
|
|
-- this case, to avoid the fallback action
|
|
|
|
-- being run instead, which would mean a
|
|
|
|
-- second connection attempt to this server
|
|
|
|
-- that is down.
|
|
|
|
if exitcode == Just (ExitFailure 255)
|
|
|
|
then return (Just cclosed)
|
|
|
|
else do
|
|
|
|
rememberunsupported
|
|
|
|
return Nothing
|
2018-03-08 18:02:18 +00:00
|
|
|
rememberunsupported = atomically $
|
|
|
|
modifyTVar' connpool $
|
|
|
|
maybe (Just P2PSshUnsupported) Just
|
2018-03-09 17:48:10 +00:00
|
|
|
|
2020-11-19 20:13:31 +00:00
|
|
|
newStderrHandler :: Handle -> ProcessHandle -> IO (TVar StderrHandlerState)
|
|
|
|
newStderrHandler errh ph = do
|
display p2pstdio stderr after auth
Display error messages that come from git-annex-shell when the p2p protocol
is used, so that diskreserve messages, IO errors, etc from the remote side
are visible again.
Felt like it should perhaps use outputError, so --json-error-messages would
include these, but as an async IO action, it can't, and this would need
MessageState to be converted to a tvar. Anyway, when not using p2pstdio,
that's not done; nor is it done for stderr from external special remotes
or other commands, so punted on the idea for now.
This commit was sponsored by mo on Patreon.
2018-06-12 18:57:45 +00:00
|
|
|
-- stderr from git-annex-shell p2pstdio is initially discarded
|
|
|
|
-- because old versions don't support the command. Once it's known
|
|
|
|
-- to be running, this is changed to DisplayStderr.
|
|
|
|
v <- newTVarIO DiscardStderr
|
2020-11-19 20:13:31 +00:00
|
|
|
void $ async $ go v
|
display p2pstdio stderr after auth
Display error messages that come from git-annex-shell when the p2p protocol
is used, so that diskreserve messages, IO errors, etc from the remote side
are visible again.
Felt like it should perhaps use outputError, so --json-error-messages would
include these, but as an async IO action, it can't, and this would need
MessageState to be converted to a tvar. Anyway, when not using p2pstdio,
that's not done; nor is it done for stderr from external special remotes
or other commands, so punted on the idea for now.
This commit was sponsored by mo on Patreon.
2018-06-12 18:57:45 +00:00
|
|
|
return v
|
|
|
|
where
|
|
|
|
go v = do
|
2020-11-19 20:13:31 +00:00
|
|
|
hGetLineUntilExitOrEOF ph errh >>= \case
|
|
|
|
Nothing -> hClose errh
|
|
|
|
Just l -> atomically (readTVar v) >>= \case
|
|
|
|
DiscardStderr -> go v
|
|
|
|
DisplayStderr -> do
|
|
|
|
hPutStrLn stderr l
|
|
|
|
go v
|
|
|
|
EndStderrHandler -> hClose errh
|
display p2pstdio stderr after auth
Display error messages that come from git-annex-shell when the p2p protocol
is used, so that diskreserve messages, IO errors, etc from the remote side
are visible again.
Felt like it should perhaps use outputError, so --json-error-messages would
include these, but as an async IO action, it can't, and this would need
MessageState to be converted to a tvar. Anyway, when not using p2pstdio,
that's not done; nor is it done for stderr from external special remotes
or other commands, so punted on the idea for now.
This commit was sponsored by mo on Patreon.
2018-06-12 18:57:45 +00:00
|
|
|
|
2018-03-09 17:48:10 +00:00
|
|
|
-- Runs a P2P Proto action on a remote when it supports that,
|
|
|
|
-- otherwise the fallback action.
|
2018-07-03 17:09:04 +00:00
|
|
|
runProto :: Remote -> P2PSshConnectionPool -> Annex a -> Annex a -> P2P.Proto a -> Annex (Maybe a)
|
|
|
|
runProto r connpool badproto fallback proto = Just <$>
|
2018-03-09 17:48:10 +00:00
|
|
|
(getP2PSshConnection r connpool >>= maybe fallback go)
|
|
|
|
where
|
|
|
|
go c = do
|
|
|
|
(c', v) <- runProtoConn proto c
|
|
|
|
case v of
|
|
|
|
Just res -> do
|
|
|
|
liftIO $ storeP2PSshConnection connpool c'
|
|
|
|
return res
|
|
|
|
-- Running the proto failed, either due to a protocol
|
2018-03-12 20:50:21 +00:00
|
|
|
-- error or a network error.
|
2018-07-03 17:09:04 +00:00
|
|
|
Nothing -> badproto
|
2018-03-09 17:48:10 +00:00
|
|
|
|
|
|
|
runProtoConn :: P2P.Proto a -> P2PSshConnection -> Annex (P2PSshConnection, Maybe a)
|
|
|
|
runProtoConn _ P2P.ClosedConnection = return (P2P.ClosedConnection, Nothing)
|
display p2pstdio stderr after auth
Display error messages that come from git-annex-shell when the p2p protocol
is used, so that diskreserve messages, IO errors, etc from the remote side
are visible again.
Felt like it should perhaps use outputError, so --json-error-messages would
include these, but as an async IO action, it can't, and this would need
MessageState to be converted to a tvar. Anyway, when not using p2pstdio,
that's not done; nor is it done for stderr from external special remotes
or other commands, so punted on the idea for now.
This commit was sponsored by mo on Patreon.
2018-06-12 18:57:45 +00:00
|
|
|
runProtoConn a conn@(P2P.OpenConnection (runst, c, _, _)) = do
|
2018-03-12 17:43:19 +00:00
|
|
|
P2P.runFullProto runst c a >>= \case
|
2018-03-09 17:48:10 +00:00
|
|
|
Right r -> return (conn, Just r)
|
|
|
|
-- When runFullProto fails, the connection is no longer
|
|
|
|
-- usable, so close it.
|
|
|
|
Left e -> do
|
2018-09-25 20:49:59 +00:00
|
|
|
warning $ "Lost connection (" ++ P2P.describeProtoFailure e ++ ")"
|
2018-03-12 20:50:21 +00:00
|
|
|
conn' <- fst <$> liftIO (closeP2PSshConnection conn)
|
2018-03-09 17:48:10 +00:00
|
|
|
return (conn', Nothing)
|
|
|
|
|
2018-03-12 17:43:19 +00:00
|
|
|
-- Allocates a P2P ssh connection from the pool, and runs the action with it,
|
|
|
|
-- returning the connection to the pool once the action is done.
|
2018-03-09 17:48:10 +00:00
|
|
|
--
|
|
|
|
-- If the remote does not support the P2P protocol, runs the fallback
|
|
|
|
-- action instead.
|
|
|
|
withP2PSshConnection
|
|
|
|
:: Remote
|
|
|
|
-> P2PSshConnectionPool
|
|
|
|
-> Annex a
|
|
|
|
-> (P2PSshConnection -> Annex (P2PSshConnection, a))
|
|
|
|
-> Annex a
|
|
|
|
withP2PSshConnection r connpool fallback a = bracketOnError get cache go
|
|
|
|
where
|
|
|
|
get = getP2PSshConnection r connpool
|
|
|
|
cache (Just conn) = liftIO $ storeP2PSshConnection connpool conn
|
|
|
|
cache Nothing = return ()
|
|
|
|
go (Just conn) = do
|
|
|
|
(conn', res) <- a conn
|
|
|
|
cache (Just conn')
|
|
|
|
return res
|
|
|
|
go Nothing = fallback
|