udev-0.1.1.0: src/System/UDev/Context.hs
-- |
-- Copyright : (c) Sam Truzjan 2013
-- License : BSD3
-- Maintainer : pxqr.sta@gmail.com
-- Stability : stable
-- Portability : portable
--
-- The context contains the default values read from the udev config
-- file, and is passed to all library operations.
--
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE OverloadedStrings #-}
module System.UDev.Context
( -- * Context
UDev
, UDevChild (..)
, newUDev
, freeUDev
, withUDev
-- * Logging
, Priority (..)
, Logger
, getLogPriority
, setLogPriority
, setLogger
, defaultLogger
-- * User data
, getUserdata
, setUserdata
) where
import Control.Applicative
import Control.Monad (void)
import Control.Exception
import Data.ByteString as BS
import Data.ByteString.Char8 as BC
import Foreign (Ptr, FunPtr)
import Foreign.C.String
import Foreign.C.Types
import Unsafe.Coerce
import System.UDev.Types
foreign import ccall unsafe "udev_new"
c_new :: IO UDev
-- | Create udev library context. This reads the udev configuration
-- file, and fills in the default values.
--
newUDev :: IO UDev
newUDev = c_new
freeUDev :: UDev -> IO ()
freeUDev = void . unref
-- | Like 'newUDev' but context will be released at exit.
withUDev :: (UDev -> IO a) -> IO a
withUDev = bracket newUDev freeUDev
{-----------------------------------------------------------------------
-- Logging
-----------------------------------------------------------------------}
-- | Log message priority.
data Priority = LogError -- ^ error conditions
| LogInfo -- ^ informational
| LogDebug -- ^ debug-level messages
deriving (Show, Read, Eq, Ord, Enum, Bounded)
-- | Convert priority to priority code.
prioToNr :: Priority -> CInt
prioToNr LogError = 3
prioToNr LogInfo = 6
prioToNr LogDebug = 7
-- | Convert priority code to priority.
nrToPrio :: CInt -> IO Priority
nrToPrio 3 = pure LogError
nrToPrio 6 = pure LogInfo
nrToPrio 7 = pure LogDebug
nrToPrio n = throwIO $ PatternMatchFail msg
where
msg = "unknown priority number: " ++ show n
foreign import ccall unsafe "udev_get_log_priority"
c_getLogPriority :: UDev -> IO CInt
-- | The initial logging priority is read from the udev config file at
-- startup.
getLogPriority :: UDev -> IO Priority
getLogPriority udev = nrToPrio =<< c_getLogPriority udev
foreign import ccall unsafe "udev_set_log_priority"
c_setLogPriority :: UDev -> CInt -> IO ()
-- | Set the current logging priority. The value controls which
-- messages are logged.
setLogPriority :: UDev -> Priority -> IO ()
setLogPriority udev prio = c_setLogPriority udev (prioToNr prio)
type CLogger = UDev -> CInt -> CString -> CInt -> CString -> CString -> IO ()
-- | Logger function will called by udev on events.
type Logger = UDev
-> Priority -- ^ message priority;
-> ByteString -- ^ position: file
-> Int -- ^ position: line
-> ByteString -- ^ position: function
-> ByteString -- ^ message body
-> IO ()
marshLogger :: Logger -> CLogger
marshLogger logger udev c_priority c_file c_line c_fn c_format = do
file <- packCString c_file
fn <- packCString c_fn
format <- packCString c_format
prio <- nrToPrio c_priority
logger udev prio file (fromIntegral c_line) fn format
foreign import ccall "wrapper"
mkLogger :: CLogger -> IO (FunPtr CLogger)
foreign import ccall "udev_set_log_fn"
c_setLogger :: UDev -> FunPtr CLogger -> IO ()
-- | The built-in logging writes to stderr. It can be overridden by a
-- custom function, to plug log messages into the users' logging
-- functionality.
setLogger :: UDev -> Logger -> IO ()
setLogger udev logger = c_setLogger udev =<< mkLogger (marshLogger logger)
-- | Default logger will just print @%PRIO %FILE:%LINE:\n%FN: %FORMAT@
-- to stdout.
defaultLogger :: Logger
defaultLogger _ priority file line fn format =
BC.putStrLn $ BS.concat
[ BC.pack (show priority), " "
, file, ":", BC.pack (show line), ":\n"
, " ", fn, ": ", format
]
{-----------------------------------------------------------------------
-- Userdata
-----------------------------------------------------------------------}
foreign import ccall unsafe "udev_get_userdata"
c_getUserdata :: UDev -> IO (Ptr ())
-- | Retrieve stored data pointer from library context. This might be
-- useful to access from callbacks like a custom logging function.
--
getUserdata :: UDev -> IO a
getUserdata udev = unsafeCoerce <$> c_getUserdata udev
foreign import ccall unsafe "udev_set_userdata"
c_setUserdata :: UDev -> Ptr () -> IO ()
-- | Store custom userdata in the library context.
setUserdata :: UDev -> a -> IO ()
setUserdata udev ud = c_setUserdata udev (unsafeCoerce ud)