socket-unix-0.1.0.0: src/System/Socket/Family/Unix.hsc
-- |
-- Stability : experimental
-- Portability : Linux
{-# language TypeFamilies #-}
{-# language FlexibleInstances #-}
module System.Socket.Family.Unix
( Unix
, SocketAddress
, socketAddressUnixPath
, socketAddressUnixAbstract
, getUnixPath
) where
import Foreign.Ptr (castPtr, plusPtr)
import Foreign.Storable (Storable(..))
import Foreign.Marshal.Utils (fillBytes, copyBytes)
import Data.Word (Word16, Word8)
import Data.ByteString (ByteString)
import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
import qualified Data.ByteString as B
import System.Socket (Family(..), SocketAddress, Protocol(..))
#include "hs_socket.h"
#if __GLASGOW_HASKELL__ < 800
#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)
#endif
-- | The [Unix domain socket]
-- (https://en.wikipedia.org/wiki/Unix_domain_socket)
data Unix
instance Family Unix where
familyNumber _ = (#const AF_UNIX)
instance Protocol Unix where
protocolNumber _ = 0
-- | A Unix socket address
data instance SocketAddress Unix
-- | Address is connected to a filesystem pathname. When used to bind
-- a socket file with this name is created in the file system.
= SocketAddressUnixPath ByteString
-- | Address is in abstract namespace which is a Linux-specific feature
-- that allows us to bind a UNIX domain socket to a name without that
-- name being created in the file system.
| SocketAddressUnixAbstract ByteString
deriving (Eq, Show)
-- | The maximal length of a address path.
-- SUSv3 doesn’t specify the size of the sun_path field. Early BSD
-- implementations used 108 and 104 bytes, and one contemporary implementation
-- (HP-UX 11) uses 92 bytes. On linux it is declared as
-- > char sun_path[108];
-- and 1 byte is reserved for null byte.
maxPathLength :: Int
maxPathLength = 107
-- | Creates address which is connected to a filesystem pathname.
-- Returns Nothing if @path@'s length exceeds maximal supported.
socketAddressUnixPath :: ByteString -> Maybe (SocketAddress Unix)
socketAddressUnixPath path
| B.length path <= maxPathLength = Just $ SocketAddressUnixPath path
| otherwise = Nothing
-- | Creates address with name in abstract namespace.
-- Returns Nothing if @path@'s length exceeds maximal supported.
socketAddressUnixAbstract :: ByteString -> Maybe (SocketAddress Unix)
socketAddressUnixAbstract path
| len <= maxPathLength = Just . SocketAddressUnixAbstract $
path `B.append` B.replicate (maxPathLength - len) 0
| otherwise = Nothing
where len = B.length path
-- | Returns filesystem pathname where address is connected to.
getUnixPath :: SocketAddress Unix -> Maybe (ByteString)
getUnixPath (SocketAddressUnixPath path) = Just path
getUnixPath _ = Nothing
-- For implementation details see @man unix@
instance Storable (SocketAddress Unix) where
sizeOf _ = (#size struct sockaddr_un)
alignment _ = (#alignment struct sockaddr_un)
peek ptr = do
first <- peek (sun_path ptr) :: IO Word8
case first of
0 -> SocketAddressUnixAbstract <$>
B.packCStringLen (castPtr $ sun_path ptr `plusPtr` 1, maxPathLength)
_ -> SocketAddressUnixPath <$> B.packCString (castPtr $ sun_path ptr)
where
sun_path = (#ptr struct sockaddr_un, sun_path)
poke ptr socketAddress = do
fillBytes ptr 0 (#const sizeof(struct sockaddr_un))
poke (sun_family ptr) ((#const AF_UNIX) :: Word16)
case socketAddress of
SocketAddressUnixPath path -> unsafeUseAsCStringLen path $
\(src, len) -> copyBytes (sun_path ptr) src len
SocketAddressUnixAbstract path -> unsafeUseAsCStringLen path $
\(src, len) -> copyBytes (sun_path ptr `plusPtr` 1) src len
where
sun_family = (#ptr struct sockaddr_un, sun_family)
sun_path = (#ptr struct sockaddr_un, sun_path)