From d13e0b91b155731a1149da51ea74abfacde20c0b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 23 Dec 2013 19:41:54 -0500 Subject: [PATCH] Add error handling in the daemonization process. --- src/Unix.hs | 53 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/src/Unix.hs b/src/Unix.hs index 9b0ddaa..96ec79a 100644 --- a/src/Unix.hs +++ b/src/Unix.hs @@ -6,12 +6,14 @@ where import Control.Concurrent ( ThreadId, myThreadId ) import Control.Exception ( throwTo ) import System.Exit ( ExitCode( ExitSuccess ) ) +import System.IO.Error ( catchIOError ) import System.Posix ( GroupEntry ( groupID ), GroupID, Handler ( Catch ), UserEntry ( userID ), UserID, + exitImmediately, getGroupEntryForName, getProcessID, getRealGroupID, @@ -29,7 +31,8 @@ import Configuration ( Configuration( pidfile, run_as_group, run_as_user )) -import Logging ( log_info ) +import Logging ( log_info, log_error ) +import Terminal ( display_error ) -- | Retrieve the uid associated with the given system user name. We -- take a Maybe String as an argument so the user name can be passed @@ -52,11 +55,20 @@ get_group_id (Just s) = fmap groupID (getGroupEntryForName s) -- | This function will be called in response to a SIGTERM; i.e. when -- someone tries to kill our process. We simply delete the PID file -- and signal our parent thread to quit (successfully). +-- +-- If that doesn't work, report the error and quit rudely. +-- 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 + catchIOError try_nicely (\e -> do + display_error (show e) + log_error (show e) + exitImmediately ExitSuccess ) + where + try_nicely = do + removeLink (pidfile cfg) + throwTo main_thread_id ExitSuccess -- | Write a PID file, install a SIGTERM handler, drop privileges, and @@ -69,27 +81,38 @@ full_daemonize cfg program = do -- can, so that we can record the previous umask value (returned by -- setFileCreationMask). orig_umask <- setFileCreationMask 0 - -- This is the 'daemonize' from System.Posix.Daemonize. - daemonize (program' orig_umask) + -- This is the 'daemonize' from System.Posix.Daemonize. If it + -- doesn't work, we report the error and do not much else. + catchIOError (daemonize (program' orig_umask)) + (\e -> do + display_error (show e) + log_error (show e)) where -- We need to do all this stuff *after* we daemonize. program' orig_umask = do - -- First write the PID file which probably requires root. + -- First we install a signal handler for sigTERM. 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 + + -- Next we drop privileges. Group ID has to go first, otherwise + -- you ain't root to change groups. + get_group_id (run_as_group cfg) >>= setGroupID + get_user_id (run_as_user cfg) >>= setUserID + + -- Now we create the PID file. pid <- getProcessID -- The PID file needs to be read-only for anyone but its -- owner. Hopefully the umask accomplishes this! _ <- setFileCreationMask orig_umask - 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 + -- When we later attempt to delete the PID file, it requires + -- write permission to the parent directory and not to the PID + -- file itself. Therefore, if that's going to work, this has to + -- work, even as a limited user. + writeFile (pidfile cfg) (show pid) -- Finally run the program we were asked to. program -- 2.43.2