How to read dbus-monitor output?

18,919

Solution 1

D-Bus introduction

  • D-Bus provides means for communicating between services. Services can be anonymous (identified solely by bus address, like :1.6), and services can aquire well-known names, like org.freedesktop.Notifications or org.freedesktop.NetworkManager. The sender and destination which you can see in the logs are services. "Null destination" means broadcast: delivery to all services.

  • A service can export one or several objects to the bus. Objects are given object paths, like /org/freedesktop/NetworkManager/ActiveConnection/1 or /org/ayatana/menu/DA00003. Object paths use slash as separator, like filesystem paths.

  • Each object can support one or several interfaces. An interface is nothing more than a set of methods and signals, coloquially known as members (very similar to OOP interface). Methods and signals have fixed signatures. Members are always namespaced within well-known interface names.

  • Once published, well-known names never change.

  • Any service can connect to another service's signals and asynchronously call its methods. Any service can emit signals.

Signals

Now to your specific questions.

signal sender=:1.1948 -> dest=(null destination) serial=1829990 path=/org/ayatana/menu/DA00003; interface=org.ayatana.dbusmenu; member=ItemPropertyUpdated
int32 23
string "enabled"
variant boolean true

Yes, you're right, this is a signal. It's broadcasted by service :1.1948, and "self" object is /org/ayatana/menu/DA00003. The signal has name ItemPropertyUpdated which is defined in the interface org.ayatana.dbusmenu (like org.ayatana.dbusmenu::ItemPropertyUpdated in C++). The serial, I guess, is a kind of unique identifier of the event on the bus.

Then we see the signal arguments. According to the interface documentation, the first int32 argument is an item's id, second string is its property name, and the third variant is the property value. So, the /org/ayatana/menu/DA00003 object is notifying us that the item id #23 changed its enabled property to true.


Another example on signals:

signal sender=:1.1602 -> dest=(null destination) serial=20408 path=/im/pidgin/purple/PurpleObject; interface=im.pidgin.purple.PurpleInterface; member=SendingChatMsg
   int32 47893
   string "test"
   uint32 1
signal sender=:1.1602 -> dest=(null destination) serial=20409 path=/im/pidgin/purple/PurpleObject; interface=im.pidgin.purple.PurpleInterface; member=IrcSendingText
   int32 64170
   string "PRIVMSG #chat :test

I sent a text message "test" using Pidgin to an IRC channel, and /im/pidgin/purple/PurpleObject emitted two signals under the im.pidgin.purple.PurpleInterface interface: first a general SendingChatMsg, then a more specific IrcSendingText.

Methods

Now methods. Methods are a way to ask D-Bus objects to do something, or to perform some query and return data. They are quite similar to classic OOP methods, except that D-Bus methods are called asynchronously.

Let's call a D-Bus method programmatically.

import dbus, dbus.proxies

#-- connect to the session bus (as opposed to the system bus)
session = dbus.SessionBus()

#-- create proxy object of D-Bus object
obj_proxy = dbus.proxies.ProxyObject(conn=session,
         bus_name="org.freedesktop.Notifications",     #-- name of the service we are retrieving object from
         object_path="/org/freedesktop/Notifications") #-- the object path

#-- create proxy object of the D-Bus object wrapped into specific interface
intf_proxy = dbus.proxies.Interface(obj_proxy, "org.freedesktop.Notifications")

#-- lastly, create proxy object of the D-Bus method
method_proxy = intf_proxy.get_dbus_method("Notify")

#-- ... and call the method
method_proxy("test from python",
             dbus.UInt32(0),
             "bluetooth",     #-- icon name
             "Notification summary",
             "Here goes notification body",
             [], {},
             5) #-- timeout

Note the arguments, especially the icon name. In your example "notification-audio-volume-medium" was the icon of medium-powered volume speaker.

Custom services

It is absolutely possible to run your own D-Bus services, export your own D-Bus objects and define your own D-Bus interfaces with your own methods and signals. This all can be done in Python pretty easily once you grasp the overall concept and read the dbus module documentation. :)

Solution 2

I was also looking for solution to collect the desktop notifications through dbus with a python script. This question was the closest I got with googling, but writing a replacement for notify-osd seemed like an overkill :)

Looking at the recent-notifications applet sources I got some hints how to monitor the dbus messages and here is the python implementation I came up with:

import gtk
import dbus
from dbus.mainloop.glib import DBusGMainLoop

def filter_cb(bus, message):
    # the NameAcquired message comes through before match string gets applied
    if message.get_member() != "Notify":
        return
    args = message.get_args_list()
    # args are
    # (app_name, notification_id, icon, summary, body, actions, hints, timeout)
    print("Notification from app '%s'" % args[0])
    print("Summary: %s" % args[3])
    print("Body: %s", args[4])


DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
bus.add_match_string(
    "type='method_call',interface='org.freedesktop.Notifications',member='Notify'")
bus.add_message_filter(filter_cb)
gtk.main()

Hope this helps someone, as it seems that there isn't many simple python examples related to monitoring the dbus messages.

