{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Use forM_" #-}
module SystemD
( systemdSubscribe,
monitorService,
-- * Convenience re-exports
connectSystem,
Client
)
where
import DBus
( IsVariant (fromVariant),
MethodCall (methodCallDestination),
Signal (..),
methodCall,
)
import DBus.Client
( MatchRule (..),
SignalHandler,
addMatch,
getProperty,
matchAny,
)
import DBus.TH.EDSL
import Data.Map (Map)
import Data.Map qualified as Map
type UnitObject = ObjectPath
getObjectProperty :: IsVariant a => Client -> BusName -> ObjectPath -> InterfaceName -> MemberName -> IO (Maybe a)
getObjectProperty client busName objPath iface property = do
getProperty client ((methodCall objPath iface property) {methodCallDestination = Just busName})
>>= \case
Right (fromVariant -> Just val) -> return (Just val)
_ -> return Nothing
getStatus :: Client -> UnitObject -> IO (Maybe String)
getStatus client unitObject =
getObjectProperty
client
"org.freedesktop.systemd1"
unitObject
"org.freedesktop.systemd1.Unit"
"ActiveState"
interface'
"org.freedesktop.systemd1"
(Just "/org/freedesktop/systemd1")
"org.freedesktop.systemd1.Manager"
Nothing
[ "GetUnit" =:: ''String :-> Return ''ObjectPath,
"Subscribe" =:: Return ''() `as` "systemdSubscribe"
]
onPropertiesChanged :: Client -> ObjectPath -> (InterfaceName -> Map String Variant -> [String] -> IO ()) -> IO SignalHandler
onPropertiesChanged client objectPath f = do
addMatch
client
( matchAny
{ matchPath = Just objectPath,
matchInterface = Just "org.freedesktop.DBus.Properties",
matchMember = Just "PropertiesChanged"
}
)
$ \signal ->
case signal.signalBody of
[ fromVariant -> Just interfaceName,
fromVariant -> Just changedProperties,
fromVariant -> Just invalidatedProperties
] -> f interfaceName changedProperties invalidatedProperties
_ -> error "Unexpected value for PropertiesChanged signal"
-- | Run an IO action whenever the ActiveState of a service changes.
-- Use the full name of the service (i.e. 'httpd.service', not 'httpd')
monitorService :: Client -> String -> (String -> IO ()) -> IO (Maybe SignalHandler)
monitorService client serviceName f = do
getUnit client serviceName >>= \case
Just unitObject -> do
handler <- onPropertiesChanged client unitObject $ \_ changedProperties _ ->
case fromVariant @String =<< Map.lookup "ActiveState" changedProperties of
Just activeState -> f activeState
Nothing -> pure ()
return (Just handler)
Nothing -> return Nothing