aboutsummaryrefslogtreecommitdiffhomepage
path: root/System/Posix/Signals.hs
diff options
context:
space:
mode:
Diffstat (limited to 'System/Posix/Signals.hs')
-rw-r--r--System/Posix/Signals.hs528
1 files changed, 528 insertions, 0 deletions
diff --git a/System/Posix/Signals.hs b/System/Posix/Signals.hs
new file mode 100644
index 0000000..1aa9dca
--- /dev/null
+++ b/System/Posix/Signals.hs
@@ -0,0 +1,528 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module : System.Posix.Signals
+-- 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 signal support
+--
+-----------------------------------------------------------------------------
+
+#include "HsBaseConfig.h"
+
+module System.Posix.Signals (
+ -- * The Signal type
+ Signal,
+
+ -- * Specific signals
+ nullSignal,
+ internalAbort, sigABRT,
+ realTimeAlarm, sigALRM,
+ busError, sigBUS,
+ processStatusChanged, sigCHLD,
+ continueProcess, sigCONT,
+ floatingPointException, sigFPE,
+ lostConnection, sigHUP,
+ illegalInstruction, sigILL,
+ keyboardSignal, sigINT,
+ killProcess, sigKILL,
+ openEndedPipe, sigPIPE,
+ keyboardTermination, sigQUIT,
+ segmentationViolation, sigSEGV,
+ softwareStop, sigSTOP,
+ softwareTermination, sigTERM,
+ keyboardStop, sigTSTP,
+ backgroundRead, sigTTIN,
+ backgroundWrite, sigTTOU,
+ userDefinedSignal1, sigUSR1,
+ userDefinedSignal2, sigUSR2,
+#if CONST_SIGPOLL != -1
+ pollableEvent, sigPOLL,
+#endif
+ profilingTimerExpired, sigPROF,
+ badSystemCall, sigSYS,
+ breakpointTrap, sigTRAP,
+ urgentDataAvailable, sigURG,
+ virtualTimerExpired, sigVTALRM,
+ cpuTimeLimitExceeded, sigXCPU,
+ fileSizeLimitExceeded, sigXFSZ,
+
+ -- * Sending signals
+ raiseSignal,
+ signalProcess,
+ signalProcessGroup,
+
+#ifdef __GLASGOW_HASKELL__
+ -- * Handling signals
+ Handler(..),
+ installHandler,
+#endif
+
+ -- * Signal sets
+ SignalSet,
+ emptySignalSet, fullSignalSet,
+ addSignal, deleteSignal, inSignalSet,
+
+ -- * The process signal mask
+ getSignalMask, setSignalMask, blockSignals, unblockSignals,
+
+ -- * The alarm timer
+ scheduleAlarm,
+
+ -- * Waiting for signals
+ getPendingSignals,
+#ifndef cygwin32_HOST_OS
+ awaitSignal,
+#endif
+
+#ifdef __GLASGOW_HASKELL__
+ -- * The @NOCLDSTOP@ flag
+ setStoppedChildFlag, queryStoppedChildFlag,
+#endif
+
+ -- MISSING FUNCTIONALITY:
+ -- sigaction(), (inc. the sigaction structure + flags etc.)
+ -- the siginfo structure
+ -- sigaltstack()
+ -- sighold, sigignore, sigpause, sigrelse, sigset
+ -- siginterrupt
+ ) where
+
+import Foreign
+import Foreign.C
+import System.IO.Unsafe
+import System.Posix.Types
+import System.Posix.Internals
+
+#ifdef __GLASGOW_HASKELL__
+#include "Signals.h"
+import GHC.Conc ( ensureIOManagerIsRunning )
+#endif
+
+-- -----------------------------------------------------------------------------
+-- Specific signals
+
+type Signal = CInt
+
+nullSignal :: Signal
+nullSignal = 0
+
+sigABRT :: CInt
+sigABRT = CONST_SIGABRT
+sigALRM :: CInt
+sigALRM = CONST_SIGALRM
+sigBUS :: CInt
+sigBUS = CONST_SIGBUS
+sigCHLD :: CInt
+sigCHLD = CONST_SIGCHLD
+sigCONT :: CInt
+sigCONT = CONST_SIGCONT
+sigFPE :: CInt
+sigFPE = CONST_SIGFPE
+sigHUP :: CInt
+sigHUP = CONST_SIGHUP
+sigILL :: CInt
+sigILL = CONST_SIGILL
+sigINT :: CInt
+sigINT = CONST_SIGINT
+sigKILL :: CInt
+sigKILL = CONST_SIGKILL
+sigPIPE :: CInt
+sigPIPE = CONST_SIGPIPE
+sigQUIT :: CInt
+sigQUIT = CONST_SIGQUIT
+sigSEGV :: CInt
+sigSEGV = CONST_SIGSEGV
+sigSTOP :: CInt
+sigSTOP = CONST_SIGSTOP
+sigTERM :: CInt
+sigTERM = CONST_SIGTERM
+sigTSTP :: CInt
+sigTSTP = CONST_SIGTSTP
+sigTTIN :: CInt
+sigTTIN = CONST_SIGTTIN
+sigTTOU :: CInt
+sigTTOU = CONST_SIGTTOU
+sigUSR1 :: CInt
+sigUSR1 = CONST_SIGUSR1
+sigUSR2 :: CInt
+sigUSR2 = CONST_SIGUSR2
+sigPOLL :: CInt
+sigPOLL = CONST_SIGPOLL
+sigPROF :: CInt
+sigPROF = CONST_SIGPROF
+sigSYS :: CInt
+sigSYS = CONST_SIGSYS
+sigTRAP :: CInt
+sigTRAP = CONST_SIGTRAP
+sigURG :: CInt
+sigURG = CONST_SIGURG
+sigVTALRM :: CInt
+sigVTALRM = CONST_SIGVTALRM
+sigXCPU :: CInt
+sigXCPU = CONST_SIGXCPU
+sigXFSZ :: CInt
+sigXFSZ = CONST_SIGXFSZ
+
+internalAbort ::Signal
+internalAbort = sigABRT
+
+realTimeAlarm :: Signal
+realTimeAlarm = sigALRM
+
+busError :: Signal
+busError = sigBUS
+
+processStatusChanged :: Signal
+processStatusChanged = sigCHLD
+
+continueProcess :: Signal
+continueProcess = sigCONT
+
+floatingPointException :: Signal
+floatingPointException = sigFPE
+
+lostConnection :: Signal
+lostConnection = sigHUP
+
+illegalInstruction :: Signal
+illegalInstruction = sigILL
+
+keyboardSignal :: Signal
+keyboardSignal = sigINT
+
+killProcess :: Signal
+killProcess = sigKILL
+
+openEndedPipe :: Signal
+openEndedPipe = sigPIPE
+
+keyboardTermination :: Signal
+keyboardTermination = sigQUIT
+
+segmentationViolation :: Signal
+segmentationViolation = sigSEGV
+
+softwareStop :: Signal
+softwareStop = sigSTOP
+
+softwareTermination :: Signal
+softwareTermination = sigTERM
+
+keyboardStop :: Signal
+keyboardStop = sigTSTP
+
+backgroundRead :: Signal
+backgroundRead = sigTTIN
+
+backgroundWrite :: Signal
+backgroundWrite = sigTTOU
+
+userDefinedSignal1 :: Signal
+userDefinedSignal1 = sigUSR1
+
+userDefinedSignal2 :: Signal
+userDefinedSignal2 = sigUSR2
+
+#if CONST_SIGPOLL != -1
+pollableEvent :: Signal
+pollableEvent = sigPOLL
+#endif
+
+profilingTimerExpired :: Signal
+profilingTimerExpired = sigPROF
+
+badSystemCall :: Signal
+badSystemCall = sigSYS
+
+breakpointTrap :: Signal
+breakpointTrap = sigTRAP
+
+urgentDataAvailable :: Signal
+urgentDataAvailable = sigURG
+
+virtualTimerExpired :: Signal
+virtualTimerExpired = sigVTALRM
+
+cpuTimeLimitExceeded :: Signal
+cpuTimeLimitExceeded = sigXCPU
+
+fileSizeLimitExceeded :: Signal
+fileSizeLimitExceeded = sigXFSZ
+
+-- -----------------------------------------------------------------------------
+-- Signal-related functions
+
+-- | @signalProcess int pid@ calls @kill@ to signal process @pid@
+-- with interrupt signal @int@.
+signalProcess :: Signal -> ProcessID -> IO ()
+signalProcess sig pid
+ = throwErrnoIfMinus1_ "signalProcess" (c_kill (fromIntegral pid) sig)
+
+foreign import ccall unsafe "kill"
+ c_kill :: CPid -> CInt -> IO CInt
+
+
+-- | @signalProcessGroup int pgid@ calls @kill@ to signal
+-- all processes in group @pgid@ with interrupt signal @int@.
+signalProcessGroup :: Signal -> ProcessGroupID -> IO ()
+signalProcessGroup sig pgid
+ = throwErrnoIfMinus1_ "signalProcessGroup" (c_killpg (fromIntegral pgid) sig)
+
+foreign import ccall unsafe "killpg"
+ c_killpg :: CPid -> CInt -> IO CInt
+
+-- | @raiseSignal int@ calls @kill@ to signal the current process
+-- with interrupt signal @int@.
+raiseSignal :: Signal -> IO ()
+raiseSignal sig = throwErrnoIfMinus1_ "raiseSignal" (c_raise sig)
+
+#if defined(__GLASGOW_HASKELL__) && (defined(openbsd_HOST_OS) || defined(freebsd_HOST_OS))
+foreign import ccall unsafe "genericRaise"
+ c_raise :: CInt -> IO CInt
+#else
+foreign import ccall unsafe "raise"
+ c_raise :: CInt -> IO CInt
+#endif
+
+#ifdef __GLASGOW_HASKELL__
+data Handler = Default
+ | Ignore
+ -- not yet: | Hold
+ | Catch (IO ())
+ | CatchOnce (IO ())
+
+-- | @installHandler int handler iset@ calls @sigaction@ to install an
+-- interrupt handler for signal @int@. If @handler@ is @Default@,
+-- @SIG_DFL@ is installed; if @handler@ is @Ignore@, @SIG_IGN@ is
+-- installed; if @handler@ is @Catch action@, a handler is installed
+-- which will invoke @action@ in a new thread when (or shortly after) the
+-- signal is received.
+-- If @iset@ is @Just s@, then the @sa_mask@ of the @sigaction@ structure
+-- is set to @s@; otherwise it is cleared. The previously installed
+-- signal handler for @int@ is returned
+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 = do
+ ensureIOManagerIsRunning -- for the threaded RTS
+ case maybe_mask of
+ Nothing -> install' nullPtr
+ Just (SignalSet x) -> withForeignPtr x $ install'
+ where
+ install' mask =
+ alloca $ \p_sp -> do
+
+ rc <- case handler of
+ Default -> stg_sig_install int STG_SIG_DFL p_sp mask
+ Ignore -> stg_sig_install int STG_SIG_IGN p_sp mask
+ Catch m -> hinstall m p_sp mask int STG_SIG_HAN
+ CatchOnce m -> hinstall m p_sp mask int STG_SIG_RST
+
+ case rc of
+ STG_SIG_DFL -> return Default
+ STG_SIG_IGN -> return Ignore
+ STG_SIG_ERR -> throwErrno "installHandler"
+ STG_SIG_HAN -> do
+ m <- peekHandler p_sp
+ return (Catch m)
+ STG_SIG_RST -> do
+ m <- peekHandler p_sp
+ return (CatchOnce m)
+ _other ->
+ error "internal error: System.Posix.Signals.installHandler"
+
+ hinstall m p_sp mask int reset = do
+ sptr <- newStablePtr m
+ poke p_sp sptr
+ stg_sig_install int reset p_sp mask
+
+ peekHandler p_sp = do
+ osptr <- peek p_sp
+ deRefStablePtr osptr
+
+foreign import ccall unsafe
+ stg_sig_install
+ :: CInt -- sig no.
+ -> CInt -- action code (STG_SIG_HAN etc.)
+ -> Ptr (StablePtr (IO ())) -- (in, out) Haskell handler
+ -> Ptr CSigset -- (in, out) blocked
+ -> IO CInt -- (ret) action code
+
+#endif /* !__PARALLEL_HASKELL__ */
+#endif /* __GLASGOW_HASKELL__ */
+
+-- -----------------------------------------------------------------------------
+-- Alarms
+
+-- | @scheduleAlarm i@ calls @alarm@ to schedule a real time
+-- alarm at least @i@ seconds in the future.
+scheduleAlarm :: Int -> IO Int
+scheduleAlarm secs = do
+ r <- c_alarm (fromIntegral secs)
+ return (fromIntegral r)
+
+foreign import ccall unsafe "alarm"
+ c_alarm :: CUInt -> IO CUInt
+
+#ifdef __GLASGOW_HASKELL__
+-- -----------------------------------------------------------------------------
+-- The NOCLDSTOP flag
+
+foreign import ccall "&nocldstop" nocldstop :: Ptr Int
+
+-- | Tells the system whether or not to set the @SA_NOCLDSTOP@ flag when
+-- installing new signal handlers.
+setStoppedChildFlag :: Bool -> IO Bool
+setStoppedChildFlag b = do
+ rc <- peek nocldstop
+ poke nocldstop $ fromEnum (not b)
+ return (rc == (0::Int))
+
+-- | Queries the current state of the stopped child flag.
+queryStoppedChildFlag :: IO Bool
+queryStoppedChildFlag = do
+ rc <- peek nocldstop
+ return (rc == (0::Int))
+#endif /* __GLASGOW_HASKELL__ */
+
+-- -----------------------------------------------------------------------------
+-- Manipulating signal sets
+
+newtype SignalSet = SignalSet (ForeignPtr CSigset)
+
+emptySignalSet :: SignalSet
+emptySignalSet = unsafePerformIO $ do
+ fp <- mallocForeignPtrBytes sizeof_sigset_t
+ throwErrnoIfMinus1_ "emptySignalSet" (withForeignPtr fp $ c_sigemptyset)
+ return (SignalSet fp)
+
+fullSignalSet :: SignalSet
+fullSignalSet = unsafePerformIO $ do
+ fp <- mallocForeignPtrBytes sizeof_sigset_t
+ throwErrnoIfMinus1_ "fullSignalSet" (withForeignPtr fp $ c_sigfillset)
+ return (SignalSet fp)
+
+infixr `addSignal`, `deleteSignal`
+addSignal :: Signal -> SignalSet -> SignalSet
+addSignal sig (SignalSet fp1) = unsafePerformIO $ do
+ fp2 <- mallocForeignPtrBytes sizeof_sigset_t
+ withForeignPtr fp1 $ \p1 ->
+ withForeignPtr fp2 $ \p2 -> do
+ copyBytes p2 p1 sizeof_sigset_t
+ throwErrnoIfMinus1_ "addSignal" (c_sigaddset p2 sig)
+ return (SignalSet fp2)
+
+deleteSignal :: Signal -> SignalSet -> SignalSet
+deleteSignal sig (SignalSet fp1) = unsafePerformIO $ do
+ fp2 <- mallocForeignPtrBytes sizeof_sigset_t
+ withForeignPtr fp1 $ \p1 ->
+ withForeignPtr fp2 $ \p2 -> do
+ copyBytes p2 p1 sizeof_sigset_t
+ throwErrnoIfMinus1_ "deleteSignal" (c_sigdelset p2 sig)
+ return (SignalSet fp2)
+
+inSignalSet :: Signal -> SignalSet -> Bool
+inSignalSet sig (SignalSet fp) = unsafePerformIO $
+ withForeignPtr fp $ \p -> do
+ r <- throwErrnoIfMinus1 "inSignalSet" (c_sigismember p sig)
+ return (r /= 0)
+
+-- | @getSignalMask@ calls @sigprocmask@ to determine the
+-- set of interrupts which are currently being blocked.
+getSignalMask :: IO SignalSet
+getSignalMask = do
+ fp <- mallocForeignPtrBytes sizeof_sigset_t
+ withForeignPtr fp $ \p ->
+ throwErrnoIfMinus1_ "getSignalMask" (c_sigprocmask 0 nullPtr p)
+ return (SignalSet fp)
+
+sigProcMask :: String -> CInt -> SignalSet -> IO ()
+sigProcMask fn how (SignalSet set) =
+ withForeignPtr set $ \p_set ->
+ throwErrnoIfMinus1_ fn (c_sigprocmask how p_set nullPtr)
+
+-- | @setSignalMask mask@ calls @sigprocmask@ with
+-- @SIG_SETMASK@ to block all interrupts in @mask@.
+setSignalMask :: SignalSet -> IO ()
+setSignalMask set = sigProcMask "setSignalMask" (CONST_SIG_SETMASK :: CInt) set
+
+-- | @blockSignals mask@ calls @sigprocmask@ with
+-- @SIG_BLOCK@ to add all interrupts in @mask@ to the
+-- set of blocked interrupts.
+blockSignals :: SignalSet -> IO ()
+blockSignals set = sigProcMask "blockSignals" (CONST_SIG_BLOCK :: CInt) set
+
+-- | @unblockSignals mask@ calls @sigprocmask@ with
+-- @SIG_UNBLOCK@ to remove all interrupts in @mask@ from the
+-- set of blocked interrupts.
+unblockSignals :: SignalSet -> IO ()
+unblockSignals set = sigProcMask "unblockSignals" (CONST_SIG_UNBLOCK :: CInt) set
+
+-- | @getPendingSignals@ calls @sigpending@ to obtain
+-- the set of interrupts which have been received but are currently blocked.
+getPendingSignals :: IO SignalSet
+getPendingSignals = do
+ fp <- mallocForeignPtrBytes sizeof_sigset_t
+ withForeignPtr fp $ \p ->
+ throwErrnoIfMinus1_ "getPendingSignals" (c_sigpending p)
+ return (SignalSet fp)
+
+#ifndef cygwin32_HOST_OS
+
+-- | @awaitSignal iset@ suspends execution until an interrupt is received.
+-- If @iset@ is @Just s@, @awaitSignal@ calls @sigsuspend@, installing
+-- @s@ as the new signal mask before suspending execution; otherwise, it
+-- calls @pause@. @awaitSignal@ returns on receipt of a signal. If you
+-- have installed any signal handlers with @installHandler@, it may be
+-- wise to call @yield@ directly after @awaitSignal@ to ensure that the
+-- signal handler runs as promptly as possible.
+awaitSignal :: Maybe SignalSet -> IO ()
+awaitSignal maybe_sigset = do
+ fp <- case maybe_sigset of
+ Nothing -> do SignalSet fp <- getSignalMask; return fp
+ Just (SignalSet fp) -> return fp
+ withForeignPtr fp $ \p -> do
+ c_sigsuspend p
+ return ()
+ -- ignore the return value; according to the docs it can only ever be
+ -- (-1) with errno set to EINTR.
+
+foreign import ccall unsafe "sigsuspend"
+ c_sigsuspend :: Ptr CSigset -> IO CInt
+#endif
+
+#ifdef __HUGS__
+foreign import ccall unsafe "sigdelset"
+ c_sigdelset :: Ptr CSigset -> CInt -> IO CInt
+
+foreign import ccall unsafe "sigfillset"
+ c_sigfillset :: Ptr CSigset -> IO CInt
+
+foreign import ccall unsafe "sigismember"
+ c_sigismember :: Ptr CSigset -> CInt -> IO CInt
+#else
+foreign import ccall unsafe "__hscore_sigdelset"
+ c_sigdelset :: Ptr CSigset -> CInt -> IO CInt
+
+foreign import ccall unsafe "__hscore_sigfillset"
+ c_sigfillset :: Ptr CSigset -> IO CInt
+
+foreign import ccall unsafe "__hscore_sigismember"
+ c_sigismember :: Ptr CSigset -> CInt -> IO CInt
+#endif /* __HUGS__ */
+
+foreign import ccall unsafe "sigpending"
+ c_sigpending :: Ptr CSigset -> IO CInt
+