dbmonitor-0.1.0: src/Monitor/Entry.hs
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ImplicitParams #-}
module Monitor.Entry where
import GHC.Conc (labelThread, atomically)
import System.FilePath
import System.FSNotify
import Control.Concurrent
import Control.Concurrent.Async
import Control.Concurrent.STM.TVar
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HM
import Monitor.Configuration.Options (Options(..))
import Monitor.Configuration.Config
import Monitor.Configuration.Read
import Monitor.Queue
import Monitor.DataModel
watchTower :: (?mutex :: Mutexes)
=> MVar () -> FilePath -> String -> Settings
-> TVar (HashMap FilePath (MVar ())) -> Event -> IO ()
watchTower monitorHolder dir tgvar cfg locksTVar event = do
let path = eventPath event
filename = takeFileName path
if isCheck filename
then do
label path . async
. flip runReaderT cfg
. getMonitor $
case event of
Modified _ _ False -> restartJob path
Removed _ _ False -> removeJob path
Added _ _ False -> startJob path
_ -> pure ()
else when (representsConfigName filename) $ do
putMVar monitorHolder ()
flip runReaderT cfg . getMonitor $ destroyQueue
logMessage ("Monitor at " <> dir <> " is stopped due to configuration change. All jobs are removed, monitor will be restarted.")
trackDatabase tgvar dir locksTVar
trackDatabase :: (?mutex :: Mutexes) => String -> FilePath
-> TVar (HashMap FilePath (MVar ())) -> IO ()
trackDatabase tgvar dbDir locksTVar = do
(cfg, checks) <- readMonitor dbDir tgvar
logMessage $ "Monitor at " <> dbDir <> " is started."
withManager $
\monitorManager -> do
lock <- newEmptyMVar
atomically $ modifyTVar locksTVar (HM.insert dbDir lock)
void $ watchTree monitorManager dbDir (const True)
(watchTower lock dbDir tgvar cfg locksTVar)
mapM_ (\f -> void . async $ runReaderT (getMonitor $ startJob f) cfg) checks
takeMVar lock
watchNewTrack :: (?mutex :: Mutexes) => String
-> TVar (HashMap FilePath (MVar ())) -> Event -> IO ()
watchNewTrack _ locksTVar (Removed path _ True) = do
locks <- liftIO $ readTVarIO locksTVar
putMVar (locks HM.! path) ()
atomically $ modifyTVar locksTVar (HM.delete path)
logMessage $ "Monitor at " <> path <> " is stopped due to directory deletion."
watchNewTrack tgvar locksTVar (Added path _ True) =
spawnMonitorThread tgvar locksTVar path
watchNewTrack _ _ _ = pure ()
label :: String -> IO (Async ()) -> IO ()
label lab action = do
asyn <- action
labelThread (asyncThreadId asyn) lab
spawnMonitorThread :: (?mutex :: Mutexes) => String
-> TVar (HashMap FilePath (MVar ())) -> FilePath -> IO ()
spawnMonitorThread tgvar locksTVar dir =
label dir . async $ trackDatabase tgvar dir locksTVar
runApp :: Options -> IO ()
runApp Options{..} = do
stdoutMutex <- newMVar ()
let ?mutex = Mutexes{..} in do
logMessage "dbmonitor process started."
databaseDirs <- collectMonitors optionsDir
eventChannel <- newChan
locksTVar <- newTVarIO HM.empty
withManager $ \mainWatcher -> do
void $ watchTreeChan mainWatcher optionsDir (const True) eventChannel
forM_ databaseDirs $ spawnMonitorThread optionsToken locksTVar
forever $ do
event <- readChan eventChannel
watchNewTrack optionsToken locksTVar event