1 -- | Non-portable code for daemonizing on unix.
6 import Control.Concurrent ( ThreadId, myThreadId )
7 import Control.Exception ( throwTo )
8 import System.Exit ( ExitCode( ExitSuccess ) )
10 GroupEntry ( groupID ),
25 import System.Posix.Daemonize ( daemonize )
27 import Configuration (
28 Configuration( pidfile,
31 import Logging ( log_info )
33 -- | Retrieve the uid associated with the given system user name. We
34 -- take a Maybe String as an argument so the user name can be passed
35 -- in directly from the config.
37 get_user_id :: Maybe String -> IO UserID
38 get_user_id Nothing = getRealUserID
39 get_user_id (Just s) = fmap userID (getUserEntryForName s)
42 -- | Retrieve the gid associated with the given system group name. We
43 -- take a Maybe String as an argument so the group name can be
44 -- passed in directly from the config.
46 get_group_id :: Maybe String -> IO GroupID
47 get_group_id Nothing = getRealGroupID
48 get_group_id (Just s) = fmap groupID (getGroupEntryForName s)
51 -- | This function will be called in response to a SIGTERM; i.e. when
52 -- someone tries to kill our process. We simply delete the PID file
53 -- and signal our parent thread to quit (successfully).
54 graceful_shutdown :: Configuration -> ThreadId -> IO ()
55 graceful_shutdown cfg main_thread_id = do
56 log_info "SIGTERM received, removing PID file and shutting down."
57 removeLink (pidfile cfg)
58 throwTo main_thread_id ExitSuccess
61 -- | Write a PID file, install a SIGTERM handler, drop privileges, and
62 -- finally do the daemonization dance.
64 full_daemonize :: Configuration -> IO () -> IO ()
65 full_daemonize cfg program =
66 -- This is the 'daemonize' from System.Posix.Daemonize.
69 -- We need to do all this stuff *after* we daemonize.
71 -- First write the PID file which probably requires root.
73 writeFile (pidfile cfg) (show pid)
75 -- We need to pass the thread ID to the signal handler so it
76 -- knows which process to "exit."
78 _ <- installHandler sigTERM (Catch (graceful_shutdown cfg tid)) Nothing
80 -- Then drop privileges.
81 get_user_id (run_as_user cfg) >>= setUserID
82 get_group_id (run_as_group cfg) >>= setGroupID
84 -- Finally run the program we were asked to.