From 4fb9b7cb676fb44d72a97eae858acf716557b04c Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 30 Jul 2025 13:21:29 -0400 Subject: [PATCH] support P2PAnnex in connectPeer This is probably enough to support accessing remotes using p2p-annex:: urls. Not tested yet of course since there is not yet support for serving the other side of such a connection, or for setting up such a connection. P2P.Generic has an implementation of the whole interface to the git-annex-p2p- commands. --- P2P/Generic.hs | 57 +++++++++++++++++++++++++++ P2P/IO.hs | 23 ++++++++--- doc/design/generic_p2p_transport.mdwn | 6 +-- git-annex.cabal | 1 + 4 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 P2P/Generic.hs diff --git a/P2P/Generic.hs b/P2P/Generic.hs new file mode 100644 index 0000000000..9dc118f820 --- /dev/null +++ b/P2P/Generic.hs @@ -0,0 +1,57 @@ +{- P2P protocol, generic transports. + - + - See doc/design/generic_p2p_transport.mdwn + - + - Copyright 2025 Joey Hess + - + - Licensed under the GNU AGPL version 3 or higher. + -} + +module P2P.Generic where + +import Common +import P2P.Address + +genericP2PCommand :: P2PNetName -> String +genericP2PCommand (P2PNetName netname) = "git-annex-p2p-" ++ netname + +connectGenericP2P :: P2PNetName -> UnderlyingP2PAddress -> CreateProcess +connectGenericP2P netname (UnderlyingP2PAddress address) = + (proc (genericP2PCommand netname) [address]) + { std_in = CreatePipe + , std_out = CreatePipe + } + +socketGenericP2P :: P2PNetName -> UnderlyingP2PAddress -> CreateProcess +socketGenericP2P netname (UnderlyingP2PAddress address) = + (proc (genericP2PCommand netname) ["socket", address]) + { std_in = CreatePipe + } + +addressGenericP2P :: P2PNetName -> CreateProcess +addressGenericP2P netname = + (proc (genericP2PCommand netname) ["address"]) + { std_in = CreatePipe + } + +getSocketGenericP2P :: P2PNetName -> UnderlyingP2PAddress -> IO (Maybe (OsPath, ProcessHandle)) +getSocketGenericP2P netname address = do + (Just hin, Nothing, Nothing, pid) <- createProcess $ + socketGenericP2P netname address + hGetLineUntilExitOrEOF pid hin >>= \case + Just l | not (null l) -> return $ Just (toOsPath l, pid) + _ -> return Nothing + +getAddressGenericP2P :: P2PNetName -> IO [P2PAddress] +getAddressGenericP2P netname = do + (Just hin, Nothing, Nothing, pid) <- createProcess $ + addressGenericP2P netname + go [] hin pid + where + go addrs hin pid = hGetLineUntilExitOrEOF pid hin >>= \case + Just l + | not (null l) -> + let addr = P2PAnnex netname (UnderlyingP2PAddress l) + in go (addr:addrs) hin pid + | otherwise -> go addrs hin pid + Nothing -> return addrs diff --git a/P2P/IO.hs b/P2P/IO.hs index d712be7d03..bc3bc61745 100644 --- a/P2P/IO.hs +++ b/P2P/IO.hs @@ -1,6 +1,6 @@ {- P2P protocol, IO implementation - - - Copyright 2016-2024 Joey Hess + - Copyright 2016-2025 Joey Hess - - Licensed under the GNU AGPL version 3 or higher. -} @@ -20,7 +20,6 @@ module P2P.IO , connectPeer , closeConnection , serveUnixSocket - , setupHandle , ProtoFailure(..) , describeProtoFailure , runNetProto @@ -31,6 +30,7 @@ module P2P.IO import Common import P2P.Protocol import P2P.Address +import P2P.Generic import Git import Git.Command import Utility.AuthToken @@ -138,7 +138,7 @@ stdioP2PConnectionDupped g = do -- Opens a connection to a peer. Does not authenticate with it. connectPeer :: Maybe Git.Repo -> P2PAddress -> IO P2PConnection connectPeer g (TorAnnex onionaddress onionport) = do - h <- setupHandle =<< connectHiddenService onionaddress onionport + h <- setupHandleFromSocket =<< connectHiddenService onionaddress onionport return $ P2PConnection { connRepo = g , connCheckAuth = const False @@ -147,6 +147,17 @@ connectPeer g (TorAnnex onionaddress onionport) = do , connProcess = Nothing , connIdent = ConnIdent Nothing } +connectPeer g (P2PAnnex netname address) = do + (Just hin, Just hout, Nothing, pid) <- createProcess $ + connectGenericP2P netname address + return $ P2PConnection + { connRepo = g + , connCheckAuth = const False + , connIhdl = P2PHandle hout + , connOhdl = P2PHandle hin + , connProcess = Just pid + , connIdent = ConnIdent Nothing + } closeConnection :: P2PConnection -> IO () closeConnection conn = do @@ -185,10 +196,10 @@ serveUnixSocket unixsocket serveconn = do S.listen soc 2 forever $ do (conn, _) <- S.accept soc - setupHandle conn >>= serveconn + setupHandleFromSocket conn >>= serveconn -setupHandle :: Socket -> IO Handle -setupHandle s = do +setupHandleFromSocket :: Socket -> IO Handle +setupHandleFromSocket s = do h <- socketToHandle s ReadWriteMode hSetBuffering h LineBuffering hSetBinaryMode h False diff --git a/doc/design/generic_p2p_transport.mdwn b/doc/design/generic_p2p_transport.mdwn index 2e06da195b..094cfc0bc0 100644 --- a/doc/design/generic_p2p_transport.mdwn +++ b/doc/design/generic_p2p_transport.mdwn @@ -33,9 +33,9 @@ to convert the P2P network's own equivilant into a unix socket file. To configure `git-annex remotedaemon` to listen on a given P2P network, the user runs `git-annex p2p --enable `. That also runs `git-annex-p2p-`, this time with the parameter "address". -That should output a single line, the P2P network address that can be used -by peers to connect to the repository. It can first do whatever it needs to -do to set up the P2P network. +That should output one or more lines, the P2P network address (or addresses) +that can be used by peers to connect to the repository. It can first do +whatever it needs to do to set up the P2P network. The program [[git-remote-p2p-annex]] is included in git-annex as a git remote helper program. git will use that program to handle `pull` and diff --git a/git-annex.cabal b/git-annex.cabal index 7170559264..ac9941b902 100644 --- a/git-annex.cabal +++ b/git-annex.cabal @@ -923,6 +923,7 @@ Executable git-annex P2P.Address P2P.Annex P2P.Auth + P2P.Generic P2P.Http.Types P2P.Http.Client P2P.Http.Url