----------------------------------------------------------------------------- -- | -- 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 ( #ifndef mingw32_HOST_OS -- * 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 #endif ) where import Prelude -- necessary to get dependencies right import Foreign import Foreign.C import System.IO.Unsafe import System.Posix.Types import System.Posix.Internals #ifndef mingw32_HOST_OS -- WHOLE FILE... #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 #endif /* mingw32_HOST_OS */