packages feed

uacpid-1.2: src/Uacpid/Events.hs

-- License: BSD3 (see LICENSE)
-- Author: Dino Morelli <dino@ui3.info>

{-# LANGUAGE FlexibleContexts #-}

module Uacpid.Events
   ( Handler (..), loadHandlers, executeHandlers )
   where

import Control.Monad.Except
import Data.List
import qualified Data.Map as M
import Data.Maybe
import System.Directory
import System.FilePath
import System.Log
import System.Process
import Text.Regex ( matchRegex, mkRegex )

import Uacpid.Conf ( ConfMap, getConfDir, parseToMap )
import Uacpid.Log ( logM )


data Handler = Handler
   { evName :: String
   , evEventRe :: String
   , evAction :: String
   }


lookupHandlerE :: (MonadError String m) =>
   String -> String -> ConfMap -> m String
lookupHandlerE name k m = maybe
   (throwError $ "Handler " ++ name ++ ": key " ++ k ++ " not found")
   return $ M.lookup k m


loadHandler :: (String, FilePath) -> IO (Maybe Handler)
loadHandler (name, path) = do
   -- Parse the event file into a map
   evMap <- liftM parseToMap $ readFile path

   -- Extract data from it to make an Handler, with error reporting
   eitherStringHandler <- runExceptT $ do
      event <- lookupHandlerE name "event" evMap
      action <- lookupHandlerE name "action" evMap
      return $ Handler name event action

   result <- case eitherStringHandler of
      Left emsg -> do
         logM WARNING emsg
         return Nothing
      Right ev -> return $ Just ev

   return result


loadHandlers :: IO [Handler]
loadHandlers = do
   confDir <- getConfDir
   let eventsDir = confDir </> "events"

   {- Load the event handler files. Files starting with . and ending 
      with ~ are discarded. We don't want . and .. of course, but you 
      can also disable one by make it look like events/.foo or 
      events/foo~
   -}
   eventFiles <- liftM 
      ( filter (\file -> not $ any ($ file)
         [ isPrefixOf ".", isSuffixOf "~" ]))
      $ getDirectoryContents eventsDir

   -- Construct a list of (filename, /path/to/filename)
   let eventPairs = map (\n -> (n, eventsDir </> n)) eventFiles

   -- Load these files and parse them
   events <- liftM catMaybes $ mapM loadHandler eventPairs

   let names = map evName events

   -- Log what we loaded for informational purposes
   if (null names)
      then logM NOTICE "No valid event handlers were found"
      else logM NOTICE $ "Handlers loaded: " ++
         (intercalate " " names)

   return events


executeHandlers :: String -> [Handler] -> IO ()
executeHandlers acpiHandler es = do
   let responders = filter (\e -> isJust $
         matchRegex (mkRegex (evEventRe e)) acpiHandler) es

   mapM_ (waitForProcess <=< runCommand . evAction) responders