posix-api 0.3.4.0 → 0.3.5.0
raw patch · 15 files changed
+771/−113 lines, 15 filesdep +byte-orderdep +run-st
Dependencies added: byte-order, run-st
Files
- CHANGELOG.md +9/−0
- cbits/HaskellPosix.c +37/−0
- include/HaskellPosix.h +17/−2
- posix-api.cabal +6/−1
- src/Foreign/C/String/Managed.hs +138/−0
- src/Linux/Systemd.hs +3/−3
- src/Posix/File.hs +149/−5
- src/Posix/File/Types.hsc +47/−0
- src/Posix/MessageQueue.hs +16/−14
- src/Posix/MessageQueue/Types.hsc +0/−21
- src/Posix/Socket.hs +113/−52
- src/Posix/Socket/Types.hsc +132/−15
- src/Posix/Struct/AddressInfo/Peek.hsc +60/−0
- src/Posix/Struct/SocketAddressInternet/Peek.hsc +38/−0
- src/Posix/Types.hsc +6/−0
CHANGELOG.md view
@@ -7,6 +7,15 @@ This project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/). +## [0.3.5.0] - 2020-??-??++- Breaking: Start using pattern synonyms for macros.+- Add dedicated modules for peeking at structures.+- Make compatible with GHC 8.10 by changing the way ArrayArray# is handled+ on the C side of the FFI.+- Add `uninterruptibleSetSocketOption`.+- Add socket options `SO_BINDTODEVICE` and `SO_REUSEADDR`.+ ## [0.3.4.0] - 2020-03-09 - Add `Posix.File`
cbits/HaskellPosix.c view
@@ -26,6 +26,21 @@ // way to support providing an offset (without just copying the bytes // into pinned memory) is to use a wrapper. +ssize_t write_offset_loop(int fd, const char *message, HsInt offset, size_t length){+ ssize_t r;+ size_t bytesSent;+ const char* buf = message + offset;+ while(length > 0){+ if ((r = write(fd, (const void*)buf, length)) == -1) {+ return (-1);+ } else {+ bytesSent = (size_t)r;+ buf = buf + bytesSent;+ length = length - bytesSent;+ }+ }+ return 0;+} ssize_t write_offset(int fd, const char *message, HsInt offset, size_t length){ return write(fd, (const void*)(message + offset), length); }@@ -105,16 +120,24 @@ return setsockopt(socket,level,option_name,&option_value,sizeof(int)); } +// There are some compatibility macros in here. Before GHC 8.10,+// no offset is applied to ArrayArray#. ssize_t sendmsg_bytearrays ( int sockfd+#if __GLASGOW_HASKELL__ >= 810+ , StgArrBytes **arrs // used for input+#else , StgMutArrPtrs *arr // used for input+#endif , HsInt off // offset into input chunk array , HsInt len0 // number of chunks to send , HsInt offC // offset into first chunk , int flags ) {+#if __GLASGOW_HASKELL__ < 810 StgClosure **arrPayload = arr->payload; StgArrBytes **arrs = (StgArrBytes**)arrPayload;+#endif struct iovec bufs[MAX_BYTEARRAYS]; HsInt len1 = len0 > MAX_BYTEARRAYS ? MAX_BYTEARRAYS : len0; // We must handle the first chunk specially since@@ -194,16 +217,24 @@ return sendmsg(sockfd,&msg,flags); } +// There are some compatibility macros in here. Before GHC 8.10,+// no offset is applied to ArrayArray#. int recvmmsg_sockaddr_in ( int sockfd , int *lens // used for output , struct sockaddr_in *addrs // used for output+#if __GLASGOW_HASKELL__ >= 810+ , StgArrBytes **bufs // used for output+#else , StgMutArrPtrs *arr // used for output+#endif , unsigned int vlen , int flags ) {+#if __GLASGOW_HASKELL__ < 810 StgClosure **bufsX = arr->payload; StgArrBytes **bufs = (StgArrBytes**)bufsX;+#endif // TODO: It's probably better to statically pick // out a maximum size for these. On the C stack, // the cost of doing this is basically nothing.@@ -238,12 +269,18 @@ int recvmmsg_sockaddr_discard ( int sockfd , int *lens // used for output+#if __GLASGOW_HASKELL__ >= 810+ , StgArrBytes **bufs // used for output+#else , StgMutArrPtrs *arr // used for output+#endif , unsigned int vlen , int flags ) {+#if __GLASGOW_HASKELL__ < 810 StgClosure **bufsX = arr->payload; StgArrBytes **bufs = (StgArrBytes**)bufsX;+#endif struct mmsghdr msgs[vlen]; struct iovec vecs[vlen]; for(unsigned int i = 0; i < vlen; i++) {
include/HaskellPosix.h view
@@ -12,6 +12,21 @@ int setsockopt_int(int socket, int level, int option_name, int option_value); -int recvmmsg_sockaddr_in (int sockfd , int *lens , struct sockaddr_in *addrs , StgMutArrPtrs *arr , unsigned int vlen , int flags);-int recvmmsg_sockaddr_discard (int sockfd , int *lens , StgMutArrPtrs *arr , unsigned int vlen , int flags);+// There are some compatibility macros in here. Before GHC 8.10,+// no offset is applied to ArrayArray#.+int recvmmsg_sockaddr_in (int sockfd , int *lens , struct sockaddr_in *addrs+#if __GLASGOW_HASKELL__ >= 810+ , StgArrBytes **bufs // used for output+#else+ , StgMutArrPtrs *arr // used for output+#endif+ , unsigned int vlen , int flags);++int recvmmsg_sockaddr_discard (int sockfd , int *lens+#if __GLASGOW_HASKELL__ >= 810+ , StgArrBytes **bufs // used for output+#else+ , StgMutArrPtrs *arr // used for output+#endif+ , unsigned int vlen , int flags);
posix-api.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: posix-api-version: 0.3.4.0+version: 0.3.5.0 synopsis: posix bindings description: This library provides a very thin wrapper around POSIX APIs. It can be@@ -78,6 +78,7 @@ library exposed-modules:+ Foreign.C.String.Managed Linux.Epoll Linux.MessageQueue Linux.Socket@@ -89,6 +90,8 @@ Posix.Select Posix.Socket Posix.Types+ Posix.Struct.SocketAddressInternet.Peek+ Posix.Struct.AddressInfo.Peek other-modules: Linux.Epoll.Types Linux.MessageQueue.Types@@ -101,11 +104,13 @@ Assertion build-depends: , base >=4.11.1 && <5+ , byte-order >= 0.1.2 && <0.2 , byteslice >= 0.1.3 && <0.3 , primitive >= 0.7 && <0.8 , primitive-addr >= 0.1 && <0.2 , primitive-offset >= 0.2 && <0.3 , primitive-unlifted >= 0.1 && <0.2+ , run-st >= 0.1.1 && <0.2 hs-source-dirs: src if flag(assertions) hs-source-dirs: src-assertions
+ src/Foreign/C/String/Managed.hs view
@@ -0,0 +1,138 @@+{-# language BangPatterns #-}+{-# language DerivingStrategies #-}+{-# language GeneralizedNewtypeDeriving #-}+{-# language MagicHash #-}+{-# language MultiWayIf #-}+{-# language TypeApplications #-}+{-# language ScopedTypeVariables #-}+{-# language UnboxedTuples #-}++module Foreign.C.String.Managed+ ( ManagedCString(..)+ , terminated+ , terminatedU+ , unterminated+ , fromBytes+ , fromLatinString+ , pinnedFromBytes+ , pin+ , touch+ , contents+ ) where++import Control.Monad.ST (ST)+import Control.Monad.ST.Run (runByteArrayST)+import Data.Bytes.Types (Bytes(Bytes))+import Data.Char (ord)+import Data.Primitive (ByteArray(..),MutableByteArray)+import Data.Word (Word8)+import Foreign.C.String (CString)+import Foreign.Ptr (castPtr)+import GHC.Exts (Int(I#),Char(C#),ByteArray#,chr#,touch#)+import GHC.IO (IO(IO))++import qualified Data.Bytes as Bytes+import qualified Data.Primitive as PM+import qualified GHC.Exts as Exts++-- | An unsliced byte sequence with @NUL@ as the final byte.+newtype ManagedCString = ManagedCString ByteArray+ deriving newtype Eq++instance Semigroup ManagedCString where+ ManagedCString a <> ManagedCString b = ManagedCString $ runByteArrayST $ do+ let lenA = PM.sizeofByteArray a+ let lenB = PM.sizeofByteArray b+ dst <- PM.newByteArray (lenA + lenB - 1)+ PM.copyByteArray dst 0 a 0 (lenA - 1)+ PM.copyByteArray dst (lenA - 1) b 0 lenB+ PM.unsafeFreezeByteArray dst++instance Monoid ManagedCString where+ mempty = ManagedCString $ runByteArrayST $ do+ dst <- PM.newByteArray 1+ PM.writeByteArray dst 0 (0 :: Word8)+ PM.unsafeFreezeByteArray dst++instance Exts.IsString ManagedCString where+ fromString = fromLatinString++instance Show ManagedCString where+ showsPrec _ (ManagedCString arr) s0 = PM.foldrByteArray+ ( \(w :: Word8) s ->+ if | w == 0 -> s+ | w < 32 -> '?' : s+ | w > 126 -> '?' : s+ | otherwise -> case fromIntegral @Word8 @Int w of+ I# i -> C# (chr# i) : s+ ) s0 arr++terminatedU :: ManagedCString -> ByteArray+terminatedU (ManagedCString x) = x++terminated :: ManagedCString -> Bytes+terminated (ManagedCString x) = Bytes.fromByteArray x++unterminated :: ManagedCString -> Bytes+unterminated (ManagedCString x) = Bytes x 0 (PM.sizeofByteArray x - 1)++-- | Copies the slice, appending a @NUL@ byte to the end.+fromBytes :: Bytes -> ManagedCString+fromBytes (Bytes arr off len) = ManagedCString $ runByteArrayST $ do+ dst <- PM.newByteArray (len + 1)+ PM.copyByteArray dst 0 arr off len+ PM.writeByteArray dst len (0 :: Word8)+ PM.unsafeFreezeByteArray dst++-- | Copies the slice into pinned memory, appending a @NUL@ byte to the end.+pinnedFromBytes :: Bytes -> ManagedCString+pinnedFromBytes (Bytes arr off len) = ManagedCString $ runByteArrayST $ do+ dst <- PM.newPinnedByteArray (len + 1)+ PM.copyByteArray dst 0 arr off len+ PM.writeByteArray dst len (0 :: Word8)+ PM.unsafeFreezeByteArray dst++pin :: ManagedCString -> ManagedCString+pin (ManagedCString x) = if PM.isByteArrayPinned x+ then ManagedCString x+ else ManagedCString $ runByteArrayST $ do+ let len = PM.sizeofByteArray x+ dst <- PM.newPinnedByteArray len+ PM.copyByteArray dst 0 x 0 len+ PM.unsafeFreezeByteArray dst++touch :: ManagedCString -> IO ()+touch (ManagedCString (ByteArray x)) = touchByteArray# x++touchByteArray# :: ByteArray# -> IO ()+touchByteArray# x = IO $ \s -> case touch# x s of s' -> (# s', () #)++-- | Convert a 'String' consisting of only characters representable+-- by ISO-8859-1. These are encoded with ISO-8859-1. Any character+-- with a codepoint above @U+00FF@ is replaced by an unspecified byte.+fromLatinString :: String -> ManagedCString+{-# noinline fromLatinString #-}+fromLatinString str = ManagedCString $ runByteArrayST $ do+ let lenPred0 = 63+ dst0 <- PM.newByteArray (lenPred0 + 1)+ go str dst0 0 lenPred0+ where+ go :: forall s. String -> MutableByteArray s -> Int -> Int -> ST s ByteArray+ go [] !dst !ix !_ = do+ PM.writeByteArray dst ix (0 :: Word8)+ PM.resizeMutableByteArray dst (ix + 1) >>= PM.unsafeFreezeByteArray+ go (c:cs) !dst !ix !lenPred = if ix < lenPred+ then do+ PM.writeByteArray dst ix (fromIntegral @Int @Word8 (ord c))+ go cs dst (ix + 1) lenPred+ else do+ let nextLenPred = lenPred * 2+ dst' <- PM.newByteArray (nextLenPred + 1)+ PM.copyMutableByteArray dst' 0 dst 0 ix+ PM.writeByteArray dst' ix (fromIntegral @Int @Word8 (ord c))+ go cs dst' (ix + 1) nextLenPred++-- | Get a pointer to the payload of the managed C string. The behavior is+-- undefined if the argument is not pinned.+contents :: ManagedCString -> CString+contents (ManagedCString x) = castPtr (PM.byteArrayContents x)
src/Linux/Systemd.hs view
@@ -6,13 +6,13 @@ import Foreign.C.Types (CInt(..)) import System.Posix.Types (Fd(..)) import Foreign.C.Error (Errno,getErrno)-import Posix.Socket.Types (Type(..),Domain(..))+import Posix.Socket.Types (Type(..),Family(..)) foreign import ccall unsafe "systemd/sd-daemon.h sd_listen_fds" c_listenFds :: CInt -> IO CInt foreign import ccall unsafe "systemd/sd-daemon.h sd_is_socket"- c_isSocket :: Fd -> Domain -> Type -> CInt -> IO CInt+ c_isSocket :: Fd -> Family -> Type -> CInt -> IO CInt -- | Check for file descriptors passed by the system manager. Returns -- the number of received file descriptors. If no file descriptors@@ -24,7 +24,7 @@ isSocket :: Fd -- ^ File descriptor- -> Domain -- ^ Socket family+ -> Family -- ^ Socket family -> Type -- ^ Socket type -> CInt -- ^ Positive: require listen mode. Zero: require non-listening mode. -> IO (Either Errno CInt)
src/Posix/File.hs view
@@ -1,6 +1,7 @@ {-# language BangPatterns #-} {-# language BinaryLiterals #-} {-# language MagicHash #-}+{-# language TypeApplications #-} {-# language UnliftedFFITypes #-} module Posix.File@@ -8,8 +9,19 @@ uninterruptibleGetDescriptorFlags , uninterruptibleGetStatusFlags , uninterruptibleWriteByteArray+ , uninterruptibleWriteBytes+ , uninterruptibleWriteBytesCompletely+ , uninterruptibleOpen+ , uninterruptibleOpenMode , writeByteArray+ , close+ , uninterruptibleClose+ , uninterruptibleErrorlessClose+ , uninterruptibleUnlink+ , uninterruptibleLink -- * Types+ , AccessMode(..)+ , CreationFlags(..) , DescriptorFlags(..) , StatusFlags(..) -- * File Descriptor Flags@@ -18,16 +30,27 @@ , isReadOnly , isWriteOnly , isReadWrite+ -- * Open Access Mode+ , Types.readOnly+ , Types.writeOnly+ , Types.readWrite+ -- * File Creation Flags+ , Types.create+ , Types.truncate+ , Types.exclusive ) where import Assertion (assertByteArrayPinned)-import Data.Bits ((.&.))-import Posix.File.Types (DescriptorFlags(..),StatusFlags(..))-import System.Posix.Types (Fd(..),CSsize(..))+import Data.Bits ((.&.),(.|.))+import Data.Primitive (ByteArray(..)) import Foreign.C.Error (Errno,getErrno)+import Foreign.C.String.Managed (ManagedCString(..)) import Foreign.C.Types (CInt(..),CSize(..))-import Data.Primitive (ByteArray(..)) import GHC.Exts (ByteArray#)+import Posix.File.Types (CreationFlags(..),AccessMode(..),StatusFlags(..))+import Posix.File.Types (DescriptorFlags(..))+import System.Posix.Types (Fd(..),CSsize(..),CMode(..))+import Data.Bytes.Types (Bytes(Bytes)) import qualified Posix.File.Types as Types @@ -50,9 +73,49 @@ foreign import ccall unsafe "HaskellPosix.h write_offset" c_unsafe_bytearray_write :: Fd -> ByteArray# -> Int -> CSize -> IO CSsize +foreign import ccall unsafe "HaskellPosix.h write_offset_loop"+ c_unsafe_bytearray_write_loop :: Fd -> ByteArray# -> Int -> CSize -> IO CInt+ foreign import ccall safe "HaskellPosix.h write_offset" c_safe_bytearray_write :: Fd -> ByteArray# -> Int -> CSize -> IO CSsize +foreign import ccall unsafe "HaskellPosix.h open"+ c_unsafe_open :: ByteArray# -> CInt -> IO Fd++foreign import ccall unsafe "HaskellPosix.h open"+ c_unsafe_open_mode :: ByteArray# -> CInt -> CMode -> IO Fd++foreign import ccall unsafe "HaskellPosix.h unlink"+ c_unsafe_unlink :: ByteArray# -> IO CInt++foreign import ccall unsafe "HaskellPosix.h link"+ c_unsafe_link :: ByteArray# -> ByteArray# -> IO CInt++foreign import ccall safe "unistd.h close"+ c_safe_close :: Fd -> IO CInt++foreign import ccall unsafe "unistd.h close"+ c_unsafe_close :: Fd -> IO CInt++uninterruptibleOpen ::+ ManagedCString -- ^ NULL-terminated file name+ -> AccessMode -- ^ Access mode+ -> CreationFlags -- ^ Creation flags+ -> StatusFlags -- ^ Status flags+ -> IO (Either Errno Fd)+uninterruptibleOpen (ManagedCString (ByteArray name)) (AccessMode x) (CreationFlags y) (StatusFlags z) =+ c_unsafe_open name (x .|. y .|. z) >>= errorsFromFd++uninterruptibleOpenMode ::+ ManagedCString -- ^ NULL-terminated file name+ -> AccessMode -- ^ Access mode, should include @O_CREAT@+ -> CreationFlags -- ^ Creation flags+ -> StatusFlags -- ^ Status flags+ -> CMode -- ^ Permissions assigned to newly created file+ -> IO (Either Errno Fd)+uninterruptibleOpenMode (ManagedCString (ByteArray name)) (AccessMode x) (CreationFlags y) (StatusFlags z) !mode =+ c_unsafe_open_mode name (x .|. y .|. z) mode >>= errorsFromFd+ errorsFromDescriptorFlags :: DescriptorFlags -> IO (Either Errno DescriptorFlags) errorsFromDescriptorFlags r@(DescriptorFlags x) = if x > (-1) then pure (Right r)@@ -63,6 +126,26 @@ then pure (Right r) else fmap Left getErrno +-- | Wrapper for @write(2)@ that takes a slice of bytes and an offset.+-- The byte array backing the slice does not need to be pinned.+uninterruptibleWriteBytesCompletely ::+ Fd -- ^ File descriptor+ -> Bytes -- ^ Source bytes+ -> IO (Either Errno ())+uninterruptibleWriteBytesCompletely !fd (Bytes (ByteArray buf) off len) =+ c_unsafe_bytearray_write_loop fd buf off (fromIntegral @Int @CSize len)+ >>= errorsFromInt_++-- | Wrapper for @write(2)@ that takes a slice of bytes and an offset.+-- The byte array backing the slice does not need to be pinned.+uninterruptibleWriteBytes ::+ Fd -- ^ File descriptor+ -> Bytes -- ^ Source bytes+ -> IO (Either Errno CSize) -- ^ Number of bytes written+uninterruptibleWriteBytes !fd (Bytes (ByteArray buf) off len) =+ c_unsafe_bytearray_write fd buf off (fromIntegral @Int @CSize len)+ >>= errorsFromSize+ -- | Wrapper for @write(2)@ that takes a byte array and an offset. -- The byte array does not need to be pinned. uninterruptibleWriteByteArray :: @@ -74,7 +157,8 @@ uninterruptibleWriteByteArray !fd (ByteArray buf) !off !len = c_unsafe_bytearray_write fd buf off len >>= errorsFromSize --- | Wrapper for @write(2)@ that takes a byte array and an offset. Uses @safe@ FFI. The byte array must be pinned.+-- | Wrapper for @write(2)@ that takes a byte array and an offset.+-- Uses @safe@ FFI. The byte array must be pinned. writeByteArray :: Fd -- ^ Socket -> ByteArray -- ^ Source byte array@@ -90,6 +174,57 @@ then pure (Right (cssizeToCSize r)) else fmap Left getErrno +errorsFromFd :: Fd -> IO (Either Errno Fd)+errorsFromFd r = if r > (-1)+ then pure (Right r)+ else fmap Left getErrno++uninterruptibleLink ::+ ManagedCString -- ^ Path to existing file+ -> ManagedCString -- ^ Path to new file+ -> IO (Either Errno ())+uninterruptibleLink (ManagedCString (ByteArray x)) (ManagedCString (ByteArray y)) =+ c_unsafe_link x y >>= errorsFromInt_++uninterruptibleUnlink ::+ ManagedCString -- ^ File name+ -> IO (Either Errno ())+uninterruptibleUnlink (ManagedCString (ByteArray x)) =+ c_unsafe_unlink x >>= errorsFromInt_++-- | Close a file descriptor.+-- The <http://pubs.opengroup.org/onlinepubs/009696899/functions/close.html POSIX specification>+-- includes more details. This uses the safe FFI.+close ::+ Fd -- ^ Socket+ -> IO (Either Errno ())+close fd = c_safe_close fd >>= errorsFromInt_++-- | Close a file descriptor. This uses the unsafe FFI. According to the+-- <http://pubs.opengroup.org/onlinepubs/009696899/functions/close.html POSIX specification>,+-- "If @fildes@ refers to a socket, @close()@ shall cause the socket to+-- be destroyed. If the socket is in connection-mode, and the @SO_LINGER@+-- option is set for the socket with non-zero linger time, and the socket+-- has untransmitted data, then @close()@ shall block for up to the current+-- linger interval until all data is transmitted."+uninterruptibleClose ::+ Fd -- ^ Socket+ -> IO (Either Errno ())+uninterruptibleClose fd = c_unsafe_close fd >>= errorsFromInt_++-- | Close a file descriptor with the unsafe FFI. Do not check for errors.+-- It is only appropriate to use this when a file descriptor is being+-- closed to handle an exceptional case. Since the user will want to+-- propogate the original exception, the exception provided by+-- 'uninterruptibleClose' would just be discarded. This function allows us+-- to potentially avoid an additional FFI call to 'getErrno'.+uninterruptibleErrorlessClose ::+ Fd -- ^ Socket+ -> IO ()+uninterruptibleErrorlessClose fd = do+ _ <- c_unsafe_close fd+ pure ()+ -- only call this when it is known that the argument is non-negative cssizeToCSize :: CSsize -> CSize cssizeToCSize = fromIntegral@@ -102,3 +237,12 @@ isReadWrite :: StatusFlags -> Bool isReadWrite (StatusFlags x) = x .&. 0b11 == 2++-- Sometimes, functions that return an int use zero to indicate+-- success and negative one to indicate failure without including+-- additional information in the value.+errorsFromInt_ :: CInt -> IO (Either Errno ())+errorsFromInt_ r = if r == 0+ then pure (Right ())+ else fmap Left getErrno+
src/Posix/File/Types.hsc view
@@ -22,11 +22,23 @@ module Posix.File.Types ( DescriptorFlags(..) , StatusFlags(..)+ , CreationFlags(..)+ , AccessMode(..)+ -- * Open Access Mode+ , readOnly+ , writeOnly+ , readWrite -- * File Status Flags , nonblocking , append+ -- * File Creation Flags+ , create+ , truncate+ , exclusive ) where +import Prelude hiding (truncate)+ import Data.Bits (Bits,(.|.)) import Foreign.C.Types (CInt) @@ -40,12 +52,35 @@ deriving stock (Eq) deriving newtype (Bits) +-- | File Creation Flags+newtype CreationFlags = CreationFlags CInt+ deriving stock (Eq)+ deriving newtype (Bits)++newtype AccessMode = AccessMode CInt+ deriving stock (Eq)+ instance Semigroup DescriptorFlags where (<>) = (.|.) instance Monoid DescriptorFlags where mempty = DescriptorFlags 0 +instance Semigroup CreationFlags where (<>) = (.|.)+instance Monoid CreationFlags where mempty = CreationFlags 0+ instance Semigroup StatusFlags where (<>) = (.|.) instance Monoid StatusFlags where mempty = StatusFlags 0 +-- | The @O_RDONLY@ access mode.+readOnly :: AccessMode+readOnly = AccessMode #{const O_RDONLY}++-- | The @O_WRONLY@ access mode.+writeOnly :: AccessMode+writeOnly = AccessMode #{const O_WRONLY}++-- | The @O_RDWR@ access mode.+readWrite :: AccessMode+readWrite = AccessMode #{const O_RDWR}+ -- | The @O_NONBLOCK@ flag nonblocking :: StatusFlags nonblocking = StatusFlags #{const O_NONBLOCK}@@ -53,3 +88,15 @@ -- | The @O_APPEND@ flag append :: StatusFlags append = StatusFlags #{const O_APPEND}++-- | The @O_CREAT@ flag+create :: CreationFlags+create = CreationFlags #{const O_CREAT}++-- | The @O_TRUNC@ flag+truncate :: CreationFlags+truncate = CreationFlags #{const O_TRUNC}++-- | The @O_EXCL@ flag+exclusive :: CreationFlags+exclusive = CreationFlags #{const O_EXCL}
src/Posix/MessageQueue.hs view
@@ -7,18 +7,18 @@ , uninterruptibleReceiveByteArray , uninterruptibleSendBytes -- * Types- , OpenMode(..)- , OpenFlags(..)- -- * Bit Twiddle- , T.applyOpenFlags+ , AccessMode(..)+ , CreationFlags(..)+ , StatusFlags(..) -- * Open Access Mode- , T.readOnly- , T.writeOnly- , T.readWrite+ , F.readOnly+ , F.writeOnly+ , F.readWrite -- * Open Flags- , T.nonblocking+ , F.nonblocking ) where +import Data.Bits ((.|.)) import GHC.Exts (RealWorld,ByteArray#,MutableByteArray#,Addr#) import GHC.Exts (Int(I#)) import System.Posix.Types (Fd(..),CSsize(..))@@ -27,11 +27,11 @@ import Foreign.C.String (CString) import Data.Primitive (MutableByteArray(..),ByteArray(..)) import Data.Bytes.Types (Bytes(Bytes))-import Posix.MessageQueue.Types (OpenMode(..),OpenFlags(..))+import Posix.File.Types (CreationFlags(..),AccessMode(..),StatusFlags(..)) import qualified GHC.Exts as Exts import qualified Data.Primitive as PM import qualified Control.Monad.Primitive as PM-import qualified Posix.MessageQueue.Types as T+import qualified Posix.File.Types as F foreign import ccall unsafe "mqueue.h mq_receive" c_unsafe_mq_receive :: Fd -> MutableByteArray# RealWorld@@ -42,14 +42,16 @@ -> ByteArray# -> Int -> CSize -> CUInt -> IO CInt foreign import ccall safe "mqueue.h mq_open"- c_safe_mq_open :: CString -> OpenMode -> IO Fd+ c_safe_mq_open :: CString -> CInt -> IO Fd open :: CString -- ^ NULL-terminated name of queue, must start with slash- -> OpenMode -- ^ Access mode and flags+ -> AccessMode -- ^ Access mode+ -> CreationFlags -- ^ Creation flags+ -> StatusFlags -- ^ Status flags -> IO (Either Errno Fd)-open !name !mode =- c_safe_mq_open name mode >>= errorsFromFd+open !name (AccessMode x) (CreationFlags y) (StatusFlags z) =+ c_safe_mq_open name (x .|. y .|. z) >>= errorsFromFd uninterruptibleReceiveByteArray :: Fd -- ^ Message queue
src/Posix/MessageQueue/Types.hsc view
@@ -4,11 +4,6 @@ module Posix.MessageQueue.Types ( OpenMode(..) , OpenFlags(..)- , applyOpenFlags- -- * Open Access Mode- , readOnly- , writeOnly- , readWrite -- * Open Flags , nonblocking ) where@@ -24,22 +19,6 @@ OpenFlags x <> OpenFlags y = OpenFlags (x .|. y) instance Monoid OpenFlags where mempty = OpenFlags 0 --- | The @O_RDONLY@ access mode.-readOnly :: OpenMode-readOnly = OpenMode #{const O_RDONLY}---- | The @O_WRONLY@ access mode.-writeOnly :: OpenMode-writeOnly = OpenMode #{const O_WRONLY}---- | The @O_RDWR@ access mode.-readWrite :: OpenMode-readWrite = OpenMode #{const O_RDWR}- -- | The @O_NONBLOCK@ open flag. nonblocking :: OpenFlags nonblocking = OpenFlags #{const O_NONBLOCK}--applyOpenFlags :: OpenFlags -> OpenMode -> OpenMode-applyOpenFlags (OpenFlags s) (OpenMode t) = OpenMode (s .|. t)-
src/Posix/Socket.hs view
@@ -6,6 +6,7 @@ {-# language LambdaCase #-} {-# language MagicHash #-} {-# language NamedFieldPuns #-}+{-# language PatternSynonyms #-} {-# language ScopedTypeVariables #-} {-# language UnboxedTuples #-} {-# language UnliftedFFITypes #-}@@ -32,6 +33,9 @@ uninterruptibleSocket -- ** Socket Pair , uninterruptibleSocketPair+ -- ** Address Resolution+ , getAddressInfo+ , uninterruptibleFreeAddressInfo -- ** Bind , uninterruptibleBind -- ** Connect@@ -48,11 +52,13 @@ -- ** Get Socket Option , uninterruptibleGetSocketOption -- ** Set Socket Option+ , uninterruptibleSetSocketOption+ , uninterruptibleSetSocketOptionByteArray , uninterruptibleSetSocketOptionInt -- ** Close- , close- , uninterruptibleClose- , uninterruptibleErrorlessClose+ , F.close+ , F.uninterruptibleClose+ , F.uninterruptibleErrorlessClose -- ** Shutdown , uninterruptibleShutdown -- ** Send@@ -96,7 +102,7 @@ , networkToHostLong , networkToHostShort -- * Types- , Domain(..)+ , Family(..) , Type(..) , Protocol(..) , OptionName(..)@@ -105,6 +111,7 @@ , Message(..) , MessageFlags(..) , ShutdownType(..)+ , AddressInfo -- * Socket Address -- ** Types , SocketAddress(..)@@ -120,10 +127,10 @@ , PSP.sizeofSocketAddressInternet -- * Data Construction -- ** Socket Domains- , PST.unix- , PST.unspecified- , PST.internet- , PST.internet6+ , pattern PST.Unix+ , pattern PST.Unspecified+ , pattern PST.Internet+ , pattern PST.Internet6 -- ** Socket Types , PST.stream , PST.datagram@@ -151,13 +158,28 @@ , PST.levelSocket -- ** Option Names , PST.optionError+ , PST.bindToDevice , PST.broadcast+ , PST.reuseAddress+ -- ** Address Info+ -- *** Peek+ , PST.peekAddressInfoFlags+ -- *** Poke+ , PST.pokeAddressInfoFlags+ -- *** Metadata+ , PST.sizeofAddressInfo -- ** Message Header -- *** Peek , PST.peekMessageHeaderName , PST.peekMessageHeaderNameLength , PST.peekMessageHeaderIOVector , PST.peekMessageHeaderIOVectorLength+ , PST.peekMessageHeaderControl+ , PST.peekMessageHeaderControlLength+ , PST.peekMessageHeaderFlags+ , PST.peekControlMessageHeaderLevel+ , PST.peekControlMessageHeaderLength+ , PST.peekControlMessageHeaderType -- *** Poke , PST.pokeMessageHeaderName , PST.pokeMessageHeaderNameLength@@ -188,19 +210,22 @@ import Data.Primitive.PrimArray.Offset (MutablePrimArrayOffset(..)) import Data.Word (Word8,Word16,Word32,byteSwap16,byteSwap32) import Data.Void (Void)-import Foreign.C.Error (Errno,getErrno)+import Foreign.C.Error (Errno(Errno),getErrno)+import Foreign.C.String (CString) import Foreign.C.Types (CInt(..),CSize(..)) import Foreign.Ptr (nullPtr) import GHC.Exts (Ptr,RealWorld,ByteArray#,MutableByteArray#) import GHC.Exts (Addr#,TYPE,RuntimeRep(UnliftedRep)) import GHC.Exts (ArrayArray#,MutableArrayArray#,Int(I#)) import GHC.Exts (shrinkMutableByteArray#,touch#)-import Posix.Socket.Types (Domain(..),Protocol(..),Type(..),SocketAddress(..))+import Posix.Socket.Types (Family(..),Protocol(..),Type(..),SocketAddress(..)) import Posix.Socket.Types (SocketAddressInternet(..)) import Posix.Socket.Types (MessageFlags(..),Message(..),ShutdownType(..)) import Posix.Socket.Types (Level(..),OptionName(..),OptionValue(..))+import Posix.Socket.Types (AddressInfo) import System.Posix.Types (Fd(..),CSsize(..)) +import qualified Posix.File as F import qualified Posix.Socket.Types as PST import qualified Data.Primitive as PM import qualified Data.Primitive.Unlifted.Array as PM@@ -211,21 +236,28 @@ -- to serialize some of various kind of socket address types. import qualified Posix.Socket.Platform as PSP +-- getaddrinfo cannot use the unsafe ffi+foreign import ccall safe "sys/socket.h getaddrinfo"+ c_safe_getaddrinfo ::+ CString+ -> CString+ -> Ptr AddressInfo+ -> MutableByteArray# RealWorld -- actually a `Ptr (Ptr AddressInfo))`.+ -> IO Errno++-- | Free the @addrinfo@ at the pointer.+foreign import ccall safe "sys/socket.h freeaddrinfo"+ uninterruptibleFreeAddressInfo :: Ptr AddressInfo -> IO ()+ foreign import ccall unsafe "sys/socket.h socket"- c_socket :: Domain -> Type -> Protocol -> IO Fd+ c_socket :: Family -> Type -> Protocol -> IO Fd foreign import ccall unsafe "sys/socket.h socketpair"- c_socketpair :: Domain -> Type -> Protocol -> MutableByteArray# RealWorld -> IO CInt+ c_socketpair :: Family -> Type -> Protocol -> MutableByteArray# RealWorld -> IO CInt foreign import ccall unsafe "sys/socket.h listen" c_listen :: Fd -> CInt -> IO CInt -foreign import ccall safe "unistd.h close"- c_safe_close :: Fd -> IO CInt--foreign import ccall unsafe "unistd.h close"- c_unsafe_close :: Fd -> IO CInt- foreign import ccall unsafe "unistd.h shutdown" c_unsafe_shutdown :: Fd -> ShutdownType -> IO CInt @@ -282,6 +314,22 @@ -> CInt -- option_value -> IO CInt +foreign import ccall unsafe "sys/socket.h setsockopt"+ c_unsafe_setsockopt :: Fd+ -> Level+ -> OptionName+ -> Ptr Void -- option_val+ -> CInt -- option_len+ -> IO CInt++foreign import ccall unsafe "sys/socket.h setsockopt"+ c_unsafe_setsockopt_ba :: Fd+ -> Level+ -> OptionName+ -> ByteArray# -- option_val+ -> CInt -- option_len+ -> IO CInt+ -- Per the spec the type signature of connect is: -- int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); -- The bytearray argument is actually SocketAddress.@@ -391,7 +439,7 @@ -- this function. The author believes that it cannot block for a prolonged -- period of time. uninterruptibleSocket ::- Domain -- ^ Communications domain (e.g. 'internet', 'unix')+ Family -- ^ Communications domain (e.g. 'internet', 'unix') -> Type -- ^ Socket type (e.g. 'datagram', 'stream') with flags -> Protocol -- ^ Protocol -> IO (Either Errno Fd)@@ -404,7 +452,7 @@ -- this function. The author believes that it cannot block for a prolonged -- period of time. uninterruptibleSocketPair ::- Domain -- ^ Communications domain (probably 'unix')+ Family -- ^ Communications domain (probably 'unix') -> Type -- ^ Socket type (e.g. 'datagram', 'stream') with flags -> Protocol -- ^ Protocol -> IO (Either Errno (Fd,Fd))@@ -420,6 +468,24 @@ pure (Right (fd1,fd2)) else fmap Left getErrno ++-- | Given node and service, which identify an Internet host and a service,+-- @getaddrinfo()@ returns one or more @addrinfo@ structures. The type of this+-- wrapper differs slightly from the type of its C counterpart. Remember to call+-- 'uninterruptibleFreeAddressInfo' when finished with the result.+getAddressInfo ::+ CString -- ^ Node, identifies an Internet host+ -> CString -- ^ Service+ -> Ptr AddressInfo -- ^ Hints+ -> IO (Either Errno (Ptr AddressInfo))+getAddressInfo !node !service !hints = do+ resBuf@(MutableByteArray resBuf#) <- PM.newPinnedByteArray (PM.sizeOf (undefined :: Ptr ()))+ c_safe_getaddrinfo node service hints resBuf# >>= \case+ Errno 0 -> do+ res <- PM.readByteArray resBuf 0+ pure (Right res)+ e -> pure (Left e)+ -- | Assign a local socket address address to a socket identified by -- descriptor socket that has no local socket address assigned. The -- <http://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html POSIX specification>@@ -614,6 +680,33 @@ uninterruptibleSetSocketOptionInt sock level optName optValue = c_unsafe_setsockopt_int sock level optName optValue >>= errorsFromInt_ +-- | Set the value for the option specified by the 'Option' argument for+-- the socket specified by the 'Fd' argument. The+-- <http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html POSIX specification>+-- of @getsockopt@ includes more details.+uninterruptibleSetSocketOption ::+ Fd -- ^ Socket+ -> Level -- ^ Socket level+ -> OptionName -- ^ Option name+ -> Ptr Void -- ^ Option value+ -> CInt -- ^ Option value length+ -> IO (Either Errno ())+uninterruptibleSetSocketOption sock level optName optValue optLen =+ c_unsafe_setsockopt sock level optName optValue optLen >>= errorsFromInt_++-- | Variant of 'uninterruptibleSetSocketOption' that accepts the option+-- as a byte array instead of a pointer into unmanaged memory. The argument+-- does not need to be pinned.+uninterruptibleSetSocketOptionByteArray ::+ Fd -- ^ Socket+ -> Level -- ^ Socket level+ -> OptionName -- ^ Option name+ -> ByteArray -- ^ Option value+ -> CInt -- ^ Option value length+ -> IO (Either Errno ())+uninterruptibleSetSocketOptionByteArray sock level optName (ByteArray optVal) optLen =+ c_unsafe_setsockopt_ba sock level optName optVal optLen >>= errorsFromInt_+ -- | Send data from a byte array over a network socket. Users -- may specify an offset and a length to send fewer bytes than are -- actually present in the array. Since this uses the safe@@ -1140,38 +1233,6 @@ touchMutableByteArray msgHdrBuf touchMutableByteArray sockAddrBuf fmap Left getErrno---- | Close a socket. The <http://pubs.opengroup.org/onlinepubs/009696899/functions/close.html POSIX specification>--- includes more details. This uses the safe FFI.-close ::- Fd -- ^ Socket- -> IO (Either Errno ())-close fd = c_safe_close fd >>= errorsFromInt_---- | Close a socket. This uses the unsafe FFI. According to the--- <http://pubs.opengroup.org/onlinepubs/009696899/functions/close.html POSIX specification>,--- "If @fildes@ refers to a socket, @close()@ shall cause the socket to--- be destroyed. If the socket is in connection-mode, and the @SO_LINGER@--- option is set for the socket with non-zero linger time, and the socket--- has untransmitted data, then @close()@ shall block for up to the current--- linger interval until all data is transmitted."-uninterruptibleClose ::- Fd -- ^ Socket- -> IO (Either Errno ())-uninterruptibleClose fd = c_unsafe_close fd >>= errorsFromInt_---- | Close a socket with the unsafe FFI. Do not check for errors. It is only--- appropriate to use this when a socket is being closed to handle an--- exceptional case. Since the user will want the propogate the original--- exception, the exception provided by 'uninterruptibleClose' would just--- be discarded. This function allows us to potentially avoid an additional--- FFI call to 'getErrno'.-uninterruptibleErrorlessClose ::- Fd -- ^ Socket- -> IO ()-uninterruptibleErrorlessClose fd = do- _ <- c_unsafe_close fd- pure () -- | Shutdown a socket. This uses the unsafe FFI. uninterruptibleShutdown ::
src/Posix/Socket/Types.hsc view
@@ -5,6 +5,7 @@ {-# language GeneralizedNewtypeDeriving #-} {-# language KindSignatures #-} {-# language MagicHash #-}+{-# language PatternSynonyms #-} {-# language UnboxedTuples #-} {-# language NamedFieldPuns #-} @@ -13,13 +14,14 @@ {-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-} #include <sys/socket.h>+#include <netdb.h> #include <netinet/in.h> #include "custom.h" -- | All of the data constructors provided by this module are unsafe. -- Only use them if you really know what you are doing. module Posix.Socket.Types- ( Domain(..)+ ( Family(..) , Type(..) , Protocol(..) , Level(..)@@ -32,11 +34,14 @@ , MessageFlags(..) , Message(..) , ShutdownType(..)+ , AddressInfoFlags(..)+ -- * Phantom Types+ , AddressInfo -- * Socket Families- , unix- , unspecified- , internet- , internet6+ , pattern Unix+ , pattern Unspecified+ , pattern Internet+ , pattern Internet6 -- * Socket Types , stream , datagram@@ -64,7 +69,25 @@ , levelSocket -- * Option Names , optionError+ , bindToDevice , broadcast+ , reuseAddress+ -- * AddressInfo+ -- ** Peek+ , peekAddressInfoFlags+ , peekAddressInfoFamily+ , peekAddressInfoSocketType+ , peekAddressInfoProtocol+ , peekAddressInfoAddressLength+ , peekAddressInfoAddress+ , peekAddressInfoNext+ -- ** Poke+ , pokeAddressInfoFlags+ , pokeAddressInfoFamily+ , pokeAddressInfoSocketType+ , pokeAddressInfoProtocol+ -- ** Metadata+ , sizeofAddressInfo -- * Message Header -- ** Peek , peekMessageHeaderName@@ -105,23 +128,43 @@ import Data.Primitive.Addr (Addr(..)) import Data.Word (Word16,Word32,Word64) import Foreign.C.Types (CInt(..),CSize)-import Foreign.Storable (peekByteOff,pokeByteOff)+import Foreign.Storable (Storable,peekByteOff,pokeByteOff) import GHC.Ptr (Ptr(..)) import GHC.Exts (Int(I##),Int##,(*##),(+##)) import qualified Data.Kind import qualified Data.Primitive as PM +-- | Phantom for pointers to @addrinfo@ in address resolution functions.+-- According to POSIX:+--+-- > struct addrinfo {+-- > int ai_flags;+-- > int ai_family;+-- > int ai_socktype;+-- > int ai_protocol;+-- > socklen_t ai_addrlen;+-- > struct sockaddr *ai_addr;+-- > char *ai_canonname;+-- > struct addrinfo *ai_next;+-- > };+data AddressInfo+ -- | A socket communications domain, sometimes referred to as a family. The spec -- mandates @AF_UNIX@, @AF_UNSPEC@, and @AF_INET@.-newtype Domain = Domain CInt+newtype Family = Family CInt+ deriving newtype (Storable) -- | A socket type. The spec mandates @SOCK_STREAM@, @SOCK_DGRAM@, -- and @SOCK_SEQPACKET@. Other types may be available on a per-platform -- basis.+--+-- TODO: Change this to SocketType newtype Type = Type CInt+ deriving newtype (Storable) newtype Protocol = Protocol { getProtocol :: CInt }+ deriving newtype (Storable) newtype Level = Level CInt @@ -148,6 +191,13 @@ instance Semigroup (MessageFlags m) where (<>) = (.|.) instance Monoid (MessageFlags m) where mempty = MessageFlags 0 +newtype AddressInfoFlags = AddressInfoFlags CInt+ deriving newtype (Eq,Storable)++instance Semigroup AddressInfoFlags where+ AddressInfoFlags x <> AddressInfoFlags y = AddressInfoFlags (x .|. y)+instance Monoid AddressInfoFlags where mempty = AddressInfoFlags 0+ -- | The @sockaddr@ data. This is an extensible tagged union, so this library -- has chosen to represent it as byte array. It is up to platform-specific -- libraries to inhabit this type with values. The byte array backing this@@ -258,24 +308,24 @@ sequencedPacket = Type #{const SOCK_SEQPACKET} -- | The @AF_UNIX@ communications domain.-unix :: Domain-unix = Domain #{const AF_UNIX}+pattern Unix :: Family+pattern Unix = Family #{const AF_UNIX} -- | The @AF_UNSPEC@ communications domain.-unspecified :: Domain-unspecified = Domain #{const AF_UNSPEC}+pattern Unspecified :: Family+pattern Unspecified = Family #{const AF_UNSPEC} -- | The @AF_INET@ communications domain.-internet :: Domain-internet = Domain #{const AF_INET}+pattern Internet :: Family+pattern Internet = Family #{const AF_INET} -- | The @AF_INET6@ communications domain. POSIX declares raw sockets -- optional. However, they are included here for convenience. Please -- open an issue if this prevents this library from compiling on a -- POSIX-compliant operating system that anyone uses for haskell -- development.-internet6 :: Domain-internet6 = Domain #{const AF_INET6}+pattern Internet6 :: Family+pattern Internet6 = Family #{const AF_INET6} -- | The @MSG_OOB@ receive flag or send flag. outOfBand :: MessageFlags m@@ -341,6 +391,14 @@ optionError :: OptionName optionError = OptionName #{const SO_ERROR} +-- | Bind to device (e.g. @SO_BINDTODEVICE@)+bindToDevice :: OptionName+bindToDevice = OptionName #{const SO_BINDTODEVICE}++-- | Allow reuse of local address (e.g. @SO_REUSEADDR@)+reuseAddress :: OptionName+reuseAddress = OptionName #{const SO_REUSEADDR}+ -- | Transmission of broadcast messages is supported (e.g. @SO_BROADCAST@) broadcast :: OptionName broadcast = OptionName #{const SO_BROADCAST}@@ -353,6 +411,7 @@ i <- #{peek struct cmsghdr, cmsg_level} (Ptr p) pure (Level i) +-- | Get @cmsg_type@. peekControlMessageHeaderType :: Addr -> IO Type peekControlMessageHeaderType (Addr p) = do i <- #{peek struct cmsghdr, cmsg_type} (Ptr p)@@ -363,17 +422,75 @@ -- advanceControlMessageHeaderData p = -- PM.plusAddr p (#{size struct cmsghdr}) +-- | Get @iov_base@. peekIOVectorBase :: Addr -> IO Addr peekIOVectorBase (Addr p) = do Ptr x <- #{peek struct iovec, iov_base} (Ptr p) pure (Addr x) +-- | Get @iov_len@. peekIOVectorLength :: Addr -> IO CSize peekIOVectorLength (Addr p) = #{peek struct iovec, iov_len} (Ptr p) -- | The size of a serialized @msghdr@. sizeofMessageHeader :: CInt sizeofMessageHeader = #{size struct msghdr}++-- | Get @ai_flags@.+peekAddressInfoFlags :: Ptr AddressInfo -> IO AddressInfoFlags+peekAddressInfoFlags ptr = do+ x <- #{peek struct addrinfo, ai_flags} ptr+ pure (AddressInfoFlags x)++-- | Set @ai_flags@.+pokeAddressInfoFlags :: Ptr AddressInfo -> AddressInfoFlags -> IO ()+pokeAddressInfoFlags ptr (AddressInfoFlags x) = #{poke struct addrinfo, ai_flags} ptr x++-- | Get @ai_family@.+peekAddressInfoFamily :: Ptr AddressInfo -> IO Family+peekAddressInfoFamily ptr = do+ x <- #{peek struct addrinfo, ai_family} ptr+ pure (Family x)++-- | Get @ai_socktype@.+peekAddressInfoSocketType :: Ptr AddressInfo -> IO Type+peekAddressInfoSocketType ptr = do+ x <- #{peek struct addrinfo, ai_socktype} ptr+ pure (Type x)++-- | Get @ai_protocol@.+peekAddressInfoProtocol :: Ptr AddressInfo -> IO Protocol+peekAddressInfoProtocol ptr = do+ x <- #{peek struct addrinfo, ai_protocol} ptr+ pure (Protocol x)++-- | Get @ai_addrlen@.+peekAddressInfoAddressLength :: Ptr AddressInfo -> IO CInt+peekAddressInfoAddressLength ptr = #{peek struct addrinfo, ai_addrlen} ptr++-- | Get @ai_addr@.+peekAddressInfoAddress :: Ptr AddressInfo -> IO (Ptr SocketAddress)+peekAddressInfoAddress ptr = #{peek struct addrinfo, ai_addr} ptr++-- | Get @ai_next@.+peekAddressInfoNext :: Ptr AddressInfo -> IO (Ptr AddressInfo)+peekAddressInfoNext ptr = #{peek struct addrinfo, ai_next} ptr++-- | Set @ai_family@.+pokeAddressInfoFamily :: Ptr AddressInfo -> Family -> IO ()+pokeAddressInfoFamily ptr (Family x) = #{poke struct addrinfo, ai_family} ptr x++-- | Set @ai_socktype@.+pokeAddressInfoSocketType :: Ptr AddressInfo -> Type -> IO ()+pokeAddressInfoSocketType ptr (Type x) = #{poke struct addrinfo, ai_socktype} ptr x++-- | Set @ai_protocol@.+pokeAddressInfoProtocol :: Ptr AddressInfo -> Protocol -> IO ()+pokeAddressInfoProtocol ptr (Protocol x) = #{poke struct addrinfo, ai_protocol} ptr x++-- | The size of a serialized @addrinfo@.+sizeofAddressInfo :: Int+sizeofAddressInfo = #{size struct addrinfo} -- | The size of a serialized @iovec@. sizeofIOVector :: CInt
+ src/Posix/Struct/AddressInfo/Peek.hsc view
@@ -0,0 +1,60 @@+#include <sys/socket.h>+#include <netdb.h>+#include <netinet/in.h>++{-# language DataKinds #-}++-- | Accessors for reading from @struct addrinfo@:+--+-- > struct addrinfo {+-- > int ai_flags;+-- > int ai_family;+-- > int ai_socktype;+-- > int ai_protocol;+-- > socklen_t ai_addrlen;+-- > struct sockaddr *ai_addr;+-- > char *ai_canonname;+-- > struct addrinfo *ai_next;+-- > };+module Posix.Struct.AddressInfo.Peek+ ( flags+ , family+ , socketType+ , protocol+ , addressLength+ , address+ , next+ ) where++import Posix.Socket.Types (AddressInfoFlags(..),SocketAddress,Family,Type,AddressInfo,Protocol)+import Foreign.C.Types (CInt)+import Foreign.Ptr (Ptr)+import Foreign.Storable (peekByteOff)++-- | Get @ai_flags@.+flags :: Ptr AddressInfo -> IO AddressInfoFlags+flags ptr = #{peek struct addrinfo, ai_flags} ptr++-- | Get @ai_family@.+family :: Ptr AddressInfo -> IO Family+family ptr = #{peek struct addrinfo, ai_family} ptr++-- | Get @ai_socktype@.+socketType :: Ptr AddressInfo -> IO Type+socketType ptr = #{peek struct addrinfo, ai_socktype} ptr++-- | Get @ai_protocol@.+protocol :: Ptr AddressInfo -> IO Protocol+protocol ptr = #{peek struct addrinfo, ai_protocol} ptr++-- | Get @ai_addrlen@.+addressLength :: Ptr AddressInfo -> IO CInt+addressLength ptr = #{peek struct addrinfo, ai_addrlen} ptr++-- | Get @ai_addr@.+address :: Ptr AddressInfo -> IO (Ptr SocketAddress)+address ptr = #{peek struct addrinfo, ai_addr} ptr++-- | Get @ai_next@.+next :: Ptr AddressInfo -> IO (Ptr AddressInfo)+next ptr = #{peek struct addrinfo, ai_next} ptr
+ src/Posix/Struct/SocketAddressInternet/Peek.hsc view
@@ -0,0 +1,38 @@+#include <sys/socket.h>+#include <netdb.h>+#include <netinet/in.h>++{-# language DataKinds #-}++-- | Accessors for reading from @struct sockaddr_in@:+--+-- > struct sockaddr_in {+-- > sa_family_t sin_family; /* address family: AF_INET */+-- > in_port_t sin_port; /* port in network byte order */+-- > struct in_addr sin_addr; /* internet address */+-- > };+module Posix.Struct.SocketAddressInternet.Peek+ ( family+ , port+ , address+ ) where++import Posix.Socket.Types (SocketAddressInternet,Family)+import Foreign.Ptr (Ptr)+import Foreign.Storable (peekByteOff)+import Data.Word (Word16,Word32)+import System.ByteOrder (Fixed,ByteOrder(BigEndian))++-- | Get @sin_family@.+family :: Ptr SocketAddressInternet -> IO Family+family = #{peek struct sockaddr_in, sin_family}++-- | Get @in_port_t@.+port :: Ptr SocketAddressInternet -> IO (Fixed 'BigEndian Word16)+port = #{peek struct sockaddr_in, sin_port}++-- | Get @sin_addr.saddr@. This works on Linux because @struct in_addr@ has+-- a single 32-bit field. I do not know how to perform this in a portable way+-- with hsc2hs.+address :: Ptr SocketAddressInternet -> IO (Fixed 'BigEndian Word32)+address = #{peek struct sockaddr_in, sin_addr}
src/Posix/Types.hsc view
@@ -11,8 +11,14 @@ import Foreign.Storable (Storable) import Data.Bits (FiniteBits,Bits)+#if MIN_VERSION_base(4,14,0)+import System.Posix.Types (CNfds(..))+#endif #include <poll.h> +-- This is a compatibility shim for older GHCs+#if !MIN_VERSION_base(4,14,0) newtype CNfds = CNfds #{type nfds_t} deriving newtype (Eq,Real,Integral,Enum,Num,Ord,Storable,FiniteBits,Bits)+#endif