Mucking around with D-Bus and XChat

XChat 2.6.0 adds a D-Bus plugin created by Zdra. This D-Bus plugin allows you to control XChat from another script. Not knowing anything about D-Bus I’ve been playing around with it, learning more along the way.

D-Bus supports two buses. One is a systemwide message bus. The other is the per-user-login-session bus. The XChat plugin uses the session bus. When dbus-launch is installed and supported by your distro every program started within an X session will communicate over the same bus. If you would log in again dbus-launch will start another message bus.

Session specific message buses are logical to have; otherwise logging in twice wouldn’t be supported or give strange results. A program could use D-Bus to only have one process per session. Starting the program another time would actually send a message to the existing program to open a new window (Mozilla, Gnome-terminal and Evince are examples of this idea; although none seem to use D-Bus). One drawback of a session bus is connecting to that bus from cron. As crond is not started by your X session, scripts run by cron cannot use D-Bus to connect to XChat. I thought of two possible ways to fix that:

  1. Let the D-Bus plugin use systemwide message bus
    This is the easiest way to fix it. However, it is a big security risk. The D-Bus plugin allows you to send ‘/exec some_command’ to XChat, not something I want other users to do. I could limit the users using D-Bus policies, but I’ll probably accidently wipe the D-Bus configuration once in a while anyway.
  2. Hacking a way to the session bus used by XChat
    A session message bus is determined by two environment variables, DBUS_SESSION_BUS_ADDRESS and DBUS_SESSION_BUS_PID. Letting an cron script use XChats session bus is as easy as setting the correct environment variables.

The environment variables of every process is stored under Linux in /proc/$PID/environ. This file contains the environment variables separated using an ASCII 0. To determine the PID of XChat I use the command: pgrep -u $USER -o xchat. Implemented as a Python script:

#!/usr/bin/python

import sys
import os

DBUS_ENV1 = "DBUS_SESSION_BUS_PID"
DBUS_ENV2 = "DBUS_SESSION_BUS_ADDRESS"

if DBUS_ENV1 not in os.environ or DBUS_ENV2 not in os.environ:
    # Steal required environment variables from XChat process
    import popen2
    p = popen2.Popen4(['pgrep', '-u', os.getlogin(), '-o', 'xchat'])
    if p.wait() != 0:
        print "Could not retrieve DBUS info, exiting"
        sys.exit(1)

    pid = p.fromchild.readline().strip()

    xchatenv = dict([i.split("=", 1) for i in open("/proc/%s/environ" % pid).read().split("00") if "=" in i])
    if DBUS_ENV1 in xchatenv and DBUS_ENV2 in xchatenv:
        os.environ[DBUS_ENV1] = xchatenv[DBUS_ENV1]
        os.environ[DBUS_ENV2] = xchatenv[DBUS_ENV2]


import dbus

bus = dbus.SessionBus()
object = bus.get_object("org.xchat.service", "/org/xchat/RemoteObject")
xchat = dbus.Interface(object, "org.xchat.interface")

version = xchat.GetInfo("version")
print version

The D-Bus plugin is lacking some commands, so above script is not interesting. Eventually I want to check if Mrkbot is still in #bugs (stupid bot is away most of the time). If the script cannot find Mrkbot the script should send an email to restart it. Still not interesting (and it could be made in various other ways), but I have a lot of fun creating it.