2

I need a class which gives me a list of avahi services of a certain type which a currently available. Therefor I run a gobject.MainLoop() (line 23-25) in a separate thread an add a browser for each service I am interested in (line 27,28). This works in principle.

My problem is, that I do not get always all services. Sometimes all services that are available are listed, sometimes none of them, sometimes just a few. My guess is, that the browser starts iterating the services (line 36) before the appropriate signals are connected (line 41-44), but I have no clue how to fix this. Below a minimal example which shows the failure.

Most examples I have seen on the net (e.g.: Stopping the Avahi service and return a list of elements) run the MainLoop after the browser is set up and the signals are connected. The loop is then quit when the "AllForNow" signal is received. This is not an option for me as the Browser should keep running and listen for new or removed services (which works reliable by the way, just the initial query is problematic).

#!/usr/bin/python
import dbus
from dbus.mainloop.glib import DBusGMainLoop
import avahi
import gobject
import threading

gobject.threads_init()
dbus.mainloop.glib.threads_init()

class ZeroconfBrowser:
    def __init__(self):
        self.service_browsers = set()
        self.services = {}
        self.lock = threading.Lock()

        loop = DBusGMainLoop(set_as_default=True)
        self._bus = dbus.SystemBus(mainloop=loop)
        self.server = dbus.Interface(
                self._bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), 
                avahi.DBUS_INTERFACE_SERVER)

        thread = threading.Thread(target=gobject.MainLoop().run)
        thread.daemon = True
        thread.start()

        self.browse("_ssh._tcp")
        self.browse("_http._tcp")

    def browse(self, service):
        if service in self.service_browsers:
            return
        self.service_browsers.add(service)

        with self.lock:
            browser = dbus.Interface(self._bus.get_object(avahi.DBUS_NAME, 
                    self.server.ServiceBrowserNew(avahi.IF_UNSPEC, 
                            avahi.PROTO_UNSPEC, service, 'local', dbus.UInt32(0))),
                    avahi.DBUS_INTERFACE_SERVICE_BROWSER)

            browser.connect_to_signal("ItemNew", self.item_new)
            browser.connect_to_signal("ItemRemove", self.item_remove)
            browser.connect_to_signal("AllForNow", self.all_for_now)
            browser.connect_to_signal("Failure", self.failure)

    def resolved(self, interface, protocol, name, service, domain, host, 
            aprotocol, address, port, txt, flags):
        print "resolved", interface, protocol, name, service, domain, flags

    def failure(self, exception):
        print "Browse error:", exception

    def item_new(self, interface, protocol, name, stype, domain, flags):
        with self.lock:
            self.server.ResolveService(interface, protocol, name, stype,
                    domain, avahi.PROTO_UNSPEC, dbus.UInt32(0),
                    reply_handler=self.resolved, error_handler=self.resolve_error)

    def item_remove(self, interface, protocol, name, service, domain, flags):
        print "removed", interface, protocol, name, service, domain, flags

    def all_for_now(self):
        print "all for now"

    def resolve_error(self, *args, **kwargs):
        with self.lock:
            print "Resolve error:", args, kwargs

import time
def main():
    browser = ZeroconfBrowser()
    while True:
        time.sleep(3)
        for key, value in browser.services.items():
            print key, str(value)

if __name__ == '__main__':
    main()
Community
  • 1
  • 1
merula
  • 384
  • 2
  • 8

1 Answers1

0

I couldn't find an installable python-dbus package. However I did find an example Avahi browser which works great -- avahi.py

pip install python-tdbus

source

#!/usr/bin/env python
#
# This file is part of python-tdbus. Python-tdbus is free software
# available under the terms of the MIT license. See the file "LICENSE" that
# was provided together with this source file for the licensing terms.
#
# Copyright (c) 2012 the python-tdbus authors. See the file "AUTHORS" for a
# complete list.

# This example shows how to access Avahi on the D-BUS.


from tdbus import SimpleDBusConnection, DBUS_BUS_SYSTEM, DBusHandler, signal_handler, DBusError

import logging

logging.basicConfig(level=logging.DEBUG)

CONN_AVAHI = 'org.freedesktop.Avahi'
PATH_SERVER = '/'
IFACE_SERVER = 'org.freedesktop.Avahi.Server'

conn = SimpleDBusConnection(DBUS_BUS_SYSTEM)

try:
    result = conn.call_method(PATH_SERVER, 'GetVersionString',
                        interface=IFACE_SERVER, destination=CONN_AVAHI)
except DBusError:
    print 'Avahi NOT available.'
    raise

print 'Avahi is available at %s' % CONN_AVAHI
print 'Avahi version: %s' % result.get_args()[0]
print
print 'Browsing service types on domain: local'
print 'Press CTRL-c to exit'
print

result = conn.call_method('/', 'ServiceTypeBrowserNew', interface=IFACE_SERVER,
                    destination=CONN_AVAHI, format='iisu', args=(-1, 0, 'local', 0))
browser = result.get_args()[0]
print browser
class AvahiHandler(DBusHandler):

    @signal_handler()
    def ItemNew(self, message):
    args = message.get_args()
        print 'service %s exists on domain %s' % (args[2], args[3])

conn.add_handler(AvahiHandler())
conn.dispatch()

output

Avahi is available at org.freedesktop.Avahi
Avahi version: avahi 0.6.31

Browsing service types on domain: local
Press CTRL-c to exit

/Client1/ServiceTypeBrowser1
service _udisks-ssh._tcp exists on domain local
service _workstation._tcp exists on domain local
service _workstation._tcp exists on domain local
service _udisks-ssh._tcp exists on domain local
johntellsall
  • 14,394
  • 4
  • 46
  • 40