通过TCP连接到DBus

24

我写了一个简单的Python程序来播放和暂停Banshee音乐播放器。 虽然在我的电脑上运行正常,但是我在连接到同一路由器(局域网)的远程计算机上操作时遇到了问题。 我编辑了远程计算机的session.conf文件,添加了这一行:

<listen>tcp:host=localhost,port=12434</listen>

这是我的程序:

    import dbus


    bus_obj=dbus.bus.BusConnection("tcp:host=localhost,port=12434")
    proxy_object=bus_obj.get_object('org.bansheeproject.Banshee',                              
    '/org/bansheeproject/Banshee/PlayerEngine')

    playerengine_iface=dbus.Interface(proxy_object,
    dbus_interface='org.bansheeproject.Banshee.PlayerEngine')

    var=0

    while (var!="3"):
        var=raw_input("\nPress\n1 to play\n2 to pause\n3 to exit\n")


            if var=="1":
                print "playing..."
                playerengine_iface.Play()

            elif var=="2":
                print "pausing"
                playerengine_iface.Pause()

当我尝试执行它时,这就是我得到的结果。

Traceback (most recent call last):
  File "dbus3.py", line 4, in <module>
    bus_obj=dbus.bus.BusConnection("tcp:host=localhost,port=12434")
  File "/usr/lib/python2.7/dist-packages/dbus/bus.py", line 125, in __new__
    bus = cls._new_for_bus(address_or_type, mainloop=mainloop)
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoServer: Failed to connect to socket "localhost:12434" Connection refused

我在这里做错了什么?我应该编辑 /usr/lib/python2.7/dist-packages/dbus/bus.py 吗?

更新:

好的,这就是问题所在。当我添加

标记时,它破坏了我的HTML。请帮我解决一下。

<listen>tcp:host=192.168.1.7,port=12434</listen>

我修改了/etc/dbus-1/session.conf文件,并希望在重新启动时能够监听,但实际上并没有成功。系统启动时卡在了加载界面上,偶尔会出现一个黑屏并显示以下文本:

Pulseaudio Configured For Per-user Sessions Saned Disabled;edit/etc/default/saned

当我按下ctrl+alt+f1,将session.conf更改为原始状态并重新启动后,它可以正确启动。

这是怎么回事?我如何使dbus守护程序在不遇到问题的情况下监听tcp连接?


