Como ler a saída dbus-monitor?

20

Estou brincando com o dbus-monitor para tentar entender como o dbus está funcionando no ambiente Ubuntu. Eu tenho várias perguntas a esse respeito:

  1. Poderia me informar como ler o seguinte corretamente? Eu entendo a grande idéia, mas não os detalhes.

    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

    Entendo que o primeiro é um sinal, enquanto o segundo é um método. O destino significa que pode haver um receptor / slot específico para um sinal? O que é um membro ? E os itens da lista seguem o sinal que os argumentos passaram no sinal? O que são remetentes e seriados ?

  2. Notei algo sobre a relação entre controle de volume e notificações. Pelo que li da saída do 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 a notificação é acionada por seu método. Eu realmente não entendo por que funciona dessa maneira. Na minha opinião, faria mais sentido se houvesse um sinal emitido "notification-audio-volume-medium" enquanto a notificação ouviria esse sinal e reagiria de acordo. Se o envio / recebimento fosse público e não privado, não permitiria mais flexibilidade e eficiência? Por exemplo, se houver um sinal público para "notification-audio-volume-medium" então vários aplicativos poderiam ouvir esse sinal (o que permitiria a existência de aplicativos de notificação concorrentes) e os desenvolvedores precisariam se preocupar com o envio de sinais, enquanto capturar e manipular um sinal seria o negócio do aplicativo de notificação (ou qualquer outro programa que precisa desses sinais).

  3. Sou novato no Dbus e quero aprender mais enquanto trabalho com o Dbus no Python, principalmente para desenvolver alguns applets. Eu já vi o tutorial do dbus-python e ensina como ouvir todos os sinais (não especificando interface nem caminho etc.). Mas como rastrear métodos quando eles são chamados, como o dbus-monitor faz?

Se você tiver paciência para ensinar como isso funciona, seja bem-vindo.

Benjamin
fonte

Respostas:

24

Introdução ao D-Bus

  • O D-Bus fornece meios para a comunicação entre serviços . Os serviços podem ser anônimos (identificados apenas pelo endereço do barramento, como: 1.6), e os serviços podem adquirir nomes conhecidos , como org.freedesktop.Notificationsou org.freedesktop.NetworkManager. O remetente e o destino que você pode ver nos logs são serviços. "Destino nulo" significa transmissão: entrega a todos os serviços.

  • Um serviço pode exportar um ou vários objetos para o barramento. Os objetos recebem caminhos de objetos , como /org/freedesktop/NetworkManager/ActiveConnection/1ou /org/ayatana/menu/DA00003. Caminhos de objeto usam barra como separador, como caminhos do sistema de arquivos.

  • Cada objeto pode suportar uma ou várias interfaces . Uma interface nada mais é do que um conjunto de métodos e sinais, conhecidos coloquialmente como membros (muito semelhantes à interface OOP). Métodos e sinais possuem assinaturas fixas. Os membros são sempre espaçados entre nomes de interface conhecidos .

  • Uma vez publicados, nomes conhecidos nunca mudam .

  • Qualquer serviço pode se conectar aos sinais de outro serviço e chamar de forma assíncrona seus métodos. Qualquer serviço pode emitir sinais.

Signals

Agora, para suas perguntas específicas.

remetente do sinal =: 1.1948 -> dest = (destino nulo) serial = 1829990 caminho = / org / ayatana / menu / DA00003; interface = org.ayatana.dbusmenu; member = ItemPropertyUpdated
int32 23
sequência "ativada"
variante booleano true

Sim, você está certo, isso é um sinal. É transmitido por serviço :1.1948, e o objeto "auto" é /org/ayatana/menu/DA00003. O sinal possui um nome ItemPropertyUpdateddefinido na interface org.ayatana.dbusmenu(como org.ayatana.dbusmenu::ItemPropertyUpdatedem C ++). A série, eu acho, é um tipo de identificador exclusivo do evento no barramento.

Então vemos os argumentos do sinal. De acordo com a documentação da interface , o primeiro argumento int32 é o ID de um item, a segunda string é o nome da propriedade e a terceira variante é o valor da propriedade. Portanto, o /org/ayatana/menu/DA00003objeto está nos notificando que o ID do item # 23 mudou sua enabledpropriedade para true.


