packages feed

angel-0.5.1: src/Angel/PidFile.hs

module Angel.PidFile ( startMaybeWithPidFile
                     , startWithPidFile
                     , clearPIDFile) where

import Control.Exception.Base ( finally
                              , onException )

import Control.Monad (when)
import System.Process ( CreateProcess
                      , createProcess
                      , ProcessHandle )

-- Wish I didn't have to do this :(
import System.Process.Internals ( PHANDLE
                                , ProcessHandle__(OpenHandle, ClosedHandle)
                                , withProcessHandle
                                )
import System.Posix.Files ( removeLink
                          , fileExist)

startMaybeWithPidFile :: CreateProcess
                      -> Maybe FilePath
                      -> (ProcessHandle -> IO a)
                      -> (ProcessHandle -> IO a)
                      -> IO a
startMaybeWithPidFile procSpec (Just pidFile) action onPidError = startWithPidFile procSpec pidFile action onPidError
startMaybeWithPidFile procSpec Nothing action _ = withPHandle procSpec action

startWithPidFile :: CreateProcess
                 -> FilePath
                 -> (ProcessHandle -> IO a)
                 -> (ProcessHandle -> IO a)
                 -> IO a
startWithPidFile procSpec pidFile action onPidError =
  withPHandle procSpec $ \pHandle -> do
    mPid               <-  getPID pHandle
    case mPid of
      Just pid -> write pid pHandle
      Nothing  -> proceed pHandle
  where
    write pid pHandle = do
      writePID pidFile pid `onException` onPidError pHandle -- re-raises
      proceed pHandle
    proceed pHandle = action pHandle `finally` clearPIDFile pidFile

withPHandle :: CreateProcess -> (ProcessHandle -> IO a) -> IO a
withPHandle procSpec action = do
  (_, _, _, pHandle) <- createProcess procSpec
  action pHandle

writePID :: FilePath -> PHANDLE -> IO ()
writePID pidFile = writeFile pidFile . show

clearPIDFile :: FilePath -> IO ()
clearPIDFile pidFile = do ex <- fileExist pidFile
                          when ex rm
  where rm = removeLink pidFile

getPID :: ProcessHandle -> IO (Maybe PHANDLE)
getPID pHandle = withProcessHandle pHandle getPID'
  where getPID' h @ (OpenHandle t) = return (Just t)
        getPID' h @ (ClosedHandle t) = return Nothing