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
|
|
|
-
|
2015-01-21 16:50:09 +00:00
|
|
|
- Copyright 2011-2013 Joey Hess <id@joeyh.name>
|
2011-03-05 19:47:00 +00:00
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
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
|
2015-04-03 20:48:30 +00:00
|
|
|
import Messages.Progress
|
2013-09-24 17:37:41 +00:00
|
|
|
import Utility.Metered
|
|
|
|
import Utility.Rsync
|
|
|
|
import Types.Remote
|
2016-08-03 16:37:12 +00:00
|
|
|
import Types.Transfer
|
2014-04-17 18:31:42 +00:00
|
|
|
import Config
|
2011-03-05 19:47:00 +00:00
|
|
|
|
|
|
|
{- Generates parameters to ssh to a repository's host and run a command.
|
|
|
|
- Caller is responsible for doing any neccessary shellEscaping of the
|
|
|
|
- passed command. -}
|
2014-05-16 11:34:43 +00:00
|
|
|
toRepo :: Git.Repo -> RemoteGitConfig -> [CommandParam] -> Annex [CommandParam]
|
2014-05-16 20:08:20 +00:00
|
|
|
toRepo r gc sshcmd = do
|
|
|
|
let opts = map Param $ remoteAnnexSshOptions gc
|
2013-11-04 18:14:44 +00:00
|
|
|
let host = fromMaybe (error "bad ssh url") $ Git.Url.hostuser r
|
2015-02-12 19:44:10 +00:00
|
|
|
params <- sshOptions (host, Git.Url.port r) gc opts
|
2013-04-13 22:10:49 +00:00
|
|
|
return $ params ++ Param host : sshcmd
|
2011-04-09 18:26:32 +00:00
|
|
|
|
|
|
|
{- Generates parameters to run a git-annex-shell command on a remote
|
|
|
|
- repository. -}
|
2012-07-02 14:57:51 +00:00
|
|
|
git_annex_shell :: Git.Repo -> String -> [CommandParam] -> [(Field, String)] -> Annex (Maybe (FilePath, [CommandParam]))
|
|
|
|
git_annex_shell 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)
|
|
|
|
sshparams <- toRepo r gc [Param sshcmd]
|
2011-04-09 18:26:32 +00:00
|
|
|
return $ Just ("ssh", sshparams)
|
|
|
|
| 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
|
|
|
|
return (Param command : File dir : params')
|
2012-11-11 04:51:07 +00:00
|
|
|
uuidcheck NoUUID = []
|
|
|
|
uuidcheck (UUID u) = ["--uuid", u]
|
|
|
|
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
|
|
|
|
:: Git.Repo
|
2014-08-10 18:52:58 +00:00
|
|
|
-> (FilePath -> [CommandParam] -> IO 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
|
2012-07-02 14:57:51 +00:00
|
|
|
onRemote r (with, errorval) command params fields = do
|
|
|
|
s <- git_annex_shell r command params fields
|
2011-04-09 18:26:32 +00:00
|
|
|
case s of
|
|
|
|
Just (c, ps) -> liftIO $ 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
|
|
|
|
onRemote r (check, cantCheck r) "inannex" [Param $ key2file k] []
|
|
|
|
where
|
2014-08-10 18:52:58 +00:00
|
|
|
check c p = dispatch =<< safeSystem c p
|
|
|
|
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. -}
|
|
|
|
dropKey :: Git.Repo -> Key -> Annex Bool
|
2014-08-10 18:52:58 +00:00
|
|
|
dropKey r key = onRemote r (boolSystem, return False) "dropkey"
|
2015-06-01 17:52:23 +00:00
|
|
|
[ Param "--quiet", Param "--force"
|
2013-09-24 17:37:41 +00:00
|
|
|
, Param $ key2file key
|
|
|
|
]
|
|
|
|
[]
|
|
|
|
|
|
|
|
rsyncHelper :: Maybe MeterUpdate -> [CommandParam] -> Annex Bool
|
2015-04-03 20:48:30 +00:00
|
|
|
rsyncHelper m params = do
|
2013-09-24 17:37:41 +00:00
|
|
|
showOutput -- make way for progress bar
|
2015-04-03 20:48:30 +00:00
|
|
|
a <- case m of
|
|
|
|
Nothing -> return $ rsync params
|
|
|
|
Just meter -> do
|
2015-04-04 18:34:03 +00:00
|
|
|
oh <- mkOutputHandler
|
|
|
|
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]
|
2015-12-26 17:59:27 +00:00
|
|
|
rsyncParamsRemote unlocked r direction key file 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 "")
|
2013-09-24 17:37:41 +00:00
|
|
|
: maybe [] (\f -> [(Fields.associatedFile, f)]) afile
|
|
|
|
Just (shellcmd, shellparams) <- git_annex_shell (repo r)
|
|
|
|
(if direction == Download then "sendkey" else "recvkey")
|
|
|
|
[ Param $ key2file key ]
|
|
|
|
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"
|