默认(Ubuntu)。抱歉,当涉及到网络时,我的知识几乎为零,我应该配置iptables以允许连接吗?我以为iptables默认情况下允许所有内容。 - Gautam
有用的网络命令:netstat -nalsof -nitcpdump -n -i eth0iptables -nvL。我认为结合使用这些命令将告诉您进程是否在监听,并且如果是,则是否被阻止。祝你好运! - hochl
1
您可以使用socat将TCP数据包转发到本地Unix套接字,这样您就不需要重新启动DBus守护程序。另外,尝试使用tcp:host=0.0.0.0,port=12434 - 这将绑定监听套接字到所有接口。(在重启后检查是否实际在侦听 - telnet 192.168.1.7 12434 - Andrey Sidorov
3个回答

42
我最近需要设置这个,并发现诀窍是:对于session.conf中的<listen>元素,顺序很重要。您应该确保TCP元素首先出现。很奇怪,但至少对于我的情况是真的。(如果我颠倒顺序并将UNIX套接字<listen>元素放在第一位,我会看到完全相同的黑屏行为。)
此外,在TCP <listen>标记之前添加内容是必要的,但不足以使通过TCP进行远程D-Bus连接生效,您需要完成三件事:
  1. Add a <listen> tag above the UNIX one, similar to this:

    <listen>tcp:host=localhost,bind=*,port=55556,family=ipv4</listen>
    <listen>unix:tmpdir=/tmp</listen>
    
  2. Add a line (right below the <listen> tags is fine) that says:

    <auth>ANONYMOUS</auth>
    
  3. Add another line below these that says:

    <allow_anonymous/>
    

<auth>标签应该添加在您的session.conf中除其他可能包含的<auth>标签之外。简而言之,您的session.conf应该包含类似于以下内容的片段:

<listen>tcp:host=localhost,bind=*,port=55556,family=ipv4</listen>
<listen>unix:tmpdir=/tmp</listen>

<auth>ANONYMOUS</auth>
<allow_anonymous/>

完成以上三个步骤后,您应该能够远程连接到会话总线。在D-Feet中指定远程连接的方式如下所示:

D-Feet screen capture

请注意,如果您想连接到系统总线,则需要对/etc/dbus-1/system.conf进行类似的更改,但需指定一个不同的TCP端口,例如55557。(奇怪的是,在这种情况下,元素顺序似乎并不重要。)
在此配置中唯一的奇怪行为是,使用sudo(例如sudo gvim)运行桌面应用程序往往会生成错误或彻底失败,并显示“未运行D-BUS守护程序”。但这是我很少需要做的事情,所以几乎没什么影响。
如果您想使用dbus-send发送到远程计算机,则需要相应地设置DBUS_SESSION_BUS_ADDRESS,例如设置为以下内容:
export DBUS_SESSION_BUS_ADDRESS=tcp:host=localhost,bind=*,port=55556,family=ipv4

这个方法适用于想要发送到远程机器的系统总线,只要设置与目标上/etc/dbus-1/system.conf中TCP <listen>标签匹配即可。 (感谢Martin Vidner提供此提示。在我偶然发现他对this question的回答之前,我不相信dbus-send支持远程操作。) 更新: 如果您正在使用systemd(并且想要访问系统总线),您可能还需要在/lib/systemd/system/dbus.socket中添加一行ListenStream=55557,如下所示:
[Socket]
ListenStream=/var/run/dbus/system_bus_socket
ListenStream=55557  # <-- Add this line

更新2:感谢@altagir指出,最新版本的D-Bus将在支持的系统上启用AppArmor调解,因此您可能还需要将<apparmor mode="disabled"/>添加到session.conf/system.conf中,以使这些说明起作用。


1
谢谢,@Shorin。我更新了我的答案,包含了bindfamily键/值对。我认为它们并不是在所有情况下都需要的(在我的情况下它们不是必需的),而且这些值可能需要根据不同的系统进行调整,但是包含它们可能会使答案更完整,对其他人更有帮助。 :-) - evadeflow
1
顺便说一下,我在安卓设备上尝试了这个。它在重启时卡住了。需要刷机才能再次正常工作。 - alanjds
如果您在Microsoft的Windows子系统上运行dbus遇到了困难,这个答案实际上会有所帮助。 - baldrs
2
@TusharVazirani:您可以使用DBUS_SESSION_BUS_ADDRESS环境变量来指定远程目标,例如使用类似于DBUS_SESSION_BUS_ADDRESS=tcp:host=192.168.1.17,bind=*,port=55556,family=ipv4 ./my_python_app.py的命令运行它。(注意:正如我在答案中提到的那样,即使您连接的总线实际上是远程机器的_system_总线,只要它的system.conf配置为允许通过TCP连接,这也可以工作。) - evadeflow
非常感谢您的回答,使用Wireshark和Netstat,我能够看到我的Python应用程序能够连接到接口并调用,但是我收到了DBusException:org.freedesktop.DBus.Error.AccessDenied :( 请问有什么建议吗? - sayo9394
显示剩余3条评论

7
自从dbus 1.6.12(例如kubuntu 13.10)以后,除非您将其添加到dbus配置文件中(位于/etc/dbus-1/mybus.conf或需要远程访问的界面即system.d/my.interface.conf),否则您的连接也将被拒绝。
<apparmor mode="disabled"/>

更新:在尝试创建一个允许服务连接到自定义dbus-daemon的apparmor配置文件时,由于DBUS中的一个错误导致连接始终被拒绝... 因此,在使用tcp=...时我们必须禁用apparmor。该错误将在14.04中修复。
我在bugs.launchpad.net上开了一个错误报告,根据与Tyler Hicks的讨论
引文: AppArmor的中介代码仅能在UNIX域套接字上检查对等标签。当获取标签并且拒绝连接时,它很可能会发生错误。
注意: 除非dbus < 1.6.12不识别禁用标志(所以你需要为系统打包不同版本的mydaemon.conf),否则如果没有apparmor的话dbus-daemon启动将失败... 我现在在我的CMakeLists.txt中使用:
IF(EXISTS "/usr/sbin/apparmor_status")
  install(FILES dbus_daemon-apparmordisabled.conf RENAME dbus_daemon.conf DESTINATION /etc/dbus-1/ )
ELSE (EXISTS "/usr/sbin/apparmor_status")
   install(FILES dbus_daemon.conf DESTINATION /etc/dbus-1/ )
ENDIF(EXISTS "/usr/sbin/apparmor_status")

3

再次感谢@Shorin,另外需要注意的是 - 我不得不像这样做才能让我的工作正常运行:

<listen>tcp:host=localhost,bind=0.0.0.0,port=55884</listen>

请注意 bind=0.0.0.0,因为 bind=* 对我无效,而且我省略了 family = ipv4 部分。 我使用的是 Ubuntu 12.04。 我在远程机器上使用 netstat 确认 dbus 正在监听该端口,并从本地使用 telnet 确认该端口已打开。
netstat -plntu | grep 55884

tcp   0     0 0.0.0.0:55884    0.0.0.0:*     LISTEN    707/dbus-daemon

您需要看到类似于0 0.0.0.0:55884而不是0 127.0.0.1:55884


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接