{-# LANGUAGE CPP #-} module FastCGIUtils(initFastCGI,loopFastCGI) where import Control.Concurrent(ThreadId,myThreadId) import Control.Exception(ErrorCall(..),throw,throwTo,catch) import Control.Monad(when,liftM,liftM2) import Data.IORef(IORef,newIORef,readIORef,writeIORef) import Prelude hiding (catch) import System.Environment(getArgs,getProgName) import System.Exit(ExitCode(..),exitWith) import System.IO(hPutStrLn,stderr) import System.IO.Unsafe(unsafePerformIO) #ifndef mingw32_HOST_OS import System.Posix #endif import Network.FastCGI import CGIUtils(logError) -- There are used in MorphoService.hs, but not in PGFService.hs initFastCGI :: IO () initFastCGI = installSignalHandlers loopFastCGI :: CGI CGIResult -> IO () loopFastCGI f = do (do runOneFastCGI f exitIfToldTo restartIfModified) `catchAborted` logError "Request aborted" loopFastCGI f -- Signal handling for FastCGI programs. #ifndef mingw32_HOST_OS installSignalHandlers :: IO () installSignalHandlers = do t <- myThreadId installHandler sigUSR1 (Catch gracefulExit) Nothing installHandler sigTERM (Catch gracelessExit) Nothing installHandler sigPIPE (Catch (requestAborted t)) Nothing return () requestAborted :: ThreadId -> IO () requestAborted t = throwTo t (ErrorCall "**aborted**") gracelessExit :: IO () gracelessExit = do logError "Graceless exit" exitWith ExitSuccess gracefulExit :: IO () gracefulExit = do logError "Graceful exit" writeIORef shouldExit True #else installSignalHandlers :: IO () installSignalHandlers = return () #endif exitIfToldTo :: IO () exitIfToldTo = do b <- readIORef shouldExit when b $ do logError "Exiting..." exitWith ExitSuccess {-# NOINLINE shouldExit #-} shouldExit :: IORef Bool shouldExit = unsafePerformIO $ newIORef False catchAborted :: IO a -> IO a -> IO a catchAborted x y = x `catch` \e -> case e of ErrorCall "**aborted**" -> y _ -> throw e -- Restart handling for FastCGI programs. #ifndef mingw32_HOST_OS {-# NOINLINE myModTimeRef #-} myModTimeRef :: IORef EpochTime myModTimeRef = unsafePerformIO (getProgModTime >>= newIORef) -- FIXME: doesn't get directory myProgPath :: IO FilePath myProgPath = getProgName getProgModTime :: IO EpochTime getProgModTime = liftM modificationTime (myProgPath >>= getFileStatus) needsRestart :: IO Bool needsRestart = liftM2 (/=) (readIORef myModTimeRef) getProgModTime exitIfModified :: IO () exitIfModified = do restart <- needsRestart when restart $ exitWith ExitSuccess restartIfModified :: IO () restartIfModified = do restart <- needsRestart when restart $ do prog <- myProgPath args <- getArgs hPutStrLn stderr $ prog ++ " has been modified, restarting ..." -- FIXME: setCurrentDirectory? executeFile prog False args Nothing #else restartIfModified :: IO () restartIfModified = return () #endif