+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