module System.Posix.Daemon
(
daemonize,
status,
kill,
pidLive,
Options(..)
)
where
import Data.Maybe
import Control.Monad hiding (forever,void)
import qualified Control.Monad as Monad (void)
import System.IO
import System.Exit
import System.Posix
import System.Posix.Process
import Foreign
import Foreign.C
import System.Posix.Daemon.IORedirection
data Options = Options {
optionsPidFile :: FilePath,
optionsStdout :: Maybe FilePath,
optionsStderr :: Maybe FilePath
} deriving (Eq, Show)
kill :: Int -> FilePath -> IO ()
kill timeout pidFile = pidRead pidFile >>= f where
f Nothing = return ()
f (Just pid) = do
islive <- pidLive pid
when islive $ do
signalProcess sigTERM pid
wait timeout pid
removeLink pidFile
status :: FilePath -> IO ()
status pidFile = fileExist pidFile >>= f where
f False = putStrLn "stopped"
f True = do
mpid <- pidRead pidFile
case mpid of
Nothing -> putStrLn "stopped"
Just pid -> do
res <- pidLive pid
if res
then putStrLn "running"
else putStrLn "stopped, pidfile remaining"
daemonize :: Options -> IO () -> IO ()
daemonize Options{..} program = do
Monad.void $ forkProcess $ do
createSession
forkProcess $ do
pidWrite optionsPidFile
installHandler sigHUP Ignore Nothing
redirectStdin "/dev/null"
case optionsStdout of
Nothing -> redirectStdout "/dev/null"
Just p -> redirectStdout p
case optionsStderr of
Nothing -> redirectStderr "/dev/null"
Just p -> redirectStderr p
program
exitImmediately ExitSuccess
exitImmediately ExitSuccess
wait :: Int -> CPid -> IO ()
wait secs pid = (when <$> pidLive pid) >>= \w -> w f
where f | secs > 0 = do
usleep 1000000
wait (secs1) pid
| otherwise = do
putStrLn $ "force killing PID " ++ (show pid)
signalProcess sigKILL pid
pidWrite :: FilePath -> IO ()
pidWrite pidPath = getProcessID >>= writeFile pidPath . show
pidRead :: FilePath -> IO (Maybe CPid)
pidRead pidFile = fileExist pidFile >>= f where
f True = fmap (Just . read) . readFile $ pidFile
f False = return Nothing
foreign import ccall unsafe "kill"
c_kill :: CPid -> CInt -> IO CInt
pidLive :: CPid -> IO Bool
pidLive pid = do
v <- (c_kill (fromIntegral pid) nullSignal)
return $ v >= 0