mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-19 07:06:30 +00:00
Extend Systemd Support / Apparmor (#506)
* Update systemd.py * Update control.py * Update control.py * Create service.py * Update info.py * Rename hassio/host/asound.tmpl to hassio/host/data/asound.tmpl * Rename hassio/host/audiodb.json to hassio/host/data/audiodb.json * Update alsa.py * Update alsa.py * Update control.py * Fix * Enable call * fix * fix args * Fix gdbus * parse service data * Change handling * Fix states * Fix parser for tuples * Fix parser v2 * Fix tuple handling * Fix regex string handling * Faster tuple finder * fix empty detector * wrong order * Finish * fix lint * better filtering * fix match * Fix mode string
This commit is contained in:
parent
96f47a4c32
commit
561e80c2be
31
API.md
31
API.md
@ -227,7 +227,7 @@ return:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"hostname": "hostname|null",
|
"hostname": "hostname|null",
|
||||||
"features": ["shutdown", "reboot", "update", "hostname"],
|
"features": ["shutdown", "reboot", "update", "hostname", "services"],
|
||||||
"operating_system": "Hass.io-OS XY|Ubuntu 16.4|null",
|
"operating_system": "Hass.io-OS XY|Ubuntu 16.4|null",
|
||||||
"kernel": "4.15.7|null",
|
"kernel": "4.15.7|null",
|
||||||
"chassis": "specific|null",
|
"chassis": "specific|null",
|
||||||
@ -259,6 +259,27 @@ Optional:
|
|||||||
|
|
||||||
- POST `/host/reload`
|
- POST `/host/reload`
|
||||||
|
|
||||||
|
#### Services
|
||||||
|
|
||||||
|
- GET `/host/services`
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"name": "xy.service",
|
||||||
|
"description": "XY ...",
|
||||||
|
"state": "active|"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- POST `/host/service/{unit}/stop`
|
||||||
|
|
||||||
|
- POST `/host/service/{unit}/start`
|
||||||
|
|
||||||
|
- POST `/host/service/{unit}/reload`
|
||||||
|
|
||||||
### Hardware
|
### Hardware
|
||||||
|
|
||||||
- GET `/hardware/info`
|
- GET `/hardware/info`
|
||||||
@ -569,14 +590,6 @@ return:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- GET `/services/xy`
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"available": "bool",
|
|
||||||
"xy": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### MQTT
|
#### MQTT
|
||||||
|
|
||||||
This service performs an auto discovery to Home-Assistant.
|
This service performs an auto discovery to Home-Assistant.
|
||||||
|
@ -57,6 +57,13 @@ class RestAPI(CoreSysAttributes):
|
|||||||
web.post('/host/shutdown', api_host.shutdown),
|
web.post('/host/shutdown', api_host.shutdown),
|
||||||
web.post('/host/update', api_host.update),
|
web.post('/host/update', api_host.update),
|
||||||
web.post('/host/reload', api_host.reload),
|
web.post('/host/reload', api_host.reload),
|
||||||
|
web.get('/host/services', api_host.services),
|
||||||
|
web.post('/host/services/{service}/stop', api_host.service_stop),
|
||||||
|
web.post('/host/services/{service}/start', api_host.service_start),
|
||||||
|
web.post(
|
||||||
|
'/host/services/{service}/restart', api_host.service_restart),
|
||||||
|
web.post(
|
||||||
|
'/host/services/{service}/reload', api_host.service_reload),
|
||||||
])
|
])
|
||||||
|
|
||||||
def _register_hardware(self):
|
def _register_hardware(self):
|
||||||
|
@ -7,11 +7,14 @@ import voluptuous as vol
|
|||||||
from .utils import api_process, api_validate
|
from .utils import api_process, api_validate
|
||||||
from ..const import (
|
from ..const import (
|
||||||
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_HOSTNAME, ATTR_FEATURES, ATTR_KERNEL,
|
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_HOSTNAME, ATTR_FEATURES, ATTR_KERNEL,
|
||||||
ATTR_TYPE, ATTR_OPERATING_SYSTEM, ATTR_CHASSIS, ATTR_DEPLOYMENT)
|
ATTR_TYPE, ATTR_OPERATING_SYSTEM, ATTR_CHASSIS, ATTR_DEPLOYMENT,
|
||||||
|
ATTR_STATE, ATTR_NAME, ATTR_DESCRIPTON, ATTR_SERVICES)
|
||||||
from ..coresys import CoreSysAttributes
|
from ..coresys import CoreSysAttributes
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
SERVICE = 'service'
|
||||||
|
|
||||||
SCHEMA_VERSION = vol.Schema({
|
SCHEMA_VERSION = vol.Schema({
|
||||||
vol.Optional(ATTR_VERSION): vol.Coerce(str),
|
vol.Optional(ATTR_VERSION): vol.Coerce(str),
|
||||||
})
|
})
|
||||||
@ -70,3 +73,42 @@ class APIHost(CoreSysAttributes):
|
|||||||
pass
|
pass
|
||||||
# body = await api_validate(SCHEMA_VERSION, request)
|
# body = await api_validate(SCHEMA_VERSION, request)
|
||||||
# version = body.get(ATTR_VERSION, self.sys_host.last_version)
|
# version = body.get(ATTR_VERSION, self.sys_host.last_version)
|
||||||
|
|
||||||
|
@api_process
|
||||||
|
async def services(self, request):
|
||||||
|
"""Return list of available services."""
|
||||||
|
services = []
|
||||||
|
for unit in self.sys_host.services:
|
||||||
|
services.append({
|
||||||
|
ATTR_NAME: unit.name,
|
||||||
|
ATTR_DESCRIPTON: unit.description,
|
||||||
|
ATTR_STATE: unit.state,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
ATTR_SERVICES: services
|
||||||
|
}
|
||||||
|
|
||||||
|
@api_process
|
||||||
|
def service_start(self, request):
|
||||||
|
"""Start a service."""
|
||||||
|
unit = request.match_info.get(SERVICE)
|
||||||
|
return asyncio.shield(self.sys_host.services.start(unit))
|
||||||
|
|
||||||
|
@api_process
|
||||||
|
def service_stop(self, request):
|
||||||
|
"""Stop a service."""
|
||||||
|
unit = request.match_info.get(SERVICE)
|
||||||
|
return asyncio.shield(self.sys_host.services.stop(unit))
|
||||||
|
|
||||||
|
@api_process
|
||||||
|
def service_reload(self, request):
|
||||||
|
"""Reload a service."""
|
||||||
|
unit = request.match_info.get(SERVICE)
|
||||||
|
return asyncio.shield(self.sys_host.services.reload(unit))
|
||||||
|
|
||||||
|
@api_process
|
||||||
|
def service_restart(self, request):
|
||||||
|
"""Restart a service."""
|
||||||
|
unit = request.match_info.get(SERVICE)
|
||||||
|
return asyncio.shield(self.sys_host.services.restart(unit))
|
||||||
|
@ -216,3 +216,4 @@ FEATURES_SHUTDOWN = 'shutdown'
|
|||||||
FEATURES_REBOOT = 'reboot'
|
FEATURES_REBOOT = 'reboot'
|
||||||
FEATURES_UPDATE = 'update'
|
FEATURES_UPDATE = 'update'
|
||||||
FEATURES_HOSTNAME = 'hostname'
|
FEATURES_HOSTNAME = 'hostname'
|
||||||
|
FEATURES_SERVICES = 'services'
|
||||||
|
@ -37,3 +37,43 @@ class Systemd(DBusInterface):
|
|||||||
Return a coroutine.
|
Return a coroutine.
|
||||||
"""
|
"""
|
||||||
return self.dbus.Manager.PowerOff()
|
return self.dbus.Manager.PowerOff()
|
||||||
|
|
||||||
|
@dbus_connected
|
||||||
|
def start_unit(self, unit, mode):
|
||||||
|
"""Start a systemd service unit.
|
||||||
|
|
||||||
|
Return a coroutine.
|
||||||
|
"""
|
||||||
|
return self.dbus.Manager.StartUnit(unit, mode)
|
||||||
|
|
||||||
|
@dbus_connected
|
||||||
|
def stop_unit(self, unit, mode):
|
||||||
|
"""Stop a systemd service unit.
|
||||||
|
|
||||||
|
Return a coroutine.
|
||||||
|
"""
|
||||||
|
return self.dbus.Manager.StopUnit(unit, mode)
|
||||||
|
|
||||||
|
@dbus_connected
|
||||||
|
def reload_unit(self, unit, mode):
|
||||||
|
"""Reload a systemd service unit.
|
||||||
|
|
||||||
|
Return a coroutine.
|
||||||
|
"""
|
||||||
|
return self.dbus.Manager.ReloadOrRestartUnit(unit, mode)
|
||||||
|
|
||||||
|
@dbus_connected
|
||||||
|
def restart_unit(self, unit, mode):
|
||||||
|
"""Restart a systemd service unit.
|
||||||
|
|
||||||
|
Return a coroutine.
|
||||||
|
"""
|
||||||
|
return self.dbus.Manager.RestartUnit(unit, mode)
|
||||||
|
|
||||||
|
@dbus_connected
|
||||||
|
def list_units(self):
|
||||||
|
"""Return a list of available systemd services.
|
||||||
|
|
||||||
|
Return a coroutine.
|
||||||
|
"""
|
||||||
|
return self.dbus.Manager.ListUnits()
|
||||||
|
@ -28,6 +28,11 @@ class HostNotSupportedError(HassioNotSupportedError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class HostServiceError(HostError):
|
||||||
|
"""Host service functions fails."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# utils/gdbus
|
# utils/gdbus
|
||||||
|
|
||||||
class DBusError(HassioError):
|
class DBusError(HassioError):
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
from .alsa import AlsaAudio
|
from .alsa import AlsaAudio
|
||||||
from .control import SystemControl
|
from .control import SystemControl
|
||||||
from .info import InfoCenter
|
from .info import InfoCenter
|
||||||
from ..const import FEATURES_REBOOT, FEATURES_SHUTDOWN, FEATURES_HOSTNAME
|
from .service import ServiceManager
|
||||||
|
from ..const import (
|
||||||
|
FEATURES_REBOOT, FEATURES_SHUTDOWN, FEATURES_HOSTNAME, FEATURES_SERVICES)
|
||||||
from ..coresys import CoreSysAttributes
|
from ..coresys import CoreSysAttributes
|
||||||
|
|
||||||
|
|
||||||
@ -16,6 +18,7 @@ class HostManager(CoreSysAttributes):
|
|||||||
self._alsa = AlsaAudio(coresys)
|
self._alsa = AlsaAudio(coresys)
|
||||||
self._control = SystemControl(coresys)
|
self._control = SystemControl(coresys)
|
||||||
self._info = InfoCenter(coresys)
|
self._info = InfoCenter(coresys)
|
||||||
|
self._services = ServiceManager(coresys)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alsa(self):
|
def alsa(self):
|
||||||
@ -32,6 +35,11 @@ class HostManager(CoreSysAttributes):
|
|||||||
"""Return host info handler."""
|
"""Return host info handler."""
|
||||||
return self._info
|
return self._info
|
||||||
|
|
||||||
|
@property
|
||||||
|
def services(self):
|
||||||
|
"""Return host services handler."""
|
||||||
|
return self._services
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supperted_features(self):
|
def supperted_features(self):
|
||||||
"""Return a list of supported host features."""
|
"""Return a list of supported host features."""
|
||||||
@ -41,6 +49,7 @@ class HostManager(CoreSysAttributes):
|
|||||||
features.extend([
|
features.extend([
|
||||||
FEATURES_REBOOT,
|
FEATURES_REBOOT,
|
||||||
FEATURES_SHUTDOWN,
|
FEATURES_SHUTDOWN,
|
||||||
|
FEATURES_SERVICES,
|
||||||
])
|
])
|
||||||
|
|
||||||
if self.sys_dbus.hostname.is_connected:
|
if self.sys_dbus.hostname.is_connected:
|
||||||
@ -53,6 +62,9 @@ class HostManager(CoreSysAttributes):
|
|||||||
if self.sys_dbus.hostname.is_connected:
|
if self.sys_dbus.hostname.is_connected:
|
||||||
await self.info.update()
|
await self.info.update()
|
||||||
|
|
||||||
|
if self.sys_dbus.systemd.is_connected:
|
||||||
|
await self.services.update()
|
||||||
|
|
||||||
def reload(self):
|
def reload(self):
|
||||||
"""Reload host information."""
|
"""Reload host information."""
|
||||||
return self.load()
|
return self.load()
|
||||||
|
@ -81,7 +81,7 @@ class AlsaAudio(CoreSysAttributes):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _audio_database():
|
def _audio_database():
|
||||||
"""Read local json audio data into dict."""
|
"""Read local json audio data into dict."""
|
||||||
json_file = Path(__file__).parent.joinpath('audiodb.json')
|
json_file = Path(__file__).parent.joinpath("data/audiodb.json")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
@ -121,7 +121,7 @@ class AlsaAudio(CoreSysAttributes):
|
|||||||
alsa_output = alsa_output or self.default.output
|
alsa_output = alsa_output or self.default.output
|
||||||
|
|
||||||
# Read Template
|
# Read Template
|
||||||
asound_file = Path(__file__).parent.joinpath('asound.tmpl')
|
asound_file = Path(__file__).parent.joinpath("data/asound.tmpl")
|
||||||
try:
|
try:
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
with asound_file.open('r') as asound:
|
with asound_file.open('r') as asound:
|
||||||
|
@ -6,6 +6,9 @@ from ..exceptions import HostNotSupportedError
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
MANAGER = 'manager'
|
||||||
|
HOSTNAME = 'hostname'
|
||||||
|
|
||||||
|
|
||||||
class SystemControl(CoreSysAttributes):
|
class SystemControl(CoreSysAttributes):
|
||||||
"""Handle host power controls."""
|
"""Handle host power controls."""
|
||||||
@ -14,15 +17,19 @@ class SystemControl(CoreSysAttributes):
|
|||||||
"""Initialize host power handling."""
|
"""Initialize host power handling."""
|
||||||
self.coresys = coresys
|
self.coresys = coresys
|
||||||
|
|
||||||
def _check_systemd(self):
|
def _check_dbus(self, flag):
|
||||||
"""Check if systemd is connect or raise error."""
|
"""Check if systemd is connect or raise error."""
|
||||||
if not self.sys_dbus.systemd.is_connected:
|
if flag == MANAGER and self.sys_dbus.systemd.is_connected:
|
||||||
_LOGGER.error("No systemd dbus connection available")
|
return
|
||||||
raise HostNotSupportedError()
|
if flag == HOSTNAME and self.sys_dbus.hostname.is_connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.error("No %s dbus connection available", flag)
|
||||||
|
raise HostNotSupportedError()
|
||||||
|
|
||||||
async def reboot(self):
|
async def reboot(self):
|
||||||
"""Reboot host system."""
|
"""Reboot host system."""
|
||||||
self._check_systemd()
|
self._check_dbus(MANAGER)
|
||||||
|
|
||||||
_LOGGER.info("Initialize host reboot over systemd")
|
_LOGGER.info("Initialize host reboot over systemd")
|
||||||
try:
|
try:
|
||||||
@ -32,7 +39,7 @@ class SystemControl(CoreSysAttributes):
|
|||||||
|
|
||||||
async def shutdown(self):
|
async def shutdown(self):
|
||||||
"""Shutdown host system."""
|
"""Shutdown host system."""
|
||||||
self._check_systemd()
|
self._check_dbus(MANAGER)
|
||||||
|
|
||||||
_LOGGER.info("Initialize host power off over systemd")
|
_LOGGER.info("Initialize host power off over systemd")
|
||||||
try:
|
try:
|
||||||
@ -42,9 +49,7 @@ class SystemControl(CoreSysAttributes):
|
|||||||
|
|
||||||
async def set_hostname(self, hostname):
|
async def set_hostname(self, hostname):
|
||||||
"""Set local a new Hostname."""
|
"""Set local a new Hostname."""
|
||||||
if not self.sys_dbus.systemd.is_connected:
|
self._check_dbus(HOSTNAME)
|
||||||
_LOGGER.error("No hostname dbus connection available")
|
|
||||||
raise HostNotSupportedError()
|
|
||||||
|
|
||||||
_LOGGER.info("Set Hostname %s", hostname)
|
_LOGGER.info("Set Hostname %s", hostname)
|
||||||
await self.sys_dbus.hostname.set_static_hostname(hostname)
|
await self.sys_dbus.hostname.set_static_hostname(hostname)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Power control for host."""
|
"""Info control for host."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from ..coresys import CoreSysAttributes
|
from ..coresys import CoreSysAttributes
|
||||||
@ -47,7 +47,7 @@ class InfoCenter(CoreSysAttributes):
|
|||||||
|
|
||||||
async def update(self):
|
async def update(self):
|
||||||
"""Update properties over dbus."""
|
"""Update properties over dbus."""
|
||||||
if not self.sys_dbus.systemd.is_connected:
|
if not self.sys_dbus.hostname.is_connected:
|
||||||
_LOGGER.error("No hostname dbus connection available")
|
_LOGGER.error("No hostname dbus connection available")
|
||||||
raise HostNotSupportedError()
|
raise HostNotSupportedError()
|
||||||
|
|
||||||
|
99
hassio/host/service.py
Normal file
99
hassio/host/service.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
"""Service control for host."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import attr
|
||||||
|
|
||||||
|
from ..coresys import CoreSysAttributes
|
||||||
|
from ..exceptions import HassioError, HostNotSupportedError, HostServiceError
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
MOD_REPLACE = 'replace'
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceManager(CoreSysAttributes):
|
||||||
|
"""Handle local service information controls."""
|
||||||
|
|
||||||
|
def __init__(self, coresys):
|
||||||
|
"""Initialize system center handling."""
|
||||||
|
self.coresys = coresys
|
||||||
|
self._services = set()
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""Iterator trought services."""
|
||||||
|
return iter(self._services)
|
||||||
|
|
||||||
|
def _check_dbus(self, unit=None):
|
||||||
|
"""Check available dbus connection."""
|
||||||
|
if not self.sys_dbus.systemd.is_connected:
|
||||||
|
_LOGGER.error("No systemd dbus connection available")
|
||||||
|
raise HostNotSupportedError()
|
||||||
|
|
||||||
|
if unit and not self.exists(unit):
|
||||||
|
_LOGGER.error("Unit '%s' not found", unit)
|
||||||
|
raise HostServiceError()
|
||||||
|
|
||||||
|
def start(self, unit):
|
||||||
|
"""Start a service on host."""
|
||||||
|
self._check_dbus(unit)
|
||||||
|
|
||||||
|
_LOGGER.info("Start local service %s", unit)
|
||||||
|
return self.sys_dbus.systemd.start_unit(unit, MOD_REPLACE)
|
||||||
|
|
||||||
|
def stop(self, unit):
|
||||||
|
"""Stop a service on host."""
|
||||||
|
self._check_dbus(unit)
|
||||||
|
|
||||||
|
_LOGGER.info("Stop local service %s", unit)
|
||||||
|
return self.sys_dbus.systemd.stop_unit(unit, MOD_REPLACE)
|
||||||
|
|
||||||
|
def reload(self, unit):
|
||||||
|
"""Reload a service on host."""
|
||||||
|
self._check_dbus(unit)
|
||||||
|
|
||||||
|
_LOGGER.info("Reload local service %s", unit)
|
||||||
|
return self.sys_dbus.systemd.reload_unit(unit, MOD_REPLACE)
|
||||||
|
|
||||||
|
def restart(self, unit):
|
||||||
|
"""Restart a service on host."""
|
||||||
|
self._check_dbus(unit)
|
||||||
|
|
||||||
|
_LOGGER.info("Restart local service %s", unit)
|
||||||
|
return self.sys_dbus.systemd.restart_unit(unit, MOD_REPLACE)
|
||||||
|
|
||||||
|
def exists(self, unit):
|
||||||
|
"""Check if a unit exists and return True."""
|
||||||
|
for service in self._services:
|
||||||
|
if unit == service.name:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def update(self):
|
||||||
|
"""Update properties over dbus."""
|
||||||
|
self._check_dbus()
|
||||||
|
|
||||||
|
_LOGGER.info("Update service information")
|
||||||
|
self._services.clear()
|
||||||
|
try:
|
||||||
|
systemd_units = await self.sys_dbus.systemd.list_units()
|
||||||
|
for service_data in systemd_units[0]:
|
||||||
|
if not service_data[0].endswith(".service") or \
|
||||||
|
service_data[2] != 'loaded':
|
||||||
|
continue
|
||||||
|
self._services.add(ServiceInfo.read_from(service_data))
|
||||||
|
except (HassioError, IndexError):
|
||||||
|
_LOGGER.warning("Can't update host service information!")
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(frozen=True)
|
||||||
|
class ServiceInfo:
|
||||||
|
"""Represent a single Service."""
|
||||||
|
|
||||||
|
name = attr.ib(type=str)
|
||||||
|
description = attr.ib(type=str)
|
||||||
|
state = attr.ib(type=str)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def read_from(unit):
|
||||||
|
"""Parse data from dbus into this object."""
|
||||||
|
return ServiceInfo(unit[0], unit[1], unit[3])
|
@ -14,10 +14,11 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
RE_GVARIANT_TYPE = re.compile(
|
RE_GVARIANT_TYPE = re.compile(
|
||||||
r"(?:boolean|byte|int16|uint16|int32|uint32|handle|int64|uint64|double|"
|
r"(?:boolean|byte|int16|uint16|int32|uint32|handle|int64|uint64|double|"
|
||||||
r"string|objectpath|signature) ")
|
r"string|objectpath|signature) ")
|
||||||
RE_GVARIANT_TULPE = re.compile(r"^\((.*),\)$")
|
|
||||||
RE_GVARIANT_VARIANT = re.compile(
|
RE_GVARIANT_VARIANT = re.compile(
|
||||||
r"(?<=(?: |{|\[))<((?:'|\").*?(?:'|\")|\d+(?:\.\d+)?)>(?=(?:|]|}|,))")
|
r"(?<=(?: |{|\[))<((?:'|\").*?(?:'|\")|\d+(?:\.\d+)?)>(?=(?:|]|}|,))")
|
||||||
RE_GVARIANT_STRING = re.compile(r"(?<=(?: |{|\[))'(.*?)'(?=(?:|]|}|,))")
|
RE_GVARIANT_STRING = re.compile(r"(?<=(?: |{|\[|\())'(.*?)'(?=(?:|]|}|,|\)))")
|
||||||
|
RE_GVARIANT_TUPLE_O = re.compile(r"\"[^\"]*?\"|(\()")
|
||||||
|
RE_GVARIANT_TUPLE_C = re.compile(r"\"[^\"]*?\"|(,?\))")
|
||||||
|
|
||||||
# Commands for dbus
|
# Commands for dbus
|
||||||
INTROSPECT = ("gdbus introspect --system --dest {bus} "
|
INTROSPECT = ("gdbus introspect --system --dest {bus} "
|
||||||
@ -76,13 +77,16 @@ class DBus:
|
|||||||
def _gvariant(raw):
|
def _gvariant(raw):
|
||||||
"""Parse GVariant input to python."""
|
"""Parse GVariant input to python."""
|
||||||
raw = RE_GVARIANT_TYPE.sub("", raw)
|
raw = RE_GVARIANT_TYPE.sub("", raw)
|
||||||
raw = RE_GVARIANT_TULPE.sub(r"[\1]", raw)
|
|
||||||
raw = RE_GVARIANT_VARIANT.sub(r"\1", raw)
|
raw = RE_GVARIANT_VARIANT.sub(r"\1", raw)
|
||||||
raw = RE_GVARIANT_STRING.sub(r'"\1"', raw)
|
raw = RE_GVARIANT_STRING.sub(r'"\1"', raw)
|
||||||
|
raw = RE_GVARIANT_TUPLE_O.sub(
|
||||||
|
lambda x: x.group(0) if not x.group(1) else"[", raw)
|
||||||
|
raw = RE_GVARIANT_TUPLE_C.sub(
|
||||||
|
lambda x: x.group(0) if not x.group(1) else"]", raw)
|
||||||
|
|
||||||
# No data
|
# No data
|
||||||
if raw.startswith("()"):
|
if raw.startswith("[]"):
|
||||||
return {}
|
return []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return json.loads(raw)
|
return json.loads(raw)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user