packages feed

mighttpd2-2.8.8: src/Mighty.hs

{-# LANGUAGE OverloadedStrings, CPP #-}

module Main where

import Control.Concurrent
import Control.Monad
import Network.Wai.Application.Classic hiding ((</>), (+++))
import Network.Wai.Logger
import Network.Wai.Logger.Prefork
import System.Directory
import System.Environment
import System.Exit
import System.FilePath
import System.IO
import System.Posix

import Config
import Daemon (background)
import Log
import Multi
import Net (listenSocket)
import Report
import Resource (amIrootUser, unlimit)
import Route
import Signal
import Single
import State
import Types

----------------------------------------------------------------

main :: IO ()
main = do
    (opt,route) <- getOptRoute
    checkTLS opt
    let reportFile = reportFileName opt
    rpt <- initReporter reportFile >>= checkReporter reportFile
    if opt_debug_mode opt then
        server opt route rpt
      else
        background opt $ server opt route rpt
  where
    getOptRoute = getArgs >>= eachCase
    eachCase args
      | n == 0 = do
          root <- amIrootUser
          let opt | root      = defaultOption { opt_port = 80 }
                  | otherwise = defaultOption
          dir <- getCurrentDirectory
          let dst = fromString . addTrailingPathSeparator $ dir
              route = [Block ["*"] [RouteFile "/" dst]]
          return (opt, route)
      | n == 2 = do
          let config_file = args !! 0
          routing_file <- getAbsoluteFile (args !! 1)
          opt   <- parseOption config_file
          route <- parseRoute  routing_file
          let opt' = opt {opt_routing_file = Just routing_file}
          return (opt',route)
      | otherwise = do
          hPutStrLn stderr "Usage: mighty"
          hPutStrLn stderr "       mighty config_file routing_file"
          exitFailure
      where
        n = length args
    getAbsoluteFile file
      | isAbsolute file = return file
      | otherwise       = do
          dir <- getCurrentDirectory
          return $ dir </> normalise file
    reportFileName opt
      | port == 80 = rfile
      | otherwise  = rfile ++ show port
      where
        rfile = opt_report_file opt
        port = opt_port opt
    checkReporter _          (Right rpt) = return rpt
    checkReporter reportFile (Left e)    = do
        hPutStrLn stderr $ reportFile ++ " is not writable"
        hPrint stderr e
        exitFailure
#ifdef TLS
    checkTLS _ = return ()
#else
    checkTLS opt = when (opt_service opt > 1) $ do
        hPutStrLn stderr "This mighty does not support TLS"
        exitFailure
#endif

----------------------------------------------------------------

server :: Option -> RouteDB -> Reporter -> IO ()
server opt route rpt = reportDo rpt $ do
    unlimit
    service <- openService opt
    unless debug writePidFile
    logCheck logtype
    myid <- getProcessID
    stt <- initStater
    if workers == 1 then do
        lgr <- initLogger FromSocket logtype
         -- killed by signal
        void . forkIO $ single opt route service rpt stt lgr
        void . forkIO $ logController logtype [myid]
        mainLoop rpt stt lgr
      else do
        cids <- multi opt route service logtype stt rpt
        void . forkIO $ logController logtype cids
        masterMainLoop rpt myid
  where
    debug = opt_debug_mode opt
    pidfile = opt_pid_file opt
    workers = opt_worker_processes opt
    writePidFile = do
        pid <- getProcessID
        writeFile pidfile $ show pid ++ "\n"
        setFileMode pidfile 0o644
    logspec = FileLogSpec {
        log_file          = opt_log_file opt
      , log_file_size     = fromIntegral $ opt_log_file_size opt
      , log_backup_number = opt_log_backup_number opt
      }
    logtype
      | not (opt_logging opt) = LogNone
      | debug                 = LogStdout
      | otherwise             = LogFile logspec sigLogCtl

openService :: Option -> IO Service
openService opt
  | service == 1 = do
      s <- listenSocket httpsPort
      debugMessage $ "HTTP/TLS service on port " ++ httpsPort ++ "."
      return $ HttpsOnly s
  | service == 2 = do
      s1 <- listenSocket httpPort
      s2 <- listenSocket httpsPort
      debugMessage $ "HTTP service on port " ++ httpPort ++ " and "
                  ++ "HTTP/TLS service on port " ++ httpsPort ++ "."
      return $ HttpAndHttps s1 s2
  | otherwise = do
      s <- listenSocket httpPort
      debugMessage $ "HTTP service on port " ++ httpPort ++ "."
      return $ HttpOnly s
  where
    httpPort  = show $ opt_port opt
    httpsPort = show $ opt_tls_port opt
    service = opt_service opt
    debug = opt_debug_mode opt
    debugMessage msg = when debug $ do
        putStrLn msg
        hFlush stdout