refactor
This commit is contained in:
parent
b8f85f7a82
commit
b8ae9528ab
2 changed files with 68 additions and 49 deletions
|
@ -48,10 +48,14 @@ start = notBareRepo $ do
|
||||||
mvar <- liftIO $ newMVar state
|
mvar <- liftIO $ newMVar state
|
||||||
next $ next $ liftIO $ withINotify $ \i -> do
|
next $ next $ liftIO $ withINotify $ \i -> do
|
||||||
let hook a = Just $ runAnnex mvar a
|
let hook a = Just $ runAnnex mvar a
|
||||||
watchDir i "." (ignored . takeFileName)
|
let hooks = WatchHooks
|
||||||
(hook onTooMany)
|
{ addHook = hook onAdd
|
||||||
(hook onAdd) (hook onAddSymlink)
|
, delHook = hook onDel
|
||||||
(hook onDel) (hook onDelDir)
|
, addSymlinkHook = hook onAddSymlink
|
||||||
|
, delDirHook = hook onDelDir
|
||||||
|
, errHook = hook onErr
|
||||||
|
}
|
||||||
|
watchDir i "." (ignored . takeFileName) hooks
|
||||||
putStrLn "(started)"
|
putStrLn "(started)"
|
||||||
waitForTermination
|
waitForTermination
|
||||||
return True
|
return True
|
||||||
|
@ -129,31 +133,9 @@ onDelDir :: FilePath -> Annex ()
|
||||||
onDelDir dir = inRepo $ Git.Command.run "rm"
|
onDelDir dir = inRepo $ Git.Command.run "rm"
|
||||||
[Params "--quiet -r --cached --ignore-unmatch --", File dir]
|
[Params "--quiet -r --cached --ignore-unmatch --", File dir]
|
||||||
|
|
||||||
{- There are too many directories for inotify to watch them all. -}
|
{- Called when there's an error with inotify. -}
|
||||||
onTooMany :: FilePath -> Annex ()
|
onErr :: String -> Annex ()
|
||||||
onTooMany dir = do
|
onErr = warning
|
||||||
sysctlval <- liftIO $ runsysctl [Param maxwatches]
|
|
||||||
warning $ unlines $
|
|
||||||
basewarning : maybe withoutsysctl withsysctl sysctlval
|
|
||||||
where
|
|
||||||
maxwatches = "fs.inotify.max_user_watches"
|
|
||||||
basewarning = "Too many directories to watch! (Not watching " ++ dir ++")"
|
|
||||||
withoutsysctl = ["Increase the value in /proc/sys/fs/inotify/max_user_watches"]
|
|
||||||
withsysctl n = let new = n * 10 in
|
|
||||||
[ "Increase the limit by running:"
|
|
||||||
, " echo " ++ maxwatches ++ "=" ++ show new ++
|
|
||||||
" | sudo tee -a /etc/sysctl.conf; sudo sysctl -p"
|
|
||||||
]
|
|
||||||
runsysctl ps = do
|
|
||||||
v <- catchMaybeIO $ hPipeFrom "sysctl" $ toCommand ps
|
|
||||||
case v of
|
|
||||||
Nothing -> return Nothing
|
|
||||||
Just (pid, h) -> do
|
|
||||||
val <- parsesysctl <$> liftIO (hGetContentsStrict h)
|
|
||||||
void $ getProcessStatus True False $ processID pid
|
|
||||||
return val
|
|
||||||
parsesysctl :: String -> Maybe Integer
|
|
||||||
parsesysctl s = readish =<< lastMaybe (words s)
|
|
||||||
|
|
||||||
{- Adds a symlink to the index, without ever accessing the actual symlink
|
{- Adds a symlink to the index, without ever accessing the actual symlink
|
||||||
- on disk. -}
|
- on disk. -}
|
||||||
|
|
|
@ -15,7 +15,15 @@ import qualified System.Posix.Files as Files
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
import Control.Exception (throw)
|
import Control.Exception (throw)
|
||||||
|
|
||||||
type Hook = Maybe (FilePath -> IO ())
|
type Hook a = Maybe (a -> IO ())
|
||||||
|
|
||||||
|
data WatchHooks = WatchHooks
|
||||||
|
{ addHook :: Hook FilePath
|
||||||
|
, addSymlinkHook :: Hook FilePath
|
||||||
|
, delHook :: Hook FilePath
|
||||||
|
, delDirHook :: Hook FilePath
|
||||||
|
, errHook :: Hook String -- error message
|
||||||
|
}
|
||||||
|
|
||||||
{- Watches for changes to files in a directory, and all its subdirectories
|
{- Watches for changes to files in a directory, and all its subdirectories
|
||||||
- that are not ignored, using inotify. This function returns after
|
- that are not ignored, using inotify. This function returns after
|
||||||
|
@ -46,10 +54,10 @@ type Hook = Maybe (FilePath -> IO ())
|
||||||
- Note: inotify has a limit to the number of watches allowed,
|
- Note: inotify has a limit to the number of watches allowed,
|
||||||
- /proc/sys/fs/inotify/max_user_watches (default 8192).
|
- /proc/sys/fs/inotify/max_user_watches (default 8192).
|
||||||
- So this will fail if there are too many subdirectories. The
|
- So this will fail if there are too many subdirectories. The
|
||||||
- toomany hook is called when this happens.
|
- errHook is called when this happens.
|
||||||
-}
|
-}
|
||||||
watchDir :: INotify -> FilePath -> (FilePath -> Bool) -> Hook -> Hook -> Hook -> Hook -> Hook -> IO ()
|
watchDir :: INotify -> FilePath -> (FilePath -> Bool) -> WatchHooks -> IO ()
|
||||||
watchDir i dir ignored toomany add addsymlink del deldir
|
watchDir i dir ignored hooks
|
||||||
| ignored dir = noop
|
| ignored dir = noop
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
lock <- newLock
|
lock <- newLock
|
||||||
|
@ -60,17 +68,17 @@ watchDir i dir ignored toomany add addsymlink del deldir
|
||||||
mapM_ walk =<< filter (not . dirCruft) <$>
|
mapM_ walk =<< filter (not . dirCruft) <$>
|
||||||
getDirectoryContents dir
|
getDirectoryContents dir
|
||||||
where
|
where
|
||||||
recurse d = watchDir i d ignored toomany add addsymlink del deldir
|
recurse d = watchDir i d ignored hooks
|
||||||
|
|
||||||
-- Select only inotify events required by the enabled
|
-- Select only inotify events required by the enabled
|
||||||
-- hooks, but always include Create so new directories can
|
-- hooks, but always include Create so new directories can
|
||||||
-- be walked.
|
-- be walked.
|
||||||
watchevents = Create : addevents ++ delevents
|
watchevents = Create : addevents ++ delevents
|
||||||
addevents
|
addevents
|
||||||
| isJust add || isJust addsymlink = [MoveIn, CloseWrite]
|
| hashook addHook || hashook addSymlinkHook = [MoveIn, CloseWrite]
|
||||||
| otherwise = []
|
| otherwise = []
|
||||||
delevents
|
delevents
|
||||||
| isJust del || isJust deldir = [MoveOut, Delete]
|
| hashook delHook || hashook delDirHook = [MoveOut, Delete]
|
||||||
| otherwise = []
|
| otherwise = []
|
||||||
|
|
||||||
walk f = unless (ignored f) $ do
|
walk f = unless (ignored f) $ do
|
||||||
|
@ -80,8 +88,8 @@ watchDir i dir ignored toomany add addsymlink del deldir
|
||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
Just s
|
Just s
|
||||||
| Files.isDirectory s -> recurse fullf
|
| Files.isDirectory s -> recurse fullf
|
||||||
| Files.isSymbolicLink s -> addsymlink <@> f
|
| Files.isSymbolicLink s -> addSymlinkHook <@> f
|
||||||
| Files.isRegularFile s -> add <@> f
|
| Files.isRegularFile s -> addHook <@> f
|
||||||
| otherwise -> return ()
|
| otherwise -> return ()
|
||||||
|
|
||||||
-- Ignore creation events for regular files, which won't be
|
-- Ignore creation events for regular files, which won't be
|
||||||
|
@ -89,33 +97,36 @@ watchDir i dir ignored toomany add addsymlink del deldir
|
||||||
-- directories and symlinks.
|
-- directories and symlinks.
|
||||||
go (Created { isDirectory = isd, filePath = f })
|
go (Created { isDirectory = isd, filePath = f })
|
||||||
| isd = recurse $ indir f
|
| isd = recurse $ indir f
|
||||||
| isJust addsymlink =
|
| hashook addSymlinkHook =
|
||||||
whenM (filetype Files.isSymbolicLink f) $
|
whenM (filetype Files.isSymbolicLink f) $
|
||||||
addsymlink <@> f
|
addSymlinkHook <@> f
|
||||||
| otherwise = noop
|
| otherwise = noop
|
||||||
-- Closing a file is assumed to mean it's done being written.
|
-- Closing a file is assumed to mean it's done being written.
|
||||||
go (Closed { isDirectory = False, maybeFilePath = Just f }) =
|
go (Closed { isDirectory = False, maybeFilePath = Just f }) =
|
||||||
whenM (filetype Files.isRegularFile f) $
|
whenM (filetype Files.isRegularFile f) $
|
||||||
add <@> f
|
addHook <@> f
|
||||||
-- When a file or directory is moved in, walk it to add new
|
-- When a file or directory is moved in, walk it to add new
|
||||||
-- stuff.
|
-- stuff.
|
||||||
go (MovedIn { filePath = f }) = walk f
|
go (MovedIn { filePath = f }) = walk f
|
||||||
go (MovedOut { isDirectory = isd, filePath = f })
|
go (MovedOut { isDirectory = isd, filePath = f })
|
||||||
| isd = deldir <@> f
|
| isd = delDirHook <@> f
|
||||||
| otherwise = del <@> f
|
| otherwise = delHook <@> f
|
||||||
-- Verify that the deleted item really doesn't exist,
|
-- Verify that the deleted item really doesn't exist,
|
||||||
-- since there can be spurious deletion events for items
|
-- since there can be spurious deletion events for items
|
||||||
-- in a directory that has been moved out, but is still
|
-- in a directory that has been moved out, but is still
|
||||||
-- being watched.
|
-- being watched.
|
||||||
go (Deleted { isDirectory = isd, filePath = f })
|
go (Deleted { isDirectory = isd, filePath = f })
|
||||||
| isd = guarded $ deldir <@> f
|
| isd = guarded $ delDirHook <@> f
|
||||||
| otherwise = guarded $ del <@> f
|
| otherwise = guarded $ delHook <@> f
|
||||||
where
|
where
|
||||||
guarded = unlessM (filetype (const True) f)
|
guarded = unlessM (filetype (const True) f)
|
||||||
go _ = noop
|
go _ = noop
|
||||||
|
|
||||||
Just a <@> f = unless (ignored f) $ a $ indir f
|
hashook h = isJust $ h hooks
|
||||||
Nothing <@> _ = noop
|
|
||||||
|
h <@> f
|
||||||
|
| ignored f = noop
|
||||||
|
| otherwise = maybe noop (\a -> a $ indir f) (h hooks)
|
||||||
|
|
||||||
indir f = dir </> f
|
indir f = dir </> f
|
||||||
|
|
||||||
|
@ -125,7 +136,33 @@ watchDir i dir ignored toomany add addsymlink del deldir
|
||||||
-- disk full error.
|
-- disk full error.
|
||||||
failedaddwatch e
|
failedaddwatch e
|
||||||
| isFullError e =
|
| isFullError e =
|
||||||
case toomany of
|
case errHook hooks of
|
||||||
Nothing -> throw e
|
Nothing -> throw e
|
||||||
Just hook -> hook dir
|
Just hook -> tooManyWatches hook dir
|
||||||
| otherwise = throw e
|
| otherwise = throw e
|
||||||
|
|
||||||
|
tooManyWatches :: (String -> IO ()) -> FilePath -> IO ()
|
||||||
|
tooManyWatches hook dir = do
|
||||||
|
sysctlval <- querySysctl [Param maxwatches] :: IO (Maybe Integer)
|
||||||
|
hook $ unlines $ basewarning : maybe withoutsysctl withsysctl sysctlval
|
||||||
|
where
|
||||||
|
maxwatches = "fs.inotify.max_user_watches"
|
||||||
|
basewarning = "Too many directories to watch! (Not watching " ++ dir ++")"
|
||||||
|
withoutsysctl = ["Increase the value in /proc/sys/fs/inotify/max_user_watches"]
|
||||||
|
withsysctl n = let new = n * 10 in
|
||||||
|
[ "Increase the limit by running:"
|
||||||
|
, " echo " ++ maxwatches ++ "=" ++ show new ++
|
||||||
|
" | sudo tee -a /etc/sysctl.conf; sudo sysctl -p"
|
||||||
|
]
|
||||||
|
|
||||||
|
querySysctl :: Read a => [CommandParam] -> IO (Maybe a)
|
||||||
|
querySysctl ps = do
|
||||||
|
v <- catchMaybeIO $ hPipeFrom "sysctl" $ toCommand ps
|
||||||
|
case v of
|
||||||
|
Nothing -> return Nothing
|
||||||
|
Just (pid, h) -> do
|
||||||
|
val <- parsesysctl <$> hGetContentsStrict h
|
||||||
|
void $ getProcessStatus True False $ processID pid
|
||||||
|
return val
|
||||||
|
where
|
||||||
|
parsesysctl s = readish =<< lastMaybe (words s)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue