diff options
author | Joey Hess <joey@kitenet.net> | 2012-06-25 16:10:10 -0400 |
---|---|---|
committer | Joey Hess <joey@kitenet.net> | 2012-06-25 16:10:24 -0400 |
commit | 0b146f9ecc36545478c4a2218981b376828c61db (patch) | |
tree | c7c758fb5421d61e6b286b76ec474dd9b04450df /Assistant/Watcher.hs | |
parent | 19eee6a1df2a6c724e6d6dbe842b40dc1c17f65b (diff) |
reorg threads
Diffstat (limited to 'Assistant/Watcher.hs')
-rw-r--r-- | Assistant/Watcher.hs | 218 |
1 files changed, 0 insertions, 218 deletions
diff --git a/Assistant/Watcher.hs b/Assistant/Watcher.hs deleted file mode 100644 index 78330c8d0..000000000 --- a/Assistant/Watcher.hs +++ /dev/null @@ -1,218 +0,0 @@ -{- git-annex assistant tree watcher - - - - Copyright 2012 Joey Hess <joey@kitenet.net> - - - - Licensed under the GNU GPL version 3 or higher. - -} - -{-# LANGUAGE CPP #-} - -module Assistant.Watcher where - -import Common.Annex -import Assistant.ThreadedMonad -import Assistant.DaemonStatus -import Assistant.Changes -import Utility.DirWatcher -import Utility.Types.DirWatcher -import qualified Annex -import qualified Annex.Queue -import qualified Git.Command -import qualified Git.UpdateIndex -import qualified Git.HashObject -import qualified Git.LsFiles -import qualified Backend -import qualified Command.Add -import Annex.Content -import Annex.CatFile -import Git.Types - -import Data.Bits.Utils -import qualified Data.ByteString.Lazy as L - -checkCanWatch :: Annex () -checkCanWatch - | canWatch = - unlessM (liftIO (inPath "lsof") <||> Annex.getState Annex.force) $ - needLsof - | otherwise = error "watch mode is not available on this system" - -needLsof :: Annex () -needLsof = error $ unlines - [ "The lsof command is needed for watch mode to be safe, and is not in PATH." - , "To override lsof checks to ensure that files are not open for writing" - , "when added to the annex, you can use --force" - , "Be warned: This can corrupt data in the annex, and make fsck complain." - ] - -watchThread :: ThreadState -> DaemonStatusHandle -> ChangeChan -> IO () -watchThread st dstatus changechan = watchDir "." ignored hooks startup - where - startup = statupScan st dstatus - hook a = Just $ runHandler st dstatus changechan a - hooks = WatchHooks - { addHook = hook onAdd - , delHook = hook onDel - , addSymlinkHook = hook onAddSymlink - , delDirHook = hook onDelDir - , errHook = hook onErr - } - -{- Initial scartup scan. The action should return once the scan is complete. -} -statupScan :: ThreadState -> DaemonStatusHandle -> IO a -> IO a -statupScan st dstatus scanner = do - runThreadState st $ - showAction "scanning" - r <- scanner - runThreadState st $ - modifyDaemonStatus dstatus $ \s -> s { scanComplete = True } - - -- Notice any files that were deleted before watching was started. - runThreadState st $ do - inRepo $ Git.Command.run "add" [Param "--update"] - showAction "started" - - return r - -ignored :: FilePath -> Bool -ignored = ig . takeFileName - where - ig ".git" = True - ig ".gitignore" = True - ig ".gitattributes" = True - ig _ = False - -type Handler = FilePath -> Maybe FileStatus -> DaemonStatusHandle -> Annex (Maybe Change) - -{- Runs an action handler, inside the Annex monad, and if there was a - - change, adds it to the ChangeChan. - - - - Exceptions are ignored, otherwise a whole watcher thread could be crashed. - -} -runHandler :: ThreadState -> DaemonStatusHandle -> ChangeChan -> Handler -> FilePath -> Maybe FileStatus -> IO () -runHandler st dstatus changechan handler file filestatus = void $ do - r <- tryIO go - case r of - Left e -> print e - Right Nothing -> noop - Right (Just change) -> recordChange changechan change - where - go = runThreadState st $ handler file filestatus dstatus - -{- During initial directory scan, this will be run for any regular files - - that are already checked into git. We don't want to turn those into - - symlinks, so do a check. This is rather expensive, but only happens - - during startup. - - - - It's possible for the file to still be open for write by some process. - - This can happen in a few ways; one is if two processes had the file open - - and only one has just closed it. We want to avoid adding a file to the - - annex that is open for write, to avoid anything being able to change it. - - - - We could run lsof on the file here to check for other writers. - - But, that's slow, and even if there is currently a writer, we will want - - to add the file *eventually*. Instead, the file is locked down as a hard - - link in a temp directory, with its write bits disabled, for later - - checking with lsof, and a Change is returned containing a KeySource - - using that hard link. The committer handles running lsof and finishing - - the add. - -} -onAdd :: Handler -onAdd file filestatus dstatus - | maybe False isRegularFile filestatus = do - ifM (scanComplete <$> getDaemonStatus dstatus) - ( go - , ifM (null <$> inRepo (Git.LsFiles.notInRepo False [file])) - ( noChange - , go - ) - ) - | otherwise = noChange - where - go = pendingAddChange =<< Command.Add.lockDown file - -{- A symlink might be an arbitrary symlink, which is just added. - - Or, if it is a git-annex symlink, ensure it points to the content - - before adding it. - -} -onAddSymlink :: Handler -onAddSymlink file filestatus dstatus = go =<< Backend.lookupFile file - where - go (Just (key, _)) = do - link <- calcGitLink file key - ifM ((==) link <$> liftIO (readSymbolicLink file)) - ( ensurestaged link =<< getDaemonStatus dstatus - , do - liftIO $ removeFile file - liftIO $ createSymbolicLink link file - addlink link - ) - go Nothing = do -- other symlink - link <- liftIO (readSymbolicLink file) - ensurestaged link =<< getDaemonStatus dstatus - - {- This is often called on symlinks that are already - - staged correctly. A symlink may have been deleted - - and being re-added, or added when the watcher was - - not running. So they're normally restaged to make sure. - - - - As an optimisation, during the status scan, avoid - - restaging everything. Only links that were created since - - the last time the daemon was running are staged. - - (If the daemon has never ran before, avoid staging - - links too.) - -} - ensurestaged link daemonstatus - | scanComplete daemonstatus = addlink link - | otherwise = case filestatus of - Just s - | not (afterLastDaemonRun (statusChangeTime s) daemonstatus) -> noChange - _ -> addlink link - - {- For speed, tries to reuse the existing blob for - - the symlink target. -} - addlink link = do - v <- catObjectDetails $ Ref $ ':':file - case v of - Just (currlink, sha) - | s2w8 link == L.unpack currlink -> - stageSymlink file sha - _ -> do - sha <- inRepo $ - Git.HashObject.hashObject BlobObject link - stageSymlink file sha - madeChange file LinkChange - -onDel :: Handler -onDel file _ _dstatus = do - Annex.Queue.addUpdateIndex =<< - inRepo (Git.UpdateIndex.unstageFile file) - madeChange file RmChange - -{- A directory has been deleted, or moved, so tell git to remove anything - - that was inside it from its cache. Since it could reappear at any time, - - use --cached to only delete it from the index. - - - - Note: This could use unstageFile, but would need to run another git - - command to get the recursive list of files in the directory, so rm is - - just as good. -} -onDelDir :: Handler -onDelDir dir _ _dstatus = do - Annex.Queue.addCommand "rm" - [Params "--quiet -r --cached --ignore-unmatch --"] [dir] - madeChange dir RmDirChange - -{- Called when there's an error with inotify. -} -onErr :: Handler -onErr msg _ _dstatus = do - warning msg - return Nothing - -{- Adds a symlink to the index, without ever accessing the actual symlink - - on disk. This avoids a race if git add is used, where the symlink is - - changed to something else immediately after creation. - -} -stageSymlink :: FilePath -> Sha -> Annex () -stageSymlink file sha = - Annex.Queue.addUpdateIndex =<< - inRepo (Git.UpdateIndex.stageSymlink file sha) |