Skip to content
Snippets Groups Projects
Commit abd398d8 authored by Martin Mareš's avatar Martin Mareš
Browse files

Removed DBUS experiments

parent 96b9d30e
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/python3
from dbus_next.aio import MessageBus, ProxyInterface
import dbus_next.introspection as intr
from dbus_next.message import Message
from dbus_next.service import ServiceInterface, method, dbus_property, signal
from dbus_next import Variant, DBusError, BusType, MessageType, RequestNameReply
import asyncio
import os
from typing import Union, Optional, Callable, Dict, List, Set
async def main():
bus = await MessageBus(bus_type=BusType.SYSTEM, negotiate_unix_fd=True).connect()
ins = await bus.introspect('cz.ucw.shipcat', '/cz/ucw/ShipCat')
pxy = bus.get_proxy_object('cz.ucw.shipcat', '/cz/ucw/ShipCat', ins)
pif = pxy.get_interface('cz.ucw.ShipCat')
# fd = os.open('/dev/null', os.O_RDWR)
# xx = await pif.call_shell(0, 1, 2)
xx = await pif.call_check('test')
print(xx)
asyncio.get_event_loop().run_until_complete(main())
#!/usr/bin/python3
import asyncio
from dbus_next import Message, Variant, DBusError, BusType, MessageType, RequestNameReply
from dbus_next.aio import MessageBus, ProxyInterface
import dbus_next.introspection as intr
import logging
import os
import sys
from typing import Union, Optional, Dict, List, Set, Any
from shipcat.config import GlobalConfig, ContainerConfig, ConfigError
class BusLogic:
config: GlobalConfig
bus: MessageBus
msg_tasks: Set[asyncio.Task]
dbus_iface: ProxyInterface
object_tree: Any
method_list: List[intr.Method]
method_dict: Dict[str, intr.Method]
interface_intro: intr.Interface
def __init__(self, config: GlobalConfig) -> None:
self.config = config
self.logger = logging.getLogger('shipscat')
if config.verbosity > 0:
self.logger.setLevel(logging.DEBUG)
else:
self.logger.setLevel(logging.INFO)
self.logger.propagate = True
self.msg_tasks = set()
self.object_tree = {
'cz': {
'ucw': {
'ShipCat': True,
}
}
}
self.method_list = [
intr.Method(
name='Check',
in_args=[
intr.Arg('s', intr.ArgDirection.IN, 'container'),
],
),
intr.Method(
name='Shell',
in_args=[
intr.Arg('h', intr.ArgDirection.IN, 'stdin_fd'),
intr.Arg('h', intr.ArgDirection.IN, 'stdout_fd'),
intr.Arg('h', intr.ArgDirection.IN, 'stderr_fd'),
],
out_args=[
intr.Arg('i', intr.ArgDirection.OUT, 'exit_code'),
],
),
]
self.method_dict = {m.name: m for m in self.method_list}
self.interface_intro = intr.Interface(
name='cz.ucw.ShipCat',
methods=self.method_list,
)
async def loop(self) -> None:
self.bus = await MessageBus(bus_type=BusType.SYSTEM, negotiate_unix_fd=True).connect()
# Obtain proxy interface for org.freedesktop.DBus
dbus_intro = await self.bus.introspect(bus_name='org.freedesktop.DBus', path='/org/freedesktop/DBus')
dbus_proxy = self.bus.get_proxy_object(bus_name='org.freedesktop.DBus', path='/org/freedesktop/DBus', introspection=dbus_intro)
self.dbus_iface = dbus_proxy.get_interface('org.freedesktop.DBus')
self.bus.add_message_handler(self.handle_message)
rn = await self.bus.request_name('cz.ucw.shipcat')
assert rn == RequestNameReply.PRIMARY_OWNER
await self.bus.wait_for_disconnect()
def handle_message(self, msg: Message) -> Optional[Union[Message, bool]]:
self.logger.debug(f'Got message: type={msg.message_type.name} dest={msg.destination} path={msg.path} iface={msg.interface} member={msg.member} sender={msg.sender} fds={msg.unix_fds}')
if (msg.destination == 'cz.ucw.shipcat'
and msg.interface == 'org.freedesktop.DBus.Introspectable'
and msg.member == 'Introspect'):
self.close_fds(msg)
result = self.handle_introspect(msg)
elif (msg.destination == 'cz.ucw.shipcat'
and msg.interface == 'cz.ucw.ShipCat'
and msg.message_type == MessageType.METHOD_CALL
and msg.member in self.method_dict
and msg.signature == self.method_dict[msg.member].in_signature):
result = self.handle_method_call(msg)
else:
self.close_fds(msg)
result = None
return result
def close_fds(self, msg: Message) -> None:
# XXX: The dbus-next library leaks file descriptors. Work around it.
# (see https://github.com/altdesktop/python-dbus-next/issues/162)
for fd in msg.unix_fds:
os.close(fd)
msg.unix_fds = []
def handle_method_call(self, msg: Message) -> bool:
async def async_method_call(msg):
self.logger.debug(f'Calling method {msg.member}')
try:
reply = await getattr(self, 'handle_' + msg.member)(msg)
except DBusError as e:
self.logger.debug(f'DBusError: {e}')
reply = Message.new_error(msg, e.type, e.text)
except Exception as e:
self.logger.error(f'Internal error: {e}')
reply = Message.new_error(msg, 'cz.ucw.ShipCat.Error.Internal', 'Internal error')
self.bus.send(reply)
self.close_fds(msg)
task = asyncio.create_task(async_method_call(msg))
self.msg_tasks.add(task)
task.add_done_callback(self.msg_tasks.discard)
return True
def handle_introspect(self, msg: Message) -> Message:
self.logger.debug(f'Introspecting {msg.path}')
intro = intr.Node.default(msg.path)
# We do not want to speak ObjectManager-ish
intro.interfaces = [i for i in intro.interfaces if i.name != 'org.freedesktop.DBus.ObjectManager']
if msg.path == '/':
path = [""]
else:
path = msg.path.split('/')
assert path[0] == ""
i = 1
node = self.object_tree
while i < len(path) and isinstance(node, dict) and path[i] in node:
node = node[path[i]]
i += 1
if i == len(path):
if isinstance(node, dict):
intro.nodes = [intr.Node(n) for n in sorted(node.keys())]
else:
intro.interfaces.append(self.interface_intro)
return Message.new_method_return(msg, signature='s', body=[intro.tostring()])
async def handle_Check(self, msg: Message) -> Message:
[container] = msg.body
await self.setup_container(container, msg)
ins = await self.bus.introspect('org.freedesktop.systemd1', '/org/freedesktop/systemd1')
pxy = self.bus.get_proxy_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1', ins)
pif = pxy.get_interface('org.freedesktop.systemd1.Manager')
p = await pif.call_get_unit('inetd.service')
print(p)
uns = await self.bus.introspect('org.freedesktop.systemd1', p)
uxy = self.bus.get_proxy_object('org.freedesktop.systemd1', p, uns)
uif = uxy.get_interface('org.freedesktop.systemd1.Unit')
active = await uif.get_active_state()
sub_state = await uif.get_sub_state()
print(active, sub_state)
await pif.call_subscribe()
def cb(a, b, c):
self.logger.debug(f'PROP: {a} {b} {c}')
prif = uxy.get_interface('org.freedesktop.DBus.Properties')
prif.on_properties_changed(cb)
await asyncio.sleep(60)
await pif.call_unsubscribe()
return Message.new_method_return(msg)
async def handle_Shell(self, msg: Message) -> Message:
stdin_fd = msg.unix_fds[msg.body[0]]
stdout_fd = msg.unix_fds[msg.body[1]]
stderr_fd = msg.unix_fds[msg.body[2]]
cred = await self.dbus_iface.call_get_connection_credentials(msg.sender)
proc = await asyncio.create_subprocess_exec('/bin/cat', '/etc/profile', stdin=stdin_fd, stdout=stdout_fd, stderr=stderr_fd)
await proc.wait()
return Message.new_method_return(msg, signature='i', body=[proc.returncode])
async def setup_container(self, name: str, msg: Message) -> ContainerConfig:
cred = await self.dbus_iface.call_get_connection_credentials(msg.sender)
assert isinstance(cred, dict)
unix_user_id = cred.get('UnixUserID', None)
if isinstance(unix_user_id, Variant) and unix_user_id.signature == 'u':
uid = unix_user_id.value
assert isinstance(uid, int)
else:
uid = -1
unix_group_ids = cred.get('UnixGroupIDs', None)
if isinstance(unix_group_ids, Variant) and unix_group_ids.signature == 'a(u)':
gids = unix_user_id.value
else:
gids = []
try:
cc = ContainerConfig.load(self.config, name)
except ConfigError as e:
self.logger.error(f'{msg.member}({name}) by uid {uid}: {e}')
raise DBusError('cz.ucw.ShipCat.Error.ContainerConfig', 'Cannot load container configuration')
if not self.check_rights(cc, uid, gids):
self.logger.error(f'{msg.member}({name}) by uid {uid}: Permission denied')
raise DBusError('cz.ucw.ShipCat.Error.PermissionDenied', 'You do not have permission to handle this container')
self.logger.info(f'{msg.member}({name}) by uid {uid}')
return cc
def check_rights(self, cc: ContainerConfig, uid: int, gids: List[int]) -> bool:
if uid == 0:
return True
if uid in cc.allowed_users:
return True
for gid in gids:
assert isinstance(gid, int)
if gid in cc.allowed_groups:
return True
return False
try:
config = GlobalConfig.load('shipcat.toml')
except ConfigError as e:
print(e, file=sys.stderr)
sys.exit(1)
logging.basicConfig(format='%(asctime)-15s %(levelname)-5.5s %(message)s')
bl = BusLogic(config)
asyncio.get_event_loop().run_until_complete(bl.loop())
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment