]> gitweb.michael.orlitzky.com - dead/htsn.git/blob - src/Unix.hs
Add more code documentation.
[dead/htsn.git] / src / Unix.hs
1 -- | Non-portable code for daemonizing on unix.
2 --
3 module Unix
4 where
5
6 import Control.Concurrent ( ThreadId, myThreadId )
7 import Control.Exception ( throwTo )
8 import System.Exit ( ExitCode( ExitSuccess ) )
9 import System.Posix (
10 GroupEntry ( groupID ),
11 GroupID,
12 Handler ( Catch ),
13 UserEntry ( userID ),
14 UserID,
15 getGroupEntryForName,
16 getProcessID,
17 getRealGroupID,
18 getRealUserID,
19 getUserEntryForName,
20 installHandler,
21 removeLink,
22 setGroupID,
23 setUserID,
24 sigTERM )
25 import System.Posix.Daemonize ( daemonize )
26
27 import Configuration (
28 Configuration( pidfile,
29 run_as_group,
30 run_as_user ))
31 import Logging ( log_info )
32
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.
36 --
37 get_user_id :: Maybe String -> IO UserID
38 get_user_id Nothing = getRealUserID
39 get_user_id (Just s) = fmap userID (getUserEntryForName s)
40
41
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.
45 --
46 get_group_id :: Maybe String -> IO GroupID
47 get_group_id Nothing = getRealGroupID
48 get_group_id (Just s) = fmap groupID (getGroupEntryForName s)
49
50
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
59
60
61 -- | Write a PID file, install a SIGTERM handler, drop privileges, and
62 -- finally do the daemonization dance.
63 --
64 full_daemonize :: Configuration -> IO () -> IO ()
65 full_daemonize cfg program =
66 -- This is the 'daemonize' from System.Posix.Daemonize.
67 daemonize program'
68 where
69 -- We need to do all this stuff *after* we daemonize.
70 program' = do
71 -- First write the PID file which probably requires root.
72 pid <- getProcessID
73 writeFile (pidfile cfg) (show pid)
74
75 -- We need to pass the thread ID to the signal handler so it
76 -- knows which process to "exit."
77 tid <- myThreadId
78 _ <- installHandler sigTERM (Catch (graceful_shutdown cfg tid)) Nothing
79
80 -- Then drop privileges.
81 get_user_id (run_as_user cfg) >>= setUserID
82 get_group_id (run_as_group cfg) >>= setGroupID
83
84 -- Finally run the program we were asked to.
85 program