Outro exemplo de sinais:

remetente do sinal =: 1.1602 -> dest = (destino nulo) serial = 20408 caminho = / im / pidgin / purple / PurpleObject; interface = im.pidgin.purple.PurpleInterface; member = SendingChatMsg
   int32 47893
   string "teste"
   uint32 1
remetente do sinal =: 1.1602 -> dest = (destino nulo) serial = 20409 caminho = / im / pidgin / purple / PurpleObject; interface = im.pidgin.purple.PurpleInterface; member = IrcSendingText
   int32 64170
   string "PRIVMSG #chat: test

Enviei uma mensagem de texto "test" usando o Pidgin para um canal de IRC e /im/pidgin/purple/PurpleObjectemiti dois sinais sob a im.pidgin.purple.PurpleInterfaceinterface: primeiro um geral SendingChatMsg, depois um mais específico IrcSendingText.

Métodos

Agora métodos. Os métodos são uma maneira de solicitar aos objetos D-Bus que façam alguma coisa ou executar alguma consulta e retornar dados. Eles são bastante semelhantes aos métodos clássicos de POO, exceto que os métodos D-Bus são chamados de forma assíncrona.

Vamos chamar um método D-Bus programaticamente.

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

Observe os argumentos, especialmente o nome do ícone. No seu exemplo, "notification-audio-volume-medium"estava o ícone do alto-falante de volume médio.

Serviços personalizados

É absolutamente possível executar seus próprios serviços D-Bus, exportar seus próprios objetos D-Bus e definir suas próprias interfaces D-Bus com seus próprios métodos e sinais. Tudo isso pode ser feito no Python com bastante facilidade quando você entender o conceito geral e ler a dbusdocumentação do módulo.:)

ulidtko
fonte
A discussão é bem-vinda, embora eu possa não estar disponível em um dia ou dois.
Ulidtko
Obrigado :) Isso esclarece muito. É engraçado que os remetentes possam ser anônimos, quando eu uso o DFeet, existe um nome de processo correspondente a cada remetente, mas isso não reflete na saída do monitor dbus. Os processos podem ser rastreados? Agora, com o Python, vi que posso enviar sinais ou fornecer métodos ou acionar os métodos de outras partes. Também é possível interceptar métodos? Suponha que eu queira ver se o programa A aciona o método Dbus de B e faz algo com ele?
Benjamin
Sobre as notificações: o notify-osd é acionado passivamente por outros aplicativos, em vez de procurar ativamente por sinais. Isso não é prático ou entendo algo errado sobre o Dbus? Quero criar um aplicativo que substitua o notify-osd e colete notificações em uma espécie de caixa de entrada. Posso interceptar notificações ouvindo sinais?
Benjamin
@ Benjamin, bem, quando você deseja interceptar chamadas de métodos direcionadas a serviços estrangeiros, provavelmente pensa em um design quebrado. O que você deve fazer para substituir o notify-osd é escrever um programa que fornece o org.freedesktop.Notificationsserviço. Dessa forma, todas as chamadas de método para este serviço serão tratadas pelo seu código.
Ulidtko 4/11
Qual é o objetivo do "eu"?
Kawing-chiu
10

Eu também estava procurando uma solução para coletar as notificações da área de trabalho através do dbus com um script python. Esta pergunta foi a mais próxima que cheguei ao Google, mas escrever um substituto para o notify-osd parecia um exagero :)

Observando as fontes do applet de notificações recentes , eu tenho algumas dicas de como monitorar as mensagens do dbus e aqui está a implementação do python que eu vim com:

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 isso ajude alguém, pois parece que não há muitos exemplos simples de python relacionados ao monitoramento das mensagens do dbus.

Keto
fonte
11
Certamente me ajudou! Muito obrigado! Algumas sugestões para você: "type = 'method_call'" não são necessárias, pois as notificações usam apenas chamadas de método. Nenhum sinal na especificação. Além disso, "membro = 'Notificar'" Também não é necessário, uma vez que já está filtrando que fora em sua função (e, como você disse corretamente, você não pode evitar que, devido à primeira NameAquiredmensagem)
MestreLion