module Unix where import Control.Concurrent ( ThreadId, myThreadId ) import Control.Exception ( throwTo ) import System.Exit ( ExitCode( ExitSuccess ) ) import System.Posix ( GroupEntry ( groupID ), GroupID, Handler ( Catch ), UserEntry ( userID ), UserID, getGroupEntryForName, getProcessID, getRealGroupID, getRealUserID, getUserEntryForName, installHandler, removeLink, setGroupID, setUserID, sigTERM ) import System.Posix.Daemonize ( daemonize ) import Configuration ( Configuration( pidfile, run_as_group, run_as_user )) import Logging ( log_info ) get_user_id :: Maybe String -> IO UserID get_user_id Nothing = getRealUserID get_user_id (Just s) = fmap userID (getUserEntryForName s) get_group_id :: Maybe String -> IO GroupID get_group_id Nothing = getRealGroupID get_group_id (Just s) = fmap groupID (getGroupEntryForName s) graceful_shutdown :: Configuration -> ThreadId -> IO () graceful_shutdown cfg main_thread_id = do log_info "SIGTERM received, removing PID file and shutting down." removeLink (pidfile cfg) throwTo main_thread_id ExitSuccess full_daemonize :: Configuration -> IO () -> IO () full_daemonize cfg program = do -- This is the 'daemonize' from System.Posix.Daemonize. daemonize program' where -- We need to do all this stuff *after* we daemonize. program' = do -- First write the PID file which probably requires root. pid <- getProcessID writeFile (pidfile cfg) (show pid) -- We need to pass the thread ID to the signal handler so it -- knows which process to "exit." tid <- myThreadId _ <- installHandler sigTERM (Catch (graceful_shutdown cfg tid)) Nothing -- Then drop privileges. get_user_id (run_as_user cfg) >>= setUserID get_group_id (run_as_group cfg) >>= setGroupID -- Finally run the program we were asked to. program