¿Cómo leer la salida del monitor dbus?

20

Estoy jugando con dbus-monitor para tratar de entender cómo funciona dbus en el entorno Ubuntu. Tengo varias preguntas al respecto:

  1. ¿Me podría decir cómo leer lo siguiente correctamente? Entiendo la gran idea, pero no los detalles.

    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

    Entiendo que el primero es una señal, mientras que el segundo es un método. ¿ Destino significa que puede haber un receptor / ranura específica para una señal? ¿Qué es un miembro ? ¿Y los elementos de la lista siguen la señal de los argumentos pasados ​​en la señal? ¿Qué son el remitente y las publicaciones seriadas ?

  2. Noté algo sobre la relación entre el control de volumen y las notificaciones. Por lo que leí de la salida del monitor 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

    Parece que la notificación se activa por su método. Simplemente no entiendo por qué funciona de esta manera. En mi opinión, tendría más sentido si hubiera una señal emitida "notificación-audio-volumen-medio", mientras que la notificación escucharía esta señal y reaccionaría en consecuencia. Si el envío / recepción fuera público en lugar de privado, ¿no permitiría una mayor flexibilidad y eficiencia? Por ejemplo, si había una señal pública para "notificación-audio-volumen-medio" varias aplicaciones podrían escuchar esta señal (lo que permitiría la existencia de aplicaciones de notificación competidoras) y los desarrolladores solo tendrían que preocuparse por enviar señales, mientras que recoger y manejar una señal sería el negocio de la aplicación notificante (o cualquier otro programa que necesita esas señales).

  3. Soy nuevo en Dbus y quiero aprender más, ya que estoy trabajando con Dbus en Python, principalmente para desarrollar algunos applets. He visto el tutorial dbus-python y enseña cómo escuchar todas las señales (sin especificar la interfaz ni la ruta, etc.) Pero, ¿cómo rastrear los métodos cuando se los llama, como lo hace dbus-monitor?

Si tienes la paciencia para enseñar cómo funciona eso, eres bienvenido.

Benjamín
fuente

Respuestas:

24

Introducción a D-Bus

  • D-Bus proporciona medios para comunicarse entre servicios . Los servicios pueden ser anónimos (identificados únicamente por la dirección del autobús, como: 1.6), y pueden adquirir nombres conocidos , como org.freedesktop.Notificationso org.freedesktop.NetworkManager. El remitente y el destino que puede ver en los registros son servicios. "Destino nulo" significa difusión: entrega a todos los servicios.

  • Un servicio puede exportar uno o varios objetos al bus. Los objetos reciben rutas de objeto , como /org/freedesktop/NetworkManager/ActiveConnection/1o /org/ayatana/menu/DA00003. Las rutas de objetos usan la barra como separador, como las rutas del sistema de archivos.

  • Cada objeto puede soportar una o varias interfaces . Una interfaz no es más que un conjunto de métodos y señales, conocidos coloquialmente como miembros (muy similar a la interfaz OOP). Los métodos y las señales tienen firmas fijas. Los miembros siempre tienen espacios de nombres dentro de nombres de interfaz conocidos .

  • Una vez publicados, los nombres conocidos nunca cambian .

  • Cualquier servicio puede conectarse a las señales de otro servicio y llamar asincrónicamente sus métodos. Cualquier servicio puede emitir señales.

Señales

Ahora a sus preguntas específicas.

remitente de señal =: 1.1948 -> dest = (destino nulo) serial = 1829990 ruta = / org / ayatana / menu / DA00003; interfaz = org.ayatana.dbusmenu; member = ItemPropertyUpdated
int32 23
cadena "habilitada"
variante boolean true

Sí, tienes razón, esta es una señal. Es transmitido por el servicio :1.1948, y el objeto "auto" es /org/ayatana/menu/DA00003. La señal tiene un nombre ItemPropertyUpdatedque se define en la interfaz org.ayatana.dbusmenu(como org.ayatana.dbusmenu::ItemPropertyUpdateden C ++). El serial, supongo, es una especie de identificador único del evento en el bus.

Luego vemos los argumentos de la señal. De acuerdo con la documentación de la interfaz , el primer argumento int32 es la identificación de un elemento, la segunda cadena es el nombre de su propiedad y la tercera variante es el valor de la propiedad. Entonces, el /org/ayatana/menu/DA00003objeto nos está notificando que la identificación del artículo # 23 cambió su enabledpropiedad a verdadero.


