2020-07-29 16:00:27 +00:00
|
|
|
{- External addon processes for special remotes and backends.
|
|
|
|
-
|
|
|
|
- Copyright 2013-2020 Joey Hess <id@joeyh.name>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
module Annex.ExternalAddonProcess where
|
|
|
|
|
|
|
|
import qualified Annex
|
|
|
|
import Annex.Common
|
|
|
|
import Git.Env
|
|
|
|
import Utility.Shell
|
|
|
|
import Messages.Progress
|
|
|
|
|
|
|
|
import Control.Concurrent.Async
|
2020-07-29 19:23:18 +00:00
|
|
|
import System.Log.Logger (debugM)
|
2020-07-29 16:00:27 +00:00
|
|
|
|
|
|
|
data ExternalAddonProcess = ExternalAddonProcess
|
|
|
|
{ externalSend :: Handle
|
|
|
|
, externalReceive :: Handle
|
|
|
|
-- Shut down the process. With True, it's forced to stop
|
|
|
|
-- immediately.
|
|
|
|
, externalShutdown :: Bool -> IO ()
|
|
|
|
, externalPid :: ExternalAddonPID
|
2020-07-29 19:23:18 +00:00
|
|
|
, externalProgram :: String
|
2020-07-29 16:00:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type ExternalAddonPID = Int
|
|
|
|
|
|
|
|
data ExternalAddonStartError
|
|
|
|
= ProgramNotInstalled String
|
|
|
|
| ProgramFailure String
|
|
|
|
|
2020-07-29 19:23:18 +00:00
|
|
|
startExternalAddonProcess :: String -> ExternalAddonPID -> Annex (Either ExternalAddonStartError ExternalAddonProcess)
|
|
|
|
startExternalAddonProcess basecmd pid = do
|
2020-07-29 16:00:27 +00:00
|
|
|
errrelayer <- mkStderrRelayer
|
|
|
|
g <- Annex.gitRepo
|
|
|
|
cmdpath <- liftIO $ searchPath basecmd
|
|
|
|
liftIO $ start errrelayer g cmdpath
|
|
|
|
where
|
|
|
|
start errrelayer g cmdpath = do
|
|
|
|
(cmd, ps) <- maybe (pure (basecmd, [])) findShellCommand cmdpath
|
|
|
|
let basep = (proc cmd (toCommand ps))
|
|
|
|
{ std_in = CreatePipe
|
|
|
|
, std_out = CreatePipe
|
|
|
|
, std_err = CreatePipe
|
|
|
|
}
|
|
|
|
p <- propgit g basep
|
|
|
|
tryNonAsync (createProcess p) >>= \case
|
2020-07-29 19:23:18 +00:00
|
|
|
Right v -> (Right <$> started cmd errrelayer v)
|
2020-07-29 16:00:27 +00:00
|
|
|
`catchNonAsync` const (runerr cmdpath)
|
|
|
|
Left _ -> runerr cmdpath
|
|
|
|
|
2020-07-29 19:23:18 +00:00
|
|
|
started cmd errrelayer pall@(Just hin, Just hout, Just herr, ph) = do
|
2020-07-29 16:00:27 +00:00
|
|
|
stderrelay <- async $ errrelayer herr
|
|
|
|
let shutdown forcestop = do
|
|
|
|
cancel stderrelay
|
|
|
|
if forcestop
|
|
|
|
then cleanupProcess pall
|
|
|
|
else flip onException (cleanupProcess pall) $ do
|
|
|
|
hClose herr
|
|
|
|
hClose hin
|
|
|
|
hClose hout
|
|
|
|
void $ waitForProcess ph
|
|
|
|
return $ ExternalAddonProcess
|
|
|
|
{ externalSend = hin
|
|
|
|
, externalReceive = hout
|
|
|
|
, externalPid = pid
|
|
|
|
, externalShutdown = shutdown
|
2020-07-29 19:23:18 +00:00
|
|
|
, externalProgram = cmd
|
2020-07-29 16:00:27 +00:00
|
|
|
}
|
2020-07-29 19:23:18 +00:00
|
|
|
started _ _ _ = giveup "internal"
|
2020-07-29 16:00:27 +00:00
|
|
|
|
|
|
|
propgit g p = do
|
|
|
|
environ <- propGitEnv g
|
|
|
|
return $ p { env = Just environ }
|
|
|
|
|
|
|
|
runerr (Just cmd) =
|
|
|
|
return $ Left $ ProgramFailure $
|
|
|
|
"Cannot run " ++ cmd ++ " -- Make sure it's executable and that its dependencies are installed."
|
|
|
|
runerr Nothing = do
|
|
|
|
path <- intercalate ":" <$> getSearchPath
|
|
|
|
return $ Left $ ProgramNotInstalled $
|
|
|
|
"Cannot run " ++ basecmd ++ " -- It is not installed in PATH (" ++ path ++ ")"
|
2020-07-29 19:23:18 +00:00
|
|
|
|
|
|
|
protocolDebug :: ExternalAddonProcess -> Bool -> String -> IO ()
|
|
|
|
protocolDebug external sendto line = debugM "external" $ unwords
|
|
|
|
[ externalProgram external ++
|
|
|
|
"[" ++ show (externalPid external) ++ "]"
|
|
|
|
, if sendto then "<--" else "-->"
|
|
|
|
, line
|
|
|
|
]
|