forked from mopidy/mopidy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathzeroconf.py
More file actions
138 lines (108 loc) · 4.21 KB
/
zeroconf.py
File metadata and controls
138 lines (108 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
from __future__ import absolute_import, unicode_literals
import logging
import string
logger = logging.getLogger(__name__)
try:
import dbus
except ImportError:
dbus = None
_AVAHI_IF_UNSPEC = -1
_AVAHI_PROTO_UNSPEC = -1
_AVAHI_PUBLISHFLAGS_NONE = 0
def _is_loopback_address(host):
return (
host.startswith('127.') or
host.startswith('::ffff:127.') or
host == '::1')
def _convert_text_list_to_dbus_format(text_list):
array = dbus.Array(signature='ay')
for text in text_list:
array.append([dbus.Byte(ord(c)) for c in text])
return array
class Zeroconf(object):
"""Publish a network service with Zeroconf.
Currently, this only works on Linux using Avahi via D-Bus.
:param str name: human readable name of the service, e.g. 'MPD on neptune'
:param str stype: service type, e.g. '_mpd._tcp'
:param int port: TCP port of the service, e.g. 6600
:param str domain: local network domain name, defaults to ''
:param str host: interface to advertise the service on, defaults to ''
:param text: extra information depending on ``stype``, defaults to empty
list
:type text: list of str
"""
def __init__(self, name, stype, port, domain='', host='', text=None):
self.stype = stype
self.port = port
self.domain = domain
self.host = host
self.text = text or []
self.bus = None
self.server = None
self.group = None
self.display_hostname = None
self.name = None
if dbus:
try:
self.bus = dbus.SystemBus()
self.server = dbus.Interface(
self.bus.get_object('org.freedesktop.Avahi', '/'),
'org.freedesktop.Avahi.Server')
self.display_hostname = '%s' % self.server.GetHostName()
self.name = string.Template(name).safe_substitute(
hostname=self.display_hostname, port=port)
except dbus.exceptions.DBusException as e:
logger.debug('%s: Server failed: %s', self, e)
def __str__(self):
return 'Zeroconf service "%s" (%s at [%s]:%d)' % (
self.name, self.stype, self.host, self.port)
def publish(self):
"""Publish the service.
Call when your service starts.
"""
if _is_loopback_address(self.host):
logger.debug(
'%s: Publish on loopback interface is not supported.', self)
return False
if not dbus:
logger.debug('%s: dbus not installed; publish failed.', self)
return False
if not self.bus:
logger.debug('%s: Bus not available; publish failed.', self)
return False
if not self.server:
logger.debug('%s: Server not available; publish failed.', self)
return False
try:
if not self.bus.name_has_owner('org.freedesktop.Avahi'):
logger.debug(
'%s: Avahi service not running; publish failed.', self)
return False
self.group = dbus.Interface(
self.bus.get_object(
'org.freedesktop.Avahi', self.server.EntryGroupNew()),
'org.freedesktop.Avahi.EntryGroup')
self.group.AddService(
_AVAHI_IF_UNSPEC, _AVAHI_PROTO_UNSPEC,
dbus.UInt32(_AVAHI_PUBLISHFLAGS_NONE),
self.name, self.stype,
self.domain, self.host, dbus.UInt16(self.port),
_convert_text_list_to_dbus_format(self.text))
self.group.Commit()
logger.debug('%s: Published', self)
return True
except dbus.exceptions.DBusException as e:
logger.debug('%s: Publish failed: %s', self, e)
return False
def unpublish(self):
"""Unpublish the service.
Call when your service shuts down.
"""
if self.group:
try:
self.group.Reset()
logger.debug('%s: Unpublished', self)
except dbus.exceptions.DBusException as e:
logger.debug('%s: Unpublish failed: %s', self, e)
finally:
self.group = None