#!/usr/bin/env python from mercurial import hg from mercurial import commands import dbus import gobject import avahi from dbus.mainloop.glib import DBusGMainLoop class AvahiReg: def __init__(self, name, port): self.verbose = False self.serviceName = "Mercurial" if name != '': self.serviceName += ": " + name self.serviceType = "_http._tcp" # See http://www.dns-sd.org/ServiceTypes.html self.servicePort = port self.serviceTXT = ["extension's author=Alessio Caiazza"] #TXT record for the service self.domain = "" # Domain to publish on, default to .local self.host = "" # Host to publish records for, default to localhost self.group = None #our entry group self.rename_count = 12 # Counter so we only rename after collisions a sensible number of times 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) if self.verbose: print "Adding service '%s' of type '%s' ..." % (self.serviceName, self.serviceType) self.group.AddService( avahi.IF_UNSPEC, #interface avahi.PROTO_UNSPEC, #protocol 0, #flags self.serviceName, self.serviceType, self.domain, self.host, dbus.UInt16(self.servicePort), avahi.string_array_to_txt_array(self.serviceTXT)) self.group.Commit() def remove_service(self): if not group is None: group.Reset() def server_state_changed(self, state): if state == avahi.SERVER_COLLISION: if self.verbose: print "WARNING: Server name collision" remove_service() elif state == avahi.SERVER_RUNNING: self.add_service() def entry_group_state_changed(self,state, error): if self.verbose: print "state change: %i" % state if state == avahi.ENTRY_GROUP_ESTABLISHED: print "Service established." elif state == avahi.ENTRY_GROUP_COLLISION: rename_count = rename_count - 1 if rename_count > 0: name = server.GetAlternativeServiceName(name) if self.verbose: print "WARNING: Service name collision, changing name to '%s' ..." % name remove_service() add_service() else: if self.verbose: print "ERROR: No suitable service name found after %i retries, exiting." % n_rename main_loop.quit() elif state == avahi.ENTRY_GROUP_FAILURE: if self.verbose: print "Error in group state changed", error main_loop.quit() return def start(self): DBusGMainLoop( set_as_default=True ) self.bus = dbus.SystemBus() 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() ) def stop(self): if self.verbose: print "Stopping avahi-publish-service" if not self.group is None: self.group.Free() # every command must take a ui and and repo as arguments. # opts is a dict where you can find other command line flags # # Other parameters are taken in order from items on the command line that # don't start with a dash. If no default value is given in the parameter list, # they are required. def avahi_serve(ui, repo, **opts): # The doc string below will show up in hg help """Serve and publish on avahi zeroconf""" #print opts if opts['port'] != 0: port = int(opts['port']) else: port = 8000 a = AvahiReg(opts['name'], port) if ui.cdata.has_option('web', 'description'): a.serviceTXT += ["description=" + ui.cdata.get('web','description')] if ui.cdata.has_option('ui', 'username'): #remove < and > cose make problems on Avahi Discovery a.serviceTXT += ["owner=" + ui.cdata.get('ui','username') .replace('<','') .replace('>','')] a.verbose = ui.verbose a.start() try: commands.serve(ui,repo, **opts) except KeyboardInterrupt: pass a.stop() cmdtable = { # cmd name function call "aserve": (avahi_serve, commands.table['^serve'][1], "hg aserve [OPTION]...") }