2012-06-13 21:54:23 +00:00
|
|
|
{- git-annex assistant sanity checker
|
|
|
|
-
|
|
|
|
- Copyright 2012 Joey Hess <joey@kitenet.net>
|
2012-06-23 05:20:40 +00:00
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
2012-06-13 21:54:23 +00:00
|
|
|
-}
|
|
|
|
|
2012-06-25 20:10:10 +00:00
|
|
|
module Assistant.Threads.SanityChecker (
|
2012-06-13 21:54:23 +00:00
|
|
|
sanityCheckerThread
|
|
|
|
) where
|
|
|
|
|
2012-07-20 23:29:59 +00:00
|
|
|
import Assistant.Common
|
2012-06-13 21:54:23 +00:00
|
|
|
import Assistant.DaemonStatus
|
|
|
|
import Assistant.ThreadedMonad
|
2012-06-19 06:40:21 +00:00
|
|
|
import Assistant.Changes
|
2012-07-29 21:53:18 +00:00
|
|
|
import Assistant.Alert
|
2012-07-05 16:58:49 +00:00
|
|
|
import Assistant.TransferQueue
|
2012-07-20 23:29:59 +00:00
|
|
|
import qualified Git.LsFiles
|
2012-06-13 21:54:23 +00:00
|
|
|
import Utility.ThreadScheduler
|
2012-06-25 20:10:10 +00:00
|
|
|
import qualified Assistant.Threads.Watcher as Watcher
|
2012-06-13 21:54:23 +00:00
|
|
|
|
|
|
|
import Data.Time.Clock.POSIX
|
|
|
|
|
2012-07-20 23:29:59 +00:00
|
|
|
thisThread :: ThreadName
|
|
|
|
thisThread = "SanityChecker"
|
|
|
|
|
2012-06-13 21:54:23 +00:00
|
|
|
{- This thread wakes up occasionally to make sure the tree is in good shape. -}
|
2012-09-06 18:56:04 +00:00
|
|
|
sanityCheckerThread :: ThreadState -> DaemonStatusHandle -> TransferQueue -> ChangeChan -> NamedThread
|
|
|
|
sanityCheckerThread st dstatus transferqueue changechan = thread $ forever $ do
|
2012-07-29 21:53:18 +00:00
|
|
|
waitForNextCheck dstatus
|
2012-06-13 21:54:23 +00:00
|
|
|
|
2012-07-20 23:29:59 +00:00
|
|
|
debug thisThread ["starting sanity check"]
|
|
|
|
|
2012-07-30 06:07:02 +00:00
|
|
|
void $ alertWhile dstatus sanityCheckAlert go
|
2012-07-20 23:29:59 +00:00
|
|
|
|
|
|
|
debug thisThread ["sanity check complete"]
|
2012-07-29 21:53:18 +00:00
|
|
|
where
|
2012-09-06 18:56:04 +00:00
|
|
|
thread = NamedThread thisThread
|
2012-07-29 21:53:18 +00:00
|
|
|
go = do
|
|
|
|
modifyDaemonStatus_ dstatus $ \s -> s
|
|
|
|
{ sanityCheckRunning = True }
|
|
|
|
|
|
|
|
now <- getPOSIXTime -- before check started
|
2012-07-30 06:07:02 +00:00
|
|
|
r <- catchIO (check st dstatus transferqueue changechan)
|
|
|
|
$ \e -> do
|
|
|
|
runThreadState st $ warning $ show e
|
|
|
|
return False
|
2012-07-29 21:53:18 +00:00
|
|
|
|
|
|
|
modifyDaemonStatus_ dstatus $ \s -> s
|
|
|
|
{ sanityCheckRunning = False
|
|
|
|
, lastSanityCheck = Just now
|
|
|
|
}
|
2012-07-20 23:29:59 +00:00
|
|
|
|
2012-07-30 06:07:02 +00:00
|
|
|
return r
|
|
|
|
|
2012-06-13 21:54:23 +00:00
|
|
|
{- Only run one check per day, from the time of the last check. -}
|
2012-07-28 22:02:11 +00:00
|
|
|
waitForNextCheck :: DaemonStatusHandle -> IO ()
|
2012-07-29 21:53:18 +00:00
|
|
|
waitForNextCheck dstatus = do
|
|
|
|
v <- lastSanityCheck <$> getDaemonStatus dstatus
|
2012-06-13 21:54:23 +00:00
|
|
|
now <- getPOSIXTime
|
|
|
|
threadDelaySeconds $ Seconds $ calcdelay now v
|
|
|
|
where
|
|
|
|
calcdelay _ Nothing = oneDay
|
|
|
|
calcdelay now (Just lastcheck)
|
2012-06-13 23:25:47 +00:00
|
|
|
| lastcheck < now = max oneDay $
|
|
|
|
oneDay - truncate (now - lastcheck)
|
2012-06-13 21:54:23 +00:00
|
|
|
| otherwise = oneDay
|
|
|
|
|
|
|
|
oneDay :: Int
|
|
|
|
oneDay = 24 * 60 * 60
|
2012-06-13 23:25:47 +00:00
|
|
|
|
|
|
|
{- It's important to stay out of the Annex monad as much as possible while
|
|
|
|
- running potentially expensive parts of this check, since remaining in it
|
|
|
|
- will block the watcher. -}
|
2012-07-30 06:07:02 +00:00
|
|
|
check :: ThreadState -> DaemonStatusHandle -> TransferQueue -> ChangeChan -> IO Bool
|
2012-07-29 21:53:18 +00:00
|
|
|
check st dstatus transferqueue changechan = do
|
2012-10-12 05:17:45 +00:00
|
|
|
g <- runThreadState st gitRepo
|
2012-06-13 23:25:47 +00:00
|
|
|
-- Find old unstaged symlinks, and add them to git.
|
2012-10-04 23:56:32 +00:00
|
|
|
(unstaged, cleanup) <- Git.LsFiles.notInRepo False ["."] g
|
2012-06-13 23:25:47 +00:00
|
|
|
now <- getPOSIXTime
|
|
|
|
forM_ unstaged $ \file -> do
|
|
|
|
ms <- catchMaybeIO $ getSymbolicLinkStatus file
|
|
|
|
case ms of
|
|
|
|
Just s | toonew (statusChangeTime s) now -> noop
|
|
|
|
| isSymbolicLink s ->
|
|
|
|
addsymlink file ms
|
|
|
|
_ -> noop
|
2012-10-04 23:56:32 +00:00
|
|
|
void cleanup
|
2012-07-30 06:07:02 +00:00
|
|
|
return True
|
2012-06-13 23:25:47 +00:00
|
|
|
where
|
|
|
|
toonew timestamp now = now < (realToFrac (timestamp + slop) :: POSIXTime)
|
|
|
|
slop = fromIntegral tenMinutes
|
2012-07-29 21:53:18 +00:00
|
|
|
insanity msg = do
|
|
|
|
runThreadState st $ warning msg
|
2012-07-29 22:07:45 +00:00
|
|
|
void $ addAlert dstatus $ sanityCheckFixAlert msg
|
2012-06-13 23:25:47 +00:00
|
|
|
addsymlink file s = do
|
always check with ls-files before adding new files
Makes it safe to use git annex unlock with the watcher/assistant.
And also to mix use of the watcher/assistant with regular files stored in git.
Long ago, I had avoided doing this check, except during the startup scan,
because it would be slow to run ls-files repeatedly.
But then I added the lsof check, and to make that fast, got it to detect
batch file adds. So let's move the ls-files check to also occur when it'll
have a batch, and can check them all with one call.
This does slow down adding a single file by just a bit, but really only
a little bit. (The lsof check is probably more expensive.) It also
speeds up the startup scan, especially when there are lots of new files
found by the scan.
Also, fixed the sleep for annex.delayadd to not run while the threadstate
lock is held, so it doesn't unnecessarily freeze everything else.
Also, --force no longer makes it skip the lsof check, which was not
documented, and seems never a good idea.
2012-10-02 21:34:22 +00:00
|
|
|
Watcher.runHandler thisThread st dstatus
|
2012-07-20 23:29:59 +00:00
|
|
|
transferqueue changechan
|
2012-06-25 20:10:10 +00:00
|
|
|
Watcher.onAddSymlink file s
|
2012-07-29 22:07:45 +00:00
|
|
|
insanity $ "found unstaged symlink: " ++ file
|