Comment lire la sortie du moniteur dbus?

20

Je joue avec dbus-monitor pour essayer de comprendre comment dbus fonctionne dans l'environnement Ubuntu. J'ai plusieurs questions à ce sujet:

  1. Pourriez-vous me faire savoir comment lire correctement ce qui suit? Je comprends la grande idée, mais pas les détails.

    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
    

    Je comprends que le premier est un signal tandis que le second est une méthode. La destination signifie- t-elle qu'il peut y avoir un récepteur / emplacement spécifique pour un signal? Qu'est-ce qu'un membre ? Et les éléments de la liste suivant le signal sont-ils les arguments passés dans le signal? Que sont l' expéditeur et les publications en série ?

  2. J'ai remarqué quelque chose sur la relation entre le contrôle du volume et les notifications. D'après ce que j'ai lu sur la sortie du moniteur dbus

    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
    

    Il semble que la notification soit déclenchée par sa méthode. Je ne comprends tout simplement pas pourquoi cela fonctionne de cette façon. À mon avis, il serait plus logique s'il y avait un signal émis "notification-audio-volume-medium" alors que la notification écouterait ce signal et réagirait en conséquence. Si l'envoi / la réception serait public plutôt que privé, ne permettrait-il pas plus de flexibilité et d'efficacité? Par exemple, s'il y avait un signal public pour "notification-audio-volume-medium" alors plusieurs applications pourraient écouter ce signal (ce qui permettrait à des applications de notification concurrentes de voir le jour) et les développeurs n'auraient qu'à se préoccuper de l'envoi de signaux, tandis que la collecte et la gestion d'un signal seraient l'affaire de l'application notifiante (ou de toute autre programme qui a besoin de ces signaux).

  3. Je suis juste nouveau sur Dbus et je veux en savoir plus car je travaille avec Dbus sur Python, principalement pour développer des applets. J'ai vu le tutoriel dbus-python et il enseigne comment écouter tous les signaux (en ne spécifiant ni interface ni chemin, etc.) Mais comment suivre les méthodes lorsqu'elles sont appelées, comme le fait dbus-monitor?

Si vous avez la patience d'enseigner comment cela fonctionne, vous êtes les bienvenus.

Benjoin
la source

Réponses:

24

