aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--System/Posix.hs7
-rw-r--r--System/Posix/Files.hsc111
-rw-r--r--System/Posix/IO.hsc327
-rw-r--r--System/Posix/Process.hsc351
-rw-r--r--System/Posix/Unistd.hsc181
-rw-r--r--cbits/HsUnix.c12
-rw-r--r--cbits/Makefile12
-rw-r--r--cbits/execvpe.c172
-rw-r--r--include/HsUnix.h32
-rw-r--r--include/Makefile9
-rw-r--r--unix.conf.in11
12 files changed, 1035 insertions, 194 deletions
diff --git a/Makefile b/Makefile
index 5b6af63..b997a0d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,14 @@
TOP=..
include $(TOP)/mk/boilerplate.mk
+SUBDIRS = cbits include
+
ALL_DIRS = System System/Posix
PACKAGE = unix
PACKAGE_DEPS = base
SRC_HADDOCK_OPTS += -t "Haskell Core Libraries (unix package)"
+SRC_HSC2HS_OPTS += -Iinclude
+SRC_HC_OPTS += -Iinclude
include $(TOP)/mk/target.mk
diff --git a/System/Posix.hs b/System/Posix.hs
index 9d4d244..fa4ac6d 100644
--- a/System/Posix.hs
+++ b/System/Posix.hs
@@ -18,9 +18,9 @@ module System.Posix (
module System.Posix.Directory,
module System.Posix.Files,
module System.Posix.Unistd,
- -- module System.Posix.IO,
+ module System.Posix.IO,
+ module System.Posix.Process,
-- module System.Posix.Time,
- -- module System.Posix.Proc,
) where
import System.Posix.Types
@@ -28,3 +28,6 @@ import System.Posix.Signals
import System.Posix.Directory
import System.Posix.Files
import System.Posix.Unistd
+import System.Posix.Process
+import System.Posix.IO
+
diff --git a/System/Posix/Files.hsc b/System/Posix/Files.hsc
index 9dc5555..69e4924 100644
--- a/System/Posix/Files.hsc
+++ b/System/Posix/Files.hsc
@@ -48,6 +48,9 @@ module System.Posix.Files (
-- * Hard links
createLink, removeLink,
+ -- * Symbolic links
+ createSymbolicLink, readSymbolicLink,
+
-- * Renaming files
rename,
@@ -57,14 +60,8 @@ module System.Posix.Files (
-- * Changing file timestamps
setFileTimes, touchFile,
- -- * Standard file descriptors
- stdInput, stdOutput, stdError,
-
- -- * Opening and closing files
- OpenMode(..),
- OpenFileFlags(..), defaultFileFlags,
- openFd, createFile,
- closeFd,
+ -- * Setting file sizes
+ setFileSize, setFdSize,
{-
-- run-time limit & POSIX feature testing
@@ -86,6 +83,7 @@ import Foreign.C
#include <unistd.h>
#include <utime.h>
#include <fcntl.h>
+#include <limits.h>
-- -----------------------------------------------------------------------------
-- POSIX file modes
@@ -334,6 +332,32 @@ removeLink name =
throwErrnoIfMinus1_ "removeLink" (c_unlink s)
-- -----------------------------------------------------------------------------
+-- Symbolic Links
+
+createSymbolicLink :: FilePath -> FilePath -> IO ()
+createSymbolicLink file1 file2 =
+ withCString file1 $ \s1 ->
+ withCString file2 $ \s2 ->
+ throwErrnoIfMinus1_ "createSymbolicLink" (c_symlink s1 s2)
+
+foreign import ccall unsafe "symlink"
+ c_symlink :: CString -> CString -> IO CInt
+
+-- ToDo: should really use SYMLINK_MAX, but not everyone supports it yet,
+-- and it seems that the intention is that SYMLINK_MAX is no larger than
+-- PATH_MAX.
+readSymbolicLink :: FilePath -> IO FilePath
+readSymbolicLink file =
+ allocaArray0 (#const PATH_MAX) $ \buf -> do
+ withCString file $ \s ->
+ throwErrnoIfMinus1_ "readSymbolicLink" $
+ c_readlink s buf (#const PATH_MAX)
+ peekCString buf
+
+foreign import ccall unsafe "readlink"
+ c_readlink :: CString -> CString -> CInt -> IO CInt
+
+-- -----------------------------------------------------------------------------
-- Renaming files
rename :: FilePath -> FilePath -> IO ()
@@ -385,67 +409,20 @@ touchFile name = do
throwErrnoIfMinus1_ "touchFile" (c_utime s nullPtr)
-- -----------------------------------------------------------------------------
--- opening files
-
-stdInput, stdOutput, stdError :: Fd
-stdInput = Fd (#const STDIN_FILENO)
-stdOutput = Fd (#const STDOUT_FILENO)
-stdError = Fd (#const STDERR_FILENO)
-
-data OpenMode = ReadOnly | WriteOnly | ReadWrite
-
-data OpenFileFlags =
- OpenFileFlags {
- append :: Bool,
- exclusive :: Bool,
- noctty :: Bool,
- nonBlock :: Bool,
- trunc :: Bool
- }
-
-defaultFileFlags :: OpenFileFlags
-defaultFileFlags =
- OpenFileFlags {
- append = False,
- exclusive = False,
- noctty = False,
- nonBlock = False,
- trunc = False
- }
-
-openFd :: FilePath
- -> OpenMode
- -> Maybe FileMode -- Just x => O_CREAT, Nothing => must exist
- -> OpenFileFlags
- -> IO Fd
-openFd name how maybe_mode (OpenFileFlags append exclusive noctty
- nonBlock truncate) = do
- withCString name $ \s -> do
- fd <- throwErrnoIfMinus1 "openFd" (c_open s all_flags mode_w)
- return (Fd fd)
- where
- all_flags = creat .|. flags .|. open_mode
-
- flags =
- (if append then (#const O_APPEND) else 0) .|.
- (if exclusive then (#const O_EXCL) else 0) .|.
- (if noctty then (#const O_NOCTTY) else 0) .|.
- (if nonBlock then (#const O_NONBLOCK) else 0) .|.
- (if truncate then (#const O_TRUNC) else 0)
+-- Setting file sizes
- (creat, mode_w) = case maybe_mode of
- Nothing -> (0,0)
- Just x -> ((#const O_CREAT), x)
+setFileSize :: FilePath -> FileOffset -> IO ()
+setFileSize file off =
+ withCString file $ \s ->
+ throwErrnoIfMinus1_ "setFileSize" (c_truncate s off)
- open_mode = case how of
- ReadOnly -> (#const O_RDONLY)
- WriteOnly -> (#const O_WRONLY)
- ReadWrite -> (#const O_RDWR)
+foreign import ccall unsafe "truncate"
+ c_truncate :: CString -> COff -> IO CInt
+setFdSize :: Fd -> FileOffset -> IO ()
+setFdSize fd off =
+ throwErrnoIfMinus1_ "setFdSize" (c_ftruncate fd off)
-createFile :: FilePath -> FileMode -> IO Fd
-createFile name mode
- = openFd name WriteOnly (Just mode) defaultFileFlags{ trunc=True }
+foreign import ccall unsafe "ftruncate"
+ c_ftruncate :: Fd -> COff -> IO CInt
-closeFd :: Fd -> IO ()
-closeFd (Fd fd) = throwErrnoIfMinus1_ "closeFd" (c_close fd)
diff --git a/System/Posix/IO.hsc b/System/Posix/IO.hsc
new file mode 100644
index 0000000..f324841
--- /dev/null
+++ b/System/Posix/IO.hsc
@@ -0,0 +1,327 @@
+{-# OPTIONS -fffi #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module : System.Posix.IO
+-- Copyright : (c) The University of Glasgow 2002
+-- License : BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer : libraries@haskell.org
+-- Stability : provisional
+-- Portability : non-portable (requires POSIX)
+--
+-- POSIX IO support
+--
+-----------------------------------------------------------------------------
+
+module System.Posix.IO (
+ -- * Input \/ Output
+
+ -- ** Standard file descriptors
+ stdInput, stdOutput, stdError,
+
+ -- ** Opening and closing files
+ OpenMode(..),
+ OpenFileFlags(..), defaultFileFlags,
+ openFd, createFile,
+ closeFd,
+
+ -- ** Reading/writing data
+{-
+ fdRead, fdWrite,
+-}
+
+ -- ** Seeking
+ fdSeek,
+
+ -- ** File options
+ FdOption(..),
+ queryFdOption,
+ setFdOption,
+
+ -- ** Locking
+ FileLock,
+ LockRequest(..),
+ getLock, setLock,
+ waitToSetLock,
+
+ -- ** Pipes
+ createPipe,
+
+ -- ** Duplicating file descriptors
+ dup, dupTo,
+
+ -- ** Converting file descriptors to/from Handles
+ handleToFd, fdToHandle,
+
+ ) where
+
+import System.IO
+import System.IO.Error
+import System.Posix.Types
+import Control.Exception ( throw )
+
+import Foreign
+import Foreign.C
+import Data.Bits
+
+#ifdef __GLASGOW_HASKELL__
+import GHC.IOBase
+import GHC.Handle hiding (fdToHandle, openFd)
+import qualified GHC.Handle
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+
+-- -----------------------------------------------------------------------------
+-- Pipes
+
+createPipe :: IO (Fd, Fd)
+createPipe =
+ allocaArray 2 $ \p_fd -> do
+ throwErrnoIfMinus1_ "createPipe" (c_pipe p_fd)
+ rfd <- peekElemOff p_fd 0
+ wfd <- peekElemOff p_fd 1
+ return (rfd, wfd)
+
+foreign import ccall unsafe "pipe"
+ c_pipe :: Ptr Fd -> IO CInt
+
+-- -----------------------------------------------------------------------------
+-- Duplicating file descriptors
+
+dup :: Fd -> IO Fd
+dup fd = do r <- throwErrnoIfMinus1 "dup" (c_dup fd); return (Fd r)
+
+foreign import ccall unsafe "dup"
+ c_dup :: Fd -> IO CInt
+
+dupTo :: Fd -> Fd -> IO Fd
+dupTo fd1 fd2 = throwErrnoIfMinus1 "dupTp" (c_dup2 fd1 fd2)
+
+foreign import ccall unsafe "dup2"
+ c_dup2 :: Fd -> Fd -> IO Fd
+
+-- -----------------------------------------------------------------------------
+-- Opening and closing files
+
+stdInput, stdOutput, stdError :: Fd
+stdInput = Fd (#const STDIN_FILENO)
+stdOutput = Fd (#const STDOUT_FILENO)
+stdError = Fd (#const STDERR_FILENO)
+
+data OpenMode = ReadOnly | WriteOnly | ReadWrite
+
+data OpenFileFlags =
+ OpenFileFlags {
+ append :: Bool,
+ exclusive :: Bool,
+ noctty :: Bool,
+ nonBlock :: Bool,
+ trunc :: Bool
+ }
+
+defaultFileFlags :: OpenFileFlags
+defaultFileFlags =
+ OpenFileFlags {
+ append = False,
+ exclusive = False,
+ noctty = False,
+ nonBlock = False,
+ trunc = False
+ }
+
+openFd :: FilePath
+ -> OpenMode
+ -> Maybe FileMode -- Just x => O_CREAT, Nothing => must exist
+ -> OpenFileFlags
+ -> IO Fd
+openFd name how maybe_mode (OpenFileFlags append exclusive noctty
+ nonBlock truncate) = do
+ withCString name $ \s -> do
+ fd <- throwErrnoIfMinus1 "openFd" (c_open s all_flags mode_w)
+ return (Fd fd)
+ where
+ all_flags = creat .|. flags .|. open_mode
+
+ flags =
+ (if append then (#const O_APPEND) else 0) .|.
+ (if exclusive then (#const O_EXCL) else 0) .|.
+ (if noctty then (#const O_NOCTTY) else 0) .|.
+ (if nonBlock then (#const O_NONBLOCK) else 0) .|.
+ (if truncate then (#const O_TRUNC) else 0)
+
+ (creat, mode_w) = case maybe_mode of
+ Nothing -> (0,0)
+ Just x -> ((#const O_CREAT), x)
+
+ open_mode = case how of
+ ReadOnly -> (#const O_RDONLY)
+ WriteOnly -> (#const O_WRONLY)
+ ReadWrite -> (#const O_RDWR)
+
+foreign import ccall unsafe "open"
+ c_open :: CString -> CInt -> CMode -> IO CInt
+
+createFile :: FilePath -> FileMode -> IO Fd
+createFile name mode
+ = openFd name WriteOnly (Just mode) defaultFileFlags{ trunc=True }
+
+closeFd :: Fd -> IO ()
+closeFd (Fd fd) = throwErrnoIfMinus1_ "closeFd" (c_close fd)
+
+foreign import ccall unsafe "close"
+ c_close :: CInt -> IO CInt
+
+-- -----------------------------------------------------------------------------
+-- Converting file descriptors to/from Handles
+
+#ifdef __GLASGOW_HASKELL__
+handleToFd :: Handle -> IO Fd
+handleToFd h = withHandle "handleToFd" h $ \ h_ -> do
+ -- converting a Handle into an Fd effectively means
+ -- letting go of the Handle; it is put into a closed
+ -- state as a result.
+ let fd = haFD h_
+ flushWriteBufferOnly h_
+ unlockFile (fromIntegral fd)
+ -- setting the Handle's fd to (-1) as well as its 'type'
+ -- to closed, is enough to disable the finalizer that
+ -- eventually is run on the Handle.
+ return (h_{haFD= (-1),haType=ClosedHandle}, Fd (fromIntegral fd))
+
+fdToHandle :: Fd -> IO Handle
+fdToHandle fd = GHC.Handle.fdToHandle (fromIntegral fd)
+#endif
+
+-- -----------------------------------------------------------------------------
+-- Fd options
+
+data FdOption = AppendOnWrite
+ | CloseOnExec
+ | NonBlockingRead
+ | SynchronousWrites
+
+queryFdOption :: Fd -> FdOption -> IO Bool
+queryFdOption fd opt = do
+ r <- throwErrnoIfMinus1 "queryFdOption" (c_fcntl_read fd flag)
+ return ((r .&. opt_val) /= 0)
+ where
+ flag = case opt of
+ CloseOnExec -> (#const F_GETFD)
+ other -> (#const F_GETFL)
+
+ opt_val = case opt of
+ CloseOnExec -> (#const FD_CLOEXEC)
+ AppendOnWrite -> (#const O_APPEND)
+ NonBlockingRead -> (#const O_NONBLOCK)
+ SynchronousWrites -> (#const O_SYNC)
+
+foreign import ccall unsafe "fcntl"
+ c_fcntl_read :: Fd -> CInt -> IO CInt
+
+setFdOption :: Fd -> FdOption -> Bool -> IO ()
+setFdOption fd opt val = do
+ r <- throwErrnoIfMinus1 "setFdOption" (c_fcntl_read fd flag)
+ let r' | val = r .|. opt_val
+ | otherwise = r .&. (complement opt_val)
+ throwErrnoIfMinus1_ "setFdOption" (c_fcntl_write fd flag r')
+ where
+ flag = case opt of
+ CloseOnExec -> (#const F_GETFD)
+ other -> (#const F_GETFL)
+ opt_val = case opt of
+ CloseOnExec -> (#const FD_CLOEXEC)
+ AppendOnWrite -> (#const O_APPEND)
+ NonBlockingRead -> (#const O_NONBLOCK)
+ SynchronousWrites -> (#const O_SYNC)
+
+foreign import ccall unsafe "fcntl"
+ c_fcntl_write :: Fd -> CInt -> CInt -> IO CInt
+
+-- -----------------------------------------------------------------------------
+-- Seeking
+
+mode2Int :: SeekMode -> CInt
+mode2Int AbsoluteSeek = (#const SEEK_SET)
+mode2Int RelativeSeek = (#const SEEK_CUR)
+mode2Int SeekFromEnd = (#const SEEK_END)
+
+fdSeek :: Fd -> SeekMode -> FileOffset -> IO FileOffset
+fdSeek fd mode off =
+ throwErrnoIfMinus1 "fdSeek" (c_lseek fd off (mode2Int mode))
+
+foreign import ccall unsafe "lseek"
+ c_lseek :: Fd -> COff -> CInt -> IO COff
+
+-- -----------------------------------------------------------------------------
+-- Locking
+
+data LockRequest = ReadLock
+ | WriteLock
+ | Unlock
+
+type FileLock = (LockRequest, SeekMode, FileOffset, FileOffset)
+
+type CFLock = ()
+
+getLock :: Fd -> FileLock -> IO (Maybe (ProcessID, FileLock))
+getLock fd lock =
+ allocaLock lock $ \p_flock -> do
+ throwErrnoIfMinus1_ "getLock" (c_fcntl_flock fd (#const F_GETLK) p_flock)
+ result <- bytes2ProcessIDAndLock p_flock
+ return (maybeResult result)
+ where
+ maybeResult (_, (Unlock, _, _, _)) = Nothing
+ maybeResult x = Just x
+
+foreign import ccall unsafe "fcntl"
+ c_fcntl_flock :: Fd -> CInt -> Ptr CFLock -> IO CInt
+
+allocaLock :: FileLock -> (Ptr CFLock -> IO a) -> IO a
+allocaLock (lockreq, mode, start, len) io =
+ allocaBytes (#const sizeof(struct flock)) $ \p -> do
+ (#poke struct flock, l_type) p (lockReqToInt lockreq :: CShort)
+ (#poke struct flock, l_whence) p (fromIntegral (mode2Int mode) :: CShort)
+ (#poke struct flock, l_start) p start
+ (#poke struct flock, l_len) p len
+ io p
+
+lockReqToInt :: LockRequest -> CShort
+lockReqToInt ReadLock = (#const F_RDLCK)
+lockReqToInt WriteLock = (#const F_WRLCK)
+lockReqToInt Unlock = (#const F_UNLCK)
+
+bytes2ProcessIDAndLock :: Ptr CFLock -> IO (ProcessID, FileLock)
+bytes2ProcessIDAndLock p = do
+ req <- (#peek struct flock, l_type) p
+ mode <- (#peek struct flock, l_whence) p
+ start <- (#peek struct flock, l_start) p
+ len <- (#peek struct flock, l_len) p
+ pid <- (#peek struct flock, l_pid) p
+ return (pid, (int2req req, int2mode mode, start, len))
+ where
+ int2req :: CShort -> LockRequest
+ int2req (#const F_RDLCK) = ReadLock
+ int2req (#const F_WRLCK) = WriteLock
+ int2req (#const F_UNLCK) = Unlock
+ int2req _ = throw (mkIOError illegalOperationErrorType "int2req"
+ Nothing Nothing)
+
+ int2mode :: CShort -> SeekMode
+ int2mode (#const SEEK_SET) = AbsoluteSeek
+ int2mode (#const SEEK_CUR) = RelativeSeek
+ int2mode (#const SEEK_END) = SeekFromEnd
+ int2mode _ = throw (mkIOError illegalOperationErrorType "int2mode"
+ Nothing Nothing)
+
+setLock :: Fd -> FileLock -> IO ()
+setLock fd lock = do
+ allocaLock lock $ \p_flock ->
+ throwErrnoIfMinus1_ "setLock" (c_fcntl_flock fd (#const F_SETLK) p_flock)
+
+waitToSetLock :: Fd -> FileLock -> IO ()
+waitToSetLock fd lock = do
+ allocaLock lock $ \p_flock ->
+ throwErrnoIfMinus1_ "waitToSetLock"
+ (c_fcntl_flock fd (#const F_SETLKW) p_flock)
diff --git a/System/Posix/Process.hsc b/System/Posix/Process.hsc
new file mode 100644
index 0000000..dfa9e9a
--- /dev/null
+++ b/System/Posix/Process.hsc
@@ -0,0 +1,351 @@
+{-# OPTIONS -fffi #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module : System.Posix.Process
+-- Copyright : (c) The University of Glasgow 2002
+-- License : BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer : libraries@haskell.org
+-- Stability : provisional
+-- Portability : non-portable (requires POSIX)
+--
+-- POSIX process support
+--
+-----------------------------------------------------------------------------
+
+module System.Posix.Process (
+ -- * Processes
+
+ -- ** Forking and executing
+ forkProcess, executeFile,
+
+ -- ** Exiting
+ exitImmediately,
+
+ -- ** Process environment
+ getProcessID,
+ getParentProcessID,
+ getProcessGroupID,
+
+ -- ** Process groups
+ createProcessGroup,
+ joinProcessGroup,
+ setProcessGroupID,
+
+ -- ** Sessions
+ createSession,
+
+ -- ** Process times
+ ProcessTimes(elapsedTime, systemTime, userTime,
+ childSystemTime, childUserTime),
+ getProcessTimes,
+
+ -- ** Scheduling priority
+ nice,
+ getProcessPriority,
+ getProcessGroupPriority,
+ getUserPriority,
+ setProcessPriority,
+ setProcessGroupPriority,
+ setUserPriority,
+
+ -- ** Process status
+ ProcessStatus(..),
+ getProcessStatus,
+ getAnyProcessStatus,
+ getGroupProcessStatus,
+
+-- ToDo:
+-- getEnvVar,
+-- getEnvironment,
+ ) where
+
+#include "HsUnix.h"
+#include <sys/times.h>
+
+import Foreign
+import Foreign.C
+import System.IO
+import System.IO.Error
+import System.Exit
+import System.Posix.Types
+import System.Posix.Signals
+
+-- -----------------------------------------------------------------------------
+-- Process environment
+
+getProcessID :: IO ProcessID
+getProcessID = c_getpid
+
+foreign import ccall unsafe "getpid"
+ c_getpid :: IO CPid
+
+getParentProcessID :: IO ProcessID
+getParentProcessID = c_getppid
+
+foreign import ccall unsafe "getppid"
+ c_getppid :: IO CPid
+
+getProcessGroupID :: IO ProcessGroupID
+getProcessGroupID = c_getpgrp
+
+foreign import ccall unsafe "getpgrp"
+ c_getpgrp :: IO CPid
+
+createProcessGroup :: ProcessID -> IO ProcessGroupID
+createProcessGroup pid = do
+ throwErrnoIfMinus1_ "createProcessGroup" (c_setpgid pid 0)
+ return pid
+
+joinProcessGroup :: ProcessGroupID -> IO ()
+joinProcessGroup pgid =
+ throwErrnoIfMinus1_ "joinProcessGroup" (c_setpgid 0 pgid)
+
+setProcessGroupID :: ProcessID -> ProcessGroupID -> IO ()
+setProcessGroupID pid pgid =
+ throwErrnoIfMinus1_ "setProcessGroupID" (c_setpgid pid pgid)
+
+foreign import ccall unsafe "setpgid"
+ c_setpgid :: CPid -> CPid -> IO CInt
+
+createSession :: IO ProcessGroupID
+createSession = throwErrnoIfMinus1 "createSession" c_setsid
+
+foreign import ccall unsafe "setsid"
+ c_setsid :: IO CPid
+
+-- -----------------------------------------------------------------------------
+-- Process times
+
+-- All times in clock ticks (see getClockTick)
+
+data ProcessTimes
+ = ProcessTimes { elapsedTime :: ClockTick
+ , userTime :: ClockTick
+ , systemTime :: ClockTick
+ , childUserTime :: ClockTick
+ , childSystemTime :: ClockTick
+ }
+
+getProcessTimes :: IO ProcessTimes
+getProcessTimes = do
+ allocaBytes (#const sizeof(struct tms)) $ \p_tms -> do
+ elapsed <- throwErrnoIfMinus1 "getProcessTimes" (c_times p_tms)
+ ut <- (#peek struct tms, tms_utime) p_tms
+ st <- (#peek struct tms, tms_stime) p_tms
+ cut <- (#peek struct tms, tms_cutime) p_tms
+ cst <- (#peek struct tms, tms_cstime) p_tms
+ return (ProcessTimes{ elapsedTime = elapsed,
+ userTime = ut,
+ systemTime = st,
+ childUserTime = cut,
+ childSystemTime = cst
+ })
+
+type CTms = ()
+
+foreign import ccall unsafe "times"
+ c_times :: Ptr CTms -> IO CClock
+
+-- -----------------------------------------------------------------------------
+-- Process scheduling priority
+
+nice :: Int -> IO ()
+nice prio = throwErrnoIfMinus1_ "nice" (c_nice (fromIntegral prio))
+
+foreign import ccall unsafe "nice"
+ c_nice :: CInt -> IO CInt
+
+getProcessPriority :: ProcessID -> IO Int
+getProcessGroupPriority :: ProcessGroupID -> IO Int
+getUserPriority :: UserID -> IO Int
+
+getProcessPriority pid = do
+ r <- throwErrnoIfMinus1 "getProcessPriority" $
+ c_getpriority (#const PRIO_PROCESS) (fromIntegral pid)
+ return (fromIntegral r)
+
+getProcessGroupPriority pid = do
+ r <- throwErrnoIfMinus1 "getProcessPriority" $
+ c_getpriority (#const PRIO_PGRP) (fromIntegral pid)
+ return (fromIntegral r)
+
+getUserPriority uid = do
+ r <- throwErrnoIfMinus1 "getUserPriority" $
+ c_getpriority (#const PRIO_USER) (fromIntegral uid)
+ return (fromIntegral r)
+
+foreign import ccall unsafe "getpriority"
+ c_getpriority :: CInt -> CInt -> IO CInt
+
+setProcessPriority :: ProcessID -> Int -> IO ()
+setProcessGroupPriority :: ProcessGroupID -> Int -> IO ()
+setUserPriority :: UserID -> Int -> IO ()
+
+setProcessPriority pid val =
+ throwErrnoIfMinus1_ "setProcessPriority" $
+ c_setpriority (#const PRIO_PROCESS) (fromIntegral pid) (fromIntegral val)
+
+setProcessGroupPriority pid val =
+ throwErrnoIfMinus1_ "setProcessPriority" $
+ c_setpriority (#const PRIO_PGRP) (fromIntegral pid) (fromIntegral val)
+
+setUserPriority uid val =
+ throwErrnoIfMinus1_ "setUserPriority" $
+ c_setpriority (#const PRIO_USER) (fromIntegral uid) (fromIntegral val)
+
+foreign import ccall unsafe "setpriority"
+ c_setpriority :: CInt -> CInt -> CInt -> IO CInt
+
+-- -----------------------------------------------------------------------------
+-- Forking, execution
+
+forkProcess :: IO (Maybe ProcessID)
+forkProcess = do
+ r <- throwErrnoIfMinus1 "forkProcess" c_fork
+ case r of
+ 0 -> return Nothing
+ pid -> return (Just (fromIntegral pid))
+
+foreign import ccall unsafe "fork"
+ c_fork :: IO CInt
+
+
+executeFile :: FilePath -- Command
+ -> Bool -- Search PATH?
+ -> [String] -- Arguments
+ -> Maybe [(String, String)] -- Environment
+ -> IO ()
+executeFile path search args Nothing = do
+ withCString path $ \s ->
+ withMany withCString args $ \cstrs ->
+ withArray0 nullPtr cstrs $ \arr ->
+ if search then
+ throwErrnoIfMinus1_ "executeFile" (c_execvp s arr)
+ else
+ throwErrnoIfMinus1_ "executeFile" (c_execv s arr)
+
+executeFile path search args (Just env) = do
+ withCString path $ \s ->
+ withMany withCString args $ \cstrs ->
+ withArray0 nullPtr cstrs $ \arg_arr ->
+ let env' = map (\ (name, val) -> name ++ ('=' : val)) env in
+ withMany withCString env' $ \cenv ->
+ withArray0 nullPtr cenv $ \env_arr ->
+ if search then
+ throwErrnoIfMinus1_ "executeFile" (c_execvpe s arg_arr env_arr)
+ else
+ throwErrnoIfMinus1_ "executeFile" (c_execve s arg_arr env_arr)
+
+foreign import ccall unsafe "execvp"
+ c_execvp :: CString -> Ptr CString -> IO CInt
+
+foreign import ccall unsafe "execv"
+ c_execv :: CString -> Ptr CString -> IO CInt
+
+foreign import ccall unsafe "execvpe"
+ c_execvpe :: CString -> Ptr CString -> Ptr CString -> IO CInt
+
+foreign import ccall unsafe "execve"
+ c_execve :: CString -> Ptr CString -> Ptr CString -> IO CInt
+
+-- -----------------------------------------------------------------------------
+-- Waiting for process termination
+
+data ProcessStatus = Exited ExitCode
+ | Terminated Signal
+ | Stopped Signal
+ deriving (Eq, Ord, Show)
+
+getProcessStatus :: Bool -> Bool -> ProcessID -> IO (Maybe ProcessStatus)
+getProcessStatus block stopped pid =
+ alloca $ \wstatp -> do
+ pid <- throwErrnoIfMinus1 "getProcessStatus"
+ (c_waitpid pid wstatp (waitOptions block stopped))
+ case pid of
+ 0 -> return Nothing
+ _ -> do ps <- decipherWaitStatus wstatp
+ return (Just ps)
+
+foreign import ccall unsafe "waitpid"
+ c_waitpid :: CPid -> Ptr CInt -> CInt -> IO CPid
+
+getGroupProcessStatus :: Bool
+ -> Bool
+ -> ProcessGroupID
+ -> IO (Maybe (ProcessID, ProcessStatus))
+getGroupProcessStatus block stopped pgid =
+ alloca $ \wstatp -> do
+ pid <- throwErrnoIfMinus1 "getGroupProcessStatus"
+ (c_waitpid (-pgid) wstatp (waitOptions block stopped))
+ case pid of
+ 0 -> return Nothing
+ _ -> do ps <- decipherWaitStatus wstatp
+ return (Just (pid, ps))
+
+getAnyProcessStatus :: Bool -> Bool -> IO (Maybe (ProcessID, ProcessStatus))
+getAnyProcessStatus block stopped = getGroupProcessStatus block stopped 1
+
+waitOptions :: Bool -> Bool -> CInt
+-- block stopped
+waitOptions False False = (#const WNOHANG)
+waitOptions False True = (#const (WNOHANG|WUNTRACED))
+waitOptions True False = 0
+waitOptions True True = (#const WUNTRACED)
+
+-- Turn a (ptr to a) wait status into a ProcessStatus
+
+decipherWaitStatus :: Ptr CInt -> IO ProcessStatus
+decipherWaitStatus wstatp = do
+ wstat <- peek wstatp
+ if c_WIFEXITED wstat /= 0
+ then do
+ let exitstatus = c_WEXITSTATUS wstat
+ if exitstatus == 0
+ then return (Exited ExitSuccess)
+ else return (Exited (ExitFailure (fromIntegral exitstatus)))
+ else do
+ if c_WIFSIGNALED wstat /= 0
+ then do
+ let termsig = c_WTERMSIG wstat
+ return (Terminated (fromIntegral termsig))
+ else do
+ if c_WIFSTOPPED wstat /= 0
+ then do
+ let stopsig = c_WSTOPSIG wstat
+ return (Stopped (fromIntegral stopsig))
+ else do
+ ioError (mkIOError illegalOperationErrorType
+ "waitStatus" Nothing Nothing)
+
+foreign import ccall unsafe "__hsunix_wifexited"
+ c_WIFEXITED :: CInt -> CInt
+
+foreign import ccall unsafe "__hsunix_wexitstatus"
+ c_WEXITSTATUS :: CInt -> CInt
+
+foreign import ccall unsafe "__hsunix_wifsignaled"
+ c_WIFSIGNALED :: CInt -> CInt
+
+foreign import ccall unsafe "__hsunix_wtermsig"
+ c_WTERMSIG :: CInt -> CInt
+
+foreign import ccall unsafe "__hsunix_wifstopped"
+ c_WIFSTOPPED :: CInt -> CInt
+
+foreign import ccall unsafe "__hsunix_wstopsig"
+ c_WSTOPSIG :: CInt -> CInt
+
+-- -----------------------------------------------------------------------------
+-- Exiting
+
+exitImmediately :: ExitCode -> IO ()
+exitImmediately exitcode = c_exit (exitcode2Int exitcode)
+ where
+ exitcode2Int ExitSuccess = 0
+ exitcode2Int (ExitFailure n) = fromIntegral n
+
+foreign import ccall unsafe "exit"
+ c_exit :: CInt -> IO ()
+
+-- -----------------------------------------------------------------------------
diff --git a/System/Posix/Unistd.hsc b/System/Posix/Unistd.hsc
index a7fae7e..bdcbc34 100644
--- a/System/Posix/Unistd.hsc
+++ b/System/Posix/Unistd.hsc
@@ -29,52 +29,45 @@ module System.Posix.Unistd (
setUserID,
setGroupID,
- -- * Process environment
- -- ** Querying the process environment
- getProcessID,
- getParentProcessID,
- getProcessGroupID,
-
- -- ** Process groups
- createProcessGroup,
- joinProcessGroup,
- setProcessGroupID,
-
- -- ** Sessions
- createSession,
-
- -- ** Process times
- ProcessTimes(elapsedTime, systemTime, userTime,
- childSystemTime, childUserTime),
- getProcessTimes,
-
-- * System environment
SystemID(..),
getSystemID,
+ SysVar(..),
+ getSysVar,
+
+ -- * Sleeping
+ sleep, usleep,
+
{-
ToDo from unistd.h:
- confstr
- dup, dup2, exec*, exit, fork, fpathconf, fsync, ftruncate,
- gethostid, gethostname, getlogin, getopt, isatty, lockf,
- lseek, nice, pathconf, pipe, pread, pwrite, read, readlink,
- sleep, symlink, sysconf, tcgetpgrp, tcsetpgrp, truncate,
- ttyname(_r), ualarm, usleep, vfork, write
+ confstr,
+ lots of sysconf variables
- SysVar(..),
- getSysVar,
+ -- use Network.BSD
+ gethostid, gethostname
-- should be in System.Posix.Files?
+ pathconf, fpathconf,
queryTerminal,
getTerminalName,
#if !defined(cygwin32_TARGET_OS)
getControllingTerminalName,
#endif
+ -- System.Posix.Signals
+ ualarm,
+
+ -- System.Posix.Terminal
+ isatty, tcgetpgrp, tcsetpgrp, ttyname(_r),
+
+ -- System.Posix.IO
+ read, write,
+
-- should be in System.Posix.Time?
epochTime,
- -- should be in System.Posix.Pwd?
+ -- should be in System.Posix.User?
getEffectiveUserName,
-}
) where
@@ -85,7 +78,6 @@ import System.Posix.Types
import GHC.Posix
#include <unistd.h>
-#include <sys/times.h>
#include <sys/utsname.h>
-- -----------------------------------------------------------------------------
@@ -152,77 +144,6 @@ foreign import ccall unsafe "setgid"
c_setgid :: CGid -> IO CInt
-- -----------------------------------------------------------------------------
--- Process environment
-
-getProcessID :: IO ProcessID
-getProcessID = c_getpid
-
-getParentProcessID :: IO ProcessID
-getParentProcessID = c_getppid
-
-foreign import ccall unsafe "getppid"
- c_getppid :: IO CPid
-
-getProcessGroupID :: IO ProcessGroupID
-getProcessGroupID = c_getpgrp
-
-foreign import ccall unsafe "getpgrp"
- c_getpgrp :: IO CPid
-
-createProcessGroup :: ProcessID -> IO ProcessGroupID
-createProcessGroup pid = do
- throwErrnoIfMinus1_ "createProcessGroup" (c_setpgid pid 0)
- return pid
-
-joinProcessGroup :: ProcessGroupID -> IO ()
-joinProcessGroup pgid =
- throwErrnoIfMinus1_ "joinProcessGroup" (c_setpgid 0 pgid)
-
-setProcessGroupID :: ProcessID -> ProcessGroupID -> IO ()
-setProcessGroupID pid pgid =
- throwErrnoIfMinus1_ "setProcessGroupID" (c_setpgid pid pgid)
-
-foreign import ccall unsafe "setpgid"
- c_setpgid :: CPid -> CPid -> IO CInt
-
-createSession :: IO ProcessGroupID
-createSession = throwErrnoIfMinus1 "createSession" c_setsid
-
-foreign import ccall unsafe "setsid"
- c_setsid :: IO CPid
-
--- -----------------------------------------------------------------------------
--- Process times
-
--- All times in clock ticks (see getClockTick)
-
-data ProcessTimes
- = ProcessTimes { elapsedTime :: ClockTick
- , userTime :: ClockTick
- , systemTime :: ClockTick
- , childUserTime :: ClockTick
- , childSystemTime :: ClockTick
- }
-
-getProcessTimes :: IO ProcessTimes
-getProcessTimes = do
- allocaBytes (#const sizeof(struct tms)) $ \p_tms -> do
- elapsed <- throwErrnoIfMinus1 "getProcessTimes" (c_times p_tms)
- ut <- (#peek struct tms, tms_utime) p_tms
- st <- (#peek struct tms, tms_stime) p_tms
- cut <- (#peek struct tms, tms_cutime) p_tms
- cst <- (#peek struct tms, tms_cstime) p_tms
- return (ProcessTimes{ elapsedTime = elapsed,
- userTime = ut,
- systemTime = st,
- childUserTime = cut,
- childSystemTime = cst
- })
-
-foreign import ccall unsafe "times"
- c_times :: Ptr CTms -> IO CClock
-
--- -----------------------------------------------------------------------------
-- System environment (uname())
data SystemID =
@@ -249,7 +170,26 @@ getSystemID = do
machine = mach
})
-{-
+-- -----------------------------------------------------------------------------
+-- sleeping
+
+sleep :: Int -> IO Int
+sleep 0 = return 0
+sleep secs = do r <- c_sleep (fromIntegral secs); return (fromIntegral r)
+
+foreign import ccall unsafe "sleep"
+ c_sleep :: CUInt -> IO CUInt
+
+usleep :: Int -> IO ()
+usleep 0 = return ()
+usleep usecs = throwErrnoIfMinus1_ "usleep" (c_usleep (fromIntegral usecs))
+
+foreign import ccall unsafe "usleep"
+ c_usleep :: CUInt -> IO CInt
+
+-- -----------------------------------------------------------------------------
+-- System variables
+
data SysVar = ArgumentLimit
| ChildLimit
| ClockTick
@@ -258,27 +198,24 @@ data SysVar = ArgumentLimit
| PosixVersion
| HasSavedIDs
| HasJobControl
+ -- ToDo: lots more
-getSysVar :: SysVar -> IO Limit
+getSysVar :: SysVar -> IO Integer
getSysVar v =
case v of
- ArgumentLimit -> sysconf ``_SC_ARG_MAX''
- ChildLimit -> sysconf ``_SC_CHILD_MAX''
- ClockTick -> sysconf ``_SC_CLK_TCK''
- GroupLimit -> sysconf ``_SC_NGROUPS_MAX''
- OpenFileLimit -> sysconf ``_SC_OPEN_MAX''
- PosixVersion -> sysconf ``_SC_VERSION''
- HasSavedIDs -> sysconf ``_SC_SAVED_IDS''
- HasJobControl -> sysconf ``_SC_JOB_CONTROL''
--- where
-
-sysconf :: Int -> IO Limit
-sysconf n = do
- rc <- _ccall_ sysconf n
- if rc /= (-1::Int)
- then return rc
- else ioException (IOError Nothing NoSuchThing
- "getSysVar"
- "no such system limit or option"
- Nothing)
--}
+ ArgumentLimit -> sysconf (#const _SC_ARG_MAX)
+ ChildLimit -> sysconf (#const _SC_CHILD_MAX)
+ ClockTick -> sysconf (#const _SC_CLK_TCK)
+ GroupLimit -> sysconf (#const _SC_NGROUPS_MAX)
+ OpenFileLimit -> sysconf (#const _SC_OPEN_MAX)
+ PosixVersion -> sysconf (#const _SC_VERSION)
+ HasSavedIDs -> sysconf (#const _SC_SAVED_IDS)
+ HasJobControl -> sysconf (#const _SC_JOB_CONTROL)
+
+sysconf :: CInt -> IO Integer
+sysconf n = do
+ r <- throwErrnoIfMinus1 "getSysVar" (c_sysconf n)
+ return (fromIntegral r)
+
+foreign import ccall unsafe "sysconf"
+ c_sysconf :: CInt -> IO CLong
diff --git a/cbits/HsUnix.c b/cbits/HsUnix.c
new file mode 100644
index 0000000..ebaeed3
--- /dev/null
+++ b/cbits/HsUnix.c
@@ -0,0 +1,12 @@
+/* -----------------------------------------------------------------------------
+ * $Id: HsUnix.c,v 1.1 2002/09/12 16:38:22 simonmar Exp $
+ *
+ * (c) The University of Glasgow 2002
+ *
+ * Definitions for package `unix' which are visible in Haskell land.
+ *
+ * ---------------------------------------------------------------------------*/
+
+// Out-of-line versions of all the inline functions from HsUnix.h
+#define INLINE /* nothing */
+#include "HsUnix.h"
diff --git a/cbits/Makefile b/cbits/Makefile
new file mode 100644
index 0000000..e23cd2f
--- /dev/null
+++ b/cbits/Makefile
@@ -0,0 +1,12 @@
+TOP = ../..
+include $(TOP)/mk/boilerplate.mk
+
+HC = $(GHC_INPLACE)
+
+SRC_CC_OPTS += -Wall
+SRC_CC_OPTS += -I$(GHC_INCLUDE_DIR) -I$(GHC_RUNTIME_DIR) -I../include
+
+LIBRARY = libHSunix_cbits.a
+LIBOBJS = $(C_OBJS)
+
+include $(TOP)/mk/target.mk
diff --git a/cbits/execvpe.c b/cbits/execvpe.c
new file mode 100644
index 0000000..9a92169
--- /dev/null
+++ b/cbits/execvpe.c
@@ -0,0 +1,172 @@
+/*
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1995-1996
+%
+*/
+
+/* Evidently non-Posix. */
+/* #include "PosixSource.h" */
+
+#include "HsUnix.h"
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/*
+ * We want the search semantics of execvp, but we want to provide our
+ * own environment, like execve. The following copyright applies to
+ * this code, as it is a derivative of execvp:
+ *-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+int
+execvpe(char *name, char **argv, char **envp)
+{
+ register int lp, ln;
+ register char *p;
+ int eacces=0, etxtbsy=0;
+ char *bp, *cur, *path, *buf;
+
+ /* If it's an absolute or relative path name, it's easy. */
+ if (strchr(name, '/')) {
+ bp = (char *) name;
+ cur = path = buf = NULL;
+ goto retry;
+ }
+
+ /* Get the path we're searching. */
+ if (!(path = getenv("PATH"))) {
+#ifdef HAVE_CONFSTR
+ ln = confstr(_CS_PATH, NULL, 0);
+ if ((cur = path = malloc(ln + 1)) != NULL) {
+ path[0] = ':';
+ (void) confstr (_CS_PATH, path + 1, ln);
+ }
+#else
+ if ((cur = path = malloc(1 + 1)) != NULL) {
+ path[0] = ':';
+ path[1] = '\0';
+ }
+#endif
+ } else
+ cur = path = strdup(path);
+
+ if (path == NULL || (bp = buf = malloc(strlen(path)+strlen(name)+2)) == NULL)
+ goto done;
+
+ while (cur != NULL) {
+ p = cur;
+ if ((cur = strchr(cur, ':')) != NULL)
+ *cur++ = '\0';
+
+ /*
+ * It's a SHELL path -- double, leading and trailing colons mean the current
+ * directory.
+ */
+ if (!*p) {
+ p = ".";
+ lp = 1;
+ } else
+ lp = strlen(p);
+ ln = strlen(name);
+
+ memcpy(buf, p, lp);
+ buf[lp] = '/';
+ memcpy(buf + lp + 1, name, ln);
+ buf[lp + ln + 1] = '\0';
+
+ retry:
+ (void) execve(bp, argv, envp);
+ switch (errno) {
+ case EACCES:
+ eacces = 1;
+ break;
+ case ENOENT:
+ break;
+ case ENOEXEC:
+ {
+ register size_t cnt;
+ register char **ap;
+
+ for (cnt = 0, ap = (char **) argv; *ap; ++ap, ++cnt)
+ ;
+ if ((ap = malloc((cnt + 2) * sizeof(char *))) != NULL) {
+ memcpy(ap + 2, argv + 1, cnt * sizeof(char *));
+
+ ap[0] = "sh";
+ ap[1] = bp;
+ (void) execve("/bin/sh", ap, envp);
+ free(ap);
+ }
+ goto done;
+ }
+ case ETXTBSY:
+ if (etxtbsy < 3)
+ (void) sleep(++etxtbsy);
+ goto retry;
+ default:
+ goto done;
+ }
+ }
+ if (eacces)
+ errno = EACCES;
+ else if (!errno)
+ errno = ENOENT;
+ done:
+ if (path)
+ free(path);
+ if (buf)
+ free(buf);
+ return (-1);
+}
+
+
+/* Copied verbatim from ghc/lib/std/cbits/system.c. */
+void pPrPr_disableITimers (void)
+{
+# ifdef HAVE_SETITIMER
+ /* Reset the itimers in the child, so it doesn't get plagued
+ * by SIGVTALRM interrupts.
+ */
+ struct timeval tv_null = { 0, 0 };
+ struct itimerval itv;
+ itv.it_interval = tv_null;
+ itv.it_value = tv_null;
+ setitimer(ITIMER_REAL, &itv, NULL);
+ setitimer(ITIMER_VIRTUAL, &itv, NULL);
+ setitimer(ITIMER_PROF, &itv, NULL);
+# endif
+}
diff --git a/include/HsUnix.h b/include/HsUnix.h
new file mode 100644
index 0000000..46af25a
--- /dev/null
+++ b/include/HsUnix.h
@@ -0,0 +1,32 @@
+/* -----------------------------------------------------------------------------
+ * $Id: HsUnix.h,v 1.1 2002/09/12 16:38:22 simonmar Exp $
+ *
+ * (c) The University of Glasgow 2002
+ *
+ * Definitions for package `unix' which are visible in Haskell land.
+ *
+ * ---------------------------------------------------------------------------*/
+
+#ifndef HSUNIX_H
+#define HSUNIX_H
+
+#include "config.h"
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+extern int execvpe(char *name, char **argv, char **envp);
+
+#ifndef INLINE
+#define INLINE extern inline
+#endif
+
+INLINE int __hsunix_wifexited (int stat) { return WIFEXITED(stat); }
+INLINE int __hsunix_wexitstatus (int stat) { return WEXITSTATUS(stat); }
+INLINE int __hsunix_wifsignaled (int stat) { return WIFSIGNALED(stat); }
+INLINE int __hsunix_wtermsig (int stat) { return WTERMSIG(stat); }
+INLINE int __hsunix_wifstopped (int stat) { return WIFSTOPPED(stat); }
+INLINE int __hsunix_wstopsig (int stat) { return WSTOPSIG(stat); }
+
+#endif
diff --git a/include/Makefile b/include/Makefile
new file mode 100644
index 0000000..ed4eaae
--- /dev/null
+++ b/include/Makefile
@@ -0,0 +1,9 @@
+TOP=../..
+include $(TOP)/mk/boilerplate.mk
+
+H_FILES = $(wildcard *.h)
+
+includedir = $(libdir)/include
+INSTALL_INCLUDES = $(H_FILES)
+
+include $(TOP)/mk/target.mk
diff --git a/unix.conf.in b/unix.conf.in
index 7b4c851..4d33322 100644
--- a/unix.conf.in
+++ b/unix.conf.in
@@ -11,12 +11,17 @@ Package {
#ifdef INSTALLING
library_dirs = [ "$libdir" ],
#else
- library_dirs = [ "$libdir/libraries/unix" ],
+ library_dirs = [ "$libdir/libraries/unix",
+ "$libdir/libraries/unix/cbits" ],
#endif
hs_libraries = [ "HSunix" ],
- extra_libraries = [],
+ extra_libraries = [ "HSunix_cbits" ],
+#ifdef INSTALLING
include_dirs = [],
- c_includes = [],
+#else
+ include_dirs = [ "$libdir/libraries/unix/include" ],
+#endif
+ c_includes = [ "HsUnix.h" ],
package_deps = [ "base" ],
extra_ghc_opts = [],
extra_cc_opts = [],