On Jan 29, 2008 9:09 AM, Chris AtLee <[EMAIL PROTECTED]> wrote:
> I'll give Cliff's suggestion a try and see how that goes.  The avahi
> bindings for python are pretty barebones, I was hoping not to have to
> get into that :)

I think I've got it working with the python bindings.  I had to fork
off another process since gobject's event loop interferes with pylons'
threading.  And this means that I need to check the new process'
parent process id periodically.  When it gets set to 1 that means the
old parent has died, so the avahi registration process needs to die as
well.  I think the same method could be used to kill off a running
avahi-publish-service process if one didn't want to use the python
bindings.

I'm putting the call to this in config/middleware.py right now, just
after the configuration is loaded.  Is this the right place for this
sort of thing?  e.g.

    # Configure the Pylons environment
    load_environment(global_conf, app_conf)

    a = AvahiRegistration(config.get("package"), config.get("publish_url"))
    a.run()

    # The Pylons WSGI app
    app = PylonsApp()


Cheers,
Chris

# lib/publish.py
"""
Publish this application via avahi.

Based on example at http://www.avahi.org/wiki/PythonPublishExample
and adapted for use with Pylons
"""
import urlparse
import os, sys
import logging

import signal
import dbus
import gobject
import avahi
from dbus.mainloop.glib import DBusGMainLoop

log = logging.getLogger(__name__)

class AvahiRegistration:
    """
    A class to advertise a server on a network via avahi.

    Typical usage:

    # In config/middleware.py just after load_environment()...
    >>> a = AvahiRegistration(config['package'], config['publish_url'])
    >>> a.run()

    ``name``:   the name of this server.  This will also be used to
                create the DNS-SD service type which will be
                published as "_<name>._tcp"

    ``publish_url``:    the url that external clients can use to access
                        this application.
    """
    def __init__(self, name, publish_url):
        s = urlparse.urlparse(publish_url)
        self.hostname = s.hostname
        self.port = s.port
        self.url = s.path

        self.serviceName = name
        self.serviceType = "_%s._tcp" % self.serviceName

        self.bus = None
        self.group = None
        self.server = None
        self.rename_count = 12

    def _add_service(self):
        if self.group is None:
            self.group = dbus.Interface(
                    self.bus.get_object( avahi.DBUS_NAME,
self.server.EntryGroupNew()),
                    avahi.DBUS_INTERFACE_ENTRY_GROUP)
            self.group.connect_to_signal('StateChanged',
                    self._entry_group_state_changed)

        log.info("Adding service '%s' of type '%s' ...", self.serviceName,
                self.serviceType)

        domain = ""
        self.group.AddService(
                avahi.IF_UNSPEC,    #interface
                avahi.PROTO_UNSPEC, #protocol
                0,                  #flags
                self.serviceName, self.serviceType,
                domain, self.hostname,
                dbus.UInt16(self.port),
                avahi.string_array_to_txt_array(self.url))
        self.group.Commit()

    def _remove_service(self):
        if not self.group is None:
            self.group.Reset()

    def _server_state_changed(self, state):
        if state == avahi.SERVER_COLLISION:
            log.warn("Server name collision")
            self._remove_service()
        elif state == avahi.SERVER_RUNNING:
            self._add_service()

    def _entry_group_state_changed(self, state, error):
        log.debug("state change: %i", state)

        if state == avahi.ENTRY_GROUP_ESTABLISHED:
            log.info("Service established.")
        elif state == avahi.ENTRY_GROUP_COLLISION:

            self.rename_count -= 1
            if self.rename_count > 0:
                name = self.server.GetAlternativeServiceName(name)
                log.warn("Service name collision, changing name to '%s' ...",
                        name)
                self._remove_service()
                add_service()

            else:
                log.error("No suitable service name found after %i
retries, exiting.", n_rename)
                main_loop.quit()
        elif state == avahi.ENTRY_GROUP_FAILURE:
            log.error("Error in group state changed %s", error)
            main_loop.quit()
            return

    def _check(self):
        """
        If our parent process' id is 1, then our real parent has gone
        away, and so should we.
        """
        if os.getppid() == 1:
            self.quit()
        return True

    def quit(self):
        if not self.group is None:
            self.group.Free()
        log.info("Exiting...")

        sys.exit(0)

    def run(self):
        # We have to fork here so that we have our own set of threads and
        # signal handlers, etc.  gobject's MainLoop doesn't seem to play nicely
        # with Pylons' threading
        if os.fork() != 0:
            # Return immediately to the controlling process
            return

        DBusGMainLoop( set_as_default=True )

        main_loop = gobject.MainLoop()
        self.bus = dbus.SystemBus()

        gobject.timeout_add(1000, self._check)

        self.server = dbus.Interface(
                self.bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
                avahi.DBUS_INTERFACE_SERVER )

        self.server.connect_to_signal( "StateChanged",
self._server_state_changed )
        self._server_state_changed( self.server.GetState() )

        try:
            main_loop.run()
        except KeyboardInterrupt:
            pass

        self.quit()

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/pylons-discuss?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to