{-# LINE 1 "System/Linux/KMod.hsc" #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LINE 2 "System/Linux/KMod.hsc" #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE ForeignFunctionInterface #-}

-- | High-level bindings to to the @libkmod@ library for manipulating
-- Linux kernel modules.
module System.Linux.KMod
    (
    -- * Context
      Context
    , new
    , loadResources
    , unloadResources
    , Resources(..)
    , validateResources
    , Index(..)
    , dumpIndex
    , setLogPriority
    , getLogPriority
    -- * Get configuration
    , Key
    , Value
    , configGetBlacklists
    , configGetInstallCommands
    , configGetRemoveCommands
    , configGetAliases
    , configGetOptions
    , configGetSoftdeps
    -- * Modules
    , Module
    , moduleNewFromLookup
    , Filter(..)
    , moduleNewFromLookupWithFilter
    , moduleNewFromName
    , moduleNewFromPath
    , Options
    , InsertFlags(..)
    , moduleInsertModule
    , ProbeFlags(..)
    , Blacklist(..)
    , RunInstall
    , PrintAction
    , BlacklistError(..)
    , moduleProbeInsertModule
    , RemoveFlags(..)
    , moduleRemoveModule
    , moduleGetInstallCommands
    , moduleGetRemoveCommands
    , moduleGetOptions
    , moduleGetPath
    , moduleGetDependencies
    , moduleGetSoftdeps
    , Symbol
    , Bind(..)
    , CRC
    , moduleGetDependencySymbols
    , Name
    , Address
    , moduleGetSections
    , moduleGetSymbols
    , moduleGetVersions
    , moduleGetInfo
    -- * Loaded modules
    , moduleNewFromLoaded
    , Initstate(..)
    , moduleGetInitstate
    , moduleGetSize
    , moduleGetRefcnt
    , moduleGetHolders
   ) where

import Control.Exception (Exception, bracket, throw)
import Data.Typeable
import Foreign hiding (new)
import Foreign.C
import System.Posix.Directory (closeDirStream, openDirStream, readDirStream)
import System.Posix.Types (Fd)


{-# LINE 80 "System/Linux/KMod.hsc" #-}

-- | Represent the key in a (key,value) pair.
type Key = String

-- | Represent the value in a (key,value) pair.
type Value = String

-- | Opaque object representing the library context.
newtype Context = Context (ForeignPtr Context)

withContext :: Context -> (Ptr Context -> IO a) -> IO a
withContext (Context fptr) = withForeignPtr fptr

foreign import ccall "&kmod_unref"
  p_unref :: FunPtr (Ptr Context -> IO (Ptr Context))

toContext :: Ptr Context -> IO Context
toContext ptr = newForeignPtr (castFunPtr p_unref) ptr >>= return . Context


foreign import ccall "kmod_new" kmod_new :: CString
                                         -> Ptr CString
                                         -> IO (Ptr Context)

-- | Create a @kmod@ @'Context'@ from configuration files.
new :: Maybe FilePath   -- ^ Linux module's directory, or @'Nothing'@
                        -- for the default (@\/lib\/modules\/`uname -r`@)
    -> Maybe [FilePath] -- ^ List of paths (directories or files) for
                        -- configuration files, or @'Nothing'@ for the
                        -- default (@\/run\/modprobe.d@,
                        -- @\/etc\/modprobe.d@ and @\/lib\/modprobe.d@)
    -> IO Context
new mdir mpaths = (throwIfNull "kmod_new returned NULL" $
                   maybeWith withCString mdir $ \d ->
                       maybeWith withCStringArray mpaths $ \p ->
                           kmod_new d p
                  ) >>= toContext

withCStringArray :: [String] -> (Ptr CString -> IO a) -> IO a
withCStringArray xs f = bracket
                        (mapM newCString xs)
                        (mapM_ free)
                        (\ps -> withArray0 nullPtr ps f)


foreign import ccall "kmod_load_resources" load_resources :: Ptr Context
                                                          -> IO CInt
-- | Load indexes and keep them open in @'Context'@.
loadResources :: Context -> IO ()
loadResources ctx =
    throwIf_ (/= 0) (\e -> "kmod_load_resources returned " ++ show e) $
    withContext ctx load_resources

foreign import ccall "kmod_unload_resources" unload_resources :: Ptr Context
                                                              -> IO ()
-- | Unload all the indexes.
unloadResources :: Context -> IO ()
unloadResources ctx = withContext ctx unload_resources

-- | Represent the return values of @'validateResources'@.
data Resources = Ok
               | MustReload
               | MustRecreate
                 deriving (Eq, Show)

toResources :: (Eq a, Num a, Show a) => a -> Resources
toResources 0 = Ok
{-# LINE 147 "System/Linux/KMod.hsc" #-}
toResources 1 = MustReload
{-# LINE 148 "System/Linux/KMod.hsc" #-}
toResources 2 = MustRecreate
{-# LINE 149 "System/Linux/KMod.hsc" #-}
toResources n = error ("System.KMod.toResources: bad argument: " ++ show n)


foreign import ccall "kmod_validate_resources"
  validate_resources :: Ptr Context -> IO CInt

-- | Check if indexes and configuration files changed on disk and the
-- current context is not valid anymore.
validateResources :: Context -> IO Resources
validateResources ctx = withContext ctx validate_resources >>=
                        return . toResources

-- | Represent an index file.
data Index = ModulesDep	 
           | ModulesAlias	 
           | ModulesSymbol	 
           | ModulesBuiltin
             deriving (Eq, Show)

fromIndex :: Num a => Index -> a
fromIndex ModulesDep = 0
{-# LINE 170 "System/Linux/KMod.hsc" #-}
fromIndex ModulesAlias = 1
{-# LINE 171 "System/Linux/KMod.hsc" #-}
fromIndex ModulesSymbol = 2
{-# LINE 172 "System/Linux/KMod.hsc" #-}
fromIndex ModulesBuiltin = 3
{-# LINE 173 "System/Linux/KMod.hsc" #-}


foreign import ccall "kmod_dump_index"
  dump_index :: Ptr Context -> CInt -> CInt -> IO CInt

-- | Dump @'Index'@ to file descriptor.
dumpIndex :: Context -> Index -> Fd -> IO ()
dumpIndex ctx i fd =
    withContext ctx $ \p ->
        throwIf_ (/= 0) (\e -> "kmod_dump_index returned " ++ show e) $
        dump_index p (fromIndex i) (fromIntegral fd)

foreign import ccall "kmod_set_log_priority"
  set_log_priority :: Ptr Context -> CInt -> IO ()

-- | Set the current logging priority. The value controls which
-- messages are logged.
setLogPriority :: Context -> Int -> IO ()
setLogPriority ctx i =
    withContext ctx (\p -> set_log_priority p (fromIntegral i))

foreign import ccall "kmod_get_log_priority"
  get_log_priority :: Ptr Context -> IO CInt

-- | Get the current logging priority.
getLogPriority :: Context -> IO Int
getLogPriority ctx =
    withContext ctx (\p -> get_log_priority p >>= return . fromIntegral)

data ConfigIter

foreign import ccall "kmod_config_iter_get_key"
  config_iter_get_key :: Ptr ConfigIter -> IO CString

foreign import ccall "kmod_config_iter_get_value"
  config_iter_get_value :: Ptr ConfigIter -> IO CString

foreign import ccall unsafe "kmod_config_iter_next"
  config_iter_next :: Ptr ConfigIter -> IO Bool

foreign import ccall unsafe "kmod_config_iter_free_iter"
  config_iter_free_iter :: Ptr ConfigIter -> IO ()


configIter2List :: (Ptr ConfigIter -> IO a)
                -> Ptr ConfigIter
                -> IO [a]
configIter2List fget p = do
  if p == nullPtr
    then throwErrno "configIter2List"
    else do b <- config_iter_next p
            if b
              then do x <- fget p
                      xs <- configIter2List fget p
                      return (x:xs)
              else do config_iter_free_iter p
                      return []


configIter2KeyValueList :: Ptr ConfigIter -> IO [(Key,Value)]
configIter2KeyValueList =
    configIter2List $ \p -> do key <- config_iter_get_key p >>= peekCString
                               val <- config_iter_get_value p >>= peekCString
                               return (key,val)

configIter2StringList :: Ptr ConfigIter -> IO [String]
configIter2StringList = configIter2List 
                        (\p -> do str <- config_iter_get_key p >>= peekCString
                                  return str)

withContextAndWith :: (Ptr Context -> IO a) -> (a -> IO b) -> Context -> IO b
withContextAndWith f g m = withContext m f >>= g

foreign import ccall "kmod_config_get_blacklists"
  config_get_blacklists :: Ptr Context -> IO (Ptr ConfigIter)

-- | Get the list of blacklisted modules.
configGetBlacklists :: Context -> IO [String]
configGetBlacklists =
    withContextAndWith config_get_blacklists configIter2StringList

foreign import ccall "kmod_config_get_install_commands"
  config_get_install_commands :: Ptr Context -> IO (Ptr ConfigIter)

-- | Get the list of modules and corresponding install commands.
configGetInstallCommands :: Context -> IO [(Key,Value)]
configGetInstallCommands =
    withContextAndWith config_get_install_commands configIter2KeyValueList

foreign import ccall "kmod_config_get_remove_commands"
  config_get_remove_commands :: Ptr Context -> IO (Ptr ConfigIter)

-- | Get the list of modules and corresponding remove commands.
configGetRemoveCommands :: Context -> IO [(Key,Value)]
configGetRemoveCommands =
    withContextAndWith config_get_remove_commands configIter2KeyValueList

foreign import ccall "kmod_config_get_aliases"
  config_get_aliases :: Ptr Context -> IO (Ptr ConfigIter)

-- | Get the list of modules and corresponding aliases.
configGetAliases :: Context -> IO [(Key,Value)]
configGetAliases = withContextAndWith config_get_aliases configIter2KeyValueList

foreign import ccall "kmod_config_get_options"
  config_get_options :: Ptr Context -> IO (Ptr ConfigIter)

-- | Get the list of modules and corresponding options.
configGetOptions :: Context -> IO [(Key,Value)]
configGetOptions = withContextAndWith config_get_options configIter2KeyValueList

foreign import ccall "kmod_config_get_softdeps"
  config_get_softdeps :: Ptr Context -> IO (Ptr ConfigIter)

-- | Get the list of modules and corresponding soft dependencies.
configGetSoftdeps :: Context -> IO [(Key,Value)]
configGetSoftdeps =
    withContextAndWith config_get_softdeps configIter2KeyValueList


-- | Opaque object representing a module. The @'Show'@ instance of
-- @'Module'@ is achieved using @kmod_module_get_name@.
data Module = Module (ForeignPtr Module) String

withModule :: Module -> (Ptr Module -> IO a) -> IO a
withModule (Module fptr _) = withForeignPtr fptr


foreign import ccall "&kmod_module_unref"
  p_module_unref :: FunPtr (Ptr Module -> IO (Ptr Module))

foreign import ccall "kmod_module_get_name"
  module_get_name :: Ptr Module -> IO CString

toModule :: Ptr Module -> IO Module
toModule ptr = do fptr <- newForeignPtr (castFunPtr p_module_unref) ptr
                  name <- module_get_name ptr >>= peekCString
                  return $ Module fptr name

instance Show Module where
    show (Module _ name) = name

foreign import ccall "kmod_module_new_from_lookup"
  module_new_from_lookup :: Ptr Context -> CString -> Ptr (Ptr List) -> IO CInt

fromLookup :: Context -> String -> IO (Ptr List)
fromLookup ctx str =
    withContext ctx $ \p ->
        withCString str $ \q ->
            alloca $ \r ->
                do poke r nullPtr
                   e <- module_new_from_lookup p q r
                   if e < 0
                     then fail ("kmod_module_new_from_lookup returned " ++ show e)
                     else peek r

-- | Create a new list of @'Module'@s using an alias or module name
-- and lookup libkmod's configuration files and indexes in order to
-- find the module. Once it's found in one of the places, it stops
-- searching and create the list of @'Module'@s.
moduleNewFromLookup :: Context -> String -> IO [Module]
moduleNewFromLookup ctx str = fromLookup ctx str >>= toModuleList

-- | Same as @'moduleNewFromLookup'@ but apply  filter the output list.
moduleNewFromLookupWithFilter :: Context -> String -> Filter -> IO [Module]
moduleNewFromLookupWithFilter ctx str fil = fromLookup ctx str >>=
                                            toModuleListWithFilter ctx fil

foreign import ccall "kmod_module_new_from_name"
  module_new_from_name :: Ptr Context -> CString -> Ptr (Ptr Module) -> IO CInt

foreign import ccall "kmod_module_new_from_path"
  module_new_from_path :: Ptr Context -> CString -> Ptr (Ptr Module) -> IO CInt


moduleNewFrom :: (Ptr Context -> CString -> Ptr (Ptr Module) -> IO CInt)
              -> String
              -> Context -> String -> IO Module
moduleNewFrom fun err ctx str =
    withContext ctx $ \p ->
        withCString str $ \q ->
            alloca $ \r ->
                do e <- fun p q r
                   if e == 0
                     then peek r >>= toModule
                     else fail (err ++ " returned " ++ show e)

-- | Create a new @'Module'@ using the module name, that can not be an
-- alias, file name or anything else; it must be a module
-- name. There's no check if the module exists in the system.
moduleNewFromName :: Context -> String -> IO Module
moduleNewFromName =
    moduleNewFrom module_new_from_name "kmod_module_new_from_name"

-- | Create a new @'Module'@ using the module path.
moduleNewFromPath :: Context -> String -> IO Module
moduleNewFromPath =
    moduleNewFrom module_new_from_path "kmod_module_new_from_path"

fromBitField :: (Bits b, Num b) => [(a -> Bool, b)] -> a -> b
fromBitField [] _ = 0
fromBitField (x:xs) a = (if fst x a then snd x else 0) .|. fromBitField xs a

-- | Flags for @'moduleInsertModule'@.
data InsertFlags = InsertFlags { insertForceVerMagic :: Bool
                               , insertForceModVersion :: Bool
                               } deriving (Eq, Show)

fromInsertFlags :: (Bits a, Num a) => InsertFlags -> a
fromInsertFlags =
    fromBitField
    [ (insertForceVerMagic, 1)
{-# LINE 385 "System/Linux/KMod.hsc" #-}
    , (insertForceModVersion, 2)
{-# LINE 386 "System/Linux/KMod.hsc" #-}
    ]


foreign import ccall "kmod_module_insert_module"
  module_insert_module :: Ptr Module -> CUInt -> CString -> IO CInt

-- | Options for module loading to pass to Linux Kernel.
type Options = String

-- | Insert a module in Linux kernel.
moduleInsertModule :: Module -> InsertFlags -> Options -> IO ()
moduleInsertModule m i o =
    withModule m $ \p ->
        withCString o $ \q ->
            throwErrnoIf_ (/=0) "kmod_module_insert_module" $
            module_insert_module p (fromInsertFlags i) q


-- | Flags for @'moduleProbeInsertModule'@.
data ProbeFlags = ProbeFlags { probeForceVerMagic :: Bool
                             , probeForceModVersion :: Bool
                             , probeIgnoreCommand :: Bool
                             , probeIgnoreLoaded :: Bool
                             , probeDryRun :: Bool
                             , probeFailOnLoaded :: Bool
                             } deriving (Eq, Show)

fromProbeFlags :: (Bits a, Num a) => ProbeFlags -> a
fromProbeFlags x =
    fromBitField [ (probeForceVerMagic, 1)
{-# LINE 416 "System/Linux/KMod.hsc" #-}
                 , (probeForceModVersion, 2)
{-# LINE 417 "System/Linux/KMod.hsc" #-}
                 , (probeIgnoreCommand, 4)
{-# LINE 418 "System/Linux/KMod.hsc" #-}
                 , (probeIgnoreLoaded, 8)
{-# LINE 419 "System/Linux/KMod.hsc" #-}
                 , (probeDryRun, 16)
{-# LINE 420 "System/Linux/KMod.hsc" #-}
                 , (probeFailOnLoaded, 32)
{-# LINE 421 "System/Linux/KMod.hsc" #-}
                 ] x

-- | @'Blacklist'@ filter for @'moduleProbeInsertModule'@ specifies
-- how blacklist configuration should be applied.
data Blacklist = BlacklistAll       -- ^ Apply blacklist configuration
                                    -- to the module and its
                                    -- dependencies
               | Blacklist          -- ^ Apply blacklist configuration
                                    -- to the module alone
               | BlacklistAliasOnly -- ^ Apply blacklist configuration
                                    -- to the module only if it is an
                                    -- alias
               | NoBlacklist        -- ^ Do not apply blacklist
                                    -- configuration
                 deriving (Eq, Show)

fromBlacklist :: Num a => Blacklist -> a
fromBlacklist BlacklistAll = 65536
{-# LINE 439 "System/Linux/KMod.hsc" #-}
fromBlacklist Blacklist = 131072
{-# LINE 440 "System/Linux/KMod.hsc" #-}
fromBlacklist BlacklistAliasOnly = 262144
{-# LINE 441 "System/Linux/KMod.hsc" #-}
fromBlacklist NoBlacklist = 0

foreign import ccall "kmod_module_probe_insert_module"
  module_probe_insert_module :: Ptr Module
                             -> CUInt
                             -> CString
                             -> FunPtr CRunInstall
                             -> Ptr ()
                             -> FunPtr (CPrintAction)
                             -> IO CInt

-- | Function to run a module install commands.
type RunInstall = Module  -- ^ Module being loaded
                -> String -- ^ Command line of the install executable
                -> IO ()

type CRunInstall = Ptr Module -> CString -> Ptr () -> IO CInt

foreign import ccall "wrapper" mkCRunInstallPtr :: CRunInstall
                                                -> IO (FunPtr CRunInstall)

withRunInstall :: Maybe RunInstall -> (FunPtr CRunInstall -> IO a) -> IO a
withRunInstall Nothing f = f nullFunPtr
withRunInstall (Just ri) f = bracket
                             (mkCRunInstallPtr toCRunInstall)
                             freeHaskellFunPtr
                             f
    where toCRunInstall p cs _ =
              do m <- module_ref p >>= toModule -- Need to increment
                                                -- ref, or segfault
                 s <- peekCString cs
                 ri m s
                 return 0

-- | @'Exception'@ @'throw'@n by @'moduleProbeInsertModule'@ if the
-- module cannot be inserted due to a @'Blacklist'@ setting.
data BlacklistError = BlacklistError
                      deriving (Eq, Show, Typeable)

instance Exception BlacklistError

-- | Insert a module in Linux kernel resolving dependencies, soft
-- dependencies, install commands and applying blacklist. If the
-- module cannot be inserted due to the @'Blacklist'@ filter, @'moduleProbeInsertModule'@ @'throw'@s a
-- @'BlacklistError'@ exception.
moduleProbeInsertModule :: Module            -- ^ Module to be loaded
                        -> ProbeFlags        -- ^ Flags
                        -> Blacklist         -- ^ How to handle
                                             -- blacklisted modules
                        -> Maybe Options     -- ^ Options to be passed
                                             -- to the module
                        -> Maybe RunInstall  -- ^ Function to execute
                                             -- module's install
                                             -- commands, if specified
                                             -- in is configuration
                                             -- (see
                                             -- @'moduleGetInstallCommands'@).
                                             -- If @'Nothing'@ then
                                             -- use @system(3)@
                        -> Maybe PrintAction -- ^ Function to print
                                             -- the action being taken
                                             -- on module loading
                        -> IO ()
moduleProbeInsertModule m flags bl opts ri pa =
    withModule m $ \p ->
        maybeWith withCString opts $ \cs ->
            withRunInstall ri $ \fptr1 ->
                withPrintAction pa $ \fptr2 ->
                    do e <- module_probe_insert_module p
                            (fromProbeFlags flags .|. fromBlacklist bl)
                            cs fptr1 nullPtr fptr2
                       if e == 0
                         then return ()
                         else if e < 0
                               then throwErrno "kmod_module_probe_insert_module"
                               else throw BlacklistError

-- | Function to print the actions being taken during the execution of
-- @'moduleProbeInsertModule'@.
type PrintAction = Module   -- ^ Module that is going to be loaded
                 -> Bool    -- ^ True if the module has install
                            -- commands specified in its configuration
                 -> Options -- ^ Options passed to the module
                 -> IO ()

type CBool = Word8
{-# LINE 527 "System/Linux/KMod.hsc" #-}

type CPrintAction = Ptr Module -> CBool -> CString -> IO ()

foreign import ccall "wrapper" mkCPrintActionPtr :: CPrintAction
                                                 -> IO (FunPtr CPrintAction)

withPrintAction :: Maybe PrintAction -> (FunPtr CPrintAction -> IO a) -> IO a
withPrintAction Nothing f = f nullFunPtr
withPrintAction (Just pa) f = bracket (mkCPrintActionPtr toCPrintAction)
                              freeHaskellFunPtr
                              f
    where toCPrintAction p b cs =
              do m <- module_ref p >>= toModule -- Need to increment
                                                -- ref, or segfault
                 s <- peekCString cs
                 pa m (toBool b) s

foreign import ccall "kmod_module_ref" module_ref :: Ptr Module
                                                  -> IO (Ptr Module)

-- | Flags for @'moduleRemoveModule'@.
data RemoveFlags = RemoveFlags { removeForce :: Bool
                               , removeNowait :: Bool
                               } deriving (Eq, Show)

fromRemoveFlags :: (Bits a, Num a) => RemoveFlags -> a
fromRemoveFlags = fromBitField [ (removeForce, 512)
{-# LINE 554 "System/Linux/KMod.hsc" #-}
                               , (removeNowait, 2048)
{-# LINE 555 "System/Linux/KMod.hsc" #-}
                               ]

foreign import ccall "kmod_module_remove_module"
  module_remove_module :: Ptr Module -> CUInt -> IO CInt

-- | Remove a module from Linux kernel.
moduleRemoveModule :: Module -> RemoveFlags -> IO ()
moduleRemoveModule m r =
    withModule m $ \p ->
        throwErrnoIf_ (/=0) "kmod_module_remove_module" $
        module_remove_module p (fromRemoveFlags r)


withModuleAndWith :: (Ptr Module -> IO a) -> (a -> IO b) -> Module -> IO b
withModuleAndWith f g m = withModule m f >>= g

foreign import ccall "kmod_module_get_install_commands"
  module_get_install_commands :: Ptr Module -> IO CString

-- | Get install commands for this @'Module'@. Install commands come
-- from the configuration file and are cached in @'Module'@. The first
-- call to this function will search for this module in configuration
-- and subsequent calls return the cached string. The install commands
-- are returned as they were in the configuration, concatenated by
-- ';'. No other processing is made in this string.
moduleGetInstallCommands :: Module -> IO (Maybe String)
moduleGetInstallCommands =
    withModuleAndWith module_get_install_commands (maybePeek peekCString)

foreign import ccall "kmod_module_get_remove_commands"
  module_get_remove_commands :: Ptr Module -> IO CString

-- | Get remove commands for this @'Module'@. Remove commands come
-- from the configuration file and are cached in @'Module'@. The first
-- call to this function will search for this module in configuration
-- and subsequent calls return the cached string. The remove commands
-- are returned as they were in the configuration, concatenated by
-- ';'. No other processing is made in this string.
moduleGetRemoveCommands :: Module -> IO (Maybe String)
moduleGetRemoveCommands =
    withModuleAndWith module_get_remove_commands (maybePeek peekCString)

foreign import ccall "kmod_module_get_options"
  module_get_options :: Ptr Module -> IO CString

-- | Get options of this @'Module'@. Options come from the
-- configuration file and are cached in @'Module'@. The first call to
-- this function will search for this @'Module'@ in configuration and
-- subsequent calls return the cached string.
moduleGetOptions :: Module -> IO (Maybe String)
moduleGetOptions = withModuleAndWith module_get_options (maybePeek peekCString)

foreign import ccall "kmod_module_get_path"
  module_get_path :: Ptr Module -> IO CString

-- | Get the path of this @'Module'@. If this @'Module'@ was not
-- created by path, it can search the modules.dep index in order to
-- find out the module under context's dirname.
moduleGetPath :: Module -> IO (Maybe String)
moduleGetPath = withModuleAndWith module_get_path (maybePeek peekCString)




data List

foreign import ccall unsafe "kmod_list_last" list_last :: Ptr List
                                                       -> IO (Ptr List)

foreign import ccall unsafe "kmod_list_next" list_next :: Ptr List
                                                       -> Ptr List
                                                       -> IO (Ptr List)



toList :: (Ptr List -> IO a)  -- ^ Function to get the entry value 
       -> (Ptr List -> IO ()) -- ^ Function to free the list
       -> Ptr List
       -> IO [a]
toList get fr p = do l <- list_last p
                     if l == nullPtr
                       then do fr p
                               return []
                       else genList p
    where genList q = do x <- get q
                         r <- list_next p q
                         if r == nullPtr
                           then do fr p
                                   return [x]
                           else do xs <- genList r 
                                   return (x:xs)


foreign import ccall unsafe "kmod_module_get_module"
  module_get_module :: Ptr List
                    -> IO (Ptr Module)

foreign import ccall unsafe "kmod_module_unref_list"
  module_unref_list :: Ptr List -> IO CInt

toModuleList :: Ptr List -> IO [Module]
toModuleList = toList
               (\p -> module_get_module p >>= toModule)
               (\p -> throwErrnoIf_ (/= 0)
                      "kmod_module_unref_list returned non-zero exit status"
                      (module_unref_list p)
               )

foreign import ccall "kmod_module_apply_filter"
  module_apply_filter :: Ptr Context
                      -> CInt
                      -> Ptr List
                      -> Ptr (Ptr List)
                      -> IO CInt

-- | Filter type for @'moduleNewFromLookupWithFilter'@.
data Filter = Filter { filterBlacklist :: Bool
                     , filterBuiltin :: Bool
                     } deriving (Eq, Show)

fromFilter :: (Bits a, Num a) => Filter -> a
fromFilter = fromBitField [ (filterBlacklist, 1)
{-# LINE 677 "System/Linux/KMod.hsc" #-}
                          , (filterBuiltin, 2)
{-# LINE 678 "System/Linux/KMod.hsc" #-}
                          ]

toModuleListWithFilter :: Context -> Filter -> Ptr List -> IO [Module]
toModuleListWithFilter c f l =
    withContext c $ \p ->
        alloca $ \q ->
            do e <- module_apply_filter p (fromFilter f) l q
               if e >= 0
                 then peek q >>= toModuleList
                 else fail ("kmod_module_apply_filter returned " ++ show e)

foreign import ccall "kmod_module_get_dependencies"
  module_get_dependencies :: Ptr Module -> IO (Ptr List)

-- | Search the modules.dep index to find the dependencies of the
-- given @'Module'@.
moduleGetDependencies :: Module -> IO [Module]
moduleGetDependencies m =
    withModule m (\p -> module_get_dependencies p >>= toModuleList)

foreign import ccall "kmod_module_get_softdeps"
  module_get_softdeps :: Ptr Module
                      -> Ptr (Ptr List)
                      -> Ptr (Ptr List)
                      -> IO CInt

-- | Get soft dependencies for this @'Module'@.
moduleGetSoftdeps :: Module -> IO ([Module], [Module])
moduleGetSoftdeps m =
    withModule m $ \p ->
        alloca $ \pre ->
            do poke pre nullPtr
               alloca $ \post ->
                   do poke post nullPtr
                      e <- module_get_softdeps p pre post
                      if e >= 0
                        then do l1 <- peek pre >>= toModuleList
                                l2 <- peek post >>= toModuleList
                                return (l1,l2)
                        else fail ("kmod_module_get_softdeps returned " ++
                                   show e)

-- | Symbol bind type.
data Bind = None
          | Local
          | Global
          | Weak
          | Undefined
            deriving (Eq, Show)

toBind :: (Eq a, Num a, Show a) => a -> Bind
toBind 0 = None
{-# LINE 730 "System/Linux/KMod.hsc" #-}
toBind 76 = Local
{-# LINE 731 "System/Linux/KMod.hsc" #-}
toBind 71 = Global
{-# LINE 732 "System/Linux/KMod.hsc" #-}
toBind 87 = Weak
{-# LINE 733 "System/Linux/KMod.hsc" #-}
toBind 85 = Undefined
{-# LINE 734 "System/Linux/KMod.hsc" #-}
toBind n = error ("System.KMod.toBind: bad argument: " ++ show n)

-- | Crc of a symbol.
type CRC = Word64
{-# LINE 738 "System/Linux/KMod.hsc" #-}

-- | Name of a symbol.
type Symbol = String

module_dependency_symbol_get :: Ptr List -> IO (Symbol,Bind,CRC)
module_dependency_symbol_get p =
    do s <- module_dependency_symbol_get_symbol p >>= peekCString
       b <- module_dependency_symbol_get_bind p
       c <- module_dependency_symbol_get_crc p
       return (s,toBind b,c)

foreign import ccall "kmod_module_dependency_symbol_get_bind"
  module_dependency_symbol_get_bind :: Ptr List -> IO CInt

foreign import ccall "kmod_module_dependency_symbol_get_crc"
  module_dependency_symbol_get_crc :: Ptr List -> IO Word64
{-# LINE 754 "System/Linux/KMod.hsc" #-}

foreign import ccall "kmod_module_dependency_symbol_get_symbol"
  module_dependency_symbol_get_symbol :: Ptr List -> IO CString

foreign import ccall "kmod_module_dependency_symbols_free_list"
  module_dependency_symbols_free_list :: Ptr List -> IO ()

toDependencySymbolList :: Ptr List -> IO [(Symbol,Bind,CRC)]
toDependencySymbolList =  toList
                          module_dependency_symbol_get
                          module_dependency_symbols_free_list

foreign import ccall "kmod_module_get_dependency_symbols"
  module_get_dependency_symbols :: Ptr Module
                                -> Ptr (Ptr List)
                                -> IO CInt

moduleGet :: (Ptr Module -> Ptr (Ptr List) -> IO CInt) -- ^ Get the list
          -> (Ptr List -> IO a)                        -- ^ Convert the list
          -> String                                    -- ^ Error string
          -> Module
          -> IO a
moduleGet fget flist err m =
    withModule m $ \p ->
        alloca $ \q ->
            do poke q nullPtr
               e <- fget p q
               if e < 0
                 then fail (err ++ " returned " ++ show e)
                 else peek q >>= flist

-- | Get the list of entries in ELF section \".symtab\" or
-- \"__ksymtab_strings\".
moduleGetDependencySymbols :: Module -> IO [(Symbol,Bind,CRC)]
moduleGetDependencySymbols =
    moduleGet
    module_get_dependency_symbols
    toDependencySymbolList
    "kmod_module_get_dependency_symbols"

-- | Name of a module section.
type Name = String

-- | Address of a module section.
type Address = Word64
{-
foreign import ccall "kmod_module_section_get_address"
        module_section_get_address :: Ptr List
                                   -> IO Address

foreign import ccall "kmod_module_section_get_name"
        module_section_get_name :: Ptr List
                                -> IO CString

foreign import ccall "kmod_module_section_free_list"
        module_section_free_list :: Ptr List
                                 -> IO ()

foreign import ccall "kmod_module_get_sections"
  module_get_sections :: Ptr Module
                      -> IO (Ptr List)

module_section_get :: Ptr List -> IO (Name,Address)
module_section_get p = do n <- module_section_get_name p >>= peekCString
                          a <- module_section_get_address p
                          return (n,a)

toSectionList :: Ptr List -> IO [(Name,Address)]
toSectionList = toList
                module_section_get
                module_section_free_list

moduleGetSections :: Module -> IO [(Name,Address)]
moduleGetSections m =
    withModule m (\p -> do s <- module_get_sections p
                           if s == nullPtr
                              then fail ("kmod_module_get_sections returned " ++
                                         show s)
                              else toSectionList s
                 )
-}
-- | Get a list of sections of this @'Module'@, as returned by Linux
-- kernel (implemented natively in Haskell by reading @\/sys\/module\/@).
moduleGetSections :: Module -> IO [(Name,Address)]
moduleGetSections m = do fs <- readDir secDir
                         mapM get (drop 2 fs)
    where secDir = "/sys/module/" ++ show m ++ "/sections"
          get f = do addr <- readFile (secDir ++ '/':f)
                     return (f, read addr)


readDir :: FilePath -> IO [FilePath]
readDir path = bracket (openDirStream path) closeDirStream getDir
    where getDir d = do f <- readDirStream d
                        case f of
                          [] -> return []
                          _ -> do fs <- getDir d
                                  return (f:fs)

foreign import ccall "kmod_module_symbol_get_crc"
  module_symbol_get_crc :: Ptr List -> IO CRC


foreign import ccall "kmod_module_symbol_get_symbol"
  module_symbol_get_symbol :: Ptr List -> IO CString

foreign import ccall "kmod_module_symbols_free_list"
  module_symbols_free_list :: Ptr List -> IO ()

foreign import ccall "kmod_module_get_symbols"
  module_get_symbols :: Ptr Module -> Ptr (Ptr List) -> IO CInt

-- | Get the list of entries in ELF section \".symtab\" or
-- \"__ksymtab_strings\".
moduleGetSymbols :: Module -> IO [(Symbol,CRC)]
moduleGetSymbols =
    moduleGet module_get_symbols toSymbolList "kmod_module_get_symbols"

module_symbol_get :: Ptr List -> IO (Symbol,CRC)
module_symbol_get p = do s <- module_symbol_get_symbol p >>= peekCString
                         c <- module_symbol_get_crc p
                         return (s,c)

toSymbolList :: Ptr List -> IO [(Symbol,CRC)]
toSymbolList = toList
               module_symbol_get
               module_symbols_free_list

foreign import ccall "kmod_module_version_get_crc"
  module_version_get_crc :: Ptr List -> IO CRC


foreign import ccall "kmod_module_version_get_symbol"
  module_version_get_symbol :: Ptr List -> IO CString

foreign import ccall "kmod_module_versions_free_list"
  module_versions_free_list :: Ptr List -> IO ()

foreign import ccall "kmod_module_get_versions"
  module_get_versions :: Ptr Module -> Ptr (Ptr List) -> IO CInt

-- | Get the list of entries in ELF section \"__versions\".
moduleGetVersions :: Module -> IO [(Symbol,CRC)]
moduleGetVersions =
    moduleGet module_get_versions toVersionList "kmod_module_get_versions"

module_version_get :: Ptr List -> IO (Symbol,CRC)
module_version_get p = do s <- module_version_get_symbol p >>= peekCString
                          c <- module_version_get_crc p
                          return (s,c)

toVersionList :: Ptr List -> IO [(Symbol,CRC)]
toVersionList = toList
                module_version_get
                module_versions_free_list



foreign import ccall "kmod_module_info_get_key"
        module_info_get_key :: Ptr List
                            -> IO CString

foreign import ccall "kmod_module_info_get_value"
        module_info_get_value :: Ptr List
                              -> IO CString

foreign import ccall "kmod_module_get_info"
  module_get_info :: Ptr Module
                  -> Ptr (Ptr List)
                  -> IO CInt

-- | Get the list of entries in ELF section \".modinfo\", these
-- contain alias, license, depends, vermagic and other keys with
-- respective values.
moduleGetInfo :: Module -> IO [(Key,Value)]
moduleGetInfo =
    moduleGet module_get_info toInfoList "kmod_module_get_info"

module_info_get :: Ptr List -> IO (String, String)
module_info_get p = do k <- module_info_get_key p >>= peekCString
                       v <- module_info_get_value p >>= peekCString
                       return (k,v)

foreign import ccall "kmod_module_info_free_list"
        module_info_free_list :: Ptr List
                              -> IO ()

toInfoList :: Ptr List -> IO [(String, String)]
toInfoList = toList
             module_info_get
             module_info_free_list

-- | Possible values of initialization state of a @'Module'@.
data Initstate = Builtin	 
               | Live	 
               | Coming	 
               | Going
                 deriving (Eq, Show)


toInitstate :: (Eq a, Num a, Show a) => a -> Initstate
toInitstate 0 = Builtin
{-# LINE 956 "System/Linux/KMod.hsc" #-}
toInitstate 1 = Live
{-# LINE 957 "System/Linux/KMod.hsc" #-}
toInitstate 2 = Coming
{-# LINE 958 "System/Linux/KMod.hsc" #-}
toInitstate 3 = Going
{-# LINE 959 "System/Linux/KMod.hsc" #-}
toInitstate n = error ("System.KMod.toInitstate: bad argument: " ++ show n)

foreign import ccall "kmod_module_new_from_loaded"
  module_new_from_loaded :: Ptr Context -> Ptr (Ptr List) -> IO CInt

-- | Get the list of @'Module'@s currently loaded in kernel.
moduleNewFromLoaded :: Context -> IO [Module]
moduleNewFromLoaded c =
    withContext c $ \p ->
        alloca $ \q ->
            do throwIfNeg_ (\e -> "kmod_module_new_from_loaded retuned " ++
                            show e ) $ module_new_from_loaded p q
               peek q >>= toModuleList

foreign import ccall "kmod_module_get_initstate"
  module_get_initstate :: Ptr Module -> IO CInt

-- | Get the @'Initstate'@ of the given @'Module'@, as returned by
-- Linux Kernel, by reading @\/sys@ filesystem.
moduleGetInitstate :: Module -> IO Initstate
moduleGetInitstate m =
    withModule m $ \p ->
        (throwIfNeg (\e -> "kmod_module_get_initstate returned " ++ show e) $
         module_get_initstate p)
        >>= return . toInitstate

foreign import ccall "kmod_module_get_size"
  module_get_size :: Ptr Module -> IO CInt

-- | Get the size of the given @'Module'@ as returned by Linux kernel.
moduleGetSize :: Module -> IO Int
moduleGetSize m = withModule m module_get_size >>= return . fromIntegral

foreign import ccall "kmod_module_get_refcnt"
  module_get_refcnt :: Ptr Module -> IO CInt

-- | Get the ref count of the given @'Module'@, as returned by Linux
-- kernel, by reading @\/sys@ filesystem.
moduleGetRefcnt :: Module -> IO Int
moduleGetRefcnt m = withModule m module_get_refcnt >>= return . fromIntegral

foreign import ccall "kmod_module_get_holders"
  module_get_holders :: Ptr Module -> IO (Ptr List)

-- | Get the list of @'Module'@s that are holding the given
-- @'Module'@, as returned by Linux kernel.
moduleGetHolders :: Module -> IO [Module]
moduleGetHolders m = withModule m (\p -> module_get_holders p >>= toModuleList)