Présentation de D-Bus

  • D-Bus fournit des moyens de communication entre les services . Les services peuvent être anonymes (identifiés uniquement par l'adresse du bus, comme: 1.6), et les services peuvent acquérir des noms connus , comme org.freedesktop.Notificationsou org.freedesktop.NetworkManager. L'expéditeur et la destination que vous pouvez voir dans les journaux sont des services. "Destination nulle" signifie diffusion: livraison à tous les services.

  • Un service peut exporter un ou plusieurs objets vers le bus. Les objets reçoivent des chemins d'objet , comme /org/freedesktop/NetworkManager/ActiveConnection/1ou /org/ayatana/menu/DA00003. Les chemins d'accès aux objets utilisent une barre oblique comme séparateur, comme les chemins d'accès aux systèmes de fichiers.

  • Chaque objet peut prendre en charge une ou plusieurs interfaces . Une interface n'est rien de plus qu'un ensemble de méthodes et de signaux, familièrement appelés membres (très similaires à l'interface OOP). Les méthodes et les signaux ont des signatures fixes. Les membres sont toujours à espace de noms dans des noms d' interface bien connus .

  • Une fois publiés, les noms connus ne changent jamais .

  • Tout service peut se connecter aux signaux d'un autre service et appeler de manière asynchrone ses méthodes. Tout service peut émettre des signaux.

Signaux

Passons maintenant à vos questions spécifiques.

expéditeur du signal =: 1.1948 -> dest = (destination nulle) serial = 1829990 path = / org / ayatana / menu / DA00003; interface = org.ayatana.dbusmenu; membre = ItemPropertyUpdated
int32 23
chaîne "activé"
variante booléenne true

Oui, vous avez raison, c'est un signal. Il est diffusé par service :1.1948, et l'objet "soi" l'est /org/ayatana/menu/DA00003. Le signal a un nom ItemPropertyUpdatedqui est défini dans l'interface org.ayatana.dbusmenu(comme org.ayatana.dbusmenu::ItemPropertyUpdateden C ++). La série, je suppose, est une sorte d'identifiant unique de l'événement sur le bus.

Ensuite, nous voyons les arguments de signal. Selon la documentation de l' interface , le premier argument int32 est l'ID d'un élément, la deuxième chaîne est le nom de sa propriété et la troisième variante est la valeur de la propriété. Ainsi, l' /org/ayatana/menu/DA00003objet nous informe que l'ID d'article # 23 a changé sa enabledpropriété en true.


Un autre exemple sur les signaux:

expéditeur du signal =: 1.1602 -> dest = (destination nulle) serial = 20408 path = / im / pidgin / purple / PurpleObject; interface = im.pidgin.purple.PurpleInterface; membre = SendingChatMsg
   int32 47893
   chaîne "test"
   uint32 1
expéditeur du signal =: 1.1602 -> dest = (destination nulle) serial = 20409 path = / im / pidgin / purple / PurpleObject; interface = im.pidgin.purple.PurpleInterface; membre = IrcSendingText
   int32 64170
   chaîne "PRIVMSG #chat: test

J'ai envoyé un message texte "test" en utilisant Pidgin à un canal IRC, et /im/pidgin/purple/PurpleObjectémis deux signaux sous l' im.pidgin.purple.PurpleInterfaceinterface: d'abord un général SendingChatMsg, puis un plus spécifique IrcSendingText.

Les méthodes

Maintenant les méthodes. Les méthodes sont un moyen de demander aux objets D-Bus de faire quelque chose ou d'exécuter des requêtes et de renvoyer des données. Ils sont assez similaires aux méthodes OOP classiques, sauf que les méthodes D-Bus sont appelées de manière asynchrone.

Appelons une méthode D-Bus par programmation.

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

Notez les arguments, en particulier le nom de l'icône. Dans votre exemple "notification-audio-volume-medium", l'icône d'un haut-parleur de volume moyen.

Services personnalisés

Il est absolument possible d'exécuter vos propres services D-Bus, d'exporter vos propres objets D-Bus et de définir vos propres interfaces D-Bus avec vos propres méthodes et signaux. Tout cela peut être fait en Python assez facilement une fois que vous avez saisi le concept global et lu la dbusdocumentation du module.:)

ulidtko
la source
La discussion est la bienvenue, même si je ne serai peut-être pas disponible dans un jour ou deux.
ulidtko
Merci :) Cela clarifie beaucoup. C'est assez drôle que les expéditeurs puissent être anonymes, lorsque j'utilise DFeet, il y a un nom de processus correspondant à chaque expéditeur, mais cela ne se reflète pas dans la sortie du moniteur dbus. Peut-on retracer les processus? Maintenant, avec Python, j'ai vu que je pouvais envoyer des signaux ou fournir des méthodes ou déclencher les méthodes d'autres parties. Est-il également possible d'intercepter des méthodes? Supposons que je veuille voir si le programme A déclenche la méthode Dbus de B et en faire quelque chose?
Benjamin
À propos des notifications: la notification-osd est déclenchée passivement par d'autres applications, plutôt que de rechercher activement des signaux. N'est-ce pas impossible ou est-ce que je comprends mal quelque chose à propos de Dbus? Je veux créer une application qui remplacerait le notifier-osd et collecterait les notifications dans une sorte de boîte de réception. Puis-je intercepter des notifications en écoutant des signaux alors?
Benjamin
@Benjamin, eh bien, lorsque vous voulez intercepter des appels de méthode dirigés vers des services étrangers, vous pensez très probablement à une conception défectueuse. Ce que vous devez faire pour remplacer notify-osd est d'écrire un programme qui fournit le org.freedesktop.Notificationsservice. De cette façon, tous les appels de méthode à ce service seront traités par votre code.
ulidtko
Qu'est-ce que le «soi» obejct?
kawing-chiu
10

Je cherchais également une solution pour collecter les notifications de bureau via dbus avec un script python. Cette question était la plus proche que j'ai eue avec Google, mais écrire un remplacement pour notify-osd semblait être une exagération :)

En regardant les sources d'applet de notifications récentes , j'ai eu quelques astuces pour surveiller les messages dbus et voici l'implémentation de python que j'ai trouvée:

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

J'espère que cela aide quelqu'un, car il semble qu'il n'y ait pas beaucoup d'exemples simples de python liés à la surveillance des messages dbus.

Keto
la source
1
Cela m'a sûrement aidé! Merci beaucoup! Quelques suggestions pour vous: "type = 'method_call'" n'est pas nécessaire, car les notifications n'utilisent que des appels de méthode. Aucun signal dans la spécification. En outre, "member = 'Notify'" n'est pas non plus nécessaire, car vous filtrez déjà cela dans votre fonction (et, comme vous l'avez dit correctement, vous ne pouvez pas l'éviter en raison du premier NameAquiredmessage)
MestreLion