You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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消息。

修正步骤

  1. 初始化MessageBus时,添加enable_fds=True参数,开启文件描述符传递支持。
  2. 保持代码其余部分不变,正常调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 16:34:50