{-# LINE 1 "System/Posix/Realtime/MQueue.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LINE 2 "System/Posix/Realtime/MQueue.hsc" #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  System.Posix.Realtime.MQueue
-- Copyright   :  (c) The University of Glasgow 2002
-- License     :  BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer  :  William N. Halchin (vigalchin@gmail.com)
-- Stability   :  provisional
-- Portability :  non-portable (requires POSIX)
--
-- POSIX 1003.1b message queue support.  See
-- <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/mqueue.h.html>.
--
-----------------------------------------------------------------------------

module System.Posix.Realtime.MQueue (
  -- ** Message queue attributes data type
  MQAttributes(..),

  -- ** Opening\/closing\/unlinking mqueues
  Name,
  mqOpen,
  mqClose,
  mqUnlink,

  -- ** Sending\/receiving data
  -- |Programmers using the 'mqSend' and 'mqReceive' API should be aware that
  -- EAGAIN exceptions may occur for non-blocking IO!
  mqSend,
  mqReceive,

  -- **  Getting\/Setting mqueue attributes
  mqGetAttributes,
  mqSetAttributes,

  -- ** Notify receipt of message
  mqNotify
  ) where


import System.IO
import System.IO.Error
import System.Posix.Realtime.RTDataTypes
import System.Posix.IO
import System.Posix.Types
import System.Posix.Error
import System.Posix.Internals

import Foreign
import Foreign.C
import Data.Bits
import Data.ByteString


{-# LINE 56 "System/Posix/Realtime/MQueue.hsc" #-}
import GHC.IO
import GHC.IO.Exception
import GHC.IO.Handle hiding (fdToHandle)
import qualified GHC.IO.Handle

{-# LINE 61 "System/Posix/Realtime/MQueue.hsc" #-}


{-# LINE 66 "System/Posix/Realtime/MQueue.hsc" #-}


{-# LINE 68 "System/Posix/Realtime/MQueue.hsc" #-}


type Name = String

data MQAttributes =
  MQAttributes {
    flags      :: Int,
    maxMsgNum  :: Int,
    maxMsgSize :: Int,
    curNumMsgs :: Int
    } deriving Show


-- | MQAttributes Storable used to marshall and unmarshall to ANSI C
instance Storable MQAttributes where
  sizeOf (MQAttributes flags maxMsgNum maxMsgSize curNumMsgs) = (64)
{-# LINE 84 "System/Posix/Realtime/MQueue.hsc" #-}
  alignment _ = 1
  poke p_attrs (MQAttributes flags maxMsgNum maxMsgSize curNumMsgs) = do
    ((\hsc_ptr -> pokeByteOff hsc_ptr 0))    p_attrs  flags
{-# LINE 87 "System/Posix/Realtime/MQueue.hsc" #-}
    ((\hsc_ptr -> pokeByteOff hsc_ptr 8))   p_attrs  maxMsgNum
{-# LINE 88 "System/Posix/Realtime/MQueue.hsc" #-}
    ((\hsc_ptr -> pokeByteOff hsc_ptr 16))  p_attrs  maxMsgSize
{-# LINE 89 "System/Posix/Realtime/MQueue.hsc" #-}
    ((\hsc_ptr -> pokeByteOff hsc_ptr 24))  p_attrs  curNumMsgs
{-# LINE 90 "System/Posix/Realtime/MQueue.hsc" #-}
  peek p_attrs = do
    flags <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) p_attrs
{-# LINE 92 "System/Posix/Realtime/MQueue.hsc" #-}
    maxMsgNum <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) p_attrs
{-# LINE 93 "System/Posix/Realtime/MQueue.hsc" #-}
    maxMsgSize <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) p_attrs
{-# LINE 94 "System/Posix/Realtime/MQueue.hsc" #-}
    curNumMsgs <- ((\hsc_ptr -> peekByteOff hsc_ptr 24)) p_attrs
{-# LINE 95 "System/Posix/Realtime/MQueue.hsc" #-}
    return (MQAttributes flags maxMsgNum maxMsgSize curNumMsgs)


-- | Open and optionally create a message queue
--
-- /Note/: The POSIX standard puts some constraints on 'Name' but leaves much as
-- "implementation-defined", meaning that one needs to read the system-specific
-- details on what a conforming name should look like.
mqOpen :: Name
       -> OpenMode
       -> Maybe FileMode -- ^ @Just x@ creates the queue with the given modes, @Nothing@ then the file must exist.
       -> Maybe MQAttributes -- ^ @Just x@ creates the queue with given attributes, @Nothing@ with default attributes.
       -> IO Fd
mqOpen name how maybe_mode (Just attrs) = do
  withCString name $ \ p_name -> do
    allocaBytes (64) $ \ p_attrs -> do
{-# LINE 111 "System/Posix/Realtime/MQueue.hsc" #-}
      poke p_attrs attrs
      mqd <- throwErrnoPathIfMinus1 "mqOpen" name (c_mq_open p_name all_flags mode_w p_attrs)
      return (Fd mqd)
        where
          all_flags  = creat .|. open_mode

          (creat, mode_w) = case maybe_mode of
            Nothing -> (0,0)
            Just x  -> ((64), x)
{-# LINE 120 "System/Posix/Realtime/MQueue.hsc" #-}

          open_mode = case how of
            ReadOnly  -> (0)
{-# LINE 123 "System/Posix/Realtime/MQueue.hsc" #-}
            WriteOnly -> (1)
{-# LINE 124 "System/Posix/Realtime/MQueue.hsc" #-}
            ReadWrite -> (2)
{-# LINE 125 "System/Posix/Realtime/MQueue.hsc" #-}

mqOpen name how maybe_mode Nothing = do
  withCString name $ \ p_name -> do
    mqd <- throwErrnoPathIfMinus1 "mqOpen" name (c_mq_open p_name all_flags mode_w nullPtr)
    return (Fd mqd)
      where
        all_flags  = creat .|. open_mode 

        (creat, mode_w) = case maybe_mode of 
          Nothing -> (0,0)
          Just x  -> ((64), x)
{-# LINE 136 "System/Posix/Realtime/MQueue.hsc" #-}

        open_mode = case how of
          ReadOnly  -> (0)
{-# LINE 139 "System/Posix/Realtime/MQueue.hsc" #-}
          WriteOnly -> (1)
{-# LINE 140 "System/Posix/Realtime/MQueue.hsc" #-}
          ReadWrite -> (2)
{-# LINE 141 "System/Posix/Realtime/MQueue.hsc" #-}

foreign import ccall unsafe "bits/mqueue.h mq_open"
  c_mq_open :: CString -> CInt -> CMode -> Ptr MQAttributes -> IO CInt


-- | Close a message queue
mqClose :: Fd -> IO ()
mqClose (Fd mqd) = throwErrnoIfMinus1_ "mqClose" (c_mq_close mqd)

foreign import ccall unsafe "mqueue.h mq_close"
  c_mq_close :: CInt -> IO CInt


-- | Unlink (destroy) an existing message queue
mqUnlink :: String -> IO ()
mqUnlink name = do
  withCString name $ \ p_name -> do
    throwErrnoPathIfMinus1 "mqUnlink" name (c_mq_unlink p_name)
    return ()

foreign import ccall unsafe "mqueue.h mq_unlink"
  c_mq_unlink :: CString -> IO CInt


-- | Get the attributes for an existing message queue
mqGetAttributes :: Fd -> IO MQAttributes
mqGetAttributes (Fd mqd) = do
  allocaBytes (64) $ \ p_attrs -> do
{-# LINE 169 "System/Posix/Realtime/MQueue.hsc" #-}
    throwErrnoIfMinus1 "mqGetAttributes" (c_mq_getattr mqd p_attrs)
    mq_attrs <- peek p_attrs
    return (mq_attrs)

foreign import ccall unsafe "mqueue.h mq_getattr"
  c_mq_getattr :: CInt ->  Ptr MQAttributes -> IO CInt


-- | Set the attributes for an existing message queue and its retrieve old attributes
mqSetAttributes :: Fd -> MQAttributes -> IO (MQAttributes)
mqSetAttributes (Fd mqd) newAttrs = do
  allocaBytes (64) $ \ p_attrs -> do
{-# LINE 181 "System/Posix/Realtime/MQueue.hsc" #-}
    allocaBytes (64) $ \ p_oldattrs -> do
{-# LINE 182 "System/Posix/Realtime/MQueue.hsc" #-}
      poke p_attrs newAttrs
      throwErrnoIfMinus1 "mqSetAttributes" (c_mq_setattr mqd p_attrs p_oldattrs)
      oldAttrs <- peek p_oldattrs
      return (oldAttrs)

foreign import ccall unsafe "mqueue.h mq_setattr"
  c_mq_setattr :: CInt ->  Ptr MQAttributes -> Ptr MQAttributes -> IO CInt


-- | Retrieve a message from a message queue
--
-- /Note/: @mq_timedreceive@ is not exposed, wrap 'mqReceive' in 'System.Timeout.timeout'
-- to get a timed receive.
mqReceive :: Fd -> ByteCount -> Maybe Int -> IO (ByteString, Int)
mqReceive (Fd mqd) len (Just prio) = do
  allocaBytes (fromIntegral len) $ \ p_buffer -> do
    with (fromIntegral prio) $ \ p_prio -> do
      rc <- throwErrnoIfMinus1 "mqReceive" (c_mq_receive mqd p_buffer (fromIntegral len) p_prio)
      case fromIntegral rc of
        0 -> ioError (IOError Nothing EOF "mqReceive" "EOF" Nothing Nothing)
        n -> do
          s <- packCStringLen (p_buffer, fromIntegral n)
          return (s, n)

mqReceive (Fd mqd) len Nothing = do
  allocaBytes (fromIntegral len) $ \ p_buffer -> do
    rc <- throwErrnoIfMinus1 "mqReceive" (c_mq_receive mqd p_buffer (fromIntegral len) nullPtr)
    case fromIntegral rc of
      0 -> ioError (IOError Nothing EOF "mqReceive" "EOF" Nothing Nothing)
      n -> do
        s <- packCStringLen (p_buffer, fromIntegral n)
        return (s, n)

foreign import ccall unsafe "mqueue.h mq_receive"
  c_mq_receive :: CInt -> Ptr CChar -> CSize -> Ptr CInt -> IO CInt


-- | Send a message on a message queue
mqSend :: Fd -> ByteString -> ByteCount -> Int -> IO ()
mqSend (Fd mqd) msg len prio = do
  useAsCString msg $ \ p_msg -> do
    throwErrnoIfMinus1 "mqSend" (c_mq_send mqd p_msg (fromIntegral len) (fromIntegral prio))
    return ()

foreign import ccall unsafe "mqueue.h mq_send"
  c_mq_send :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt


-- | Notify a registered process of the new-message-in-empty-queue event
mqNotify :: Fd -> Maybe Sigevent -> IO ()
mqNotify (Fd mqd) Nothing = do
  throwErrnoIfMinus1 "mqNotify" (c_mq_notify mqd nullPtr)
  return ()

mqNotify (Fd mqd) (Just sigEvent) = do
  allocaBytes (64) $ \ p_sigevent -> do
{-# LINE 238 "System/Posix/Realtime/MQueue.hsc" #-}
    poke p_sigevent sigEvent
    throwErrnoIfMinus1 "mqNotify" (c_mq_notify mqd p_sigevent)
    return ()

foreign import ccall unsafe "mqueue.h mq_notify"
  c_mq_notify :: CInt -> Ptr Sigevent -> IO CInt