{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} module Program.Mighty.Config ( -- * Parsing a configuration file. parseOption #ifdef DHALL , parseOptionDhall #else , Natural #endif -- * Creating 'Option'. , defaultOption -- * Types , Option(..) ) where import Data.List.Split (splitOn) import Text.Parsec import Text.Parsec.ByteString.Lazy #ifdef DHALL import Data.String (fromString) import qualified Data.Text as T import Dhall(Generic, Natural, input, auto, FromDhall) import qualified Program.Mighty.Dhall.Option as Do #else import Program.Mighty.Types #endif import Program.Mighty.Parser ---------------------------------------------------------------- data Option = Option { opt_port :: Natural , opt_host :: String , opt_debug_mode :: Bool , opt_user :: String , opt_group :: String , opt_pid_file :: FilePath , opt_report_file :: FilePath , opt_logging :: Bool , opt_log_file :: FilePath , opt_log_file_size :: Natural , opt_log_backup_number :: Natural , opt_index_file :: FilePath , opt_index_cgi :: FilePath , opt_status_file_dir :: FilePath , opt_connection_timeout :: Natural , opt_proxy_timeout :: Natural , opt_fd_cache_duration :: Natural , opt_service :: Natural , opt_tls_port :: Natural , opt_tls_cert_file :: FilePath , opt_tls_chain_files :: FilePath , opt_tls_key_file :: FilePath , opt_quic_port :: Natural , opt_quic_addr :: [String] , opt_quic_debug_dir :: Maybe FilePath , opt_quic_qlog_dir :: Maybe FilePath , opt_server_name :: String , opt_routing_file :: Maybe String #ifdef DHALL } deriving (Eq, Show, Generic) #else } deriving (Eq, Show) #endif #ifdef DHALL instance FromDhall Option #endif -- | Getting a default 'Option'. defaultOption :: Option defaultOption = Option { opt_port = 8080 , opt_host = "*" , opt_debug_mode = True , opt_user = "root" , opt_group = "root" , opt_pid_file = "/var/run/mighty.pid" , opt_report_file = "/tmp/mighty_report" , opt_logging = True , opt_log_file = "/var/log/mighty" , opt_log_file_size = 16777216 , opt_log_backup_number = 10 , opt_index_file = "index.html" , opt_index_cgi = "index.cgi" , opt_status_file_dir = "/usr/local/share/mighty/status" , opt_connection_timeout = 30 , opt_proxy_timeout = 0 , opt_fd_cache_duration = 10 , opt_service = 0 , opt_tls_port = 443 , opt_tls_cert_file = "cert.pem" , opt_tls_chain_files = "chain.pem" , opt_tls_key_file = "privkey.pem" , opt_quic_port = 443 , opt_quic_addr = ["127.0.0.1"] , opt_quic_debug_dir = Nothing , opt_quic_qlog_dir = Nothing , opt_server_name = "Dummy" , opt_routing_file = Nothing } ---------------------------------------------------------------- -- | Parsing a configuration file to get an 'Option'. parseOption :: FilePath -> IO Option parseOption file = makeOpt defaultOption <$> parseConfig file #ifdef DHALL parseOptionDhall :: FilePath -> IO Option parseOptionDhall = fmap optionFromDhall . input auto . fromString optionFromDhall :: Do.Option -> Option optionFromDhall o = Option { opt_port = Do.port o , opt_host = T.unpack $ Do.host o , opt_debug_mode = Do.debugMode o , opt_user = T.unpack $ Do.user o , opt_group = T.unpack $ Do.group o , opt_pid_file = T.unpack $ Do.pidFile o , opt_report_file = T.unpack $ Do.reportFile o , opt_logging = Do.logging o , opt_log_file = T.unpack $ Do.logFile o , opt_log_file_size = Do.logFileSize o , opt_log_backup_number = Do.logBackupNumber o , opt_index_file = T.unpack $ Do.indexFile o , opt_index_cgi = T.unpack $ Do.indexCgi o , opt_status_file_dir = T.unpack $ Do.statusFileDir o , opt_connection_timeout = Do.connectionTimeout o , opt_proxy_timeout = Do.proxyTimeout o , opt_fd_cache_duration = Do.fdCacheDuration o , opt_service = Do.service o , opt_tls_port = Do.tlsPort o , opt_tls_cert_file = T.unpack $ Do.tlsCertFile o , opt_tls_chain_files = T.unpack $ Do.tlsChainFiles o , opt_tls_key_file = T.unpack $ Do.tlsKeyFile o , opt_quic_addr = T.unpack <$> Do.quicAddr o , opt_quic_port = Do.quicPort o , opt_quic_debug_dir = T.unpack <$> Do.quicDebugDir o , opt_quic_qlog_dir = T.unpack <$> Do.quicQlogDir o , opt_server_name = "Dummy" , opt_routing_file = Nothing } #endif ---------------------------------------------------------------- makeOpt :: Option -> [Conf] -> Option makeOpt def conf = Option { opt_port = get "Port" opt_port , opt_host = get "Host" opt_host , opt_debug_mode = get "Debug_Mode" opt_debug_mode , opt_user = get "User" opt_user , opt_group = get "Group" opt_group , opt_pid_file = get "Pid_File" opt_pid_file , opt_report_file = get "Report_File" opt_report_file , opt_logging = get "Logging" opt_logging , opt_log_file = get "Log_File" opt_log_file , opt_log_file_size = get "Log_File_Size" opt_log_file_size , opt_log_backup_number = get "Log_Backup_Number" opt_log_backup_number , opt_index_file = get "Index_File" opt_index_file , opt_index_cgi = get "Index_Cgi" opt_index_cgi , opt_status_file_dir = get "Status_File_Dir" opt_status_file_dir , opt_connection_timeout = get "Connection_Timeout" opt_connection_timeout , opt_proxy_timeout = get "Proxy_Timeout" opt_proxy_timeout , opt_fd_cache_duration = get "Fd_Cache_Duration" opt_fd_cache_duration , opt_service = get "Service" opt_service , opt_tls_port = get "Tls_Port" opt_tls_port , opt_tls_cert_file = get "Tls_Cert_File" opt_tls_cert_file , opt_tls_chain_files = get "Tls_Chain_Files" opt_tls_chain_files , opt_tls_key_file = get "Tls_Key_File" opt_tls_key_file , opt_quic_addr = get "Quic_Addr" opt_quic_addr , opt_quic_port = get "Quic_Port" opt_quic_port , opt_quic_debug_dir = get "Quic_Debug_Dir" opt_quic_debug_dir , opt_quic_qlog_dir = get "Quic_Qlog_Dir" opt_quic_qlog_dir , opt_server_name = "Dummy" , opt_routing_file = Nothing } where get k func = maybe (func def) fromConf $ lookup k conf ---------------------------------------------------------------- type Conf = (String, ConfValue) data ConfValue = CV_Natural Natural | CV_Bool Bool | CV_String String deriving (Eq,Show) class FromConf a where fromConf :: ConfValue -> a instance FromConf Natural where fromConf (CV_Natural n) = n fromConf _ = error "fromConf int" instance FromConf Bool where fromConf (CV_Bool b) = b fromConf _ = error "fromConf bool" instance FromConf String where fromConf (CV_String s) = s fromConf _ = error "fromConf string" instance FromConf (Maybe String) where fromConf (CV_String "") = Nothing fromConf (CV_String s) = Just s fromConf _ = error "fromConf string" instance FromConf [String] where fromConf (CV_String s) = splitOn "," s fromConf _ = error "fromConf string" ---------------------------------------------------------------- parseConfig :: FilePath -> IO [Conf] parseConfig = parseFile config ---------------------------------------------------------------- config :: Parser [Conf] config = commentLines *> many cfield <* eof where cfield = field <* commentLines field :: Parser Conf field = (,) <$> key <*> (sep *> value) key :: Parser String key = many1 (oneOf $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_") <* spcs sep :: Parser () sep = () <$ char ':' *> spcs value :: Parser ConfValue value = choice [try cv_natural, try cv_bool, cv_string] -- Trailing should be included in try to allow IP addresses. cv_natural :: Parser ConfValue cv_natural = CV_Natural . read <$> many1 digit <* trailing cv_bool :: Parser ConfValue cv_bool = CV_Bool True <$ string "Yes" <* trailing <|> CV_Bool False <$ string "No" <* trailing cv_string :: Parser ConfValue cv_string = CV_String <$> many (noneOf " \t\n") <* trailing