Author: dmeyer
Date: Tue Mar 4 04:55:29 2008
New Revision: 3152
Log:
Add multicast DNS and service discovery module based on Avahi
Added:
trunk/base/src/net/mdns.py
trunk/base/test/mdns.py
Added: trunk/base/src/net/mdns.py
==============================================================================
--- (empty file)
+++ trunk/base/src/net/mdns.py Tue Mar 4 04:55:29 2008
@@ -0,0 +1,214 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------------
+# mdns.py - Simple Multicast DNS Interface
+# -----------------------------------------------------------------------------
+# $Id$
+#
+# This module provides a simple interface to multicast DNS (zeroconf) and
+# service discovery. Right now it requires Avahi as mdns service. Other
+# mdns implementations are not supported yet but should be added later to
+# support other platforms like OSX.
+#
+# -----------------------------------------------------------------------------
+# Copyright (C) 2008 Dirk Meyer
+#
+# First Edition: Dirk Meyer <[EMAIL PROTECTED]>
+# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
+#
+# Please see the file AUTHORS for a complete list of authors.
+#
+# This library is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version
+# 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 USA
+#
+# -----------------------------------------------------------------------------
+
+# python imports
+import logging
+
+# dbus and avahi support
+import dbus
+import avahi
+
+# kaa imports
+import kaa
+from kaa.utils import property
+
+# get logging object
+log = logging.getLogger('mdns')
+
+class Service(object):
+ """
+ A service object with all available mdns information
+ """
+ def __init__(self, interface, protocol, name, domain, host, address, port,
txt):
+ self.interface = interface
+ self.protocol = protocol
+ self.name = name
+ self.domain = domain
+ self.host = host
+ self.address = address
+ self.port = port
+ self.txt = txt
+
+ def __repr__(self):
+ return '<mdns.Service %s at %s:%s>' % (self.name, self.address,
self.port)
+
+
+class ServiceList(object):
+ """
+ An object handling all services of a given type.
+ """
+ def __init__(self):
+ self.signals = kaa.Signals('added', 'removed')
+ self._dict = {}
+
+ def _add(self, interface, protocol, name, domain, host, address, port,
txt):
+ s = Service(interface, protocol, name, domain, host, address, port,
txt)
+ self._dict[(interface, protocol, name, domain)] = s
+ self.signals['added'].emit(s)
+
+ def _remove(self, interface, protocol, name, domain):
+ value = self._dict.pop((interface, protocol, name, domain))
+ self.signals['removed'].emit(value)
+
+ @property
+ def services(self):
+ return self._dict.values()
+
+
+class Avahi(object):
+ """
+ Simple multicast DNS service based on Avahi. This service requires avahi
+ installed and running as service, dbus python and the gobject mainloop
+ running. The mainloop can either run as kaa mainloop (gtk or twisted with
+ a gtk based mainloop) or as thread using kaa.gobject_set_threaded().
+ """
+ def __init__(self):
+ self._bus = None
+ self._services = {}
+ self._announce = None
+
+ @kaa.threaded(kaa.GOBJECT)
+ def provide(self, name, type, port, txt):
+ """
+ Provide a service with the given name and type listening on the given
+ port with additional information in the txt record.
+ """
+ if self._bus is None:
+ self._dbus_connect()
+ if self._announce is None:
+ self._announce = dbus.Interface(
+ self._bus.get_object( avahi.DBUS_NAME,
self._avahi.EntryGroupNew()),
+ avahi.DBUS_INTERFACE_ENTRY_GROUP)
+ self._announce.AddService(
+ avahi.IF_UNSPEC, # interface
+ avahi.PROTO_UNSPEC, # protocol
+ 0, # flags
+ name, type, # name, service type
+ "", # domain
+ "", # host
+ dbus.UInt16(port), # port
+ avahi.string_array_to_txt_array([ '%s=%s' % t for t in txt.items()
]))
+ self._announce.Commit()
+
+ def get_type(self, service):
+ """
+ Get a ServiceList object for the given type.
+ e.g. get_type('_ssh._tcp')
+ """
+ if not service in self._services:
+ self._services[service] = ServiceList()
+ self._service_add_browser(service)
+ return self._services[service]
+
+ def _dbus_connect(self):
+ """
+ Connect to dbus and avahi. This is an internal function that has to be
+ called from a function running in the GOBJECT thread.
+ """
+ self._bus = dbus.SystemBus()
+ self._avahi = dbus.Interface(
+ self._bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
+ avahi.DBUS_INTERFACE_SERVER )
+
+ @kaa.threaded(kaa.GOBJECT)
+ def _service_add_browser(self, service):
+ """
+ Add a service browser to avahi.
+ """
+ if self._bus is None:
+ self._dbus_connect()
+ # fixed values (everything)
+ interface = avahi.IF_UNSPEC
+ protocol = avahi.PROTO_INET
+ domain = ''
+ # Call method to create new browser, and get back an object path for
it.
+ obj = self._avahi.ServiceBrowserNew(
+ interface, protocol, service, domain, dbus.UInt32(0))
+ # Create browser interface for the new object
+ browser = dbus.Interface(self._bus.get_object(avahi.DBUS_NAME, obj),
+ avahi.DBUS_INTERFACE_SERVICE_BROWSER)
+ browser.connect_to_signal('ItemNew', self._service_new)
+ browser.connect_to_signal('ItemRemove', self._service_remove)
+
+ @kaa.threaded(kaa.MAINTHREAD)
+ def _service_remove(self, interface, protocol, name, type, domain, flags):
+ """
+ Callback from dbus when a service is removed.
+ """
+ self._services[type]._remove(
+ int(interface), int(protocol), str(name), str(domain))
+
+ def _service_new(self, interface, protocol, name, type, domain, flags):
+ """
+ Callback from dbus when a new service is available. This function is
+ called inside the GOBJECT thread and uses dbus again.
+ """
+ try:
+ if flags & avahi.LOOKUP_RESULT_LOCAL:
+ # This is a local service
+ pass
+ except dbus.DBusException:
+ pass
+ self._avahi.ResolveService(
+ interface, protocol, name, type, domain, avahi.PROTO_INET,
dbus.UInt32(0),
+ reply_handler=self._service_resolved, error_handler=self._error)
+
+ @kaa.threaded(kaa.MAINTHREAD)
+ def _service_resolved(self, interface, protocol, name, type, domain, host,
+ aprotocol, address, port, txt, flags):
+ """
+ Callback from dbus when a new service is available and resolved.
+ """
+ txtdict = {}
+ for record in avahi.txt_array_to_string_array(txt):
+ if record.find('=') > 0:
+ k, v = record.split('=', 2)
+ txtdict[k] = v
+ self._services[type]._add(
+ int(interface), int(protocol), str(name), str(domain),
+ str(host), str(address), int(port), txtdict
+ )
+
+ @kaa.threaded(kaa.MAINTHREAD)
+ def _error(self, err):
+ log.error(str(err))
+
+
+# create singleton object
+mdns = kaa.utils.Singleton(Avahi)
+
+# create functions to use from the outside
+provide = mdns.provide
+get_type = mdns.get_type
Added: trunk/base/test/mdns.py
==============================================================================
--- (empty file)
+++ trunk/base/test/mdns.py Tue Mar 4 04:55:29 2008
@@ -0,0 +1,34 @@
+import sys
+import kaa
+from kaa.net import mdns
+
+# some setup stuff
+from dbus.mainloop.glib import DBusGMainLoop
+DBusGMainLoop( set_as_default=True )
+kaa.main.select_notifier('generic')
+kaa.gobject_set_threaded()
+
+def added(service, type):
+ print 'found', service, type
+ s = mdns.get_type(type)
+ print 'all'
+ for s in s.services:
+ print '', s
+
+def removed(service):
+ print 'lost', service
+
+if len(sys.argv) > 1:
+ # go into provide mode
+ # mdns.py ServiceName Port
+ mdns.provide(sys.argv[1], '_test._tcp', int(sys.argv[2]), {'foo': 'bar'})
+else:
+ # go into listen mode
+ # monitor printer
+ s = mdns.get_type('_ipp._tcp')
+ s.signals['added'].connect(added, '_ipp._tcp')
+ # monitor test apps that provide stuff
+ s = mdns.get_type('_test._tcp')
+ s.signals['added'].connect(added, '_test._tcp')
+ s.signals['removed'].connect(removed)
+kaa.main.run()
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog