{-# LINE 1 "System/Posix/Syslog.hsc" #-} {-# LANGUAGE ForeignFunctionInterface #-} {-# LINE 2 "System/Posix/Syslog.hsc" #-} {-# LINE 3 "System/Posix/Syslog.hsc" #-} {-# LANGUAGE DeriveGeneric #-} {-# LINE 5 "System/Posix/Syslog.hsc" #-} {- | Module : System.Posix.Syslog Maintainer : simons@cryp.to Stability : provisional Portability : Posix FFI bindings to syslog(3) from <http://www.opengroup.org/onlinepubs/009695399/basedefs/syslog.h.html POSIX.1-2001>. -} module System.Posix.Syslog where import Control.Exception ( bracket_ ) import Data.Bits import Foreign.C {-# LINE 21 "System/Posix/Syslog.hsc" #-} import GHC.Generics {-# LINE 23 "System/Posix/Syslog.hsc" #-} {-# LINE 25 "System/Posix/Syslog.hsc" #-} {-# LINE 28 "System/Posix/Syslog.hsc" #-} {-# LINE 32 "System/Posix/Syslog.hsc" #-} {-# LINE 36 "System/Posix/Syslog.hsc" #-} -- * Marshaled Data Types -- |Log messages are prioritized. -- -- Note that the 'Enum' instance for this class is incomplete. We abuse -- 'toEnum' and 'fromEnum' to map these constructors to their -- corresponding bit-mask value in C, but not all uses cases provided by -- of enumerating that class are fully supported -- (<https://github.com/peti/hsyslog/issues/5 issue #5>). data Priority = Emergency -- ^ system is unusable | Alert -- ^ action must be taken immediately | Critical -- ^ critical conditions | Error -- ^ error conditions | Warning -- ^ warning conditions | Notice -- ^ normal but significant condition | Info -- ^ informational | Debug -- ^ debug-level messages deriving ( Eq, Bounded, Show, Read {-# LINE 58 "System/Posix/Syslog.hsc" #-} , Generic {-# LINE 60 "System/Posix/Syslog.hsc" #-} ) instance Enum Priority where toEnum 0 = Emergency {-# LINE 64 "System/Posix/Syslog.hsc" #-} toEnum 1 = Alert {-# LINE 65 "System/Posix/Syslog.hsc" #-} toEnum 2 = Critical {-# LINE 66 "System/Posix/Syslog.hsc" #-} toEnum 3 = Error {-# LINE 67 "System/Posix/Syslog.hsc" #-} toEnum 4 = Warning {-# LINE 68 "System/Posix/Syslog.hsc" #-} toEnum 5 = Notice {-# LINE 69 "System/Posix/Syslog.hsc" #-} toEnum 6 = Info {-# LINE 70 "System/Posix/Syslog.hsc" #-} toEnum 7 = Debug {-# LINE 71 "System/Posix/Syslog.hsc" #-} toEnum i = error (showString "Syslog.Priority cannot be mapped from value " (show i)) fromEnum Emergency = 0 {-# LINE 74 "System/Posix/Syslog.hsc" #-} fromEnum Alert = 1 {-# LINE 75 "System/Posix/Syslog.hsc" #-} fromEnum Critical = 2 {-# LINE 76 "System/Posix/Syslog.hsc" #-} fromEnum Error = 3 {-# LINE 77 "System/Posix/Syslog.hsc" #-} fromEnum Warning = 4 {-# LINE 78 "System/Posix/Syslog.hsc" #-} fromEnum Notice = 5 {-# LINE 79 "System/Posix/Syslog.hsc" #-} fromEnum Info = 6 {-# LINE 80 "System/Posix/Syslog.hsc" #-} fromEnum Debug = 7 {-# LINE 81 "System/Posix/Syslog.hsc" #-} -- |Syslog distinguishes various system facilities. Most -- applications should log in 'USER'. data Facility = KERN -- ^ kernel messages | USER -- ^ user-level messages (default unless set otherwise) | MAIL -- ^ mail system | DAEMON -- ^ system daemons | AUTH -- ^ security\/authorization messages | SYSLOG -- ^ messages generated internally by syslogd | LPR -- ^ line printer subsystem | NEWS -- ^ network news subsystem | UUCP -- ^ UUCP subsystem | CRON -- ^ clock daemon | AUTHPRIV -- ^ security\/authorization messages (effectively equals 'AUTH' on some systems) | FTP -- ^ ftp daemon (effectively equals 'DAEMON' on some systems) | LOCAL0 -- ^ reserved for local use | LOCAL1 -- ^ reserved for local use | LOCAL2 -- ^ reserved for local use | LOCAL3 -- ^ reserved for local use | LOCAL4 -- ^ reserved for local use | LOCAL5 -- ^ reserved for local use | LOCAL6 -- ^ reserved for local use | LOCAL7 -- ^ reserved for local use deriving (Eq, Bounded, Show, Read) instance Enum Facility where toEnum 0 = KERN {-# LINE 110 "System/Posix/Syslog.hsc" #-} toEnum 8 = USER {-# LINE 111 "System/Posix/Syslog.hsc" #-} toEnum 16 = MAIL {-# LINE 112 "System/Posix/Syslog.hsc" #-} toEnum 24 = DAEMON {-# LINE 113 "System/Posix/Syslog.hsc" #-} toEnum 32 = AUTH {-# LINE 114 "System/Posix/Syslog.hsc" #-} toEnum 40 = SYSLOG {-# LINE 115 "System/Posix/Syslog.hsc" #-} toEnum 48 = LPR {-# LINE 116 "System/Posix/Syslog.hsc" #-} toEnum 56 = NEWS {-# LINE 117 "System/Posix/Syslog.hsc" #-} toEnum 64 = UUCP {-# LINE 118 "System/Posix/Syslog.hsc" #-} toEnum 72 = CRON {-# LINE 119 "System/Posix/Syslog.hsc" #-} toEnum 80 = AUTHPRIV {-# LINE 120 "System/Posix/Syslog.hsc" #-} toEnum 88 = FTP {-# LINE 121 "System/Posix/Syslog.hsc" #-} toEnum 128 = LOCAL0 {-# LINE 122 "System/Posix/Syslog.hsc" #-} toEnum 136 = LOCAL1 {-# LINE 123 "System/Posix/Syslog.hsc" #-} toEnum 144 = LOCAL2 {-# LINE 124 "System/Posix/Syslog.hsc" #-} toEnum 152 = LOCAL3 {-# LINE 125 "System/Posix/Syslog.hsc" #-} toEnum 160 = LOCAL4 {-# LINE 126 "System/Posix/Syslog.hsc" #-} toEnum 168 = LOCAL5 {-# LINE 127 "System/Posix/Syslog.hsc" #-} toEnum 176 = LOCAL6 {-# LINE 128 "System/Posix/Syslog.hsc" #-} toEnum 184 = LOCAL7 {-# LINE 129 "System/Posix/Syslog.hsc" #-} toEnum i = error ("Syslog.Facility cannot be mapped to value " ++ show i) fromEnum KERN = 0 {-# LINE 132 "System/Posix/Syslog.hsc" #-} fromEnum USER = 8 {-# LINE 133 "System/Posix/Syslog.hsc" #-} fromEnum MAIL = 16 {-# LINE 134 "System/Posix/Syslog.hsc" #-} fromEnum DAEMON = 24 {-# LINE 135 "System/Posix/Syslog.hsc" #-} fromEnum AUTH = 32 {-# LINE 136 "System/Posix/Syslog.hsc" #-} fromEnum SYSLOG = 40 {-# LINE 137 "System/Posix/Syslog.hsc" #-} fromEnum LPR = 48 {-# LINE 138 "System/Posix/Syslog.hsc" #-} fromEnum NEWS = 56 {-# LINE 139 "System/Posix/Syslog.hsc" #-} fromEnum UUCP = 64 {-# LINE 140 "System/Posix/Syslog.hsc" #-} fromEnum CRON = 72 {-# LINE 141 "System/Posix/Syslog.hsc" #-} fromEnum AUTHPRIV = 80 {-# LINE 142 "System/Posix/Syslog.hsc" #-} fromEnum FTP = 88 {-# LINE 143 "System/Posix/Syslog.hsc" #-} fromEnum LOCAL0 = 128 {-# LINE 144 "System/Posix/Syslog.hsc" #-} fromEnum LOCAL1 = 136 {-# LINE 145 "System/Posix/Syslog.hsc" #-} fromEnum LOCAL2 = 144 {-# LINE 146 "System/Posix/Syslog.hsc" #-} fromEnum LOCAL3 = 152 {-# LINE 147 "System/Posix/Syslog.hsc" #-} fromEnum LOCAL4 = 160 {-# LINE 148 "System/Posix/Syslog.hsc" #-} fromEnum LOCAL5 = 168 {-# LINE 149 "System/Posix/Syslog.hsc" #-} fromEnum LOCAL6 = 176 {-# LINE 150 "System/Posix/Syslog.hsc" #-} fromEnum LOCAL7 = 184 {-# LINE 151 "System/Posix/Syslog.hsc" #-} -- |Options for the syslog service. Set with 'withSyslog'. data Option = PID -- ^ log the pid with each message | CONS -- ^ log on the console if errors in sending | ODELAY -- ^ delay open until first @syslog()@ (default) | NDELAY -- ^ don't delay open | NOWAIT -- ^ don't wait for console forks: DEPRECATED | PERROR -- ^ log to 'stderr' as well (might be a no-op on some systems) deriving (Eq, Bounded, Show) instance Enum Option where toEnum 1 = PID {-# LINE 165 "System/Posix/Syslog.hsc" #-} toEnum 2 = CONS {-# LINE 166 "System/Posix/Syslog.hsc" #-} toEnum 4 = ODELAY {-# LINE 167 "System/Posix/Syslog.hsc" #-} toEnum 8 = NDELAY {-# LINE 168 "System/Posix/Syslog.hsc" #-} toEnum 16 = NOWAIT {-# LINE 169 "System/Posix/Syslog.hsc" #-} toEnum 32 = PERROR {-# LINE 170 "System/Posix/Syslog.hsc" #-} toEnum i = error ("Syslog.Option cannot be mapped to value " ++ show i) fromEnum PID = 1 {-# LINE 173 "System/Posix/Syslog.hsc" #-} fromEnum CONS = 2 {-# LINE 174 "System/Posix/Syslog.hsc" #-} fromEnum ODELAY = 4 {-# LINE 175 "System/Posix/Syslog.hsc" #-} fromEnum NDELAY = 8 {-# LINE 176 "System/Posix/Syslog.hsc" #-} fromEnum NOWAIT = 16 {-# LINE 177 "System/Posix/Syslog.hsc" #-} fromEnum PERROR = 32 {-# LINE 178 "System/Posix/Syslog.hsc" #-} -- * Haskell API to syslog -- |Bracket an 'IO' computation between calls to '_openlog', -- '_setlogmask', and '_closelog'. The function can be used as follows: -- -- > main = withSyslog "my-ident" [PID, PERROR] USER (logUpTo Debug) $ do -- > putStrLn "huhu" -- > syslog Debug "huhu" -- -- Note that these are /process-wide/ settings, so multiple calls to -- this function will interfere with each other in unpredictable ways. withSyslog :: String -> [Option] -> Facility -> [Priority] -> IO a -> IO a withSyslog ident opts facil prio f = withCString ident $ \p -> bracket_ (_openlog p opt fac >> _setlogmask pri) (_closelog) f where fac = toEnum . fromEnum $ facil pri = toEnum . foldl1 (.|.) . map (shift 1 . fromEnum) $ if null prio then [minBound .. maxBound] else prio opt = toEnum . sum . map fromEnum $ opts -- |Log a message with the given priority. -- -- Note that the API of this function is somewhat unsatisfactory and is -- likely to change in the future: -- -- 1. The function should accept a @['Facility']@ argument so that -- messages can be logged to certain facilities without depending on -- the process-wide global default value set by 'openlog' -- (<https://github.com/peti/hsyslog/issues/6 issue #6>). -- -- 2. The 'Priority' argument should be @['Priority']@. -- -- 3. Accepting a 'ByteString' instead of 'String' would be preferrable -- because we can log those more efficiently, i.e. without -- marshaling. On top of that, we can provide a wrapper for this -- function that accepts anything that can be marshaled into a -- 'ByteString' (<https://github.com/peti/hsyslog/issues/7 issue #7>). syslog :: Priority -> String -> IO () syslog l msg = withCString (safeMsg msg) (\p -> _syslog (toEnum (fromEnum l)) p) -- |Returns the list of priorities up to and including the argument. -- Note that the syslog priority 'Debug' is considered the highest one -- in this context, which may counter-intuitive for some. -- -- >>> logUpTo(Debug) -- [Emergency,Alert,Critical,Error,Warning,Notice,Info,Debug] -- -- >>> logUpTo(Emergency) -- [Emergency] logUpTo :: Priority -> [Priority] logUpTo p = [minBound .. p] -- * Helpers -- |Escape any occurances of \'@%@\' in a string, so that it is safe to -- pass it to '_syslog'. The 'syslog' wrapper does this automatically. -- -- Unfortunately, the application of this function to every single -- syslog message is a performence nightmare. Instead, we should call -- syslog the existence of this function is a kludge, in a way that -- doesn't require any escaping -- (<https://github.com/peti/hsyslog/issues/8 issue #8>). safeMsg :: String -> String safeMsg [] = [] safeMsg ('%':xs) = '%' : '%' : safeMsg xs safeMsg ( x :xs) = x : safeMsg xs -- * Low-level C functions -- |Open a connection to the system logger for a program. The string -- identifier passed as the first argument is prepended to every -- message, and is typically set to the program name. The behavior is -- unspecified by POSIX.1-2008 if that identifier is 'nullPtr'. foreign import ccall unsafe "openlog" _openlog :: CString -> CInt -> CInt -> IO () -- |Close the descriptor being used to write to the system logger. foreign import ccall unsafe "closelog" _closelog :: IO () -- |A process has a log priority mask that determines which calls to -- 'syslog' may be logged. All other calls will be ignored. Logging is -- enabled for the priorities that have the corresponding bit set in -- mask. The initial mask is such that logging is enabled for all -- priorities. This function sets this logmask for the calling process, -- and returns the previous mask. If the mask argument is 0, the current -- logmask is not modified. foreign import ccall unsafe "setlogmask" _setlogmask :: CInt -> IO CInt -- |Generate a log message, which will be distributed by @syslogd(8)@. -- The priority argument is formed by ORing the facility and the level -- values (explained below). The remaining arguments are a format, as in -- printf(3) and any arguments required by the format, except that the -- two character sequence %m will be replaced by the error message -- string strerror(errno). A trailing newline may be added if needed. foreign import ccall unsafe "syslog" _syslog :: CInt -> CString -> IO ()