packages feed

NetSNMP 0.2.0.2 → 0.3.0.1

raw patch · 2 files changed

+87/−111 lines, 2 filesdep +bytestringPVP ok

version bump matches the API change (PVP)

Dependencies added: bytestring

API changes (from Hackage documentation)

+ Network.Protocol.NetSNMP: type OIDpart = CULong
+ Network.Protocol.NetSNMP: type RawOID = [OIDpart]
- Network.Protocol.NetSNMP: IpAddress :: String -> [Word8] -> ASNValue
+ Network.Protocol.NetSNMP: IpAddress :: ByteString -> [Word8] -> ASNValue
- Network.Protocol.NetSNMP: OID :: String -> String -> [Word32] -> ASNValue
+ Network.Protocol.NetSNMP: OID :: ByteString -> ByteString -> [Word32] -> ASNValue
- Network.Protocol.NetSNMP: OctetString :: String -> [Word8] -> ASNValue
+ Network.Protocol.NetSNMP: OctetString :: ByteString -> [Word8] -> ASNValue
- Network.Protocol.NetSNMP: SnmpResult :: String -> ASNValue -> SnmpResult
+ Network.Protocol.NetSNMP: SnmpResult :: RawOID -> ASNValue -> SnmpResult
- Network.Protocol.NetSNMP: TimeTicks :: String -> Word32 -> ASNValue
+ Network.Protocol.NetSNMP: TimeTicks :: ByteString -> Word32 -> ASNValue
- Network.Protocol.NetSNMP: Unsupported :: Int -> String -> ASNValue
+ Network.Protocol.NetSNMP: Unsupported :: Int -> ByteString -> ASNValue
- Network.Protocol.NetSNMP: oid :: SnmpResult -> String
+ Network.Protocol.NetSNMP: oid :: SnmpResult -> RawOID
- Network.Protocol.NetSNMP: snmpBulkWalk :: Hostname -> Community -> String -> IO (Either String [SnmpResult])
+ Network.Protocol.NetSNMP: snmpBulkWalk :: Hostname -> Community -> RawOID -> IO (Either String [SnmpResult])
- Network.Protocol.NetSNMP: snmpGet :: SnmpVersion -> Hostname -> Community -> String -> IO (Either String SnmpResult)
+ Network.Protocol.NetSNMP: snmpGet :: SnmpVersion -> Hostname -> Community -> RawOID -> IO (Either String SnmpResult)
- Network.Protocol.NetSNMP: snmpNext :: SnmpVersion -> Hostname -> Community -> String -> IO (Either String SnmpResult)
+ Network.Protocol.NetSNMP: snmpNext :: SnmpVersion -> Hostname -> Community -> RawOID -> IO (Either String SnmpResult)
- Network.Protocol.NetSNMP: snmpWalk :: SnmpVersion -> Hostname -> Community -> String -> IO (Either String [SnmpResult])
+ Network.Protocol.NetSNMP: snmpWalk :: SnmpVersion -> Hostname -> Community -> RawOID -> IO (Either String [SnmpResult])
- Network.Protocol.NetSNMP: type Community = String
+ Network.Protocol.NetSNMP: type Community = ByteString
- Network.Protocol.NetSNMP: type Hostname = String
+ Network.Protocol.NetSNMP: type Hostname = ByteString

Files

