6
6
# This program is partly derived from an example program for an Avahi
7
7
# service publisher, downloaded from
8
8
# <http://avahi.org/wiki/PythonPublishExample>. This includes the
9
# methods "add", "remove", "server_state_changed",
10
# "entry_group_state_changed", "cleanup", and "activate" in the
11
# "AvahiService" class, and some lines in "main".
9
# methods "add" and "remove" in the "AvahiService" class, the
10
# "server_state_changed" and "entry_group_state_changed" functions,
11
# and some lines in "main".
13
13
# Everything else is
14
14
# Copyright © 2008,2009 Teddy Hogeborn
125
124
max_renames: integer; maximum number of renames
126
125
rename_count: integer; counter so we only rename after collisions
127
126
a sensible number of times
128
group: D-Bus Entry Group
131
128
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
132
129
servicetype = None, port = None, TXT = None,
133
130
domain = u"", host = u"", max_renames = 32768,
134
protocol = avahi.PROTO_UNSPEC, bus = None):
131
protocol = avahi.PROTO_UNSPEC):
135
132
self.interface = interface
137
134
self.type = servicetype
152
146
u" after %i retries, exiting.",
153
147
self.rename_count)
154
148
raise AvahiServiceError(u"Too many renames")
155
self.name = self.server.GetAlternativeServiceName(self.name)
149
self.name = server.GetAlternativeServiceName(self.name)
156
150
logger.info(u"Changing Zeroconf service name to %r ...",
158
152
syslogger.setFormatter(logging.Formatter
159
153
(u'Mandos (%s) [%%(process)d]:'
160
154
u' %%(levelname)s: %%(message)s'
164
158
self.rename_count += 1
165
159
def remove(self):
166
160
"""Derived from the Avahi example code"""
167
if self.group is not None:
161
if group is not None:
170
164
"""Derived from the Avahi example code"""
171
if self.group is None:
172
self.group = dbus.Interface(
173
self.bus.get_object(avahi.DBUS_NAME,
174
self.server.EntryGroupNew()),
175
avahi.DBUS_INTERFACE_ENTRY_GROUP)
176
self.group.connect_to_signal('StateChanged',
177
self.entry_group_state_changed)
167
group = dbus.Interface(bus.get_object
169
server.EntryGroupNew()),
170
avahi.DBUS_INTERFACE_ENTRY_GROUP)
171
group.connect_to_signal('StateChanged',
172
entry_group_state_changed)
178
173
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
179
self.name, self.type)
180
self.group.AddService(
183
dbus.UInt32(0), # flags
184
self.name, self.type,
185
self.domain, self.host,
186
dbus.UInt16(self.port),
187
avahi.string_array_to_txt_array(self.TXT))
189
def entry_group_state_changed(self, state, error):
190
"""Derived from the Avahi example code"""
191
logger.debug(u"Avahi state change: %i", state)
193
if state == avahi.ENTRY_GROUP_ESTABLISHED:
194
logger.debug(u"Zeroconf service established.")
195
elif state == avahi.ENTRY_GROUP_COLLISION:
196
logger.warning(u"Zeroconf service name collision.")
198
elif state == avahi.ENTRY_GROUP_FAILURE:
199
logger.critical(u"Avahi: Error in group state changed %s",
201
raise AvahiGroupError(u"State changed: %s"
204
"""Derived from the Avahi example code"""
205
if self.group is not None:
208
def server_state_changed(self, state):
209
"""Derived from the Avahi example code"""
210
if state == avahi.SERVER_COLLISION:
211
logger.error(u"Zeroconf server name collision")
213
elif state == avahi.SERVER_RUNNING:
216
"""Derived from the Avahi example code"""
217
if self.server is None:
218
self.server = dbus.Interface(
219
self.bus.get_object(avahi.DBUS_NAME,
220
avahi.DBUS_PATH_SERVER),
221
avahi.DBUS_INTERFACE_SERVER)
222
self.server.connect_to_signal(u"StateChanged",
223
self.server_state_changed)
224
self.server_state_changed(self.server.GetState())
174
service.name, service.type)
176
self.interface, # interface
177
self.protocol, # protocol
178
dbus.UInt32(0), # flags
179
self.name, self.type,
180
self.domain, self.host,
181
dbus.UInt16(self.port),
182
avahi.string_array_to_txt_array(self.TXT))
185
# From the Avahi example code:
186
group = None # our entry group
187
# End of Avahi example code
190
def _datetime_to_dbus(dt, variant_level=0):
191
"""Convert a UTC datetime.datetime() to a D-Bus type."""
192
return dbus.String(dt.isoformat(), variant_level=variant_level)
227
195
class Client(object):
483
451
# dbus.service.Object doesn't use super(), so we can't either.
485
def __init__(self, bus = None, *args, **kwargs):
453
def __init__(self, *args, **kwargs):
487
454
Client.__init__(self, *args, **kwargs)
488
455
# Only now, when this client is initialized, can it show up on
490
457
self.dbus_object_path = (dbus.ObjectPath
492
459
+ self.name.replace(u".", u"_")))
493
dbus.service.Object.__init__(self, self.bus,
460
dbus.service.Object.__init__(self, bus,
494
461
self.dbus_object_path)
497
def _datetime_to_dbus(dt, variant_level=0):
498
"""Convert a UTC datetime.datetime() to a D-Bus type."""
499
return dbus.String(dt.isoformat(),
500
variant_level=variant_level)
502
462
def enable(self):
503
463
oldstate = getattr(self, u"enabled", False)
504
464
r = Client.enable(self)
506
466
# Emit D-Bus signals
507
467
self.PropertyChanged(dbus.String(u"enabled"),
508
468
dbus.Boolean(True, variant_level=1))
509
self.PropertyChanged(
510
dbus.String(u"last_enabled"),
511
self._datetime_to_dbus(self.last_enabled,
469
self.PropertyChanged(dbus.String(u"last_enabled"),
470
(_datetime_to_dbus(self.last_enabled,
515
474
def disable(self, signal = True):
619
578
dbus.String(u"host"):
620
579
dbus.String(self.host, variant_level=1),
621
580
dbus.String(u"created"):
622
self._datetime_to_dbus(self.created,
581
_datetime_to_dbus(self.created, variant_level=1),
624
582
dbus.String(u"last_enabled"):
625
(self._datetime_to_dbus(self.last_enabled,
583
(_datetime_to_dbus(self.last_enabled,
627
585
if self.last_enabled is not None
628
586
else dbus.Boolean(False, variant_level=1)),
629
587
dbus.String(u"enabled"):
630
588
dbus.Boolean(self.enabled, variant_level=1),
631
589
dbus.String(u"last_checked_ok"):
632
(self._datetime_to_dbus(self.last_checked_ok,
590
(_datetime_to_dbus(self.last_checked_ok,
634
592
if self.last_checked_ok is not None
635
593
else dbus.Boolean (False, variant_level=1)),
636
594
dbus.String(u"timeout"):
1091
1049
return timevalue
1052
def server_state_changed(state):
1053
"""Derived from the Avahi example code"""
1054
if state == avahi.SERVER_COLLISION:
1055
logger.error(u"Zeroconf server name collision")
1057
elif state == avahi.SERVER_RUNNING:
1061
def entry_group_state_changed(state, error):
1062
"""Derived from the Avahi example code"""
1063
logger.debug(u"Avahi state change: %i", state)
1065
if state == avahi.ENTRY_GROUP_ESTABLISHED:
1066
logger.debug(u"Zeroconf service established.")
1067
elif state == avahi.ENTRY_GROUP_COLLISION:
1068
logger.warning(u"Zeroconf service name collision.")
1070
elif state == avahi.ENTRY_GROUP_FAILURE:
1071
logger.critical(u"Avahi: Error in group state changed %s",
1073
raise AvahiGroupError(u"State changed: %s" % unicode(error))
1094
1075
def if_nametoindex(interface):
1095
1076
"""Call the C function if_nametoindex(), or equivalent
1300
1281
(gnutls.library.functions
1301
1282
.gnutls_global_set_log_function(debug_gnutls))
1285
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1286
service = AvahiService(name = server_settings[u"servicename"],
1287
servicetype = u"_mandos._tcp",
1288
protocol = protocol)
1289
if server_settings["interface"]:
1290
service.interface = (if_nametoindex
1291
(str(server_settings[u"interface"])))
1303
1293
global main_loop
1304
1296
# From the Avahi example code
1305
1297
DBusGMainLoop(set_as_default=True )
1306
1298
main_loop = gobject.MainLoop()
1307
1299
bus = dbus.SystemBus()
1300
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1301
avahi.DBUS_PATH_SERVER),
1302
avahi.DBUS_INTERFACE_SERVER)
1308
1303
# End of Avahi example code
1310
1305
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1311
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1312
service = AvahiService(name = server_settings[u"servicename"],
1313
servicetype = u"_mandos._tcp",
1314
protocol = protocol, bus = bus)
1315
if server_settings["interface"]:
1316
service.interface = (if_nametoindex
1317
(str(server_settings[u"interface"])))
1319
1307
client_class = Client
1321
client_class = functools.partial(ClientDBus, bus = bus)
1309
client_class = ClientDBus
1322
1310
clients.update(set(
1323
1311
client_class(name = section,
1324
1312
config= dict(client_config.items(section)))