Otro ejemplo de señales:

remitente de señal =: 1.1602 -> dest = (destino nulo) serial = 20408 ruta = / im / pidgin / purple / PurpleObject; interfaz = im.pidgin.purple.PurpleInterface; miembro = SendingChatMsg
   int32 47893
   cadena "prueba"
   uint32 1
remitente de señal =: 1.1602 -> dest = (destino nulo) serial = 20409 ruta = / im / pidgin / purple / PurpleObject; interfaz = im.pidgin.purple.PurpleInterface; miembro = IrcSendingText
   int32 64170
   cadena "PRIVMSG #chat: prueba

Envié un mensaje de texto "prueba" usando Pidgin a un canal IRC, y /im/pidgin/purple/PurpleObjectemití dos señales debajo de la im.pidgin.purple.PurpleInterfaceinterfaz: primero una general SendingChatMsg, luego una más específica IrcSendingText.

Métodos

Ahora métodos. Los métodos son una forma de pedirle a los objetos de D-Bus que hagan algo o realizar alguna consulta y devolver datos. Son bastante similares a los métodos clásicos de OOP, excepto que los métodos D-Bus se llaman de forma asincrónica.

Llamemos a un método D-Bus mediante programación.

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

Tenga en cuenta los argumentos, especialmente el nombre del icono. En su ejemplo, "notification-audio-volume-medium"fue el ícono del altavoz de volumen de potencia media.

Servicios personalizados

Es absolutamente posible ejecutar sus propios servicios D-Bus, exportar sus propios objetos D-Bus y definir sus propias interfaces D-Bus con sus propios métodos y señales. Todo esto se puede hacer en Python con bastante facilidad una vez que comprenda el concepto general y lea la dbusdocumentación del módulo.:)

ulidtko
fuente
La discusión es bienvenida, aunque podría no estar disponible en un día o dos.
ulidtko
Gracias :) Esto aclara mucho. Es de alguna manera divertido que los remitentes puedan ser anónimos, cuando uso DFeet, hay un nombre de proceso correspondiente a cada remitente, pero eso no se refleja en la salida del monitor dbus. ¿Se pueden rastrear los procesos? Ahora con Python he visto que puedo enviar señales o proporcionar métodos o activar los métodos de otras partes. ¿También es posible interceptar métodos? ¿Supongamos que quiero ver si el programa A activa el método Dbus de B y hago algo con él?
Benjamin
Acerca de las notificaciones: el aviso-osd se activa pasivamente por otras aplicaciones, en lugar de buscar señales activamente. ¿No es poco práctico o no entiendo algo sobre Dbus? Quiero hacer una aplicación que reemplace el osmos de notificación y recopile notificaciones en una especie de bandeja de entrada. ¿Puedo interceptar notificaciones escuchando señales entonces?
Benjamin
@Benjamin, bueno, cuando quieres interceptar llamadas de método dirigidas a servicios extranjeros, es muy probable que pienses en un diseño roto. Lo que debe hacer para reemplazar notify-osd es escribir un programa que brinde el org.freedesktop.Notificationsservicio. De esta manera, todas las llamadas de método a este servicio serán manejadas por su código.
ulidtko
¿Qué es el "yo" obejct?
kawing-chiu
10

También estaba buscando una solución para recopilar las notificaciones de escritorio a través de dbus con un script de Python. Esta pregunta fue la más cercana que tuve con googlear, pero escribir un reemplazo para notify-osd parecía una exageración :)

Mirando las fuentes de applet de notificaciones recientes, recibí algunos consejos sobre cómo monitorear los mensajes dbus y aquí está la implementación de Python que se me ocurrió:

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

Espero que esto ayude a alguien, ya que parece que no hay muchos ejemplos simples de Python relacionados con el monitoreo de los mensajes dbus.

Keto
fuente
1
¡Seguramente me ayudó! ¡Muchas gracias! Algunas sugerencias para usted: "type = 'method_call'" no es necesario, ya que las notificaciones solo usan llamadas a métodos. No hay señales en la especificación. Además, "member = 'Notify'" tampoco es necesario, ya que ya está filtrando eso en su función (y, como dijo correctamente, no puede evitarlo debido al primer NameAquiredmensaje)
MestreLion