packages feed

angel-0.5.1: src/Angel/Process.hs

{-# LANGUAGE ScopedTypeVariables #-}
module Angel.Process ( getProcessHandleStatus
                     , isProcessHandleDead
                     , softKillProcessHandle
                     , hardKillProcessHandle
                     , signalProcessHandle ) where

import Control.Exception (catchJust)
import Control.Monad ( join
                     , void )
import Data.Maybe (isJust)
import System.IO.Error ( catchIOError
                       , isDoesNotExistError)
import System.Process (ProcessHandle)
import System.Process.Internals ( ProcessHandle__(OpenHandle, ClosedHandle)
                                , withProcessHandle )
import System.Posix.Types (ProcessID)
import System.Posix.Process ( ProcessStatus
                            , getProcessStatus )
import System.Posix.Signals ( Signal
                            , sigTERM
                            , sigKILL
                            , signalProcess )

withPid :: (ProcessID -> IO a) -> ProcessHandle -> IO (Maybe a)
withPid action ph = withProcessHandle ph callback
  where callback (ClosedHandle _) = return Nothing
        callback (OpenHandle pid) = do res <- action pid
                                       return (Just res)

getProcessHandleStatus :: ProcessHandle -> IO (Maybe ProcessStatus)
getProcessHandleStatus ph = catchJust exPred getStatus handleDNE
  where shouldBlock    = False
        includeStopped = True
        getStatus = fmap join $ withPid (getProcessStatus shouldBlock includeStopped) ph
        exPred e
          | isDoesNotExistError e = Just ()
          | otherwise             = Nothing
        handleDNE = const $ return Nothing -- ehhhhhhhhhhhhh, Nothing means not available?

signalProcessHandle :: Signal -> ProcessHandle -> IO ()
signalProcessHandle sig = void . withPid (signalProcess sig)

softKillProcessHandle :: ProcessHandle -> IO ()
softKillProcessHandle = signalProcessHandle sigTERM

hardKillProcessHandle :: ProcessHandle -> IO ()
hardKillProcessHandle = signalProcessHandle sigKILL

isProcessHandleDead :: ProcessHandle -> IO Bool
isProcessHandleDead ph = catchIOError checkHandle (const $ return True)
  where
    checkHandle = fmap isJust $ getProcessHandleStatus ph