4ac2fd0a22
I now have this topology working: assistant ---> {bare repo, special remote} <--- assistant And, I think, also this one: +----------- bare repo --------+ v v assistant ---> special remote <--- assistant While before with assistant <---> assistant connections, both sides got location info updated after a transfer, in this topology, the bare repo *might* get its location info updated, but the other assistant has no way to know that it did. And a special remote doesn't record location info, so transfers to it won't propigate out location log changes at all. So, for these to work, after a transfer succeeds, the git-annex branch needs to be pushed. This is done by recording a synthetic commit has occurred, which lets the pusher handle pushing out the change (which will include actually committing any still journalled changes to the git-annex branch). Of course, this means rather a lot more syncing action than happened before. At least the pusher bundles together very close together pushes, somewhat. Currently it just waits 2 seconds between each push.
145 lines
4.5 KiB
Haskell
145 lines
4.5 KiB
Haskell
{- git-annex assistant webapp utilities
|
|
-
|
|
- Copyright 2012 Joey Hess <joey@kitenet.net>
|
|
-
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
|
-}
|
|
|
|
module Assistant.WebApp.Utility where
|
|
|
|
import Assistant.Common
|
|
import Assistant.WebApp
|
|
import Assistant.WebApp.Types
|
|
import Assistant.DaemonStatus
|
|
import Assistant.ThreadedMonad
|
|
import Assistant.TransferQueue
|
|
import Assistant.TransferSlots
|
|
import Assistant.Sync
|
|
import qualified Remote
|
|
import qualified Types.Remote as Remote
|
|
import qualified Remote.List as Remote
|
|
import qualified Assistant.Threads.Transferrer as Transferrer
|
|
import Logs.Transfer
|
|
import Locations.UserConfig
|
|
import qualified Config
|
|
|
|
import Yesod
|
|
import qualified Data.Map as M
|
|
import Control.Concurrent
|
|
import System.Posix.Signals (signalProcessGroup, sigTERM, sigKILL)
|
|
import System.Posix.Process (getProcessGroupIDOf)
|
|
|
|
{- Use Nothing to change global sync setting. -}
|
|
changeSyncable :: (Maybe Remote) -> Bool -> Handler ()
|
|
changeSyncable Nothing _ = noop -- TODO
|
|
changeSyncable (Just r) True = do
|
|
changeSyncFlag r True
|
|
syncRemote r
|
|
changeSyncable (Just r) False = do
|
|
changeSyncFlag r False
|
|
webapp <- getYesod
|
|
let dstatus = daemonStatus webapp
|
|
let st = fromJust $ threadState webapp
|
|
liftIO $ runThreadState st $ updateSyncRemotes dstatus
|
|
{- Stop all transfers to or from this remote.
|
|
- XXX Can't stop any ongoing scan, or git syncs. -}
|
|
void $ liftIO $ dequeueTransfers (transferQueue webapp) dstatus tofrom
|
|
mapM_ (cancelTransfer False) =<<
|
|
filter tofrom . M.keys <$>
|
|
liftIO (currentTransfers <$> getDaemonStatus dstatus)
|
|
where
|
|
tofrom t = transferUUID t == Remote.uuid r
|
|
|
|
changeSyncFlag :: Remote -> Bool -> Handler ()
|
|
changeSyncFlag r enabled = runAnnex undefined $ do
|
|
Config.setConfig key value
|
|
void $ Remote.remoteListRefresh
|
|
where
|
|
key = Config.remoteConfig (Remote.repo r) "sync"
|
|
value
|
|
| enabled = "true"
|
|
| otherwise = "false"
|
|
|
|
{- Start syncing remote, using a background thread. -}
|
|
syncRemote :: Remote -> Handler ()
|
|
syncRemote remote = do
|
|
webapp <- getYesod
|
|
liftIO $ syncNewRemote
|
|
(fromJust $ threadState webapp)
|
|
(daemonStatus webapp)
|
|
(scanRemotes webapp)
|
|
remote
|
|
|
|
pauseTransfer :: Transfer -> Handler ()
|
|
pauseTransfer = cancelTransfer True
|
|
|
|
cancelTransfer :: Bool -> Transfer -> Handler ()
|
|
cancelTransfer pause t = do
|
|
webapp <- getYesod
|
|
let dstatus = daemonStatus webapp
|
|
m <- getCurrentTransfers
|
|
liftIO $ do
|
|
unless pause $
|
|
{- remove queued transfer -}
|
|
void $ dequeueTransfers (transferQueue webapp) dstatus $
|
|
equivilantTransfer t
|
|
{- stop running transfer -}
|
|
maybe noop (stop dstatus) (M.lookup t m)
|
|
where
|
|
stop dstatus info = do
|
|
{- When there's a thread associated with the
|
|
- transfer, it's signaled first, to avoid it
|
|
- displaying any alert about the transfer having
|
|
- failed when the transfer process is killed. -}
|
|
maybe noop signalthread $ transferTid info
|
|
maybe noop killproc $ transferPid info
|
|
if pause
|
|
then void $
|
|
alterTransferInfo dstatus t $ \i -> i
|
|
{ transferPaused = True }
|
|
else void $
|
|
removeTransfer dstatus t
|
|
signalthread tid
|
|
| pause = throwTo tid PauseTransfer
|
|
| otherwise = killThread tid
|
|
{- In order to stop helper processes like rsync,
|
|
- kill the whole process group of the process running the
|
|
- transfer. -}
|
|
killproc pid = do
|
|
g <- getProcessGroupIDOf pid
|
|
void $ tryIO $ signalProcessGroup sigTERM g
|
|
threadDelay 50000 -- 0.05 second grace period
|
|
void $ tryIO $ signalProcessGroup sigKILL g
|
|
|
|
startTransfer :: Transfer -> Handler ()
|
|
startTransfer t = do
|
|
m <- getCurrentTransfers
|
|
maybe startqueued go (M.lookup t m)
|
|
where
|
|
go info = maybe (start info) resume $ transferTid info
|
|
startqueued = do
|
|
webapp <- getYesod
|
|
let dstatus = daemonStatus webapp
|
|
let q = transferQueue webapp
|
|
is <- liftIO $ map snd <$> getMatchingTransfers q dstatus (== t)
|
|
maybe noop start $ headMaybe is
|
|
resume tid = do
|
|
webapp <- getYesod
|
|
let dstatus = daemonStatus webapp
|
|
liftIO $ do
|
|
alterTransferInfo dstatus t $ \i -> i
|
|
{ transferPaused = False }
|
|
throwTo tid ResumeTransfer
|
|
start info = do
|
|
webapp <- getYesod
|
|
let st = fromJust $ threadState webapp
|
|
let dstatus = daemonStatus webapp
|
|
let slots = transferSlots webapp
|
|
let commitchan = commitChan webapp
|
|
liftIO $ inImmediateTransferSlot dstatus slots $ do
|
|
program <- readProgramFile
|
|
Transferrer.startTransfer st dstatus commitchan program t info
|
|
|
|
getCurrentTransfers :: Handler TransferMap
|
|
getCurrentTransfers = currentTransfers
|
|
<$> (liftIO . getDaemonStatus =<< daemonStatus <$> getYesod)
|