Mining Bits and Knowledge
Python e UDisks – Parte 1
14/03/11
| This post is also available in English |
Recentemente recebi diversos contatos de pessoas querendo saber se o USBManager iria suportar ou não o DeviceKit/UDev, uma vez que o HAL foi oficialmente descontinuado, então acabei retomando o desenvolvimento desse aplicativo.
Acabei percebendo algumas diferenças e pouca referência na internet, por isso vou postar algumas informações a respeito do desenvolvimento usando o DBus e o DeviceKit para trabalhar com dispositivos de armazenamento via USB.
Há algum tempo, fiz uma pesquisa sobre Python e o DeviceKit e encontrei esse post: Accessing DeviceKit with DBus and Python. Eu havia feito alguns testes e o código funcionou perfeitamente. Escrevi algumas linhas a mais e guardei para mexer em um outro dia. Depois de muito tempo (e uma atualização de distribuição) resolvi mexer novamente no USBManager e percebi que o trecho de código não funciona mais:
>>> import dbus
>>>
>>> bus = dbus.SystemBus()
>>>
>>> proxy = bus.get_object("org.freedesktop.DeviceKit.Disks", "/org/freedesktop/DeviceKit/Disks")
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "/usr/lib/pymodules/python2.6/dbus/bus.py", line 244, in get_object
follow_name_owner_changes=follow_name_owner_changes)
File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 241, in __init__
self._named_service = conn.activate_name_owner(bus_name)
File "/usr/lib/pymodules/python2.6/dbus/bus.py", line 183, in activate_name_owner
self.start_service_by_name(bus_name)
File "/usr/lib/pymodules/python2.6/dbus/bus.py", line 281, in start_service_by_name
'su', (bus_name, flags)))
File "/usr/lib/pymodules/python2.6/dbus/connection.py", line 620, in call_blocking
message, timeout)
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.DeviceKit.Disks was not provided by any .service files
>>>
Python e UDisks – Parte 2
30/03/11
| This post is also available in English |
Há alguns posts eu mostrei como listar todos os dispositivos de armazenamento conectados ao seu PC usando Python e UDisks. Neste post irei mostrar como trabalhar com a interface Disks do DeviceKit.
Primeiramente vamor ler algumas documentações! A documentação do UDisks é dividida em cinco sessões:
- Interface Disks;
- Interface Device;
- Interface Adapter;
- Interface Expander;
- Interface Port.
Python e UDisks – Parte 3
25/04/11
| This post is also available in English |
No último post desta série eu mostrei como obter algumas informações úteis sobre o daemon UDisks, agora vamos ver como buscar dispositivos com ele.
O primeiro método que iremos ver é o EnumerateDevices:
>>> import dbus
>>>
>>> bus = dbus.SystemBus()
>>> proxy = bus.get_object("org.freedesktop.UDisks", "/org/freedesktop/UDisks")
>>> iface = dbus.Interface(proxy, "org.freedesktop.UDisks")
>>>
>>> devs = iface.EnumerateDevices()
>>> print devs
dbus.Array([dbus.ObjectPath('/org/freedesktop/UDisks/devices/fd0'), dbus.ObjectPath('/org/freedesktop/UDisks/devices/sdb'), dbus.ObjectPath('/org/freedesktop/UDisks/devices/sr0'), dbus.ObjectPath('/org/freedesktop/UDisks/devices/sda1'), dbus.ObjectPath('/org/freedesktop/UDisks/devices/sda2'), dbus.ObjectPath('/org/freedesktop/UDisks/devices/sdc1'), dbus.ObjectPath('/org/freedesktop/UDisks/devices/sdb1'), dbus.ObjectPath('/org/freedesktop/UDisks/devices/sda'), dbus.ObjectPath('/org/freedesktop/UDisks/devices/sdb3'), dbus.ObjectPath('/org/freedesktop/UDisks/devices/sdc'), dbus.ObjectPath('/org/freedesktop/UDisks/devices/sdb2')], signature=dbus.Signature('o'))
>>> devs[0]
dbus.ObjectPath('/org/freedesktop/UDisks/devices/fd0')
>>> type(devs[0])
<type 'dbus.ObjectPath'>
>>> dir(devs[0])
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>>
Python e UDisks – Parte 4
23/05/11
| This post is also available in English |
Nos já vimos, como conectar ao DBus e o UDisks, em seguida vimos como verificar algumas configurações e informações do daemon do UDisks e também como buscar por dispositivos conectados. Tudo isso não é muito “usável” se você não souber quando uma novo dispositivo foi conectado ao seu computador. Claro que você pode ficar testando periodicamente se algum dispositivo foi adicionado, mas esta não é uma solução muito elegante. Para uma detecção de dispositivos “em tempo real” nos utilizamos sinais.
Existem diversos sinais especificados no Udisks, mas os mais úteis (para o nosso caso) são:
- O sinal DeviceAdded – Emitido sempre que um dispositivo é adicionado.
- O sinal DeviceRemoved – Emitido quando um dispositivo é removido.
- O sinal DeviceChanged – Emitido sempre que a propriedade de um dispositivo é alterada.
Essas descrições foram tiradas do Manual de Referência do UDisks.
Mas antes de seguirmos adiante, o que são sinais? Sinais são chamadas assíncronas, de uma forma simples podemos dizer que são “eventos”. Com a utilização de chamadas assíncronas nosso programa pode realizar operações e processamentos e ser “interrompido” quando um evento ocorrer. Vinculado a esse evento está uma função de tratamento (handler).
Agora vamos ver um trecho de código Python que mostra como lidar com esses sinais.
>>> import dbus #Importa módulos necessários
>>> import gobject
>>> from dbus.mainloop.glib import DBusGMainLoop
>>>
>>> DBusGMainLoop(set_as_default=True) #Informa a utilização de um main loop
>>>
>>> bus = dbus.SystemBus() #Inicia o DBus
>>> proxy = bus.get_object("org.freedesktop.UDisks", "/org/freedesktop/UDisks")
>>> iface = dbus.Interface(proxy, "org.freedesktop.UDisks")
>>>
>>> def on_device_add(dev_path):
... '''Handler para o evento DeviceAdded'''
... print 'Adicionado:',dev_path
...
>>> def on_device_changed(dev_path):
... '''Handler para o evento DeviceChanged'''
... print 'Modificado:',dev_path
...
>>> def on_device_removed(dev_path):
... '''Handler para o evento DeviceRemoved'''
... print 'Removido:',dev_path
...
>>>
>>> iface.connect_to_signal('DeviceAdded', on_device_add)
< at 9cb8b0c "type='signal',sender=':1.38',path='/org/freedesktop/UDisks',interface='org.freedesktop.UDisks',member='DeviceAdded'" on conn >
>>> iface.connect_to_signal('DeviceChanged', on_device_changed)
< at 9cb8c2c "type='signal',sender=':1.38',path='/org/freedesktop/UDisks',interface='org.freedesktop.UDisks',member='DeviceChanged'" on conn >
>>> iface.connect_to_signal('DeviceRemoved', on_device_removed)
< at 9cb8cec "type='signal',sender=':1.38',path='/org/freedesktop/UDisks',interface='org.freedesktop.UDisks',member='DeviceRemoved'" on conn >
>>>
>>> loop = gobject.MainLoop()
>>> loop.run() #loop infinito
Adicionado: /org/freedesktop/UDisks/devices/sdc
Adicionado: /org/freedesktop/UDisks/devices/sdc1
Modificado: /org/freedesktop/UDisks/devices/sdc1
Modificado: /org/freedesktop/UDisks/devices/sdc1
Modificado: /org/freedesktop/UDisks/devices/sdc
Removido: /org/freedesktop/UDisks/devices/sdc1
Removido: /org/freedesktop/UDisks/devices/sdc
As linhas 24, 26 e 28 são responsáveis por conectar o “evento” do sinal à função de tratamento do evento (event handler).
Após a linha loop.run(), o programa parece que está “suspenso”, mas está apenas aguardando a ocorrência de algum sinal. Após isso, eu inseri uma pen drive, e ela foi montada automaticamente. Em seguida eu a desmontei e a removi. Olhando rapidamente (pelo menos se você estiver olhando a saída estática acima) temos a impressão de que estão sendo geradas outputs duplicadas. Mas isso tem uma boa explicação…
- As primeiras duas linhas notificam que um novo “root device” (tabela de partição) foi detectado (sdc), em seguida a “nova partição” (sdc1) foi detectada. Se sua pendrive tivesse mais partições teríamos mais outputs.
- A terceira linha notifica que a partição sdc1 foi montada.
- A quarta, quinta e sexta linha notifica que o dispositivo foi desmontado (ambas as partições) e a partição “removida”.
- E a última linha notifica que o dispositivo foi realmente removido.
Agora tudo parece mais claro, não? Então, quando buscamos por novos dispositivos é interessante filtrar essas notificações e manter somente as que são relevantes. Isso será feito buscando informações sobre o dispositivo. Eu irei mostrar como fazer isso no próximo post, até lá…
... '''Handler para o evendo DeviceAdded'''
Python e UDisks – Parte 5
05/07/11
| This post is also available in English |
Ressuscitando a série de artigos sobre Python e Udisks hoje vou mostrar como obter mais informações dos dispositivos através das propriedades.
Sobre o Atraso
Antes de tudo, peço desculpas pela demora em postar essa continuação, ela foi mais difícil de escrever do que parece. Essa demora se deve à mudança da forma como o DBus se comunica com o UDisks. Como tudo ainda é muito novo e não há um documentação completa e tão pouco tutoriais na internet, minhas buscas pela linha de erro não retornavam nenhum resultado. Abaixo está o código que eu estava utilizando e o erro que o DBus me apresentava:
>>> import dbus
>>>
>>> bus = dbus.SystemBus()
>>> proxy = bus.get_object("org.freedesktop.UDisks", "/org/freedesktop/UDisks")
>>> iface = dbus.Interface(proxy, "org.freedesktop.UDisks")
>>>
>>> devs = iface.EnumerateDevices()
>>> print devs
>>>
>>> device = devs[14]
>>> volume_obj = bus.get_object("org.freedesktop.UDisks", device)
>>> volume = dbus.Interface(volume_obj, "org.freedesktop.UDisks.Device")
>>> volume_obj.Get('','DriveConnectionInterface')
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.AccessDenied: Rejected send message, 4 matched rules; type="method_call", sender=":1.60" (uid=1000 pid=19165 comm="/usr/bin/python2 ./test-me ") interface="(unset)" member="Get" error name="(unset)" requested_reply="0" destination=":1.19" (uid=0 pid=1010 comm="/usr/lib/udisks/udisks-daemon ")
Após dias de pesquisas e tentativas frustradas resolvi fazer algo que muitos evitam: ler código fonte de outros projetos. Depois de algumas pesquisas acabei encontrando o projeto liveusb-creator que utiliza o UDisks e o DBus para detectar dispositivos removíveis. Minha solução estava no arquivo creator.py (mais especificamente no método handle_reply, linhas 445 a 450) disponível aqui.





