[project @ 1998-02-02 17:27:26 by simonm]
[ghc-hetmet.git] / ghc / lib / posix / PosixProcPrim.lhs
diff --git a/ghc/lib/posix/PosixProcPrim.lhs b/ghc/lib/posix/PosixProcPrim.lhs
new file mode 100644 (file)
index 0000000..d8c1a91
--- /dev/null
@@ -0,0 +1,502 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1995-1997
+%
+\section[PosixProcPrim]{Haskell 1.3 POSIX Process Primitives}
+
+\begin{code}
+
+#include "config.h"
+
+module PosixProcPrim (
+    Handler(..),
+    SignalSet,
+    Signal,
+    ProcessStatus(..),
+
+    addSignal,
+    awaitSignal,
+    backgroundRead,
+    backgroundWrite,
+    blockSignals,
+#ifndef cygwin32_TARGET_OS
+    continueProcess,
+#endif
+    deleteSignal,
+    emptySignalSet,
+    executeFile,
+    exitImmediately,
+    floatingPointException,
+    forkProcess,
+    fullSignalSet,
+    getAnyProcessStatus,
+    getEnvVar,
+    getEnvironment,
+    getGroupProcessStatus,
+    getPendingSignals,
+    getProcessStatus,
+    getSignalMask,
+    illegalInstruction,
+    inSignalSet,
+    installHandler,
+    internalAbort,
+    keyboardSignal,
+    keyboardStop,
+    keyboardTermination,
+    killProcess,
+    lostConnection,
+    nullSignal,
+    openEndedPipe,
+    processStatusChanged,
+    queryStoppedChildFlag,
+    raiseSignal,
+    realTimeAlarm,
+    removeEnvVar,
+    scheduleAlarm,
+    segmentationViolation,
+    setEnvVar,
+    setEnvironment,
+    setSignalMask,
+    setStoppedChildFlag,
+    sigABRT,
+    sigALRM,
+    sigCHLD,
+#ifndef cygwin32_TARGET_OS
+    sigCONT,
+#endif
+    sigFPE,
+    sigHUP,
+    sigILL,
+    sigINT,
+    sigKILL,
+    sigPIPE,
+    sigProcMask,
+    sigQUIT,
+    sigSEGV,
+    sigSTOP,
+    sigSetSize,
+    sigTERM,
+    sigTSTP,
+    sigTTIN,
+    sigTTOU,
+    sigUSR1,
+    sigUSR2,
+    signalProcess,
+    signalProcessGroup,
+    sleep,
+    softwareStop,
+    softwareTermination,
+    unBlockSignals,
+    userDefinedSignal1,
+    userDefinedSignal2,
+
+    ExitCode
+
+    ) where
+
+import GlaExts
+import IO
+import PrelIOBase
+import PackedString (psToByteArrayST)
+import Foreign  -- stable pointers
+import PosixErr
+import PosixUtil
+
+import System(ExitCode(..))
+import PosixProcEnv (getProcessID)
+
+forkProcess :: IO (Maybe ProcessID)
+forkProcess = do
+    pid <-_ccall_ fork
+    case pid of
+      -1 -> syserr "forkProcess"
+      0  -> return Nothing
+      _  -> return (Just pid)
+
+executeFile :: FilePath                            -- Command
+            -> Bool                        -- Search PATH?
+            -> [String]                            -- Arguments
+            -> Maybe [(String, String)]            -- Environment
+            -> IO ()
+executeFile path search args Nothing = do
+    prog <- psToByteArrayIO path
+    argv <- vectorize (basename path:args)
+    (if search then
+        _casm_ ``execvp(%0,(char **)%1);'' prog argv
+     else
+        _casm_ ``execv(%0,(char **)%1);'' prog argv
+     )
+    syserr "executeFile"
+
+executeFile path search args (Just env) = do
+    prog <- psToByteArrayIO path
+    argv <- vectorize (basename path:args)
+    envp <- vectorize (map (\ (name, val) -> name ++ ('=' : val)) env)
+    (if search then
+        _casm_ `` execvpe(%0,(char **)%1,(char **)%2);'' prog argv envp
+     else
+        _casm_ `` execve(%0,(char **)%1,(char **)%2);'' prog argv envp
+     )
+    syserr "executeFile"
+
+data ProcessStatus = Exited ExitCode
+                   | Terminated Signal
+                   | Stopped Signal
+                  deriving (Eq, Ord, Show)
+
+getProcessStatus :: Bool -> Bool -> ProcessID -> IO (Maybe ProcessStatus)
+getProcessStatus block stopped pid = do
+    wstat <- allocWords 1
+    pid   <-_casm_ ``%r = waitpid(%0, (int *)%1, %2);'' pid wstat
+               (waitOptions block stopped)
+    case pid of
+      -1 -> syserr "getProcessStatus"
+      0  -> return Nothing
+      _  -> do ps <- decipherWaitStatus wstat
+              return (Just ps)
+
+getGroupProcessStatus :: Bool
+                      -> Bool
+                      -> ProcessGroupID
+                      -> IO (Maybe (ProcessID, ProcessStatus))
+getGroupProcessStatus block stopped pgid = do
+    wstat <- allocWords 1
+    pid   <-_casm_ ``%r = waitpid(%0, (int *)%1, %2);'' (-pgid) wstat
+                  (waitOptions block stopped)
+    case pid of
+      -1 -> syserr "getGroupProcessStatus"
+      0  -> return Nothing
+      _  -> do ps <- decipherWaitStatus wstat
+              return (Just (pid, ps))
+
+getAnyProcessStatus :: Bool -> Bool -> IO (Maybe (ProcessID, ProcessStatus))
+getAnyProcessStatus block stopped =
+    getGroupProcessStatus block stopped 1          `catch`
+    \ err -> syserr "getAnyProcessStatus"
+
+exitImmediately :: ExitCode -> IO ()
+exitImmediately exitcode = do
+    _casm_ ``_exit(%0);'' (exitcode2Int exitcode)
+    syserr "exitImmediately"
+  where
+    exitcode2Int ExitSuccess = 0
+    exitcode2Int (ExitFailure n) = n
+
+getEnvironment :: IO [(String, String)]
+getEnvironment = do
+    env <- unvectorize ``environ'' 0
+    return (map (split "") env)
+  where
+    split :: String -> String -> (String, String)
+    split x [] = error ("PosixProcPrim.getEnvironment:no `='? in: "++reverse x)
+    split x ('=' : xs) = (reverse x, xs)
+    split x (c:cs) = split (c:x) cs
+
+setEnvironment :: [(String, String)] -> IO ()
+setEnvironment pairs = do
+    env <- vectorize (map (\ (var,val) -> var ++ ('=' : val)) pairs)
+    nonzero_error (_casm_ ``%r = setenviron((char **)%0);'' env)
+       "setEnvironment"
+
+getEnvVar :: String -> IO String
+getEnvVar name = do
+    str <- psToByteArrayIO name
+    str <- _ccall_ getenv str
+    if str == ``NULL''
+       then fail (IOError Nothing NoSuchThing
+                "getEnvVar: no such environment variable")
+       else strcpy str
+
+setEnvVar :: String -> String -> IO ()
+setEnvVar name value = do
+    str <- psToByteArrayIO (name ++ ('=' : value))
+    nonzero_error (_casm_ ``%r = _setenv(%0);'' str) "setEnvVar"
+
+removeEnvVar :: String -> IO ()
+removeEnvVar name = do
+    str <- psToByteArrayIO name
+    nonzero_error (_ccall_ delenv str) "removeEnvVar"
+
+type Signal = Int
+
+nullSignal :: Signal
+nullSignal = 0
+
+backgroundRead, sigTTIN :: Signal
+backgroundRead = ``SIGTTIN''
+sigTTIN = ``SIGTTIN''
+
+backgroundWrite, sigTTOU :: Signal
+backgroundWrite = ``SIGTTOU''
+sigTTOU = ``SIGTTOU''
+
+#ifndef cygwin32_TARGET_OS
+continueProcess, sigCONT :: Signal
+continueProcess = ``SIGCONT''
+sigCONT = ``SIGCONT''
+#endif
+
+floatingPointException, sigFPE :: Signal
+floatingPointException = ``SIGFPE''
+sigFPE = ``SIGFPE''
+
+illegalInstruction, sigILL :: Signal
+illegalInstruction = ``SIGILL''
+sigILL = ``SIGILL''
+
+internalAbort, sigABRT ::Signal
+internalAbort = ``SIGABRT''
+sigABRT = ``SIGABRT''
+
+keyboardSignal, sigINT :: Signal
+keyboardSignal = ``SIGINT''
+sigINT = ``SIGINT''
+
+keyboardStop, sigTSTP :: Signal
+keyboardStop = ``SIGTSTP''
+sigTSTP = ``SIGTSTP''
+
+keyboardTermination, sigQUIT :: Signal
+keyboardTermination = ``SIGQUIT''
+sigQUIT = ``SIGQUIT''
+
+killProcess, sigKILL :: Signal
+killProcess = ``SIGKILL''
+sigKILL = ``SIGKILL''
+
+lostConnection, sigHUP :: Signal
+lostConnection = ``SIGHUP''
+sigHUP = ``SIGHUP''
+
+openEndedPipe, sigPIPE :: Signal
+openEndedPipe = ``SIGPIPE''
+sigPIPE = ``SIGPIPE''
+
+processStatusChanged, sigCHLD :: Signal
+processStatusChanged = ``SIGCHLD''
+sigCHLD = ``SIGCHLD''
+
+realTimeAlarm, sigALRM :: Signal
+realTimeAlarm = ``SIGALRM''
+sigALRM = ``SIGALRM''
+
+segmentationViolation, sigSEGV :: Signal
+segmentationViolation = ``SIGSEGV''
+sigSEGV = ``SIGSEGV''
+
+softwareStop, sigSTOP :: Signal
+softwareStop = ``SIGSTOP''
+sigSTOP = ``SIGSTOP''
+
+softwareTermination, sigTERM :: Signal
+softwareTermination = ``SIGTERM''
+sigTERM = ``SIGTERM''
+
+userDefinedSignal1, sigUSR1 :: Signal
+userDefinedSignal1 = ``SIGUSR1''
+sigUSR1 = ``SIGUSR1''
+
+userDefinedSignal2, sigUSR2 :: Signal
+userDefinedSignal2 = ``SIGUSR2''
+sigUSR2 = ``SIGUSR2''
+
+signalProcess :: Signal -> ProcessID -> IO ()
+signalProcess int pid =
+    nonzero_error (_ccall_ kill pid int) "signalProcess"
+
+raiseSignal :: Signal -> IO ()
+raiseSignal int = getProcessID >>= signalProcess int
+
+signalProcessGroup :: Signal -> ProcessGroupID -> IO ()
+signalProcessGroup int pgid = signalProcess int (-pgid)
+
+setStoppedChildFlag :: Bool -> IO Bool
+setStoppedChildFlag b = do
+    rc <- _casm_ ``%r = nocldstop; nocldstop = %0;'' x
+    return (rc == 0)
+  where
+    x = case b of {True -> 0; False -> 1}
+
+queryStoppedChildFlag :: IO Bool
+queryStoppedChildFlag = do
+    rc <- _casm_ ``%r = nocldstop;''
+    return (rc == 0)
+
+data Handler = Default
+             | Ignore
+             | Catch (IO ())
+
+type SignalSet = ByteArray ()
+
+sigSetSize :: Int
+sigSetSize = ``sizeof(sigset_t)''
+
+emptySignalSet :: SignalSet
+emptySignalSet = unsafePerformPrimIO $ do
+    bytes <- allocChars sigSetSize
+    _casm_ ``(void) sigemptyset((sigset_t *)%0);'' bytes
+    freeze bytes
+
+fullSignalSet :: SignalSet
+fullSignalSet = unsafePerformPrimIO $ do
+    bytes <- allocChars sigSetSize
+    _casm_ ``(void) sigfillset((sigset_t *)%0);'' bytes
+    freeze bytes
+
+addSignal :: Signal -> SignalSet -> SignalSet
+addSignal int oldset = unsafePerformPrimIO $ do
+    bytes <- allocChars sigSetSize
+    _casm_ ``*(sigset_t *)%0 = *(sigset_t *)%1;
+            (void) sigaddset((sigset_t *)%0, %2);''
+       bytes oldset int
+    freeze bytes
+
+inSignalSet :: Signal -> SignalSet -> Bool
+inSignalSet int sigset = unsafePerformPrimIO $ do
+    rc <- _casm_ ``%r = sigismember((sigset_t *)%0, %1);'' sigset int
+    return (rc == 1)
+
+deleteSignal :: Signal -> SignalSet -> SignalSet
+deleteSignal int oldset = unsafePerformPrimIO $ do
+    bytes <- allocChars sigSetSize
+    _casm_ ``*(sigset_t *)%0 = *(sigset_t *)%1;
+            (void) sigdelset((sigset_t *)%0, %2);''
+           bytes oldset int
+    freeze bytes
+
+installHandler :: Signal
+               -> Handler
+               -> Maybe SignalSet      -- other signals to block
+               -> IO Handler           -- old handler
+
+#ifdef __PARALLEL_HASKELL__
+installHandler = error "installHandler: not available for Parallel Haskell"
+#else
+installHandler int handler maybe_mask = (
+    case handler of
+      Default -> _ccall_ stg_sig_default int mask
+      Ignore  -> _ccall_ stg_sig_ignore  int mask
+      Catch m -> do
+        sptr <- makeStablePtr (ioToPrimIO m)
+       _ccall_ stg_sig_catch int sptr mask
+    ) >>= \rc ->
+
+    if rc >= 0 then do
+        osptr <- _casm_ ``%r = (StgStablePtr) (%0);'' rc
+        m     <- deRefStablePtr osptr
+       return (Catch m)
+    else if rc == ``STG_SIG_DFL'' then
+       return Default
+    else if rc == ``STG_SIG_IGN'' then
+       return Ignore
+    else
+       syserr "installHandler"
+  where
+    mask = case maybe_mask of
+            Nothing -> emptySignalSet
+             Just x -> x
+
+#endif {-!__PARALLEL_HASKELL__-}
+
+getSignalMask :: IO SignalSet
+getSignalMask = do
+    bytes <- allocChars sigSetSize
+    rc    <- _casm_ ``%r = sigprocmask(0, NULL, (sigset_t *)%0);'' bytes
+    if rc == 0
+       then freeze bytes
+       else syserr "getSignalMask"
+
+sigProcMask :: String -> Int -> SignalSet -> IO SignalSet
+sigProcMask name how sigset = do
+    bytes <- allocChars sigSetSize
+    rc <- _casm_ ``%r = sigprocmask(%0, (sigset_t *)%1, (sigset_t *)%2);''
+                how sigset bytes
+    if rc == 0
+       then freeze bytes
+       else syserr name
+
+setSignalMask :: SignalSet -> IO SignalSet
+setSignalMask = sigProcMask "setSignalMask" ``SIG_SETMASK''
+
+blockSignals :: SignalSet -> IO SignalSet
+blockSignals = sigProcMask "blockSignals" ``SIG_BLOCK''
+
+unBlockSignals :: SignalSet -> IO SignalSet
+unBlockSignals = sigProcMask "unBlockSignals" ``SIG_UNBLOCK''
+
+getPendingSignals :: IO SignalSet
+getPendingSignals = do
+    bytes <- allocChars sigSetSize
+    rc <- _casm_ ``%r = sigpending((sigset_t *)%0);'' bytes
+    if rc == 0
+       then freeze bytes
+       else syserr "getPendingSignals"
+
+awaitSignal :: Maybe SignalSet -> IO ()
+awaitSignal maybe_sigset = do
+    pause maybe_sigset
+    err <- getErrorCode
+    if err == interruptedOperation
+       then return ()
+       else syserr "awaitSignal"
+--  where
+
+pause :: Maybe SignalSet -> IO ()
+pause maybe_sigset =
+  case maybe_sigset of
+   Nothing -> _casm_ ``(void) pause();''
+   Just sigset -> _casm_ ``(void) sigsuspend((sigset_t *)%0);'' sigset
+
+scheduleAlarm :: Int -> IO Int
+scheduleAlarm (I# secs#) =
+    _ccall_ alarm (W# (int2Word# secs#))           >>= \ (W# w#) ->
+    return (I# (word2Int# w#))
+
+sleep :: Int -> IO ()
+sleep 0 = return ()
+sleep (I# secs#) = do
+    _ccall_ sleep (W# (int2Word# secs#))
+    return ()
+\end{code}
+
+Local utility functions
+
+\begin{code}
+
+-- Get the trailing component of a path
+
+basename :: String -> String
+basename "" = ""
+basename (c:cs)
+  | c == '/' = basename cs
+  | otherwise = c : basename cs
+
+-- Convert wait options to appropriate set of flags
+
+waitOptions :: Bool -> Bool -> Int
+--             block   stopped
+waitOptions False False = ``WNOHANG''
+waitOptions False True  = ``(WNOHANG|WUNTRACED)''
+waitOptions True  False = 0
+waitOptions True  True  = ``WUNTRACED''
+
+-- Turn a (ptr to a) wait status into a ProcessStatus
+
+decipherWaitStatus :: MutableByteArray s x -> IO ProcessStatus
+decipherWaitStatus wstat = do
+    exited <- _casm_ ``%r = WIFEXITED(*(int *)%0);'' wstat
+    if exited /= 0
+      then do
+        exitstatus <- _casm_ ``%r = WEXITSTATUS(*(int *)%0);'' wstat
+        if exitstatus == 0
+          then return (Exited ExitSuccess)
+          else return (Exited (ExitFailure exitstatus))
+      else do
+        signalled <- _casm_ ``%r = WIFSIGNALED(*(int *)%0);'' wstat
+        if signalled /= 0
+          then do
+               termsig <- _casm_ ``%r = WTERMSIG(*(int *)%0);'' wstat
+               return (Terminated termsig)
+          else do
+               stopsig <-_casm_ ``%r = WSTOPSIG(*(int *)%0);'' wstat
+               return (Stopped stopsig)
+\end{code}