Share:
18,919

Related videos on Youtube

neydroydrec
Author by

neydroydrec

South Asianist, polyglot, amateur programmer, urban farming entrepreneur, development analyst, philosopher, control freak, coffee addict, etc. etc.

Updated on September 18, 2022

Comments

  • neydroydrec
    neydroydrec over 1 year

    I'm playing around with dbus-monitor to try and understand how dbus is working in Ubuntu environment. I have several questions in this regard:

    1. Could you please let me know how to read the following properly? I understand the big idea, but not the details.

      signal sender=:1.1948 -> dest=(null destination) serial=1829990 path=/org/ayatana/menu/DA00003; interface=org.ayatana.dbusmenu; member=ItemPropertyUpdated
      int32 23
      string "enabled"
      variant boolean true
      method call sender=:1.6 -> dest=org.freedesktop.Notifications serial=1399 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications;
      member=GetCapabilities
      

      I get that the first one is a signal whereas the second is a method. Does destination mean there can be a specific receiver/slot for a signal? What's a member? And are the items of the list following the signal the arguments passed in the signal? What are sender and serials?

    2. I noticed something about the relationship between volume-control and notifications. From what I read from the dbus-monitor output

      method call sender=:1.6 -> dest=org.freedesktop.Notifications serial=1400 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications; member=Notify
      string "gnome-settings-daemon"
      uint32 0
      string "notification-audio-volume-medium"
      string " "
      string ""
      array [
      ]
      array [
      dict entry(
      string "value"
      variant int32 38
      )
      dict entry(
      string "x-canonical-private-synchronous"
      variant string "volume"
      )
      ]
      int32 -1
      

      It seems that the notification is triggered by its method. I just don't really understand why it works this way. In my view it would make more sense if there was a signal emitted "notification-audio-volume-medium" while the notification would listen for this signal and react accordingly. If the sending / receiving would be public rather than private, wouldn't it allow for more flexibility and efficiency? For instance if there was a public signal for "notification-audio-volume-medium" then several applications could listen for this signal (which would allow for competing notification applications to come to existence) and developers would just have to be concerned with sending signals, while picking up and handling a signal would be the notifying application's business (or any other program that needs those signals).

    3. I'm just new to Dbus and want to learn more as I am working with Dbus on Python, mainly to develop some applets. I've seen the dbus-python tutorial and it teaches how to listen to all signals (by specifying neither interface nor path etc.) But how to track methods when they are called, like dbus-monitor does?

    If you have the patience to teach how that works, you're welcome.

  • ulidtko
    ulidtko about 13 years
    Discussion is welcome, though I might not be available in a day or two.
  • neydroydrec
    neydroydrec about 13 years
    Thanks :) This clarifies a lot. It's somehow funny that senders can be anonymous, when i use DFeet, there is a process name corresponding to each sender, but that doesn't reflect in dbus-monitor output. Can the processes be traced? Now with Python i've seen i can send signals or provide methods or trigger other parties' methods. Is it also possible to intercept methods? Suppose i want to see if program A triggers B's Dbus method and do something with it?
  • neydroydrec
    neydroydrec about 13 years
    About Notifications: the notify-osd is passively triggered by other applications, rather than actively looking for signals. Isn't that impractical or do i misunderstand something about Dbus? I want to make an application that would replace the notify-osd and collect notifications in a sort of inbox. Can i intercept notifications by listening to signals then?
  • ulidtko
    ulidtko about 13 years
    @Benjamin, well, when you want to intercept method calls directed to foreign services, you very likely think of a broken design. What you should do to replace notify-osd is to write a program which provides the org.freedesktop.Notifications service. This way all the method calls to this service will be handled by your code.
  • MestreLion
    MestreLion over 11 years
    It surely helped me! Thank you very much! A few suggestions for you: "type='method_call'" is not necessacy, since notifications use only method calls. No signals in the spec. Also, "member='Notify'" is also not necessary, since you're already filtering that out in your function (and, as you correctly said, you cannot avoid that due to first NameAquired message)
  • kawing-chiu
    kawing-chiu over 6 years
    What is the "self" obejct?
  • ulidtko
    ulidtko over 6 years
    @kawing-chiu what do you mean? I know the convention of self-named parameters in python func... methods, but I don't understand the context of your question.
  • kawing-chiu
    kawing-chiu over 6 years
    @ulidtko In your answer, there is a line ..., and "self" object is /org/ayatana/menu/DA00003. I was asking what does self object mean in that line.
  • ulidtko
    ulidtko over 6 years
    @kawing-chiu ah... sorry, it's been a while since I wrote this. The point is that D-Bus apparently tries an "object-oriented" API modelling. In case of receiving a signal, we can't really say "emitter" or "sender" about the /org/ayatana/menu/DA00003 object -- because the sender is the service, :1.1948. So if we're to keep within traditional OO nomenclature, we might want to refer to that /org... object as self. If you know how this in JavaScript works, this is very similar (pretty much the same deal).