#include "HsUnixConfig.h"
module System.Posix.Signals (
Signal,
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,
pollableEvent, sigPOLL,
profilingTimerExpired, sigPROF,
badSystemCall, sigSYS,
breakpointTrap, sigTRAP,
urgentDataAvailable, sigURG,
virtualTimerExpired, sigVTALRM,
cpuTimeLimitExceeded, sigXCPU,
fileSizeLimitExceeded, sigXFSZ,
raiseSignal,
signalProcess,
signalProcessGroup,
addSignalHandler, addOneShotSignalHandler, removeSignalHandler,
SignalHandler, SignalInfo(..), SignalSpecificInfo(..),
Handler(..), installHandler,
SignalSet,
emptySignalSet, fullSignalSet,
addSignal, deleteSignal, inSignalSet,
getSignalMask, setSignalMask, blockSignals, unblockSignals,
scheduleAlarm,
getPendingSignals,
awaitSignal,
setStoppedChildFlag, queryStoppedChildFlag,
) where
import Foreign
import Foreign.C
import System.IO.Unsafe
import System.Posix.Types
import System.Posix.Internals
import System.Posix.Process.Internals
import System.Posix.Process
import Control.Concurrent
import Control.Exception (bracket)
import Data.List (partition)
import Control.Monad
#include "Signals.h"
import qualified GHC.Conc
import GHC.Conc hiding (Handler)
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
pollableEvent :: Signal
pollableEvent = sigPOLL
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
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 :: 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 :: Signal -> IO ()
raiseSignal sig = throwErrnoIfMinus1_ "raiseSignal" (c_raise sig)
foreign import ccall unsafe "raise"
c_raise :: CInt -> IO CInt
data Handler = Default
| Ignore
| Catch (IO ())
| CatchOnce (IO ())
installHandler :: Signal
-> Handler
-> Maybe SignalSet
-> IO Handler
installHandler sig handler maybe_mask = do
old <- remove_old_handler
case handler of
Default -> return ()
Ignore -> add_handler sig (return ()) addSignalHandler
Catch action -> add_handler sig action addSignalHandler
CatchOnce action -> add_handler sig action addOneShotSignalHandler
return old
where
remove_old_handler = do
modifyMVar old_handler_ref $ \old_handlers -> do
let (this, others) = partition ((== sig) . fst3) old_handlers
fst3 (x,_,_) = x
case this of
[] -> return (others, Default)
((t,id,h):_) -> do
removeSignalHandler sig id
return (others, h)
add_handler sig action add = do
id <- add sig (const action)
modifyMVar_ old_handler_ref $ \xs ->
return ((sig, id, Catch action) : xs)
old_handler_ref :: MVar [(Signal, HandlerId, Handler)]
old_handler_ref = unsafePerformIO (newMVar [])
data SignalState = SignalDefault | SignalHandled
setSignalState :: Signal -> SignalState -> IO SignalState
setSignalState sig state = do
ensureIOManagerIsRunning
r <- stg_sig_install sig (fromState state) nullPtr
if (r == STG_SIG_ERR)
then throwErrno "setSignalState"
else return (toState r)
where
fromState SignalDefault = STG_SIG_DFL
fromState SignalHandled = STG_SIG_HAN
toState STG_SIG_DFL = SignalDefault
toState STG_SIG_HAN = SignalHandled
foreign import ccall unsafe
stg_sig_install
:: CInt
-> CInt
-> Ptr CSigset
-> IO CInt
type SignalHandler = SignalInfo -> IO ()
addSignalHandler :: Signal -> SignalHandler -> IO HandlerId
addSignalHandler sig handler = do
setSignalState sig SignalHandled
GHC.Conc.addHandler sig (getinfo handler) False
addOneShotSignalHandler :: Signal -> SignalHandler -> IO HandlerId
addOneShotSignalHandler sig handler = do
setSignalState sig SignalHandled
GHC.Conc.addHandler sig (getinfo handler) True
getinfo :: (SignalInfo -> IO ()) -> ForeignPtr Word8 -> IO ()
getinfo handler fp_info = do
si <- unmarshalSigInfo fp_info
handler si
removeSignalHandler :: Signal -> HandlerId -> IO Bool
removeSignalHandler sig id = do
r <- GHC.Conc.removeHandler sig id
none <- noHandlers sig
when none $ do setSignalState sig SignalDefault; return ()
return r
unmarshalSigInfo :: ForeignPtr Word8 -> IO SignalInfo
unmarshalSigInfo fp = do
withForeignPtr fp $ \p -> do
sig <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) p
errno <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) p
extra <- case sig of
_ | sig == sigCHLD -> do
pid <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) p
uid <- ((\hsc_ptr -> peekByteOff hsc_ptr 20)) p
wstat <- ((\hsc_ptr -> peekByteOff hsc_ptr 24)) p
pstat <- decipherWaitStatus wstat
return SigChldInfo { siginfoPid = pid,
siginfoUid = uid,
siginfoStatus = pstat }
_ | otherwise ->
return NoSignalSpecificInfo
return
SignalInfo {
siginfoSignal = sig,
siginfoError = Errno errno,
siginfoSpecific = extra }
data SignalInfo = SignalInfo {
siginfoSignal :: Signal,
siginfoError :: Errno,
siginfoSpecific :: SignalSpecificInfo
}
data SignalSpecificInfo
= NoSignalSpecificInfo
| SigChldInfo {
siginfoPid :: ProcessID,
siginfoUid :: UserID,
siginfoStatus :: ProcessStatus
}
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
foreign import ccall "&nocldstop" nocldstop :: Ptr Int
setStoppedChildFlag :: Bool -> IO Bool
setStoppedChildFlag b = do
rc <- peek nocldstop
poke nocldstop $ fromEnum (not b)
return (rc == (0::Int))
queryStoppedChildFlag :: IO Bool
queryStoppedChildFlag = do
rc <- peek nocldstop
return (rc == (0::Int))
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 :: 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 :: SignalSet -> IO ()
setSignalMask set = sigProcMask "setSignalMask" (CONST_SIG_SETMASK :: CInt) set
blockSignals :: SignalSet -> IO ()
blockSignals set = sigProcMask "blockSignals" (CONST_SIG_BLOCK :: CInt) set
unblockSignals :: SignalSet -> IO ()
unblockSignals set = sigProcMask "unblockSignals" (CONST_SIG_UNBLOCK :: CInt) set
getPendingSignals :: IO SignalSet
getPendingSignals = do
fp <- mallocForeignPtrBytes sizeof_sigset_t
withForeignPtr fp $ \p ->
throwErrnoIfMinus1_ "getPendingSignals" (c_sigpending p)
return (SignalSet fp)
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 ()
foreign import ccall unsafe "sigsuspend"
c_sigsuspend :: Ptr CSigset -> IO CInt
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
foreign import ccall unsafe "sigpending"
c_sigpending :: Ptr CSigset -> IO CInt