NetSNMP.cabal view
@@ -1,6 +1,6 @@  Name:               NetSNMP-Version:            0.2.0.2+Version:            0.3.0.1 Synopsis:           Bindings for net-snmp's C API for clients Description:        Network.Protocol.NetSNMP is a partial binding to the                     library portion of the net-snmp package.  It uses the@@ -23,6 +23,7 @@ library   hs-source-dirs:   src   build-depends:    base >=4 && <5+                   ,bytestring >= 0.10 && < 0.11   build-tools:      hsc2hs   exposed-modules:  Network.Protocol.NetSNMP   extensions:       ForeignFunctionInterface, CPP, EmptyDataDecls
src/Network/Protocol/NetSNMP.hsc view
@@ -1,5 +1,5 @@ -{-# LANGUAGE CPP, DoAndIfThenElse, ForeignFunctionInterface, EmptyDataDecls #-}+{-# LANGUAGE CPP, DoAndIfThenElse, ForeignFunctionInterface, EmptyDataDecls, OverloadedStrings #-}  ---------------------------------------------------------------------- -- |@@ -16,7 +16,7 @@  module Network.Protocol.NetSNMP (   -- * Types-  ASNValue(..), SnmpResult(..), SnmpVersion(..),+  ASNValue(..), SnmpResult(..), SnmpVersion(..), RawOID, OIDpart,   Hostname, Community,   -- * Constants   snmp_version_1, snmp_version_2c, snmp_version_3,@@ -32,7 +32,10 @@  import           Control.Applicative import           Control.Monad+import           Data.ByteString (ByteString)+import qualified Data.ByteString as B import           Data.List+import		 Data.String import           Foreign import           Foreign.C.String import           Foreign.C.Types@@ -72,6 +75,14 @@ type OIDpart = CULong #endif +type RawOID = [OIDpart]++showOid :: RawOID -> String+showOid = concatMap (('.':) . show)++oidToByteString :: RawOID -> ByteString+oidToByteString = fromString . showOid+ -- I don't know whether (or which of) net-snmp's library functions -- account for bytesex; there may be endian bugs lurking here. data CVarList                           -- C struct variable_list@@ -80,31 +91,31 @@ --     returned in more than one format for different uses.  Some --     include a descriptive string built by the underlying C library. data ASNValue-  = OctetString String [Word8] -- ^@ASN_OCTET_STR@ Returned as a character+  = OctetString ByteString [Word8] -- ^@ASN_OCTET_STR@ Returned as a character                                --     string, and as opaque data.-  | OID String String [Word32] -- ^@ASN_OBJECT_ID@ Returned as the C library's+  | OID ByteString ByteString [Word32] -- ^@ASN_OBJECT_ID@ Returned as the C library's                                --     description, a dotted-decimal string, and                                --     a numeric list   | Integer32   Int32          -- ^@ASN_INTEGER@  32bit signed   | Integer64   Int64          -- ^@ASN_INTEGER64@  64bit signed   | Counter32   Word32         -- ^@ASN_COUNTER@ 32bit nondecreasing-  | Counter64   Word64          -- ^@ASN_COUNTER64@ 64bit nondecreasing+  | Counter64   Word64         -- ^@ASN_COUNTER64@ 64bit nondecreasing   | Unsigned32  Word32         -- ^@ASN_UNSIGNED@ 32bit unsigned   | Unsigned64  Word64         -- ^@ASN_UNSIGNED64@ 64bit unsigned   | Gauge32     Word32         -- ^@ASN_GAUGE@ 32bit signed with min and max-  | IpAddress   String [Word8] -- ^@ASN_IPADDRESS@ IP address in string+  | IpAddress   ByteString [Word8] -- ^@ASN_IPADDRESS@ IP address in string                                --     and numeric form. Example:                                --     (IpAddress \"1.2.3.4\" [1,2,3,4])   | Opaque      [Word8]        -- ^@ASN_OPAQUE@ (Deprecated) application                                --     specific data.  Use OctetString instead.-  | TimeTicks   String Word32  -- ^@ASN_TIMETICKS@ Time interval in 1/100 sec+  | TimeTicks   ByteString Word32  -- ^@ASN_TIMETICKS@ Time interval in 1/100 sec                                --     ticks.  The C library's description is                                --     returned along with the raw value.   | Boolean     Bool           -- ^@ASN_BOOLEAN@ Unimplemented.   | IEEEFloat   Float          -- ^@ASN_FLOAT@ IEEE float. Unimplemented.   | IEEEDouble  Double         -- ^@ASN_DOUBLE@ IEEE double. Unimplemented.   | Null                       -- ^@ASN_NULL@ Null value-  | Unsupported Int String     -- ^Unsupported type from an agent.  Returns+  | Unsupported Int ByteString -- ^Unsupported type from an agent.  Returns                                --     the numeric type and the C library's                                --     description of the value.   deriving (Eq, Show)@@ -112,7 +123,7 @@ -- |An SNMP value together with its OID.  Returned by the query --     routines 'snmpGet', 'snmpNext', and 'snmpWalk'. data SnmpResult  = SnmpResult {-  oid   :: String,             -- ^Dotted-decimal ObjectId of the value+  oid   :: RawOID,           -- ^Dotted-decimal ObjectId of the value   value :: ASNValue            -- ^Typed representation of the value   } deriving (Eq, Show) @@ -122,8 +133,8 @@   unSnmpVersion :: CLong -- ^Numeric version.  Generally unneeded.   } deriving (Eq, Show) -type Hostname  = String-type Community = String+type Hostname  = ByteString+type Community = ByteString  -- SNMP protocol versions, omitting those that will never be supported -- (see README)@@ -230,11 +241,10 @@ initialize :: IO () initialize = do   withCString "Haskell bindings" c_init_snmp-  withCString "127.0.0.1" $ \localhost -> do-  withCString "public"    $ \public    -> do-  alloca                  $ \session   -> runTrouble $-    (readyCommunitySession snmp_version_2c localhost public session)-      >>= closeSession+  withCString "127.0.0.1" $ \localhost -> +    withCString "public"  $ \public    -> +    alloca                $ \session   -> runTrouble $ +    readyCommunitySession snmp_version_2c localhost public session >>= closeSession   return ()  -- |Create an abstract session, suitable for reuse, responsible@@ -266,20 +276,20 @@ -- -- Examples: ----- * snmpGet \"localhost\" \"public\" \".1.3.6.1.2.1.1.1.0\"+-- * snmpGet \"localhost\" \"public\" [1,3,6,1,2,1,1,1,0] ----- * snmpGet \"tcp:localhost:5161\" \"mypassword\" \".1.3.6.1.2.1.1.1.0\"+-- * snmpGet \"tcp:localhost:5161\" \"mypassword\" [1,3,6,1,2,1,1,1,0] snmpGet   :: SnmpVersion -- ^'snmp_version_1' or 'snmp_version_2c'   -> Hostname    -- ^IP or hostname of the agent to be queried.  May have                  --     prefix of @tcp:@ or suffix of @:port@   -> Community   -- ^SNMP community (password)-  -> String      -- ^OID to be queried+  -> RawOID         -- ^OID to be queried   -> IO (Either String SnmpResult) snmpGet version hostname community oid =-  withCString hostname  $ \cshost  ->-  withCString community $ \cscomm  ->-  alloca                $ \session ->+  B.useAsCString hostname  $ \cshost  ->+  B.useAsCString community $ \cscomm  ->+  alloca                   $ \session ->   runTrouble $ bracketT     (readyCommunitySession version cshost cscomm session)     closeSession@@ -290,20 +300,20 @@ -- -- Examples: ----- * snmpNext \"localhost\" \"public\" \".1.3.6.1.2.1.1.1.0\"+-- * snmpNext \"localhost\" \"public\" [1,3,6,1,2,1,1,1,0] ----- * snmpNext \"tcp:localhost:5161\" \"mypassword\" \".1.3.6.1.2.1.1.1.0\"+-- * snmpNext \"tcp:localhost:5161\" \"mypassword\" [1,3,6,1,2,1,1,1,0] snmpNext   :: SnmpVersion -- ^'snmp_version_1' or 'snmp_version_2c'   -> Hostname    -- ^IP or hostname of the agent to be queried.  May have                  --     prefix of @tcp:@ or suffix of @:port@   -> Community   -- ^SNMP community (password)-  -> String      -- ^OID to be queried+  -> RawOID         -- ^OID to be queried   -> IO (Either String SnmpResult) snmpNext version hostname community oid =-  withCString hostname  $ \cshost  ->-  withCString community $ \cscomm  ->-  alloca                $ \session ->+  B.useAsCString hostname  $ \cshost  ->+  B.useAsCString community $ \cscomm  ->+  alloca                   $ \session ->   runTrouble $ bracketT     (readyCommunitySession version cshost cscomm session)     closeSession@@ -319,26 +329,26 @@ -- -- Examples: ----- * snmpWalk snmp_version_2c \"localhost\" \"public\" \".1.3.6.1.2.1.1\"+-- * snmpWalk snmp_version_2c \"localhost\" \"public\" [1,3,6,1,2,1,1] ----- * snmpWalk snmp_version_2c \"tcp:localhost:5161\" \"mypassword\" \".1.3.6.1.2.1.1\"+-- * snmpWalk snmp_version_2c \"tcp:localhost:5161\" \"mypassword\" [1,3,6,1,2,1,1] snmpWalk   :: SnmpVersion -- ^'snmp_version_1' or 'snmp_version_2c'   -> Hostname    -- ^IP or hostname of the agent to be queried.  May have                  --     prefix of @tcp:@ or suffix of @:port@   -> Community   -- ^SNMP community (password)-  -> String      -- ^OID to be queried+  -> RawOID         -- ^OID to be queried   -> IO (Either String [SnmpResult]) snmpWalk version hostname community walkoid =-    withCString hostname  $ \cshost  ->-    withCString community $ \cscomm  ->-    alloca                $ \session ->+    B.useAsCString hostname  $ \cshost  ->+    B.useAsCString community $ \cscomm  ->+    alloca                   $ \session ->     runTrouble $ bracketT       (readyCommunitySession version cshost cscomm session)       closeSession       (go walkoid . mkSnmpGet snmp_msg_getnext)   where-    go :: String -> (String -> Trouble SnmpResult) -> Trouble [SnmpResult]+    go :: RawOID -> (RawOID -> Trouble SnmpResult) -> Trouble [SnmpResult]     go oid next = do       v@(SnmpResult nextoid val) <- next oid       case () of@@ -353,42 +363,37 @@ -- -- Examples: ----- * snmpBulkWalk \"localhost\" \"public\" \".1.3.6.1.2.1.1\"+-- * snmpBulkWalk \"localhost\" \"public\" [1,3,6,1,2,1,1] ----- * snmpBulkWalk \"tcp:localhost:5161\" \"mypassword\" \".1.3.6.1.2.1.1\"+-- * snmpBulkWalk \"tcp:localhost:5161\" \"mypassword\" [1,3,6,1,2,1,1] snmpBulkWalk    :: Hostname    -- ^IP or hostname of the agent to be queried.  May have                  --     prefix of @tcp:@ or suffix of @:port@   -> Community   -- ^SNMP community (password)-  -> String      -- ^OID to be queried+  -> RawOID         -- ^OID to be queried   -> IO (Either String [SnmpResult]) snmpBulkWalk hostname community walkoid =-    withCString hostname  $ \cshost  ->-    withCString community $ \cscomm  ->-    alloca                $ \session ->+    B.useAsCString hostname  $ \cshost  ->+    B.useAsCString community $ \cscomm  ->+    alloca                   $ \session ->     runTrouble $ bracketT       (readyCommunitySession snmp_version_2c cshost cscomm session)       closeSession       (bulkWalk walkoid walkoid)   where-    bulkWalk :: String -> String -> Session -> Trouble [SnmpResult]+    bulkWalk :: RawOID -> RawOID -> Session -> Trouble [SnmpResult]     bulkWalk rootoid startoid session = do-      vals <- filter (\r -> (oid r) `isSubIdOf` rootoid) <$> mkSnmpBulkGet 0 50 startoid session+      vals <- filter (\r -> oid r `isSubIdOf` rootoid) <$> mkSnmpBulkGet 0 50 startoid session       case vals of         [] -> return []         rs -> (vals ++) <$> bulkWalk rootoid (oid (last rs)) session-    isSubIdOf :: String -> String -> Bool-    isSubIdOf oa ob = (splitAt '.' ob) `isPrefixOf` (splitAt '.' oa)-    splitAt :: Char -> String -> [String]-    splitAt c s = case dropWhile (c ==) s of-                      "" -> []-                      s' -> w : splitAt c s''-                            where (w, s'') = break (c ==) s'+    isSubIdOf :: RawOID -> RawOID -> Bool+    isSubIdOf oa ob = ob `isPrefixOf` oa    -- getbulk, using session info from a 'data Session' and -- the supplied oid -- It is the caller's obligation to ensure the session's validity.  -mkSnmpBulkGet :: CLong -> CLong -> String -> Session -> Trouble [SnmpResult]+mkSnmpBulkGet :: CLong -> CLong -> RawOID -> Session -> Trouble [SnmpResult] mkSnmpBulkGet non_repeaters max_repetitions oid session =   allocaArrayT (fromIntegral max_oid_len) $ \oids -> do   let version = getVersion session@@ -400,17 +405,17 @@ -- get or getnext, using session info from a 'data Session' and -- the supplied oid -- It is the caller's obligation to ensure the session's validity.-mkSnmpGet :: SnmpPDUType -> Session -> String -> Trouble SnmpResult+mkSnmpGet :: SnmpPDUType -> Session -> RawOID -> Trouble SnmpResult mkSnmpGet pdutype session oid = do-  res <- (allocaArrayT (fromIntegral max_oid_len) $ \oids -> do+  res <- allocaArrayT (fromIntegral max_oid_len) $ \oids -> do          let version = getVersion session          pdu_req <- buildPDU pdutype oid oids version-         dispatchSnmpReq pdu_req session)-  if (res == []) then throwT ("Could not get the snmp value at " ++ oid)+         dispatchSnmpReq pdu_req session+  if null res then throwT ("Could not get the snmp value at " ++ showOid oid)   else return $ head res  dispatchSnmpReq :: Ptr SnmpPDU -> Session -> Trouble [SnmpResult]-dispatchSnmpReq pdu_req session = do+dispatchSnmpReq pdu_req session =    allocaT $ \response_ptr -> do   let sessp = getSessp session   let sptr = getSptr session@@ -431,12 +436,11 @@       return vars)    -- caller is obliged to ensure rv is valid and non-null-vlist2oid :: Ptr CVarList -> Trouble String+vlist2oid :: Ptr CVarList -> Trouble RawOID vlist2oid rv = do   oidptr <- peekVariableName rv   len    <- peekVariableLen  rv-  oids   <- peekArrayT (fromIntegral len) oidptr :: Trouble [OIDpart]-  return $ concatMap (('.':) . show) oids+  peekArrayT (fromIntegral len) oidptr  extractVars :: Ptr CVarList -> Trouble [SnmpResult] extractVars rv@@ -473,7 +477,7 @@ extractOctetStr rv = do   ptr <- peekVariableValString rv   len <- peekVariableValLen rv-  s <- peekCStringLenT (ptr , (fromIntegral len))+  s <- peekCStringLenT (ptr , fromIntegral len)   octets <- peekArrayT (fromIntegral len) (castPtr ptr)   return (OctetString s octets) @@ -496,7 +500,7 @@ extractIpAddress rv = do   ptr <- peekVariableValInt rv   octets <- peekArrayT 4 (castPtr ptr) :: Trouble [Word8]-  let str = foldr1 (\a b -> (a++"."++b)) (map show octets)+  let str = B.intercalate "." (map (fromString . show) octets)   return (IpAddress str octets)  extractOID :: Ptr CVarList -> Trouble ASNValue@@ -504,10 +508,10 @@   oidptr <- peekVariableValObjid rv :: Trouble (Ptr OIDpart)   len <- peekVariableValLen rv   let oidlen = (fromIntegral len) `div` #{size oid}-  oids <- peekArrayT oidlen oidptr :: Trouble [OIDpart]-  let str = concatMap (('.':) . show) oids+  oid <- peekArrayT oidlen oidptr :: Trouble RawOID+  let str = oidToByteString oid   descr <- rawvar2cstring rv-  return (OID descr str (map fromIntegral oids))+  return (OID descr str (map fromIntegral oid))  extractTimeTicks rv = do   intptr <- peekVariableValInt rv@@ -520,35 +524,35 @@ --     and this function may disappear in a future version. showASNValue :: ASNValue -> String showASNValue v = case v of-  OctetString s _     -> s-  IpAddress   s _     -> s+  OctetString s _     -> show s+  IpAddress   s _     -> show s   Counter32   c       -> show c    Gauge32     c       -> show c -  OID         d os ol -> os+  OID         d os ol -> show os   Opaque      cs      -> show cs   Integer32   c       -> show c   Unsigned32  c       -> show c    Counter64   c       -> show c    Integer64   c       -> show c   Unsigned64  c       -> show c -  TimeTicks   s _     -> s+  TimeTicks   s _     -> show s   Boolean     c       -> show c   IEEEDouble  c       -> show c   IEEEFloat   c       -> show c   Null                -> "ASN_NULL"-  Unsupported t s     -> "Unknown type " ++ show t ++ ": " ++ s+  Unsupported t s     -> "Unknown type " ++ show t ++ ": " ++ show s  -- allocates space for the pdu request; snmp_sess_synch_response -- appears to free the request pdu buildPDU   :: SnmpPDUType  -- eg. snmp_msg_get-  -> String       -- eg. ".1.3.6.1.2.1.1.1.0"+  -> RawOID       -- eg. [1,3,6,1,2,1,1,1,0]   -> Ptr OIDpart  -- OIDpart array passed in b/c I don't know when it dallocs   -> SnmpVersion  -- eg. snmp_version_1 or snmp_version_2c   -> Trouble (Ptr SnmpPDU) -- returns pdu and oid length buildPDU pdutype oid oids version =-  withCStringT oid $ \oid_cstr   ->-  allocaT              $ \oidlen_ptr -> do+  withCStringT (showOid oid) $ \oid_cstr   ->+  allocaT                    $ \oidlen_ptr -> do     pdu_req <- t_snmp_pdu_create pdutype     pokePDUVersion pdu_req (unSnmpVersion version)     pokePDUCommand pdu_req (unSnmpPDUType pdutype)@@ -558,7 +562,7 @@     t_snmp_add_null_var pdu_req oids oidlen     return pdu_req -rawvar2cstring :: Ptr CVarList -> Trouble String+rawvar2cstring :: Ptr CVarList -> Trouble ByteString rawvar2cstring rv =   allocaArray0T max_string_len $ \buf -> do   rc <- t_snprint_by_type buf (fromIntegral max_string_len) rv@@ -577,8 +581,8 @@ withCStringT :: String -> (CString -> Trouble b) -> Trouble b withCStringT s f = Trouble $ withCString s $ \p -> runTrouble (f p) -peekCStringT    = hoistT1 peekCString-peekCStringLenT = hoistT1 peekCStringLen+peekCStringT    = hoistT1 B.packCString+peekCStringLenT = hoistT1 B.packCStringLen  peekT :: (Storable a) => Ptr a -> Trouble a peekT = hoistT1 peek@@ -736,20 +740,12 @@         -> Ptr (Ptr SnmpPDU) -> IO CInt  -- improved (?) version with fuller error handling-t_snmp_sess_synch_response :: Ptr SnmpSession -> Ptr SnmpSession-  -> Ptr SnmpPDU -> Ptr (Ptr SnmpPDU) -> Trouble ()+t_snmp_sess_synch_response :: Ptr SnmpSession -> Ptr SnmpSession -> Ptr SnmpPDU -> Ptr (Ptr SnmpPDU) -> Trouble () t_snmp_sess_synch_response sessp sptr pdu_req response_ptr = Trouble $ do   success <- c_snmp_sess_synch_response sessp pdu_req response_ptr-  -- snmpSessError was giving bus errors on x86_64-  if (success == snmp_stat_success)+  if success == snmp_stat_success     then return (Right ())     else Left <$> snmpError sptr-  -- return $ case () of-  --   _ | success == snmp_stat_success -> Right ()-  --     | success == snmp_stat_error   -> Left "snmp_sess_synch_response error"-  --     | success == snmp_stat_timeout -> Left "snmp_sess_synch_response timeout"-  --     | otherwise -> Left-  --         ("snmp_sess_synch_response unknown error code " ++ show success)  -- Deallocate PDU struct foreign import ccall unsafe "net-snmp/net-snmp-includes.h snmp_free_pdu"@@ -787,9 +783,9 @@       -> Ptr CString -> IO ()  snmpError :: Ptr SnmpSession -> IO String-snmpError p = do-  alloca $ \libp -> do  -- pointer to library error code-  alloca $ \sysp -> do  -- pointer to system error code+snmpError p = +  alloca $ \libp ->     -- pointer to library error code+  alloca $ \sysp ->     -- pointer to system error code   alloca $ \errp -> do  -- pointer to error CString   c_snmp_error p libp sysp errp   liberr <- peek libp@@ -809,27 +805,6 @@     c_snmp_sess_error :: Ptr SnmpSession -> Ptr CInt -> Ptr CInt       -> Ptr CString -> IO () --- Using abbreviated error routine because th c_snmp_sess_error--- call appears to be giving bus errors on my x86_64 test machine.-snmpSessError :: Ptr SnmpSession -> IO String-snmpSessError sptr =-  return ("disabled snmp_sess_error check; sptr=" ++ show sptr)--_snmpSessError :: Ptr SnmpSession -> IO String-_snmpSessError sptr | sptr == nullPtr = return "snmp error; null sptr"-_snmpSessError sptr = do-  alloca $ \libp -> do  -- pointer to library error code-  alloca $ \sysp -> do  -- pointer to system error code-  alloca $ \errp -> do  -- pointer to error CString-  c_snmp_sess_error sptr libp sysp errp-  liberr <- peek libp :: IO CInt-  syserr <- peek sysp :: IO CInt-  cserr  <- peek errp-  err    <- peekCString cserr-  free cserr-  return $ "snmpSessError: lib:" ++ show liberr ++ " ; sys:" ++ show syserr-           ++ " ; " ++ err- -- int snprint_by_type(char *buf, size_t buf_len, netsnmp_variable_list * var, --   const struct enum_list *enums, const char *hint, const char *units); foreign import ccall unsafe "net-snmp/net-snmp-includes.h snprint_by_type"@@ -924,4 +899,4 @@ hoistTE6 e f a = hoistTE5 e (f a)  predToMaybe :: (a -> Bool) -> b -> a -> Maybe b-predToMaybe p b a = if (p a) then Just b else Nothing+predToMaybe p b a = if p a then Just b else Nothing