2012-07-19 17:01:41 +00:00
|
|
|
{- git-annex assistant mount watcher, using either dbus or mtab polling
|
|
|
|
-
|
2015-01-21 16:50:09 +00:00
|
|
|
- Copyright 2012 Joey Hess <id@joeyh.name>
|
2012-07-19 17:01:41 +00:00
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
{-# LANGUAGE CPP #-}
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
|
|
|
module Assistant.Threads.MountWatcher where
|
|
|
|
|
2012-07-20 23:29:59 +00:00
|
|
|
import Assistant.Common
|
2012-07-19 17:01:41 +00:00
|
|
|
import Assistant.DaemonStatus
|
2012-08-22 18:32:17 +00:00
|
|
|
import Assistant.Sync
|
2012-07-22 19:06:18 +00:00
|
|
|
import qualified Annex
|
|
|
|
import qualified Git
|
2012-07-19 17:01:41 +00:00
|
|
|
import Utility.ThreadScheduler
|
|
|
|
import Utility.Mounts
|
2012-07-22 19:06:18 +00:00
|
|
|
import Remote.List
|
|
|
|
import qualified Types.Remote as Remote
|
2013-10-29 20:48:06 +00:00
|
|
|
import Assistant.Types.UrlRenderer
|
|
|
|
import Assistant.Fsck
|
2012-07-19 17:01:41 +00:00
|
|
|
|
|
|
|
import qualified Data.Set as S
|
|
|
|
|
|
|
|
#if WITH_DBUS
|
2012-08-21 23:58:53 +00:00
|
|
|
import Utility.DBus
|
2012-07-19 17:01:41 +00:00
|
|
|
import DBus.Client
|
2012-07-20 03:34:33 +00:00
|
|
|
import DBus
|
2012-07-20 05:59:21 +00:00
|
|
|
import Data.Word (Word32)
|
2012-09-29 20:09:07 +00:00
|
|
|
import Control.Concurrent
|
|
|
|
import qualified Control.Exception as E
|
2012-07-19 17:01:41 +00:00
|
|
|
#else
|
2016-05-24 20:51:10 +00:00
|
|
|
#ifdef linux_HOST_OS
|
2012-07-19 17:01:41 +00:00
|
|
|
#warning Building without dbus support; will use mtab polling
|
|
|
|
#endif
|
2016-05-24 20:51:10 +00:00
|
|
|
#endif
|
2012-07-19 17:01:41 +00:00
|
|
|
|
2013-10-29 20:48:06 +00:00
|
|
|
mountWatcherThread :: UrlRenderer -> NamedThread
|
|
|
|
mountWatcherThread urlrenderer = namedThread "MountWatcher" $
|
2012-07-19 17:01:41 +00:00
|
|
|
#if WITH_DBUS
|
2013-10-29 20:48:06 +00:00
|
|
|
dbusThread urlrenderer
|
2012-07-19 17:01:41 +00:00
|
|
|
#else
|
2013-10-29 20:48:06 +00:00
|
|
|
pollingThread urlrenderer
|
2012-07-19 17:01:41 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if WITH_DBUS
|
2012-07-20 03:34:33 +00:00
|
|
|
|
2013-10-29 20:48:06 +00:00
|
|
|
dbusThread :: UrlRenderer -> Assistant ()
|
|
|
|
dbusThread urlrenderer = do
|
2012-10-30 21:14:26 +00:00
|
|
|
runclient <- asIO1 go
|
2016-01-22 20:50:08 +00:00
|
|
|
r <- liftIO $ E.try $ runClient getSystemAddress runclient
|
2012-10-29 17:09:58 +00:00
|
|
|
either onerr (const noop) r
|
|
|
|
where
|
|
|
|
go client = ifM (checkMountMonitor client)
|
|
|
|
( do
|
|
|
|
{- Store the current mount points in an MVar, to be
|
|
|
|
- compared later. We could in theory work out the
|
|
|
|
- mount point from the dbus message, but this is
|
|
|
|
- easier. -}
|
|
|
|
mvar <- liftIO $ newMVar =<< currentMountPoints
|
2012-10-30 21:14:26 +00:00
|
|
|
handleevent <- asIO1 $ \_event -> do
|
2012-10-29 17:09:58 +00:00
|
|
|
nowmounted <- liftIO $ currentMountPoints
|
|
|
|
wasmounted <- liftIO $ swapMVar mvar nowmounted
|
2013-10-29 20:48:06 +00:00
|
|
|
handleMounts urlrenderer wasmounted nowmounted
|
2012-10-29 17:09:58 +00:00
|
|
|
liftIO $ forM_ mountChanged $ \matcher ->
|
2014-04-26 22:57:51 +00:00
|
|
|
void $ addMatch client matcher handleevent
|
2012-10-29 17:09:58 +00:00
|
|
|
, do
|
|
|
|
liftAnnex $
|
|
|
|
warning "No known volume monitor available through dbus; falling back to mtab polling"
|
2013-10-29 20:48:06 +00:00
|
|
|
pollingThread urlrenderer
|
2012-10-29 17:09:58 +00:00
|
|
|
)
|
|
|
|
onerr :: E.SomeException -> Assistant ()
|
|
|
|
onerr e = do
|
|
|
|
liftAnnex $
|
|
|
|
warning $ "dbus failed; falling back to mtab polling (" ++ show e ++ ")"
|
2013-10-29 20:48:06 +00:00
|
|
|
pollingThread urlrenderer
|
2012-07-20 03:34:33 +00:00
|
|
|
|
2012-07-20 22:14:57 +00:00
|
|
|
{- Examine the list of services connected to dbus, to see if there
|
2012-07-20 05:59:21 +00:00
|
|
|
- are any we can use to monitor mounts. If not, will attempt to start one. -}
|
2012-10-29 17:09:58 +00:00
|
|
|
checkMountMonitor :: Client -> Assistant Bool
|
2012-07-20 22:14:57 +00:00
|
|
|
checkMountMonitor client = do
|
|
|
|
running <- filter (`elem` usableservices)
|
2012-10-29 17:09:58 +00:00
|
|
|
<$> liftIO (listServiceNames client)
|
2012-08-05 19:08:58 +00:00
|
|
|
case running of
|
2012-10-29 18:30:10 +00:00
|
|
|
[] -> startOneService client startableservices
|
2012-08-05 19:08:58 +00:00
|
|
|
(service:_) -> do
|
2012-10-29 17:09:58 +00:00
|
|
|
debug [ "Using running DBUS service"
|
2012-08-05 19:08:58 +00:00
|
|
|
, service
|
2012-07-20 22:14:57 +00:00
|
|
|
, "to monitor mount events."
|
|
|
|
]
|
|
|
|
return True
|
2012-10-29 17:09:58 +00:00
|
|
|
where
|
2016-01-22 20:50:08 +00:00
|
|
|
startableservices = [udisks2]
|
|
|
|
usableservices = startableservices
|
|
|
|
udisks2 = "org.freedesktop.UDisks2"
|
2012-07-20 22:14:57 +00:00
|
|
|
|
2012-10-29 18:30:10 +00:00
|
|
|
startOneService :: Client -> [ServiceName] -> Assistant Bool
|
2012-07-20 22:14:57 +00:00
|
|
|
startOneService _ [] = return False
|
|
|
|
startOneService client (x:xs) = do
|
2013-01-14 17:22:19 +00:00
|
|
|
_ <- liftIO $ tryNonAsync $ callDBus client "StartServiceByName"
|
2012-07-20 22:14:57 +00:00
|
|
|
[toVariant x, toVariant (0 :: Word32)]
|
2012-10-29 18:30:10 +00:00
|
|
|
ifM (liftIO $ elem x <$> listServiceNames client)
|
2012-07-20 22:14:57 +00:00
|
|
|
( do
|
2012-10-29 18:30:10 +00:00
|
|
|
debug
|
|
|
|
[ "Started DBUS service", x
|
2012-07-20 22:14:57 +00:00
|
|
|
, "to monitor mount events."
|
|
|
|
]
|
|
|
|
return True
|
|
|
|
, startOneService client xs
|
|
|
|
)
|
2012-07-20 03:34:33 +00:00
|
|
|
|
2012-08-23 22:58:54 +00:00
|
|
|
{- Filter matching events recieved when drives are mounted and unmounted. -}
|
|
|
|
mountChanged :: [MatchRule]
|
2016-01-22 20:50:08 +00:00
|
|
|
mountChanged = [udisks2mount, udisks2umount]
|
2012-10-31 06:34:03 +00:00
|
|
|
where
|
2016-01-22 20:50:08 +00:00
|
|
|
udisks2mount = matchAny
|
|
|
|
{ matchPath = Just "/org/freedesktop/UDisks2"
|
|
|
|
, matchInterface = Just "org.freedesktop.DBus.ObjectManager"
|
|
|
|
, matchMember = Just "InterfacesAdded"
|
2012-10-31 06:34:03 +00:00
|
|
|
}
|
2016-01-22 20:50:08 +00:00
|
|
|
udisks2umount = matchAny
|
|
|
|
{ matchPath = Just "/org/freedesktop/UDisks2"
|
|
|
|
, matchInterface = Just "org.freedesktop.DBus.ObjectManager"
|
|
|
|
, matchMember = Just "InterfacesRemoved"
|
2012-10-31 06:34:03 +00:00
|
|
|
}
|
2012-07-19 17:01:41 +00:00
|
|
|
#endif
|
|
|
|
|
2013-10-29 20:48:06 +00:00
|
|
|
pollingThread :: UrlRenderer -> Assistant ()
|
|
|
|
pollingThread urlrenderer = go =<< liftIO currentMountPoints
|
2012-10-31 06:34:03 +00:00
|
|
|
where
|
|
|
|
go wasmounted = do
|
|
|
|
liftIO $ threadDelaySeconds (Seconds 10)
|
|
|
|
nowmounted <- liftIO currentMountPoints
|
2013-10-29 20:48:06 +00:00
|
|
|
handleMounts urlrenderer wasmounted nowmounted
|
2012-10-31 06:34:03 +00:00
|
|
|
go nowmounted
|
2012-07-19 17:01:41 +00:00
|
|
|
|
2013-10-29 20:48:06 +00:00
|
|
|
handleMounts :: UrlRenderer -> MountPoints -> MountPoints -> Assistant ()
|
|
|
|
handleMounts urlrenderer wasmounted nowmounted =
|
|
|
|
mapM_ (handleMount urlrenderer . mnt_dir) $
|
2012-08-05 01:18:57 +00:00
|
|
|
S.toList $ newMountPoints wasmounted nowmounted
|
2012-07-19 17:01:41 +00:00
|
|
|
|
2013-10-29 20:48:06 +00:00
|
|
|
handleMount :: UrlRenderer -> FilePath -> Assistant ()
|
|
|
|
handleMount urlrenderer dir = do
|
2012-10-29 17:09:58 +00:00
|
|
|
debug ["detected mount of", dir]
|
|
|
|
rs <- filter (Git.repoIsLocal . Remote.repo) <$> remotesUnder dir
|
2013-10-29 20:48:06 +00:00
|
|
|
mapM_ (fsckNudge urlrenderer . Just) rs
|
2016-11-14 18:26:20 +00:00
|
|
|
reconnectRemotes rs
|
2012-07-22 19:06:18 +00:00
|
|
|
|
|
|
|
{- Finds remotes located underneath the mount point.
|
|
|
|
-
|
|
|
|
- Updates state to include the remotes.
|
|
|
|
-
|
|
|
|
- The config of git remotes is re-read, as it may not have been available
|
|
|
|
- at startup time, or may have changed (it could even be a different
|
|
|
|
- repository at the same remote location..)
|
|
|
|
-}
|
2012-10-29 17:09:58 +00:00
|
|
|
remotesUnder :: FilePath -> Assistant [Remote]
|
|
|
|
remotesUnder dir = do
|
|
|
|
repotop <- liftAnnex $ fromRepo Git.repoPath
|
|
|
|
rs <- liftAnnex remoteList
|
|
|
|
pairs <- liftAnnex $ mapM (checkremote repotop) rs
|
2012-07-22 19:06:18 +00:00
|
|
|
let (waschanged, rs') = unzip pairs
|
2013-10-03 02:59:07 +00:00
|
|
|
when (or waschanged) $ do
|
2013-09-12 19:54:35 +00:00
|
|
|
liftAnnex $ Annex.changeState $ \s -> s { Annex.remotes = catMaybes rs' }
|
2012-10-30 19:39:15 +00:00
|
|
|
updateSyncRemotes
|
2013-10-03 02:59:07 +00:00
|
|
|
return $ mapMaybe snd $ filter fst pairs
|
2012-10-31 06:34:03 +00:00
|
|
|
where
|
|
|
|
checkremote repotop r = case Remote.localpath r of
|
|
|
|
Just p | dirContains dir (absPathFrom repotop p) ->
|
|
|
|
(,) <$> pure True <*> updateRemote r
|
2013-09-12 19:54:35 +00:00
|
|
|
_ -> return (False, Just r)
|
2012-07-19 17:01:41 +00:00
|
|
|
|
2012-07-20 01:25:26 +00:00
|
|
|
type MountPoints = S.Set Mntent
|
2012-07-19 17:01:41 +00:00
|
|
|
|
|
|
|
currentMountPoints :: IO MountPoints
|
2012-07-20 01:25:26 +00:00
|
|
|
currentMountPoints = S.fromList <$> getMounts
|
2012-07-19 17:01:41 +00:00
|
|
|
|
|
|
|
newMountPoints :: MountPoints -> MountPoints -> MountPoints
|
|
|
|
newMountPoints old new = S.difference new old
|