]> gitweb.michael.orlitzky.com - dead/htsn.git/blobdiff - src/Unix.hs
Add a bunch of new options allowing htsn to daemonize.
[dead/htsn.git] / src / Unix.hs
diff --git a/src/Unix.hs b/src/Unix.hs
new file mode 100644 (file)
index 0000000..8931326
--- /dev/null
@@ -0,0 +1,66 @@
+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