30 from gi.repository
import Gio
33 __all__ = [
"DEFAULT_SERVICE_TYPE",
"DEFAULT_SERVICE_DOMAIN",
"Publisher",
"Listener",
"ServiceBrowser"]
36 __author__ =
"Kipp Cannon <kipp.cannon@ligo.org>" 50 DEFAULT_SERVICE_TYPE =
"_http._tcp" 51 DEFAULT_SERVICE_DOMAIN =
"gw.local" 65 Add a service to a group, and allow its properties to be updated 69 def properties_to_txt_array(properties):
70 if properties
is None:
72 elif any(
"=" in key
for key
in properties):
73 raise ValueError(
"'=' not permitted in property keys")
74 return avahi.dict_to_txt_array(properties)
77 def __init__(self, group, sname, port, stype = None, sdomain = None, host = None, properties = None):
79 Add a service to the collection of services currently 80 advertised. sname and port specify the service name and 81 the port number on which the service can be found. stype 82 and sdomain set the service type and service domain; if 83 not set the module-level symbols DEFAULT_SERVICE_TYPE and 84 DEFAULT_SERVICE_DOMAIN are used, respectively. 86 Avahi is asked to advertise the service on all network 87 interfaces to which it is connected. If host is "" (the 88 default) then on each interface avahi will use the host 89 name corresponding to that network interface (as determined 90 by itself). This is a convenient way to ensure the service 91 is advertised on each interface with a host name that 92 exists on that interface's network. 94 properties is a dictionary of name-value pairs all of which 95 are strings. "=" is not allowed in any of the names. 103 self.
stype = stype
if stype
is not None else DEFAULT_SERVICE_TYPE
104 self.
sdomain = sdomain
if sdomain
is not None else DEFAULT_SERVICE_DOMAIN
105 if self.
sdomain.split(
".")[-1] !=
"local":
106 raise ValueError(
"sdomain must end in 'local': %s" % self.
sdomain)
120 host
if host
is not None else "",
128 properties is a dictionary of name-value pairs all of which 129 are strings. "=" is not allowed in any of the names. 131 self.
group.UpdateServiceTxt(
145 Glue code to connect to the avahi daemon through dbus and manage 146 the advertisement of services. 149 bus = Gio.bus_get_sync(Gio.BusType.SYSTEM,
None)
150 server = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE,
None, avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER, avahi.DBUS_INTERFACE_SERVER,
None)
151 group_path = server.EntryGroupNew(
"()")
152 self.
group = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE,
None, avahi.DBUS_NAME, group_path, avahi.DBUS_INTERFACE_ENTRY_GROUP,
None)
155 def add_service(self, sname, port, stype = None, sdomain = None, host = None, properties = None, commit = True):
157 See the Service class for the meaning of the arguments. 159 If commit is True (the default), then the new service is 160 advertised immediately along with all other previously 161 unadvertised services; otherwise the calling code is 162 responsible for calling the .commit() method itself. 164 service =
Service(self.
group, sname, port, stype, sdomain, host, properties)
170 self.
group.Commit(
"()")
172 def __exit__(self, exc_type, exc_value, traceback):
174 Unpublish all services. 176 self.
group.Reset(
"()")
190 Parent class for Listener implementations. Each method corresponds 191 to an event type. Subclasses override the desired methods with the 192 code to be invoked upon those events. The default methods are all 193 no-ops. An instance of a Listener implementation is required to 194 initialize a ServiceBrowser. 196 def add_service(self, sname, stype, sdomain, host, port, properties):
199 def remove_service(self, sname, stype, sdomain):
202 def all_for_now(self):
205 def failure(self, *args):
209 class ServiceBrowser(object):
211 Glue code to connect a Listener implementation to the avahi daemon 214 def __init__(self, listener, stype = DEFAULT_SERVICE_TYPE, sdomain = DEFAULT_SERVICE_DOMAIN, ignore_local = False):
216 Connects to the avahi daemon through dbus, requests an 217 avahi ServiceBrowser instance from the daemon configured to 218 browse for the given service type and domain, then connects 219 signal handlers that forward information from avahi to the 220 methods of a Listener instance. 222 listener is an instance of a subclass of Listener (or any 223 other object that provides the required methods to be used 226 if ignore_local is True then services discovered on the 227 local machine itself will be ignored (the default is False, 228 all discovered services are reported to the Listener). 232 bus = Gio.bus_get_sync(Gio.BusType.SYSTEM,
None)
233 self.
server = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE,
None, avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER, avahi.DBUS_INTERFACE_SERVER,
None)
234 browser_path = self.
server.ServiceBrowserNew(
242 bus.signal_subscribe(
None,
None,
"ItemNew", browser_path,
None, Gio.DBusSignalFlags.NONE, self.
itemnew_handler,
None)
243 bus.signal_subscribe(
None,
None,
"ItemRemove", browser_path,
None, Gio.DBusSignalFlags.NONE, self.
itemremove_handler,
None)
244 bus.signal_subscribe(
None,
None,
"AllForNow", browser_path,
None, Gio.DBusSignalFlags.NONE, self.
allfornow_handler,
None)
245 bus.signal_subscribe(
None,
None,
"Failure", browser_path,
None, Gio.DBusSignalFlags.NONE, self.
failure_handler,
None)
247 def itemnew_handler(self, bus, sender_name, object_path, interface_name, signal_name, (interface, protocol, sname, stype, sdomain, flags), data):
249 Internal ItemNew signal handler. Forwards the essential 250 information to the Listener's .add_service() method. 252 if self.
ignore_local and (flags & avahi.LOOKUP_RESULT_LOCAL):
255 interface, protocol, sname, stype, sdomain, host, aprotocol, address, port, txt, flags = self.
server.ResolveService(
265 self.
listener.add_service(sname, stype, sdomain, host, port, dict(s.split(
"=", 1)
for s
in avahi.txt_array_to_string_array(txt)))
267 def itemremove_handler(self, bus, sender_name, object_path, interface_name, signal_name, (interface, protocol, sname, stype, sdomain, flags), data):
269 Internal ItemRemove signal handler. Forwards the essential 270 information to the Listener's .remove_service() method. 272 if self.
ignore_local and (flags & avahi.LOOKUP_RESULT_LOCAL):
275 self.
listener.remove_service(sname, stype, sdomain)
277 def allfornow_handler(self, bus, sender_name, object_path, interface_name, signal_name, parameters, data):
279 Internal AllForNow signal handler. Forwards the essential 280 information to the Listener's .all_for_now() method. 284 def failure_handler(self, bus, sender_name, object_path, interface_name, signal_name, parameters, data):
286 Internal Failure signal handler. Forwards the essential 287 information to the Listener's .failure() method. 301 if __name__ ==
"__main__":
312 from gi.repository
import GLib
315 if sys.argv[-1] ==
"publish":
321 publisher.add_service(
322 sname =
"My Test Service",
330 raw_input(
"Service published. Press return to unpublish and quit.\n")
337 def print_msg(self, action, sname, stype, sdomain, host, port, properties):
338 print >>sys.stderr,
"Service \"%s\" %s" % (sname, action)
339 print >>sys.stderr,
"\tType is \"%s\"" % stype
340 print >>sys.stderr,
"\tDomain is \"%s\"" % sdomain
341 print >>sys.stderr,
"\tHost is \"%s\"" % host
342 print >>sys.stderr,
"\tPort is %s" % port
343 print >>sys.stderr,
"\tProperties are %s\n" % properties
344 def add_service(self, sname, stype, sdomain, host, port, properties):
345 self.
print_msg(
"added", sname, stype, sdomain, host, port, properties)
346 def remove_service(self, sname, stype, sdomain):
347 self.
print_msg(
"removed", sname, stype, sdomain,
None,
None,
None)
349 def all_for_now(self):
350 print >>sys.stderr,
"All for now\n" 352 def failure(self, *args):
353 print >>sys.stderr,
"failure", args
354 mainloop = GLib.MainLoop()
356 print "Browsing for services. Press CTRL-C to quit.\n"
def add_service(self, sname, port, stype=None, sdomain=None, host=None, properties=None, commit=True)
def itemnew_handler(self, bus, sender_name, object_path, interface_name, signal_name, interface, protocol, sname, stype, sdomain, flags, data)
def allfornow_handler(self, bus, sender_name, object_path, interface_name, signal_name, parameters, data)
def failure_handler(self, bus, sender_name, object_path, interface_name, signal_name, parameters, data)
def __init__(self, listener, stype=DEFAULT_SERVICE_TYPE, sdomain=DEFAULT_SERVICE_DOMAIN, ignore_local=False)
def set_properties(self, properties=None)
def __init__(self, group, sname, port, stype=None, sdomain=None, host=None, properties=None)
def itemremove_handler(self, bus, sender_name, object_path, interface_name, signal_name, interface, protocol, sname, stype, sdomain, flags, data)
def properties_to_txt_array(properties)
def __exit__(self, exc_type, exc_value, traceback)
def print_msg(self, action, sname, stype, sdomain, host, port, properties)