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 ),
26 import System.Posix.Daemonize ( daemonize )
28 import Configuration (
29 Configuration( pidfile,
32 import Logging ( log_info )
34 -- | Retrieve the uid associated with the given system user name. We
35 -- take a Maybe String as an argument so the user name can be passed
36 -- in directly from the config.
38 get_user_id :: Maybe String -> IO UserID
39 get_user_id Nothing = getRealUserID
40 get_user_id (Just s) = fmap userID (getUserEntryForName s)
43 -- | Retrieve the gid associated with the given system group name. We
44 -- take a Maybe String as an argument so the group name can be
45 -- passed in directly from the config.
47 get_group_id :: Maybe String -> IO GroupID
48 get_group_id Nothing = getRealGroupID
49 get_group_id (Just s) = fmap groupID (getGroupEntryForName s)
52 -- | This function will be called in response to a SIGTERM; i.e. when
53 -- someone tries to kill our process. We simply delete the PID file
54 -- and signal our parent thread to quit (successfully).
55 graceful_shutdown :: Configuration -> ThreadId -> IO ()
56 graceful_shutdown cfg main_thread_id = do
57 log_info "SIGTERM received, removing PID file and shutting down."
58 removeLink (pidfile cfg)
59 throwTo main_thread_id ExitSuccess
62 -- | Write a PID file, install a SIGTERM handler, drop privileges, and
63 -- finally do the daemonization dance.
65 full_daemonize :: Configuration -> IO () -> IO ()
66 full_daemonize cfg program = do
67 -- The call to 'daemonize' will set the umask to zero, but we want
68 -- to retain it. So, we set the umask to zero before 'daemonize'
69 -- can, so that we can record the previous umask value (returned by
70 -- setFileCreationMask).
71 orig_umask <- setFileCreationMask 0
72 -- This is the 'daemonize' from System.Posix.Daemonize.
73 daemonize (program' orig_umask)
75 -- We need to do all this stuff *after* we daemonize.
76 program' orig_umask = do
77 -- First write the PID file which probably requires root.
80 -- The PID file needs to be read-only for anyone but its
81 -- owner. Hopefully the umask accomplishes this!
82 _ <- setFileCreationMask orig_umask
83 writeFile (pidfile cfg) (show pid)
85 -- We need to pass the thread ID to the signal handler so it
86 -- knows which process to "exit."
88 _ <- installHandler sigTERM (Catch (graceful_shutdown cfg tid)) Nothing
90 -- Then drop privileges.
91 get_user_id (run_as_user cfg) >>= setUserID
92 get_group_id (run_as_group cfg) >>= setGroupID
94 -- Finally run the program we were asked to.