]> gitweb.michael.orlitzky.com - dead/htsn.git/blob - src/Unix.hs
Retain umask after daemonizing.
[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 setFileCreationMask,
23 setGroupID,
24 setUserID,
25 sigTERM )
26 import System.Posix.Daemonize ( daemonize )
27
28 import Configuration (
29 Configuration( pidfile,
30 run_as_group,
31 run_as_user ))
32 import Logging ( log_info )
33
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.
37 --
38 get_user_id :: Maybe String -> IO UserID
39 get_user_id Nothing = getRealUserID
40 get_user_id (Just s) = fmap userID (getUserEntryForName s)
41
42
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.
46 --
47 get_group_id :: Maybe String -> IO GroupID
48 get_group_id Nothing = getRealGroupID
49 get_group_id (Just s) = fmap groupID (getGroupEntryForName s)
50
51
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
60
61
62 -- | Write a PID file, install a SIGTERM handler, drop privileges, and
63 -- finally do the daemonization dance.
64 --
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)
74 where
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.
78 pid <- getProcessID
79
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)
84
85 -- We need to pass the thread ID to the signal handler so it
86 -- knows which process to "exit."
87 tid <- myThreadId
88 _ <- installHandler sigTERM (Catch (graceful_shutdown cfg tid)) Nothing
89
90 -- Then drop privileges.
91 get_user_id (run_as_user cfg) >>= setUserID
92 get_group_id (run_as_group cfg) >>= setGroupID
93
94 -- Finally run the program we were asked to.
95 program