packages feed

unix-fcntl-0.0.0: src/System/Posix/FileControl.hsc

{-# LANGUAGE GADTs #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
module System.Posix.FileControl
  ( fcntl
  , Fcntl(..)

  -- * File descriptor flags
  , FileDescriptorFlags
  , pattern FD_CLOEXEC

  -- * File status flags
  , FileStatusFlags
  , pattern O_RDONLY
  , pattern O_WRONLY
  , pattern O_RDWR
  , pattern O_ACCMODE
  , pattern O_CREAT
  , pattern O_EXCL
  , pattern O_NONBLOCK
  , pattern O_NOCTTY
  , pattern O_TRUNC
  , pattern O_APPEND
  , pattern O_NDELAY

  -- * Advisory and mandatory locking
  , Flock
  , newFlock
  , flockType
  , FlockType
  , pattern F_RDLCK
  , pattern F_WRLCK
  , pattern F_UNLCK
  , flockWhence
  , FlockWhence
  , pattern SEEK_SET
  , pattern SEEK_CUR
  , pattern SEEK_END
  , flockStart
  , flockLen
  , flockPid


#if defined(_GNU_SOURCE)
  -- * Managing signals
  , FOwnerEx
  , newFOwnerEx
  , fOwnerExType
  , fOwnerExPid
  , OwnerType
  , pattern F_OWNER_TID
  , pattern F_OWNER_PID
  , pattern F_OWNER_PGRP

  -- * File and directory change notification (dnotify)
  , DNotify
  , pattern DN_ACCESS
  , pattern DN_MODIFY
  , pattern DN_CREATE
  , pattern DN_DELETE
  , pattern DN_RENAME
  , pattern DN_ATTRIB
#endif

#if defined(F_GET_SEALS)
  -- * File sealing
  , FileSeal
  , pattern F_SEAL_SEAL
  , pattern F_SEAL_SHRINK
  , pattern F_SEAL_GROW
  , pattern F_SEAL_WRITE
#endif
  ) where
import Control.Applicative
import Foreign
import Foreign.C
import System.Posix.Types
import Prelude

import Foreign.Var hiding (get)

#if defined(_GNU_SOURCE)
import GHC.Conc (Signal)
#endif

#include <unistd.h>
#include <fcntl.h>
#include "custom-template-hsc.h"

-- | Perform one of the operations in 'Fcntl'
fcntl :: Fd -> Fcntl a -> IO a
fcntl fd cmd = case cmd of
  -- Duplicating a file descriptor
  F_DUPFD minFd ->
    fcntl_set_int fd (#const F_DUPFD) minFd
  F_DUPFD_CLOEXEC minFd ->
    fcntl_set_int fd (#const F_DUPFD_CLOEXEC) minFd

  -- File descriptor flags
  F_GETFD ->
    FileDescriptorFlags <$> fcntl_get_int fd (#const F_GETFD)
  F_SETFD (FileDescriptorFlags flags) ->
    fcntl_set_int_ fd (#const F_SETFD) flags

  -- File status flags
  F_GETFL ->
    FileStatusFlags <$> fcntl_get_int fd (#const F_GETFL)
  F_SETFL (FileStatusFlags flags) ->
    fcntl_set_int_ fd (#const F_SETFL) flags

  -- Advisory or mandatory locking
  F_GETLK ->
    fcntl_get_flock fd (#const F_GETLK)
  F_SETLK flock ->
    fcntl_set_flock fd (#const F_SETLK) flock
  F_SETLKW flock ->
    fcntl_set_flock fd (#const F_SETLKW) flock

#if defined(_GNU_SOURCE) && defined(F_OFD_GETLK)
  -- Open description locks
  F_OFD_GETLK ->
    fcntl_get_flock fd (#const F_OFD_GETLK)
  F_OFD_SETLK flock ->
    fcntl_set_flock fd (#const F_OFD_SETLK) flock
  F_OFD_SETLKW flock ->
    fcntl_set_flock fd (#const F_OFD_SETLKW) flock
#endif

  -- Managing signals
  F_GETOWN ->
    fcntl_get_int fd (#const F_GETOWN)
  F_SETOWN pid ->
    fcntl_set_int_ fd (#const F_SETFL) pid
#if defined(_GNU_SOURCE)
  F_GETOWN_EX ->
    fcntl_get_f_owner_ex fd (#const F_GETOWN_EX)
  F_SETOWN_EX foe ->
    fcntl_set_f_owner_ex fd (#const F_SETOWN_EX) foe
  F_GETSIG ->
    fcntl_get_int fd (#const F_GETSIG)
  F_SETSIG sig ->
    fcntl_set_int_ fd (#const F_SETSIG) sig

  -- Leases (Linux 2.4)
  F_GETLEASE ->
    FlockType <$> fcntl_get_int fd (#const F_GETLEASE)
  F_SETLEASE (FlockType ty) ->
    fcntl_set_int_ fd (#const F_SETLEASE) ty

  -- File and directory change notification (dnotify; Linux 2.4)
  F_NOTIFY (DNotify n) ->
    fcntl_set_int_ fd (#const F_NOTIFY) n

  -- Changing the capacity of a pipe
  F_GETPIPE_SZ ->
    fcntl_get_int fd (#const F_GETPIPE_SZ)
  F_SETPIPE_SZ size ->
    fcntl_set_int_ fd (#const F_SETPIPE_SZ) size
#endif

#if defined(F_GET_SEALS)
  -- File sealing
  F_GET_SEALS ->
    fcntl_get_int fd (#const F_GET_SEALS)
  F_ADD_SEALS ->
    fcntl_set_int_ fd (#const F_ADD_SEALS)
#endif

-- | Type of operations which 'fcntl' can perform. Available operations vary
-- depending on platforms. Please consult manpage on your platform for details.
--
-- All possible operations are:
--
-- * Duplicating a file descriptor
--
--     * 'F_DUPFD'
--     * 'F_DUPFD_CLOEXEC'
--
-- * File descriptor flags
--
--     * 'F_GETFD':
--     * 'F_SETFD'
--
-- * File status flags
--
--    * 'F_GETFL'
--    * 'F_SETFL'
--
-- * Advisory or mandatory locking
--
--    * 'F_GETLK'
--    * 'F_SETLK'
--    * 'F_SETLKW'
--
-- * Open file description locks (Linux 3.15 or later).
--
--    @-fgnu@ flag needs to be enabled to use this feature.
--
--    * 'F_OFD_GETLK'
--    * 'F_OFD_SETLK'
--    * 'F_OFD_SETLKW'
--
-- * Managing signals
--
--    * 'F_GETOWN'
--    * 'F_SETOWN'
--    * 'F_GETOWN_EX'
--    * 'F_SETOWN_EX'
--    * 'F_GETSIG'
--    * 'F_SETSIG'
--
-- * Leases
--
--    * 'F_GETLEASE'
--    * 'F_SETLEASE'
--
-- * File and directory change notification (dnotify; Linux 2.4 or later)
--
--    * 'F_NOTIFY'
--
-- * Changing the capacity of a pipe
--
--    * 'F_GETPIPE_SZ'
--    * 'F_SETPIPE_SZ'
--
-- * File leasing
--
--    * 'F_GET_SEALS'
--    * 'F_ADD_SEALS'
data Fcntl a where
  -- Duplicating a file descriptor
  F_DUPFD :: Fd -> Fcntl Fd
  F_DUPFD_CLOEXEC :: Fd -> Fcntl Fd

  -- File descriptor flags
  F_GETFD :: Fcntl FileDescriptorFlags
  F_SETFD :: FileDescriptorFlags -> Fcntl ()

  -- File status flags
  F_GETFL :: Fcntl FileStatusFlags
  F_SETFL :: FileStatusFlags -> Fcntl ()

  -- Advisory or mandatory locking
  F_GETLK :: Fcntl Flock
  F_SETLK :: Flock -> Fcntl ()
  F_SETLKW :: Flock -> Fcntl ()

#if defined(_GNU_SOURCE) && defined(F_OFD_GETLK)
  -- Open file description locks
  F_OFD_GETLK :: Fcntl Flock
  F_OFD_SETLK :: Flock -> Fcntl ()
  F_OFD_SETLKW :: Flock -> Fcntl ()
#endif

  -- Managing signals
  F_GETOWN :: Fcntl ProcessID
  F_SETOWN :: ProcessID -> Fcntl ()

#if defined(_GNU_SOURCE)
  F_GETOWN_EX :: Fcntl FOwnerEx
  F_SETOWN_EX :: FOwnerEx -> Fcntl ()

  F_GETSIG :: Fcntl Signal
  F_SETSIG :: Signal -> Fcntl ()

  -- Leases
  F_GETLEASE :: Fcntl FlockType
  F_SETLEASE :: FlockType -> Fcntl ()

  -- File and directory change notification (dnotify; Linux 2.4)
#if defined(F_NOTIFY)
  F_NOTIFY :: DNotify -> Fcntl ()
#endif

  -- Changing the capacity of a pipe
  F_GETPIPE_SZ :: Fcntl Int
  F_SETPIPE_SZ :: Int -> Fcntl ()
#endif

#if defined(F_GET_SEALS)
  -- File sealing (Linux 3.17)
  F_GET_SEALS :: Fcntl Seal
  F_ADD_SEALS :: Seal -> Fcntl ()
#endif

-- Helper functions

fcntl_get_int :: Integral a => Fd -> CInt -> IO a
fcntl_get_int fd cmd =
  fromIntegral <$> throwErrnoIfMinus1 "fcntl"
    (c_fcntl_get_int (fromIntegral fd) cmd)

foreign import ccall safe "fcntl"
  c_fcntl_get_int :: CInt -> CInt -> IO CInt

fcntl_set_int :: (Integral a, Integral b) => Fd -> CInt -> a -> IO b
fcntl_set_int fd cmd n =
  fromIntegral <$> throwErrnoIfMinus1 "fcntl"
    (c_fcntl_set_int (fromIntegral fd) cmd (fromIntegral n))

fcntl_set_int_ :: Integral a => Fd -> CInt -> a -> IO ()
fcntl_set_int_ fd cmd n =
  throwErrnoIfMinus1_ "fcntl"
    (c_fcntl_set_int (fromIntegral fd) cmd (fromIntegral n))

foreign import ccall safe "fcntl"
  c_fcntl_set_int :: CInt -> CInt -> CInt -> IO CInt

fcntl_get_flock :: Fd -> CInt -> IO Flock
fcntl_get_flock fd cmd = do
  flock <- newFlock
  throwErrnoIfMinus1_ "fcntl" $
    withFlock flock $ c_fcntl_get_flock (fromIntegral fd) cmd
  return flock

foreign import ccall safe "fcntl"
  c_fcntl_get_flock :: CInt -> CInt -> Ptr Flock -> IO CInt

fcntl_set_flock :: Fd -> CInt -> Flock -> IO ()
fcntl_set_flock fd cmd flock =
  throwErrnoIfMinus1_ "fcntl" $
    withFlock flock $ c_fcntl_set_flock (fromIntegral fd) cmd

foreign import ccall safe "fcntl"
  c_fcntl_set_flock :: CInt -> CInt -> Ptr Flock -> IO CInt

#if defined(_GNU_SOURCE)

fcntl_get_f_owner_ex :: Fd -> CInt -> IO FOwnerEx
fcntl_get_f_owner_ex fd cmd = do
  owner <- newFOwnerEx
  throwErrnoIfMinus1_ "fcntl" $
    withFOwnerEx owner $ c_fcntl_get_f_owner_ex (fromIntegral fd) cmd
  return owner

foreign import ccall safe "fcntl"
  c_fcntl_get_f_owner_ex :: CInt -> CInt -> Ptr FOwnerEx -> IO CInt

fcntl_set_f_owner_ex :: Fd -> CInt -> FOwnerEx -> IO ()
fcntl_set_f_owner_ex fd cmd owner =
  throwErrnoIfMinus1_ "fcntl" $
    withFOwnerEx owner $ c_fcntl_set_f_owner_ex (fromIntegral fd) cmd

foreign import ccall safe "fcntl"
  c_fcntl_set_f_owner_ex :: CInt -> CInt -> Ptr FOwnerEx -> IO CInt

#endif

-----------------------------------------------------------
-- File descriptor flags

newtype FileDescriptorFlags = FileDescriptorFlags CInt

#DEFINE_PATTERN "FD_CLOEXEC", "FileDescriptorFlags"

-----------------------------------------------------------
-- File status flags

newtype FileStatusFlags = FileStatusFlags CInt

-- File access modes
#DEFINE_BIDIRECTIONAL_PATTERN "O_RDONLY", "FileStatusFlags"
#DEFINE_BIDIRECTIONAL_PATTERN "O_WRONLY", "FileStatusFlags"
#DEFINE_BIDIRECTIONAL_PATTERN "O_RDWR", "FileStatusFlags"
#DEFINE_BIDIRECTIONAL_PATTERN "O_ACCMODE", "FileStatusFlags"

-- Open-time flags
#DEFINE_BIDIRECTIONAL_PATTERN "O_CREAT", "FileStatusFlags"
#DEFINE_BIDIRECTIONAL_PATTERN "O_EXCL", "FileStatusFlags"
#DEFINE_BIDIRECTIONAL_PATTERN "O_NONBLOCK", "FileStatusFlags"
#DEFINE_BIDIRECTIONAL_PATTERN "O_NOCTTY", "FileStatusFlags"
#DEFINE_BIDIRECTIONAL_PATTERN "O_TRUNC", "FileStatusFlags"

-- I/O operating modes
#DEFINE_BIDIRECTIONAL_PATTERN "O_APPEND", "FileStatusFlags"
#DEFINE_BIDIRECTIONAL_PATTERN "O_NDELAY", "FileStatusFlags"

-----------------------------------------------------------
-- Advisory and mandatory locking

newtype Flock = Flock (ForeignPtr Flock)

withFlock :: Flock -> (Ptr Flock -> IO a) -> IO a
withFlock (Flock fptr) = withForeignPtr fptr

-- | Allocate a flock structure. The allocated memory will be garbage collected
-- automatically.
newFlock :: IO Flock
newFlock = Flock <$> mallocForeignPtrBytes (#size struct flock)

newtype FlockType = FlockType CInt

#DEFINE_PATTERN "F_RDLCK", "FlockType"
#DEFINE_PATTERN "F_WRLCK", "FlockType"
#DEFINE_PATTERN "F_UNLCK", "FlockType"

flockType :: Flock -> Var FlockType
flockType flock = Var get set
  where
    get = FlockType <$> withFlock flock (#peek struct flock, l_type)
    set (FlockType ty) = withFlock flock $ \p ->
      (#poke struct flock, l_type) p ty

newtype FlockWhence = FlockWhence CInt

#DEFINE_PATTERN "SEEK_SET", "FlockWhence"
#DEFINE_PATTERN "SEEK_CUR", "FlockWhence"
#DEFINE_PATTERN "SEEK_END", "FlockWhence"

flockWhence :: Flock -> Var FlockWhence
flockWhence flock = Var get set
  where
    get = FlockWhence <$> withFlock flock (#peek struct flock, l_whence)
    set (FlockWhence whence) = withFlock flock $ \p ->
      (#poke struct flock, l_whence) p whence

flockStart :: Flock -> Var FileOffset
flockStart flock = Var get set
  where
    get = withFlock flock (#peek struct flock, l_start)
    set offset = withFlock flock $ \p -> (#poke struct flock, l_start) p offset

flockLen :: Flock -> Var FileOffset
flockLen flock = Var get set
  where
    get = withFlock flock (#peek struct flock, l_len)
    set len = withFlock flock $ \p -> (#poke struct flock, l_len) p len

flockPid :: Flock -> Var ProcessID
flockPid flock = Var get set
  where
    get = withFlock flock (#peek struct flock, l_pid)
    set pid = withFlock flock $ \p -> (#poke struct flock, l_pid) p pid

-----------------------------------------------------------
-- Managing signals

#if defined(_GNU_SOURCE)

newtype FOwnerEx = FOwnerEx (ForeignPtr FOwnerEx)

withFOwnerEx :: FOwnerEx -> (Ptr FOwnerEx -> IO a) -> IO a
withFOwnerEx (FOwnerEx fptr) = withForeignPtr fptr

newFOwnerEx :: IO FOwnerEx
newFOwnerEx = FOwnerEx <$> mallocForeignPtrBytes (#size struct f_owner_ex)

newtype OwnerType = OwnerType CInt

#DEFINE_PATTERN "F_OWNER_TID", "OwnerType"
#DEFINE_PATTERN "F_OWNER_PID", "OwnerType"
#DEFINE_PATTERN "F_OWNER_PGRP", "OwnerType"

fOwnerExType :: FOwnerEx -> Var OwnerType
fOwnerExType foe = Var get set
  where
    get = OwnerType <$> withFOwnerEx foe (#peek struct f_owner_ex, type)
    set (OwnerType ty) = withFOwnerEx foe $ \p ->
      (#poke struct f_owner_ex, type) p ty

fOwnerExPid :: FOwnerEx -> Var ProcessID
fOwnerExPid foe = Var get set
  where
    get = withFOwnerEx foe (#peek struct f_owner_ex, pid)
    set pid = withFOwnerEx foe $ \p -> (#poke struct f_owner_ex, pid) p pid

#endif

-----------------------------------------------------------
-- File and directory change notification (dnotify)

#if defined(DN_ACCESS)

newtype DNotify = DNotify CInt

#DEFINE_BIDIRECTIONAL_PATTERN "DN_ACCESS", "DNotify"
#DEFINE_BIDIRECTIONAL_PATTERN "DN_MODIFY", "DNotify"
#DEFINE_BIDIRECTIONAL_PATTERN "DN_CREATE", "DNotify"
#DEFINE_BIDIRECTIONAL_PATTERN "DN_DELETE", "DNotify"
#DEFINE_BIDIRECTIONAL_PATTERN "DN_RENAME", "DNotify"
#DEFINE_BIDIRECTIONAL_PATTERN "DN_ATTRIB", "DNotify"

#endif

-----------------------------------------------------------
-- File sealing

#if defined(F_GET_SEALS)

newtype FileSeal = FileSeal CInt

#DEFINE_BIDIRECTIONAL_PATTERN "F_SEAL_SEAL", "FileSeal"
#DEFINE_BIDIRECTIONAL_PATTERN "F_SEAL_SHRINK", "FileSeal"
#DEFINE_BIDIRECTIONAL_PATTERN "F_SEAL_GROW", "FileSeal"
#DEFINE_BIDIRECTIONAL_PATTERN "F_SEAL_WRITE", "FileSeal"

#endif