使用dbus-fast调用org.freedesktop.login1.Manager的Inhibit方法时遇EOFError
问题
我正在开发一款将以多种形式(Flatpak、PyInstaller可执行文件等)分发的Linux平台Python应用,需要调用org.freedesktop.login1.Manager的dbus接口中的Inhibit方法。由于分发需求复杂多样,我选择了纯Python实现的dbus-fast库,且dbus-python文档开头存在诸多警告,这也让dbus-fast更具优势。
目前dbus-fast整体运行正常,例如调用ListUsers方法没有问题,代码如下:
import asyncio from dbus_fast import BusType from dbus_fast.aio import MessageBus async def list_users() -> None: system_bus = await MessageBus(bus_type=BusType.SYSTEM).connect() introspection = await system_bus.introspect( "org.freedesktop.login1", "/org/freedesktop/login1", ) login_object = system_bus.get_proxy_object( "org.freedesktop.login1", "/org/freedesktop/login1", introspection, ) login_manager_interface = login_object.get_interface( "org.freedesktop.login1.Manager", ) user_list = await login_manager_interface.call_list_users() print(user_list) asyncio.run(list_users())
运行后可得到如下用户列表:
$ python -m dbus_list_users [[1000, 'newbyte', '/org/freedesktop/login1/user/_1000']]
但尝试调用Inhibit方法时,出现了EOFError,相关代码如下:
import asyncio from dbus_fast import BusType from dbus_fast.aio import MessageBus async def do_inhibit() -> None: system_bus = await MessageBus(bus_type=BusType.SYSTEM).connect() introspection = await system_bus.introspect( "org.freedesktop.login1", "/org/freedesktop/login1", ) login_object = system_bus.get_proxy_object( "org.freedesktop.login1", "/org/freedesktop/login1", introspection, ) login_manager_interface = login_object.get_interface( "org.freedesktop.login1.Manager", ) inhibit_fd = await login_manager_interface.call_inhibit( "sleep", "Stack Overflow example man", "To demonstrate that it does not work", "delay", ) print(inhibit_fd) asyncio.run(do_inhibit())
运行后得到如下回溯信息:
$ python -m dbus_inhibit Traceback (most recent call last): File "<frozen runpy>", line 198, in _run_module_as_main File "<frozen runpy>", line 88, in _run_code File "/mnt/storage/Programming/sparvio_toolbox/dbus_inhibit.py", line 28, in <module> asyncio.run(do_inhibit()) File "/usr/lib64/python3.12/asyncio/runners.py", line 194, in run return runner.run(main) ^^^^^^^^^^^^^^^^ File "/usr/lib64/python3.12/asyncio/runners.py", line 118, in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib64/python3.12/asyncio/base_events.py", line 687, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "/mnt/storage/Programming/sparvio_toolbox/dbus_inhibit.py", line 20, in do_inhibit inhibit_fd = await login_manager_interface.call_inhibit( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/mnt/storage/Programming/sparvio_toolbox/_host-env/lib64/python3.12/site-packages/dbus_fast/aio/proxy_object.py", line 92, in method_fn msg = await self.bus.call( ^^^^^^^^^^^^^^^^^^^^ File "/mnt/storage/Programming/sparvio_toolbox/_host-env/lib64/python3.12/site-packages/dbus_fast/aio/message_bus.py", line 385, in call await future File "src/dbus_fast/aio/message_reader.py", line 19, in dbus_fast.aio.message_reader._message_reader File "src/dbus_fast/_private/unmarshaller.py", line 775, in dbus_fast._private.unmarshaller.Unmarshaller._unmarshall File "src/dbus_fast/_private/unmarshaller.py", line 636, in dbus_fast._private.unmarshaller.Unmarshaller._read_header File "src/dbus_fast/_private/unmarshaller.py", line 376, in dbus_fast._private.unmarshaller.Unmarshaller._read_to_pos File "src/dbus_fast/_private/unmarshaller.py", line 339, in dbus_fast._private.unmarshaller.Unmarshaller._read_sock_without_fds EOFError
我查阅了systemd官方的inhibitor locks文档,但仍未找到解决办法。请问如何在Python中使用dbus-fast调用org.freedesktop.login1.Manager的Inhibit方法并获取抑制锁?
解决方案
出现EOFError的核心原因是Inhibit方法会通过DBus传递一个文件描述符(FD),而默认的MessageBus初始化没有启用FD传递功能,dbus-fast需要显式配置才能处理带FD的DBus消息。
修正步骤
- 初始化
MessageBus时,添加enable_fds=True参数,开启文件描述符传递支持。 - 保持代码其余部分不变,正常调用
call_inhibit方法即可获取抑制锁对应的文件描述符。
修正后的完整代码
import asyncio from dbus_fast import BusType from dbus_fast.aio import MessageBus async def do_inhibit() -> None: # 关键:添加enable_fds=True启用FD传递 system_bus = await MessageBus(bus_type=BusType.SYSTEM, enable_fds=True).connect() introspection = await system_bus.introspect( "org.freedesktop.login1", "/org/freedesktop/login1", ) login_object = system_bus.get_proxy_object( "org.freedesktop.login1", "/org/freedesktop/login1", introspection, ) login_manager_interface = login_object.get_interface( "org.freedesktop.login1.Manager", ) inhibit_fd = await login_manager_interface.call_inhibit( "sleep", "Stack Overflow example man", "To demonstrate that it does not work", "delay", ) print(f"获取到抑制锁文件描述符:{inhibit_fd}") # 注意:需要保持该文件描述符处于打开状态才能维持抑制锁 # 可以根据业务需求在合适的时机关闭它:import os; os.close(inhibit_fd) asyncio.run(do_inhibit())
补充说明
- 抑制锁的生命周期与返回的文件描述符绑定,只要FD保持打开,抑制锁就会生效;关闭FD后,抑制锁自动释放。
- 运行该代码需要足够的权限:普通用户通常可以抑制
sleep、idle等操作,但部分操作(如shutdown)可能需要root权限或特定的PolicyKit授权。 - 对于Flatpak分发的应用,需要在Flatpak manifest中添加对应的权限,例如
--talk-name=org.freedesktop.login1以及文件描述符传递相关的权限配置。
内容的提问来源于stack exchange,提问作者Newbyte
相关产品推荐
相关产品推荐

