udev-0.0.0.0: src/System/UDev/Device.hs
-- |
-- Copyright : (c) Sam Truzjan 2013
-- License : BSD3
-- Maintainer : pxqr.sta@gmail.com
-- Stability : stable
-- Portability : portable
--
-- Representation of kernel sys devices. Devices are uniquely
-- identified by their syspath, every device has exactly one path in
-- the kernel sys filesystem. Devices usually belong to a kernel
-- subsystem, and have a unique name inside that subsystem.
--
module System.UDev.Device
( Device
, Devnum
-- * Create
, newFromSysPath
, newFromDevnum
, newFromSubsystemSysname
, newFromDeviceId
, newFromEnvironment
, getParent
, getParentWithSubsystemDevtype
-- * Query
, getDevpath
, getSubsystem
, getDevtype
, getSyspath
, getSysname
, getSysnum
, getDevnode
, isInitialized
, getDevlinksListEntry
, getPropertiesListEntry
, getTagsListEntry
, getPropertyValue
, getDriver
, getDevnum
, getAction
-- * Sysattrs
, getSysattrValue
, setSysattrValue
, getSysattrListEntry
-- * Misc
, getSeqnum
, getUsecSinceInitialized
, hasTag
) where
import Control.Applicative
import Data.ByteString as BS
import Foreign hiding (unsafePerformIO)
import Foreign.C
import System.IO.Unsafe
import System.UDev.Context
import System.UDev.List
import System.UDev.Types
foreign import ccall unsafe "udev_device_new_from_syspath"
c_newFromSysPath :: UDev -> CString -> IO Device
-- TODO type SysPath = FilePath
type SysPath = ByteString
-- | Create new udev device, and fill in information from the sys
-- device and the udev database entry. The syspath is the absolute
-- path to the device, including the sys mount point.
--
newFromSysPath :: UDev -> SysPath -> IO Device
newFromSysPath udev sysPath = do
Device <$> (throwErrnoIfNull "newFromSysPath" $ do
useAsCString sysPath $ \ c_sysPath -> do
getDevice <$> c_newFromSysPath udev c_sysPath)
type Dev_t = CULong
foreign import ccall unsafe "udev_device_new_from_devnum"
c_newFromDevnum :: UDev -> CChar -> Dev_t -> IO Device
-- | Device number.
type Devnum = Int
-- | Create new udev device, and fill in information from the sys
-- device and the udev database entry. The device is looked-up by its
-- major/minor number and type. Character and block device numbers are
-- not unique across the two types.
--
newFromDevnum :: UDev -> Char -> Devnum -> IO Device
newFromDevnum udev char devnum
= c_newFromDevnum udev (toEnum (fromEnum char)) (fromIntegral devnum)
{-# INLINE newFromDevnum #-}
foreign import ccall unsafe "udev_device_new_from_subsystem_sysname"
c_newFromSubsystemSysname :: UDev -> CString -> CString -> IO Device
-- | The device is looked up by the subsystem and name string of the
-- device, like \"mem\" \/ \"zero\", or \"block\" \/ \"sda\".
--
newFromSubsystemSysname :: UDev -> ByteString -> ByteString -> IO Device
newFromSubsystemSysname udev subsystem sysname = do
useAsCString subsystem $ \ c_subsystem ->
useAsCString sysname $ \ c_sysname ->
c_newFromSubsystemSysname udev c_subsystem c_sysname
foreign import ccall unsafe "udev_device_new_from_device_id"
c_newFromDeviceId :: UDev -> CString -> IO Device
-- | The device is looked-up by a special string:
--
-- * b8:2 - block device major:minor
--
-- * c128:1 - char device major:minor
--
-- * n3 - network device ifindex
--
-- * +sound:card29 - kernel driver core subsystem:device name
--
newFromDeviceId :: UDev -> ByteString -> IO Device
newFromDeviceId udev devId = do
useAsCString devId $ \ c_devId ->
c_newFromDeviceId udev c_devId
foreign import ccall unsafe "udev_device_new_from_environment"
c_newFromEnvironment :: UDev -> IO Device
-- | Create new udev device, and fill in information from the current
-- process environment. This only works reliable if the process is
-- called from a udev rule. It is usually used for tools executed from
-- @IMPORT=@ rules.
--
newFromEnvironment :: UDev -> IO Device
newFromEnvironment = c_newFromEnvironment
foreign import ccall unsafe "udev_device_get_parent"
c_getParent :: Device -> IO Device
-- TODO: [MEM]: The returned the device is not referenced. It is
-- attached to the child device, and will be cleaned up when the child
-- device is cleaned up.
-- | Find the next parent device, and fill in information from the sys
-- device and the udev database entry.
getParent :: Device -> IO Device
getParent = c_getParent
foreign import ccall unsafe "udev_device_get_parent_with_subsystem_devtype"
c_getParentWithSubsystemDevtype :: Device -> CString -> CString
-> IO Device
-- | Find the next parent device, with a matching subsystem and devtype
-- value, and fill in information from the sys device and the udev
-- database entry.
--
getParentWithSubsystemDevtype :: Device -> ByteString -> ByteString
-> IO (Maybe Device)
getParentWithSubsystemDevtype udev subsystem devtype = do
mdev <- useAsCString subsystem $ \ c_subsystem ->
useAsCString devtype $ \ c_devtype ->
c_getParentWithSubsystemDevtype udev c_subsystem c_devtype
return $ if getDevice mdev == nullPtr then Nothing else Just mdev
foreign import ccall unsafe "udev_device_get_devpath"
c_getDevpath :: Device -> IO CString
-- TODO use RawFilePath
{-----------------------------------------------------------------------
-- Query
-----------------------------------------------------------------------}
-- | Retrieve the kernel devpath value of the udev device. The path
-- does not contain the sys mount point, and starts with a \'/\'.
--
getDevpath :: Device -> IO ByteString
getDevpath dev = packCString =<< c_getDevpath dev
foreign import ccall unsafe "udev_device_get_subsystem"
c_getSubsystem :: Device -> IO CString
packCStringMaybe :: CString -> IO (Maybe ByteString)
packCStringMaybe cstring =
if cstring == nullPtr
then return Nothing
else Just <$> packCString cstring
-- | Retrieve the subsystem string of the udev device. The string does
-- not contain any \"/\".
--
getSubsystem :: Device -> Maybe ByteString
getSubsystem dev = unsafePerformIO $ packCStringMaybe =<< c_getSubsystem dev
foreign import ccall unsafe "udev_device_get_devtype"
c_getDevtype :: Device -> IO CString
-- | Retrieve the devtype string of the udev device.
getDevtype :: Device -> Maybe ByteString
getDevtype dev = unsafePerformIO $ packCStringMaybe =<< c_getDevtype dev
foreign import ccall unsafe "udev_device_get_syspath"
c_getSyspath :: Device -> IO CString
-- | Retrieve the sys path of the udev device. The path is an absolute
-- path and starts with the sys mount point.
--
getSyspath :: Device -> ByteString
getSyspath dev = unsafePerformIO $ packCString =<< c_getSyspath dev
foreign import ccall unsafe "udev_device_get_sysname"
c_getSysname :: Device -> IO CString
-- | Get the kernel device name in /sys.
getSysname :: Device -> ByteString
getSysname dev = unsafePerformIO $ packCString =<< c_getSysname dev
foreign import ccall unsafe "udev_device_get_sysnum"
c_getSysnum :: Device -> IO CString
-- TODO :: Device -> Maybe Int ?
-- | Get the instance number of the device.
getSysnum :: Device -> Maybe ByteString
getSysnum dev = unsafePerformIO $ packCStringMaybe =<< c_getSysnum dev
foreign import ccall unsafe "udev_device_get_devnode"
c_getDevnode :: Device -> IO CString
-- | Retrieve the device node file name belonging to the udev
-- device. The path is an absolute path, and starts with the device
-- directory.
--
getDevnode :: Device -> Maybe ByteString
getDevnode udev = unsafePerformIO $ packCStringMaybe =<< c_getDevnode udev
foreign import ccall unsafe "udev_device_get_is_initialized"
c_isInitialized :: Device -> IO CInt
-- | Check if udev has already handled the device and has set up
-- device node permissions and context, or has renamed a network
-- device.
--
-- This is only implemented for devices with a device node or network
-- interfaces. All other devices return 1 here.
--
isInitialized :: Device -> IO Bool
isInitialized dev = (< 0) <$> c_isInitialized dev
foreign import ccall unsafe "udev_device_get_devlinks_list_entry"
c_getDevlinksListEntry :: Device -> IO List
-- | Retrieve the list of device links pointing to the device file of
-- the udev device. The next list entry can be retrieved with
-- 'getNext', which returns 'Nothing' if no more entries exist. The
-- devlink path can be retrieved from the list entry by 'getName'. The
-- path is an absolute path, and starts with the device directory.
--
getDevlinksListEntry :: Device -> IO List
getDevlinksListEntry = c_getDevlinksListEntry
{-# INLINE getDevlinksListEntry #-}
foreign import ccall unsafe "udev_device_get_properties_list_entry"
c_getPropertiesListEntry :: Device -> IO List
-- | Retrieve the list of key/value device properties of the udev
-- device. The next list entry can be retrieved with 'getNext', which
-- returns 'Nothing' if no more entries exist. The property name can
-- be retrieved from the list entry by 'getName', the property value
-- by 'getValue'.
--
getPropertiesListEntry :: Device -> IO List
getPropertiesListEntry = c_getPropertiesListEntry
{-# INLINE getPropertiesListEntry #-}
foreign import ccall unsafe "udev_device_get_tags_list_entry"
c_getTagsListEntry :: Device -> IO List
-- | Retrieve the list of tags attached to the udev device. The next
-- list entry can be retrieved with 'getNext', which returns 'Nothing'
-- if no more entries exist. The tag string can be retrieved from the
-- list entry by 'getName'.
--
getTagsListEntry :: Device -> IO List
getTagsListEntry = c_getTagsListEntry
{-# INLINE getTagsListEntry #-}
foreign import ccall unsafe "udev_device_get_property_value"
c_getPropertyValue :: Device -> CString -> IO CString
-- | Get the value of a given property.
getPropertyValue :: Device -> ByteString -> IO (Maybe ByteString)
getPropertyValue dev prop = do
res <- useAsCString prop $ \ c_prop ->
c_getPropertyValue dev c_prop
if res == nullPtr then return Nothing else Just <$> packCString res
foreign import ccall unsafe "udev_device_get_driver"
c_getDriver :: Device -> IO CString
-- TODO ByteString -> Text ?
-- | Get the kernel driver name.
getDriver :: Device -> IO ByteString
getDriver dev = packCString =<< c_getDriver dev
foreign import ccall unsafe "udev_device_get_devnum"
c_getDevnum :: Device -> IO Devnum
-- | Get the device major/minor number.
getDevnum :: Device -> IO Devnum
getDevnum = c_getDevnum
{-# INLINE getDevnum #-}
foreign import ccall unsafe "udev_device_get_action"
c_getAction :: Device -> CString
-- TODO data Action
-- | This is only valid if the device was received through a
-- monitor. Devices read from sys do not have an action string.
--
getAction :: Device -> Maybe ByteString
getAction dev
| c_action == nullPtr = Nothing
| otherwise = Just $ unsafePerformIO $ packCString c_action
where
c_action = c_getAction dev
foreign import ccall unsafe "udev_device_get_sysattr_value"
c_getSysattrValue :: Device -> CString -> CString
-- | The retrieved value is cached in the device. Repeated calls will
-- return the same value and not open the attribute again.
--
getSysattrValue :: Device -> ByteString -> ByteString
getSysattrValue dev sysattr = do
unsafePerformIO $ do
packCString =<< useAsCString sysattr (return . c_getSysattrValue dev)
foreign import ccall unsafe "udev_device_set_sysattr_value"
c_setSysattrValue :: Device -> CString -> CString -> IO CInt
-- | Update the contents of the sys attribute and the cached value of
-- the device.
--
setSysattrValue :: Device
-> ByteString -- ^ attribute name
-> ByteString -- ^ new value to be set
-> IO ()
setSysattrValue dev sysattr value = do
throwErrnoIf_ (0 <) "setSysattrValue" $ do
useAsCString sysattr $ \ c_sysattr ->
useAsCString value $ \ c_value ->
c_setSysattrValue dev c_sysattr c_value
foreign import ccall unsafe "udev_device_get_sysattr_list_entry"
c_getSysAttrListEntry :: Device -> IO List
-- | Retrieve the list of available sysattrs, with value being empty;
-- This just return all available sysfs attributes for a particular
-- device without reading their values.
--
getSysattrListEntry :: Device -> IO List
getSysattrListEntry = c_getSysAttrListEntry
{-# INLINE getSysattrListEntry #-}
toMaybe :: CULLong -> Maybe Int
toMaybe 0 = Nothing
toMaybe n = Just (fromIntegral n)
{-# INLINE toMaybe #-}
foreign import ccall unsafe "udev_device_get_seqnum"
c_getSeqnum :: Device -> IO CULLong
-- | This is only valid if the device was received through a
-- monitor. Devices read from sys do not have a sequence number.
--
getSeqnum :: Device -> IO (Maybe Int)
getSeqnum dev = toMaybe <$> c_getSeqnum dev
{-# INLINE getSeqnum #-}
foreign import ccall unsafe "udev_device_get_usec_since_initialized"
c_getUsecSinceInitialized :: Device -> IO CULLong
-- | Return the number of microseconds passed since udev set up the
-- device for the first time.
--
-- This is only implemented for devices with need to store
-- properties in the udev database. All other devices return
-- 'Nothing' here.
--
getUsecSinceInitialized :: Device -> IO (Maybe Int)
getUsecSinceInitialized dev = toMaybe <$> c_getUsecSinceInitialized dev
foreign import ccall unsafe "udev_device_has_tag"
c_hasTag :: Device -> CString -> IO CInt
-- | Check if a given device has a certain tag associated.
hasTag :: Device -> ByteString -> IO Bool
hasTag dev tag = do
(1 ==) <$> do
useAsCString tag $ \ c_tag ->
c_hasTag dev c_tag