Skip to content
Snippets Groups Projects
Commit f9e51bd8 authored by Mr. Snow Ball / projects's avatar Mr. Snow Ball / projects :arrows_counterclockwise:
Browse files

Add more Qubes API calls, e.g. for listing labels

parent b25e8ba4
No related branches found
No related tags found
No related merge requests found
module QBar.Qubes.AdminAPI where module QBar.Qubes.AdminAPI where
import Control.Monad (forM_) import Control.Monad (forM_, guard)
import Data.Binary import Data.Binary
import Data.Binary.Get import Data.Binary.Get
import Data.Binary.Put import Data.Binary.Put
...@@ -12,6 +12,7 @@ import Data.Function ((&)) ...@@ -12,6 +12,7 @@ import Data.Function ((&))
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Network.HostName import Network.HostName
import Numeric (showHex, readHex)
import Pipes import Pipes
import qualified Pipes.Prelude as P import qualified Pipes.Prelude as P
import qualified Pipes.Safe as P import qualified Pipes.Safe as P
...@@ -162,6 +163,8 @@ data QubesEvent ...@@ -162,6 +163,8 @@ data QubesEvent
| DomainShutdown { domainName :: BL.ByteString } | DomainShutdown { domainName :: BL.ByteString }
| DomainUpdatesAvailable { domainName :: BL.ByteString, updatesAvailable :: Bool, updatesAvailableOld :: Bool } | DomainUpdatesAvailable { domainName :: BL.ByteString, updatesAvailable :: Bool, updatesAvailableOld :: Bool }
| DomainStartFailed { domainName :: BL.ByteString, reason :: BL.ByteString } | DomainStartFailed { domainName :: BL.ByteString, reason :: BL.ByteString }
| PropertySet { domainName :: BL.ByteString, changedProperty :: BL.ByteString, newValue :: BL.ByteString, oldValue :: BL.ByteString }
| PropertyDel { domainName :: BL.ByteString, changedProperty :: BL.ByteString, oldValue :: BL.ByteString } -- reset to default value
deriving (Eq, Ord, Show, Read) deriving (Eq, Ord, Show, Read)
qubesEventsRaw :: Producer QubesAdminReturn (P.SafeT IO) () qubesEventsRaw :: Producer QubesAdminReturn (P.SafeT IO) ()
...@@ -182,7 +185,12 @@ qubesEvents = qubesEventsRaw >-> P.mapFoldable parse where ...@@ -182,7 +185,12 @@ qubesEvents = qubesEventsRaw >-> P.mapFoldable parse where
DomainUpdatesAvailable evSubject (boolPropViaInt "value") (boolPropViaInt "oldvalue") DomainUpdatesAvailable evSubject (boolPropViaInt "value") (boolPropViaInt "oldvalue")
"domain-start-failed" -> "domain-start-failed" ->
DomainStartFailed evSubject (fromMaybe "" $ getProp "reason") DomainStartFailed evSubject (fromMaybe "" $ getProp "reason")
_ -> OtherEvent ev _ -> case BLC.break (==':') evEvent of
("property-set", _) ->
PropertySet evSubject (fromMaybe "" $ getProp "name") (fromMaybe "" $ getProp "newvalue") (fromMaybe "" $ getProp "oldvalue")
("property-del", _) ->
PropertyDel evSubject (fromMaybe "" $ getProp "name") (fromMaybe "" $ getProp "oldvalue")
_ -> OtherEvent ev
where where
getProp :: BL.ByteString -> Maybe BL.ByteString getProp :: BL.ByteString -> Maybe BL.ByteString
getProp name = lookup name evProperties getProp name = lookup name evProperties
...@@ -220,14 +228,17 @@ instance Read QubesVMState where ...@@ -220,14 +228,17 @@ instance Read QubesVMState where
"Halted" -> VMHalted "Halted" -> VMHalted
_ -> UnknownState _ -> UnknownState
qubesListVMs :: IO (Map.Map BL.ByteString QubesVMInfo) qubesAdminCallLines :: BL.ByteString -> [BL.ByteString] -> IO [BL.ByteString]
qubesListVMs = qubesAdminCall "admin.vm.List" [] >>= parse qubesAdminCallLines serviceName args = qubesAdminCall serviceName args >>= parse
where where
parse reply = BLC.split '\n' reply parse reply = BLC.split '\n' reply
& filter (/="") & filter (/="")
& map parseLine
& Map.fromList
& return & return
qubesListVMs :: IO (Map.Map BL.ByteString QubesVMInfo)
qubesListVMs = parse <$> qubesAdminCallLines "admin.vm.List" []
where
parse = Map.fromList . map parseLine
parseLine line = parseLine line =
(vmName, QubesVMInfo (readPropEmpty "state") (tryReadProp "class" & fromMaybe UnknownClass)) (vmName, QubesVMInfo (readPropEmpty "state") (tryReadProp "class" & fromMaybe UnknownClass))
where where
...@@ -240,25 +251,33 @@ qubesListVMs = qubesAdminCall "admin.vm.List" [] >>= parse ...@@ -240,25 +251,33 @@ qubesListVMs = qubesAdminCall "admin.vm.List" [] >>= parse
tryReadProp :: Read a => BL.ByteString -> Maybe a tryReadProp :: Read a => BL.ByteString -> Maybe a
tryReadProp name = readMaybe . BLC.unpack =<< getProp name tryReadProp name = readMaybe . BLC.unpack =<< getProp name
qubesGetProperty :: BL.ByteString -> IO (Bool, BL.ByteString, BL.ByteString) data QubesPropertyInfo = QubesPropertyInfo { propIsDefault :: Bool, propType :: BL.ByteString, propValue :: BL.ByteString }
qubesGetProperty name = qubesAdminCall "admin.property.Get" [name] >>= parse deriving (Eq, Ord, Show, Read)
qubesGetProperty :: BL.ByteString -> IO QubesPropertyInfo
qubesGetProperty name = parse <$> qubesAdminCall "admin.property.Get" [name]
where where
parse reply = return (isDefault == "default=True", BL.drop 5 typeStr, value) parse reply = QubesPropertyInfo (isDefault == "default=True") (BL.drop 5 typeStr) value
where where
splitOn ch = fmap BLC.tail . BLC.break (==ch) splitOn ch = fmap BLC.tail . BLC.break (==ch)
(isDefault, (typeStr, value)) = splitOn ' ' reply & fmap (splitOn ' ') (isDefault, (typeStr, value)) = splitOn ' ' reply & fmap (splitOn ' ')
qubesListPropertyNames :: IO [BL.ByteString]
qubesListPropertyNames = qubesAdminCallLines "admin.property.List" []
qubesListProperties :: IO [(BL.ByteString, QubesPropertyInfo)]
qubesListProperties = qubesListLabelNames >>= mapM (toSndM qubesGetProperty)
where
toSndM :: Applicative m => (a -> m b) -> a -> m (a, b)
toSndM f x = sequenceA (x, f x)
qubesGetDefaultPool :: IO BL.ByteString qubesGetDefaultPool :: IO BL.ByteString
qubesGetDefaultPool = third <$> qubesGetProperty "default_pool" qubesGetDefaultPool = propValue <$> qubesGetProperty "default_pool"
where third (_, _, x) = x where third (_, _, x) = x
qubesGetPoolInfo :: BL.ByteString -> IO [(BL.ByteString, BL.ByteString)] qubesGetPoolInfo :: BL.ByteString -> IO [(BL.ByteString, BL.ByteString)]
qubesGetPoolInfo name = qubesAdminCall "admin.pool.Info" [name] >>= parse qubesGetPoolInfo name = map parseLine <$> qubesAdminCallLines "admin.pool.Info" [name]
where where
parse reply = BLC.split '\n' reply
& filter (/="")
& map parseLine
& return
parseLine = fmap BLC.tail . BLC.break (=='=') parseLine = fmap BLC.tail . BLC.break (=='=')
qubesUsageOfDefaultPool :: IO (Maybe Int, Maybe Int) qubesUsageOfDefaultPool :: IO (Maybe Int, Maybe Int)
...@@ -267,3 +286,44 @@ qubesUsageOfDefaultPool = qubesGetDefaultPool >>= qubesGetPoolInfo >>= extract ...@@ -267,3 +286,44 @@ qubesUsageOfDefaultPool = qubesGetDefaultPool >>= qubesGetPoolInfo >>= extract
extract props = return (tryReadProp "usage" props, tryReadProp "size" props) extract props = return (tryReadProp "usage" props, tryReadProp "size" props)
tryReadProp :: Read a => BL.ByteString -> [(BL.ByteString, BL.ByteString)] -> Maybe a tryReadProp :: Read a => BL.ByteString -> [(BL.ByteString, BL.ByteString)] -> Maybe a
tryReadProp name props = readMaybe . BLC.unpack =<< lookup name props tryReadProp name props = readMaybe . BLC.unpack =<< lookup name props
newtype QubesLabelColor = QubesLabelColor { fromQubesLabelColor :: Int }
deriving (Eq, Ord)
instance Show QubesLabelColor where
showsPrec _ (QubesLabelColor x) = \s -> "0x" <> pad 6 (showHex x "") <> s
where pad l s = replicate (l - length s) '0' <> s
instance Read QubesLabelColor where
readsPrec _ ('0' : 'x' : xs) = do
let (num, remainder) = splitAt 6 xs
guard $ length num == 6
(num', []) <- readHex num
[(QubesLabelColor num', remainder)]
qubesGetLabelColor :: BL.ByteString -> IO QubesLabelColor
qubesGetLabelColor name = read . BLC.unpack <$> qubesAdminCall "admin.label.Get" [name]
qubesListLabelNames :: IO [BL.ByteString]
qubesListLabelNames = qubesAdminCallLines "admin.label.List" []
qubesListLabels :: IO [(BL.ByteString, QubesLabelColor)]
qubesListLabels = qubesListLabelNames >>= mapM (toSndM qubesGetLabelColor)
where
toSndM :: Applicative m => (a -> m b) -> a -> m (a, b)
toSndM f x = sequenceA (x, f x)
qubesMonitorProperty :: Producer QubesEvent (P.SafeT IO) () -> BL.ByteString -> Producer QubesPropertyInfo (P.SafeT IO) ()
qubesMonitorProperty events name = events >-> P.filter isRelevant >-> fetchValue
where
fetchValue = liftIO (qubesGetProperty name) >>= go
go x = do
yield x
ev <- await
case ev of
PropertySet {newValue} -> go $ x { propIsDefault = False, propValue = newValue }
PropertyDel {} -> fetchValue
_ -> go x
isRelevant PropertySet {changedProperty} = name == changedProperty
isRelevant PropertyDel {changedProperty} = name == changedProperty
isRelevant _ = False
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment