Wie lese ich die Ausgabe des dbus-Monitors?

20

Ich spiele mit dbus-monitor herum, um zu verstehen, wie dbus in der Ubuntu-Umgebung funktioniert. Ich habe diesbezüglich mehrere Fragen:

  1. Können Sie mir bitte mitteilen, wie ich Folgendes richtig lesen soll? Ich verstehe die große Idee, aber nicht die 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

    Ich verstehe, dass der erste ein Signal ist, während der zweite eine Methode ist. Bedeutet das Ziel , dass es einen bestimmten Empfänger / Steckplatz für ein Signal geben kann? Was ist ein Mitglied ? Und folgen die Elemente der Liste nach dem Signal den im Signal übergebenen Argumenten? Was sind Absender und Serien ?

  2. Mir ist etwas über die Beziehung zwischen Lautstärkeregelung und Benachrichtigungen aufgefallen. Nach dem, was ich vom dbus-monitor-Ausgang gelesen habe

    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

    Es scheint, dass die Benachrichtigung durch seine Methode ausgelöst wird. Ich verstehe nur nicht wirklich, warum es so funktioniert. Meiner Meinung nach wäre es sinnvoller, wenn ein Signal "Benachrichtigung-Audio-Lautstärke-Medium" gesendet würde, während die Benachrichtigung auf dieses Signal warten und entsprechend reagieren würde. Wenn das Senden / Empfangen eher öffentlich als privat wäre, würde es dann nicht mehr Flexibilität und Effizienz ermöglichen? Zum Beispiel, wenn es ein öffentliches Signal für "Benachrichtigung-Audio-Lautstärke-Medium" gab Dann könnten mehrere Anwendungen auf dieses Signal warten (wodurch konkurrierende Benachrichtigungsanwendungen entstehen könnten), und Entwickler müssten sich nur mit dem Senden von Signalen befassen, während das Aufnehmen und Verarbeiten eines Signals das Geschäft der benachrichtigenden Anwendung (oder eines anderen) sein würde Programm, das diese Signale benötigt).

  3. Ich bin gerade neu in Dbus und möchte mehr erfahren, während ich mit Dbus an Python arbeite, hauptsächlich, um einige Applets zu entwickeln. Ich habe das dbus-python-Tutorial gesehen und es zeigt, wie man alle Signale abhört (indem man weder Schnittstelle noch Pfad usw. angibt). Aber wie man Methoden verfolgt, wenn sie aufgerufen werden, wie es dbus-monitor tut?

Wenn Sie die Geduld haben, zu lehren, wie das funktioniert, sind Sie herzlich willkommen.

Benjamin
quelle

Antworten:

24

D-Bus Einführung

  • D-Bus bietet Mittel zur Kommunikation zwischen Diensten . Dienste können anonym sein (nur durch die Busadresse identifiziert, wie zum Beispiel: 1.6), und Dienste können bekannte Namen wie org.freedesktop.Notificationsoder erhalten org.freedesktop.NetworkManager. Der Absender und das Ziel, die Sie in den Protokollen sehen, sind Dienste. "Nullziel" bedeutet Rundsendung: Zustellung an alle Dienste.

  • Ein Service kann ein oder mehrere Objekte auf den Bus exportieren . Objekte erhalten Objektpfade wie /org/freedesktop/NetworkManager/ActiveConnection/1oder /org/ayatana/menu/DA00003. Objektpfade verwenden wie Dateisystempfade einen Schrägstrich als Trennzeichen.

  • Jedes Objekt kann eine oder mehrere Schnittstellen unterstützen . Eine Schnittstelle ist nichts anderes als eine Reihe von Methoden und Signalen, die umgangssprachlich als Member bezeichnet werden (sehr ähnlich der OOP-Schnittstelle). Methoden und Signale haben feste Signaturen. Mitglieder werden immer in bekannten Schnittstellennamen mit einem Namensraum versehen.

  • Einmal veröffentlicht, ändern sich bekannte Namen nie mehr .

  • Jeder Dienst kann eine Verbindung zu den Signalen eines anderen Dienstes herstellen und dessen Methoden asynchron aufrufen. Jeder Dienst kann Signale aussenden.

Signale

Nun zu Ihren spezifischen Fragen.

Signalsender =: 1.1948 -> Ziel = (Nullziel) Seriennummer = 1829990 Pfad = / org / ayatana / menu / DA00003; interface = org.ayatana.dbusmenu; member = ItemPropertyUpdated
int32 23
Zeichenfolge "aktiviert"
variant boolean true

Ja, du hast recht, das ist ein Signal. Es wird vom Dienst ausgestrahlt :1.1948, und "Selbst" -Objekt ist /org/ayatana/menu/DA00003. Das Signal hat einen Namen, ItemPropertyUpdatedder in der Schnittstelle definiert ist org.ayatana.dbusmenu(wie org.ayatana.dbusmenu::ItemPropertyUpdatedin C ++). Die Seriennummer ist eine Art eindeutige Kennung des Ereignisses auf dem Bus.

Dann sehen wir die Signalargumente. Gemäß der Schnittstellendokumentation ist das erste int32-Argument die ID eines Elements, die zweite Zeichenfolge der Eigenschaftsname und die dritte Variante der Eigenschaftswert. Das /org/ayatana/menu/DA00003Objekt benachrichtigt uns also, dass die Artikel-ID # 23 ihre enabledEigenschaft auf true geändert hat .


Ein weiteres Beispiel für Signale:

Signalsender =: 1.1602 -> Ziel = (Nullziel) serial = 20408 Pfad = / im / pidgin / purple / PurpleObject; interface = im.pidgin.purple.PurpleInterface; member = SendingChatMsg
   int32 47893
   Zeichenfolge "Test"
   uint32 1
Signalsender =: 1.1602 -> Ziel = (Nullziel) serial = 20409 Pfad = / im / pidgin / purple / PurpleObject; interface = im.pidgin.purple.PurpleInterface; member = IrcSendingText
   int32 64170
   Zeichenfolge "PRIVMSG #chat: test

Ich habe eine SMS "test" mit Pidgin an einen IRC-Kanal gesendet und /im/pidgin/purple/PurpleObjectzwei Signale unter der im.pidgin.purple.PurpleInterfaceSchnittstelle ausgesendet : erst ein allgemeines SendingChatMsg, dann ein spezifisches IrcSendingText.

Methoden

Jetzt Methoden. Methoden sind eine Möglichkeit, D-Bus-Objekte aufzufordern, etwas zu tun oder Daten abzufragen und zurückzugeben. Sie sind den klassischen OOP-Methoden ziemlich ähnlich, außer dass D-Bus-Methoden asynchron aufgerufen werden.

Rufen wir eine D-Bus-Methode programmgesteuert auf.

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

Beachten Sie die Argumente, insbesondere den Symbolnamen. In Ihrem Beispiel "notification-audio-volume-medium"war das Symbol für Lautsprecher mit mittlerer Lautstärke.

Kundendienste

Es ist durchaus möglich, eigene D-Bus-Dienste zu betreiben, eigene D-Bus-Objekte zu exportieren und eigene D-Bus-Schnittstellen mit eigenen Methoden und Signalen zu definieren. Dies alles ist in Python ziemlich einfach möglich, wenn Sie das Gesamtkonzept verstanden und die dbusModuldokumentation gelesen haben .:)

ulidtko
quelle
Diskussionen sind willkommen, obwohl ich in ein oder zwei Tagen möglicherweise nicht erreichbar bin.
ulidtko
Danke :) Das macht vieles klar. Es ist irgendwie komisch, dass Absender anonym sein können, wenn ich DFeet verwende, gibt es einen Prozessnamen, der jedem Absender entspricht, der sich jedoch nicht auf die Ausgabe von dbus-monitor auswirkt. Können die Prozesse nachvollzogen werden? Mit Python kann ich nun Signale senden, Methoden bereitstellen oder Methoden anderer Parteien auslösen. Ist es auch möglich, Methoden abzufangen? Angenommen, ich möchte sehen, ob Programm A die Dbus-Methode von B auslöst, und etwas damit tun?
Benjamin
Über Benachrichtigungen: Der Benachrichtigungs-OSD wird passiv von anderen Anwendungen ausgelöst, anstatt aktiv nach Signalen zu suchen. Ist das nicht unpraktisch oder verstehe ich etwas über Dbus falsch? Ich möchte eine Anwendung erstellen, die den notify-osd ersetzt und Benachrichtigungen in einer Art Posteingang sammelt. Kann ich Benachrichtigungen abfangen, indem ich Signale abhöre?
Benjamin
@Benjamin, na ja, wenn Sie Methodenaufrufe abfangen möchten, die an ausländische Dienste gerichtet sind, denken Sie höchstwahrscheinlich an ein kaputtes Design. Was Sie tun sollten, um notify-osd zu ersetzen, ist ein Programm zu schreiben, das den Dienst bereitstelltorg.freedesktop.Notifications . Auf diese Weise werden alle Methodenaufrufe für diesen Dienst von Ihrem Code verarbeitet.
ulidtko
Was ist das "Selbst" -Objekt?
Kawing-Chiu
10

Ich suchte auch nach einer Lösung, um die Desktop-Benachrichtigungen über dbus mit einem Python-Skript zu sammeln. Diese Frage kam mir beim Googeln am nächsten, aber ein Ersatz für notify-osd zu schreiben schien mir ein Overkill zu sein :)

Wenn ich mir die Applet-Quellen für die letzten Benachrichtigungen anschaue, bekomme ich einige Hinweise zum Überwachen der dbus-Nachrichten und hier ist die Python-Implementierung, die ich mir ausgedacht habe:

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()

Ich hoffe, dies hilft jemandem, da es anscheinend nicht viele einfache Python-Beispiele für die Überwachung der dbus-Nachrichten gibt.

Keto
quelle
1
Es hat mir sicherlich geholfen! Vielen Dank! Ein paar Vorschläge für Sie: "type = 'method_call'" ist nicht erforderlich, da Benachrichtigungen nur Methodenaufrufe verwenden. Keine Signale in der Spezifikation. Außerdem ist "member = 'Benachrichtigen'" ebenfalls nicht erforderlich, da Sie dies bereits in Ihrer Funktion herausfiltern (und Sie können dies, wie Sie richtig sagten, aufgrund der ersten NameAquiredNachricht nicht vermeiden )
MestreLion,