packages feed

netw-0.1.0.0: src/Netw/Internal/Socket.hs

{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE UnliftedFFITypes #-}
{-# LANGUAGE MagicHash #-}
module Netw.Internal.Socket 
( retryOrThrowErrnoIfMinus1
, throwErrnoIfNothing

, socket

, bind#
, bind

, listen

, getsockname#
, getsockname

, accept#
, accept

, connect#
, connect

, send#, send, sendto#
, sendto, sendmsg#, sendmsg

, recv#, recv
, recvfrom#, recvfrom
, recvmsg#, recvmsg

, shutdown
) where
import Netw.Internal.Enum
import Netw.Internal.Type

import Foreign
import Foreign.C

import System.Posix.Types

import GHC.Exts

retryOrThrowErrnoIfMinus1 :: (Eq a, Num a) => String -> IO a -> IO a
retryOrThrowErrnoIfMinus1 msg action = do
  r <- action
  if r == -1
  then do
    errno <- getErrno
    if errno == eAGAIN || errno == eWOULDBLOCK then
      retryOrThrowErrnoIfMinus1 msg action
    else
      throwErrno msg
  else
    return r
-- Remember to check a (the returned value) to determine if an error occure
-- since something else might have failed before that and errno is no longer EOK
throwErrnoIfNothing :: String -> (a -> Errno -> Maybe b) -> IO a -> IO b
throwErrnoIfNothing msg func act = do
  ret <- act
  err <- getErrno
  let may = func ret err
  case may of
    Nothing -> throwErrno msg
    Just b  -> return b

zeroIfMinus1AndEAGAINOrEWOULDBLOCK :: (Num a, Eq a) => a -> Errno -> Maybe a
zeroIfMinus1AndEAGAINOrEWOULDBLOCK (-1) errno
  | errno == eAGAIN || errno == eWOULDBLOCK = Just 0
  | otherwise = Nothing
zeroIfMinus1AndEAGAINOrEWOULDBLOCK a _ = Just a

throwErrnoIfMinus1UnlessEAGAINorEWOULDBLOCK :: (Num a, Eq a) => String -> IO a -> IO a
throwErrnoIfMinus1UnlessEAGAINorEWOULDBLOCK msg act = do
  ret <- act
  if ret == -1 
  then do
    errno <- getErrno
    if errno == eAGAIN || errno == eWOULDBLOCK
    then 
      return 0
    else
      throwErrno msg
  else
    return ret

foreign import ccall unsafe "socket_wrapper"
  socket_  :: ProtocolFamily -> SocketType -> Protocol -> IO CInt
socket :: ProtocolFamily -> SocketType -> Protocol -> IO Fd
socket domain stype protocol = Fd <$>
  throwErrnoIfMinus1 "socket failed to create socket"
    (socket_ domain stype protocol)

foreign import ccall unsafe "bind"
  bind_#   :: Fd -> ByteArray# -> CSocklen -> IO CInt
bind# :: Fd -> ByteArray# -> CSocklen -> IO ()
bind# sockfd address addressLen =
  throwErrnoIfMinus1_ "bind failed to bind sockfd"
    (bind_# sockfd address addressLen)
foreign import ccall unsafe "bind"
  bind_    :: Fd -> Ptr () -> CSocklen -> IO CInt
bind :: Fd -> Ptr () -> CSocklen -> IO ()
bind sockfd address addressLen =
  throwErrnoIfMinus1_ "bind failed to bind sockfd"
    (bind_ sockfd address addressLen)


foreign import ccall unsafe "listen"
  listen_   :: Fd -> CInt -> IO CInt
listen :: Fd -> CInt -> IO ()
listen sockfd backlog =
  throwErrnoIfMinus1_ "listen fail"
    (listen_ sockfd backlog)

-- TODO: Add an extra argument to specify retry wait time
foreign import ccall unsafe "accept"
  accept_# :: Fd -> MutableByteArray# RealWorld -> Ptr CSocklen -> IO CInt
accept# :: Fd -> MutableByteArray# RealWorld -> Ptr CSocklen -> IO Fd
accept# sockfd address addressLen = Fd <$>
  retryOrThrowErrnoIfMinus1 "accept failed" (accept_# sockfd address addressLen)
foreign import ccall unsafe "accept"
  accept_  :: Fd -> Ptr () -> Ptr CSocklen -> IO CInt    
accept :: Fd -> Ptr () -> Ptr CSocklen -> IO Fd
accept sockfd address addressLen = Fd <$>
  retryOrThrowErrnoIfMinus1 "accept failed" (accept_ sockfd address addressLen)

foreign import ccall unsafe "getsockname"
  getsockname_# :: Fd -> MutableByteArray# RealWorld -> Ptr CSocklen -> IO CInt
getsockname# :: Fd -> MutableByteArray# RealWorld -> Ptr CSocklen -> IO ()
getsockname# sockfd address addressLen =
  throwErrnoIfMinus1_ "getsockname failed"
    (getsockname_# sockfd address addressLen)

foreign import ccall unsafe "getsockname"
  getsockname_ :: Fd -> Ptr () -> Ptr CSocklen -> IO CInt
getsockname :: Fd -> Ptr () -> Ptr CSocklen -> IO ()
getsockname sockfd address addressLen =
  throwErrnoIfMinus1_ "getsockname failed"
    (getsockname_ sockfd address addressLen)

foreign import ccall unsafe "connect"
  connect_# :: Fd -> ByteArray# -> CSocklen -> IO CInt
connect# :: Fd -> ByteArray# -> CSocklen -> IO ()
connect# sockfd address addressLen =
  throwErrnoIfMinus1_ "connect failed"
    (connect_# sockfd address addressLen)
foreign import ccall unsafe "connect"
  connect_  :: Fd -> Ptr () -> CSocklen -> IO CInt
connect :: Fd -> Ptr () -> CSocklen -> IO ()
connect sockfd address addressLen =
  throwErrnoIfMinus1_ "connect failed"
    (connect_ sockfd address addressLen)

-- Actually sending and recving data
-- The send* function might return zero in case no byte is sent
-- failure is handled
foreign import ccall unsafe "send_wrapper"
  send_# :: Fd -> ByteArray# -> CPtrdiff -> CSize -> MsgFlags -> IO CSsize 
send# :: Fd -> ByteArray# -> CPtrdiff -> CSize -> MsgFlags -> IO CSsize
send# fd buf# offs size flags =
  throwErrnoIfMinus1UnlessEAGAINorEWOULDBLOCK "send failed"
    (send_# fd buf# offs size flags)

foreign import ccall unsafe "send"
  send_ :: Fd -> Ptr Word8 -> CSize -> MsgFlags -> IO CSsize 
send :: Fd -> Ptr Word8 -> CSize -> MsgFlags -> IO CSsize
send fd buf size flags = 
  throwErrnoIfMinus1UnlessEAGAINorEWOULDBLOCK "send failed"
    (send_ fd buf size flags)

foreign import ccall unsafe "sendto_wrapper"
  sendto_# :: Fd -> ByteArray# -> CPtrdiff -> CSize -> MsgFlags -> ByteArray# -> CSocklen -> IO CSsize
sendto# :: Fd -> ByteArray# -> CPtrdiff -> CSize -> MsgFlags -> ByteArray# -> CSocklen -> IO CSsize
sendto# fd buf# offs size flags addr# addrlen =
  throwErrnoIfMinus1UnlessEAGAINorEWOULDBLOCK "sendto failed"
    (sendto_# fd buf# offs size flags addr# addrlen)
foreign import ccall unsafe "sendto"
  sendto_  :: Fd -> Ptr Word8 -> CSize -> MsgFlags -> ByteArray# -> CSocklen -> IO CSsize
sendto :: Fd -> Ptr Word8 -> CSize -> MsgFlags -> ByteArray# -> CSocklen -> IO CSsize
sendto fd buf size flags addr# addrlen = 
  throwErrnoIfMinus1UnlessEAGAINorEWOULDBLOCK "sendto failed"
    (sendto_ fd buf size flags addr# addrlen)

foreign import ccall unsafe "sendmsg_wrapper0" -- iovecs             offs         size          nvec
  sendmsg_# :: Fd -> ByteArray# -> CSocklen -> Array# ByteArray# -> ByteArray# -> ByteArray# -> CSize -> Ptr () -> CSize -> MsgFlags -> IO CSsize
sendmsg# :: Fd -> ByteArray# -> CSocklen -> Array# ByteArray# -> ByteArray# -> ByteArray# -> CSize -> Ptr () -> CSize -> MsgFlags -> IO CSsize
sendmsg# fd addr# addrlen iovecs offs size nvec cmsg cmsglen flags =
  throwErrnoIfMinus1UnlessEAGAINorEWOULDBLOCK "sendmsg failed"
    (sendmsg_# fd addr# addrlen iovecs offs size nvec cmsg cmsglen flags)

foreign import ccall unsafe "sendmsg_wrapper1"
  sendmsg_  :: Fd -> ByteArray# -> CSocklen -> Ptr Iovec -> CSize -> Ptr () -> CSize -> MsgFlags -> IO CSsize
sendmsg :: Fd -> ByteArray# -> CSocklen -> Ptr Iovec -> CSize -> Ptr () -> CSize -> MsgFlags -> IO CSsize
sendmsg fd addr# addrlen iovecs ioveclen cmsg cmsglen flags =
  throwErrnoIfMinus1UnlessEAGAINorEWOULDBLOCK "sendmsg failed"
    (sendmsg_ fd addr# addrlen iovecs ioveclen cmsg cmsglen flags)

foreign import ccall unsafe "recv_wrapper"
  recv_# :: Fd -> MutableByteArray# RealWorld -> CPtrdiff -> CSize -> MsgFlags -> IO CSsize
recv# :: Fd -> MutableByteArray# RealWorld -> CPtrdiff -> CSize -> MsgFlags -> IO CSsize
recv# fd buf# offs size flags =
  retryOrThrowErrnoIfMinus1 "recv failed"
    (recv_# fd buf# offs size flags)

foreign import ccall unsafe "recv"
  recv_ :: Fd -> Ptr Word8 -> CSize -> MsgFlags -> IO CSsize
recv :: Fd -> Ptr Word8 -> CSize -> MsgFlags -> IO CSsize
recv fd buf size flags =
  retryOrThrowErrnoIfMinus1 "recv failed"
    (recv_ fd buf size flags)

foreign import ccall unsafe "recvfrom_wrapper"
  recvfrom_# :: Fd -> MutableByteArray# RealWorld -> CPtrdiff -> CSize -> MsgFlags -> MutableByteArray# RealWorld -> Ptr CSocklen -> IO CSsize
recvfrom# :: Fd -> MutableByteArray# RealWorld -> CPtrdiff -> CSize -> MsgFlags -> MutableByteArray# RealWorld -> Ptr CSocklen -> IO CSsize
recvfrom# fd buf# offs size flags addr# addrlen =
  retryOrThrowErrnoIfMinus1 "recvfrom failed"
    (recvfrom_# fd buf# offs size flags addr# addrlen)

foreign import ccall unsafe "recvfrom"
  recvfrom_ :: Fd -> Ptr Word8 -> CSize -> MsgFlags -> MutableByteArray# RealWorld -> Ptr CSocklen -> IO CSsize
recvfrom :: Fd -> Ptr Word8 -> CSize -> MsgFlags -> MutableByteArray# RealWorld -> Ptr CSocklen -> IO CSsize
recvfrom fd buf size flags addr# addrlen =
  retryOrThrowErrnoIfMinus1 "recvfrom failed"
    (recvfrom_ fd buf size flags addr# addrlen)

foreign import ccall unsafe "recvmsg_wrapper0"
  recvmsg_# :: Fd -> MutableByteArray# RealWorld -> Ptr CSocklen -> Array# (MutableByteArray# RealWorld) -> ByteArray# -> ByteArray# -> CSize -> Ptr a -> Ptr CSize -> Ptr MsgFlags -> IO CSsize
recvmsg# :: Fd -> MutableByteArray# RealWorld -> Ptr CSocklen -> Array# (MutableByteArray# RealWorld) -> ByteArray# -> ByteArray# -> CSize -> Ptr a -> Ptr CSize -> Ptr MsgFlags -> IO CSsize
recvmsg# fd addr# addrlen iovecs# offs size ioveclen cmsg cmsglen flags =
  retryOrThrowErrnoIfMinus1 "recvmsg failed"
    (recvmsg_# fd addr# addrlen iovecs# offs size ioveclen cmsg cmsglen flags)

foreign import ccall unsafe "recvmsg_wrapper1"
  recvmsg_  :: Fd -> MutableByteArray# RealWorld -> Ptr CSocklen -> Ptr Iovec -> CSize -> Ptr a -> Ptr CSize -> Ptr MsgFlags -> IO CSsize
recvmsg :: Fd -> MutableByteArray# RealWorld -> Ptr CSocklen -> Ptr Iovec -> CSize -> Ptr a -> Ptr CSize -> Ptr MsgFlags -> IO CSsize
recvmsg fd addr# addrlen iovecs ioveclen cmsg cmsglen flags =
  retryOrThrowErrnoIfMinus1 "recvmsg failed"
    (recvmsg_ fd addr# addrlen iovecs ioveclen cmsg cmsglen flags)

foreign import ccall unsafe "shutdown"
  shutdown_ :: Fd -> ShutHow -> IO CInt
shutdown :: Fd -> ShutHow -> IO ()
shutdown fd = throwErrnoIfMinus1_ "shutdown failed" . shutdown_ fd