/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2010-06-19 00:37:04 UTC
  • mto: (24.1.149 mandos)
  • mto: This revision was merged to the branch mainline in revision 417.
  • Revision ID: teddy@fukt.bsnet.se-20100619003704-vpicvssvv1ktg2om
* mandos (ClientHandler.handle): Set up the GnuTLS session object
                                 before reading the protocol number.
 (ClientHandler.handle/ProxyObject): New.

Show diffs side-by-side

added added

removed removed

Lines of Context:
55
55
import logging
56
56
import logging.handlers
57
57
import pwd
58
 
from contextlib import closing
 
58
import contextlib
59
59
import struct
60
60
import fcntl
61
61
import functools
 
62
import cPickle as pickle
 
63
import select
62
64
 
63
65
import dbus
64
66
import dbus.service
67
69
from dbus.mainloop.glib import DBusGMainLoop
68
70
import ctypes
69
71
import ctypes.util
 
72
import xml.dom.minidom
 
73
import inspect
70
74
 
71
75
try:
72
76
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
77
81
        SO_BINDTODEVICE = None
78
82
 
79
83
 
80
 
version = "1.0.8"
 
84
version = "1.0.14"
81
85
 
82
86
logger = logging.Logger(u'mandos')
83
87
syslogger = (logging.handlers.SysLogHandler
174
178
                                    self.server.EntryGroupNew()),
175
179
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
176
180
            self.group.connect_to_signal('StateChanged',
177
 
                                         self.entry_group_state_changed)
 
181
                                         self
 
182
                                         .entry_group_state_changed)
178
183
        logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
179
184
                     self.name, self.type)
180
185
        self.group.AddService(
239
244
    enabled:    bool()
240
245
    last_checked_ok: datetime.datetime(); (UTC) or None
241
246
    timeout:    datetime.timedelta(); How long from last_checked_ok
242
 
                                      until this client is invalid
 
247
                                      until this client is disabled
243
248
    interval:   datetime.timedelta(); How often to start a new checker
244
249
    disable_hook:  If set, called by disable() as disable_hook(self)
245
250
    checker:    subprocess.Popen(); a running checker process used
246
251
                                    to see if the client lives.
247
252
                                    'None' if no process is running.
248
253
    checker_initiator_tag: a gobject event source tag, or None
249
 
    disable_initiator_tag:    - '' -
 
254
    disable_initiator_tag: - '' -
250
255
    checker_callback_tag:  - '' -
251
256
    checker_command: string; External command which is run to check if
252
257
                     client lives.  %() expansions are done at
256
261
    """
257
262
    
258
263
    @staticmethod
259
 
    def _datetime_to_milliseconds(dt):
260
 
        "Convert a datetime.datetime() to milliseconds"
261
 
        return ((dt.days * 24 * 60 * 60 * 1000)
262
 
                + (dt.seconds * 1000)
263
 
                + (dt.microseconds // 1000))
 
264
    def _timedelta_to_milliseconds(td):
 
265
        "Convert a datetime.timedelta() to milliseconds"
 
266
        return ((td.days * 24 * 60 * 60 * 1000)
 
267
                + (td.seconds * 1000)
 
268
                + (td.microseconds // 1000))
264
269
    
265
270
    def timeout_milliseconds(self):
266
271
        "Return the 'timeout' attribute in milliseconds"
267
 
        return self._datetime_to_milliseconds(self.timeout)
 
272
        return self._timedelta_to_milliseconds(self.timeout)
268
273
    
269
274
    def interval_milliseconds(self):
270
275
        "Return the 'interval' attribute in milliseconds"
271
 
        return self._datetime_to_milliseconds(self.interval)
 
276
        return self._timedelta_to_milliseconds(self.interval)
272
277
    
273
278
    def __init__(self, name = None, disable_hook=None, config=None):
274
279
        """Note: the 'checker' key in 'config' sets the
287
292
        if u"secret" in config:
288
293
            self.secret = config[u"secret"].decode(u"base64")
289
294
        elif u"secfile" in config:
290
 
            with closing(open(os.path.expanduser
291
 
                              (os.path.expandvars
292
 
                               (config[u"secfile"])))) as secfile:
 
295
            with open(os.path.expanduser(os.path.expandvars
 
296
                                         (config[u"secfile"])),
 
297
                      "rb") as secfile:
293
298
                self.secret = secfile.read()
294
299
        else:
295
300
            raise TypeError(u"No secret or secfile for client %s"
321
326
        self.checker_initiator_tag = (gobject.timeout_add
322
327
                                      (self.interval_milliseconds(),
323
328
                                       self.start_checker))
324
 
        # Also start a new checker *right now*.
325
 
        self.start_checker()
326
329
        # Schedule a disable() when 'timeout' has passed
327
330
        self.disable_initiator_tag = (gobject.timeout_add
328
331
                                   (self.timeout_milliseconds(),
329
332
                                    self.disable))
330
333
        self.enabled = True
 
334
        # Also start a new checker *right now*.
 
335
        self.start_checker()
331
336
    
332
 
    def disable(self):
 
337
    def disable(self, quiet=True):
333
338
        """Disable this client."""
334
339
        if not getattr(self, "enabled", False):
335
340
            return False
336
 
        logger.info(u"Disabling client %s", self.name)
 
341
        if not quiet:
 
342
            logger.info(u"Disabling client %s", self.name)
337
343
        if getattr(self, u"disable_initiator_tag", False):
338
344
            gobject.source_remove(self.disable_initiator_tag)
339
345
            self.disable_initiator_tag = None
391
397
        # client would inevitably timeout, since no checker would get
392
398
        # a chance to run to completion.  If we instead leave running
393
399
        # checkers alone, the checker would have to take more time
394
 
        # than 'timeout' for the client to be declared invalid, which
395
 
        # is as it should be.
 
400
        # than 'timeout' for the client to be disabled, which is as it
 
401
        # should be.
396
402
        
397
403
        # If a checker exists, make sure it is not a zombie
398
 
        if self.checker is not None:
 
404
        try:
399
405
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
 
406
        except (AttributeError, OSError), error:
 
407
            if (isinstance(error, OSError)
 
408
                and error.errno != errno.ECHILD):
 
409
                raise error
 
410
        else:
400
411
            if pid:
401
412
                logger.warning(u"Checker was a zombie")
402
413
                gobject.source_remove(self.checker_callback_tag)
458
469
        logger.debug(u"Stopping checker for %(name)s", vars(self))
459
470
        try:
460
471
            os.kill(self.checker.pid, signal.SIGTERM)
461
 
            #os.sleep(0.5)
 
472
            #time.sleep(0.5)
462
473
            #if self.checker.poll() is None:
463
474
            #    os.kill(self.checker.pid, signal.SIGKILL)
464
475
        except OSError, error:
465
476
            if error.errno != errno.ESRCH: # No such process
466
477
                raise
467
478
        self.checker = None
468
 
    
469
 
    def still_valid(self):
470
 
        """Has the timeout not yet passed for this client?"""
471
 
        if not getattr(self, u"enabled", False):
472
 
            return False
473
 
        now = datetime.datetime.utcnow()
474
 
        if self.last_checked_ok is None:
475
 
            return now < (self.created + self.timeout)
476
 
        else:
477
 
            return now < (self.last_checked_ok + self.timeout)
478
 
 
479
 
 
480
 
class ClientDBus(Client, dbus.service.Object):
 
479
 
 
480
 
 
481
def dbus_service_property(dbus_interface, signature=u"v",
 
482
                          access=u"readwrite", byte_arrays=False):
 
483
    """Decorators for marking methods of a DBusObjectWithProperties to
 
484
    become properties on the D-Bus.
 
485
    
 
486
    The decorated method will be called with no arguments by "Get"
 
487
    and with one argument by "Set".
 
488
    
 
489
    The parameters, where they are supported, are the same as
 
490
    dbus.service.method, except there is only "signature", since the
 
491
    type from Get() and the type sent to Set() is the same.
 
492
    """
 
493
    # Encoding deeply encoded byte arrays is not supported yet by the
 
494
    # "Set" method, so we fail early here:
 
495
    if byte_arrays and signature != u"ay":
 
496
        raise ValueError(u"Byte arrays not supported for non-'ay'"
 
497
                         u" signature %r" % signature)
 
498
    def decorator(func):
 
499
        func._dbus_is_property = True
 
500
        func._dbus_interface = dbus_interface
 
501
        func._dbus_signature = signature
 
502
        func._dbus_access = access
 
503
        func._dbus_name = func.__name__
 
504
        if func._dbus_name.endswith(u"_dbus_property"):
 
505
            func._dbus_name = func._dbus_name[:-14]
 
506
        func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
 
507
        return func
 
508
    return decorator
 
509
 
 
510
 
 
511
class DBusPropertyException(dbus.exceptions.DBusException):
 
512
    """A base class for D-Bus property-related exceptions
 
513
    """
 
514
    def __unicode__(self):
 
515
        return unicode(str(self))
 
516
 
 
517
 
 
518
class DBusPropertyAccessException(DBusPropertyException):
 
519
    """A property's access permissions disallows an operation.
 
520
    """
 
521
    pass
 
522
 
 
523
 
 
524
class DBusPropertyNotFound(DBusPropertyException):
 
525
    """An attempt was made to access a non-existing property.
 
526
    """
 
527
    pass
 
528
 
 
529
 
 
530
class DBusObjectWithProperties(dbus.service.Object):
 
531
    """A D-Bus object with properties.
 
532
 
 
533
    Classes inheriting from this can use the dbus_service_property
 
534
    decorator to expose methods as D-Bus properties.  It exposes the
 
535
    standard Get(), Set(), and GetAll() methods on the D-Bus.
 
536
    """
 
537
    
 
538
    @staticmethod
 
539
    def _is_dbus_property(obj):
 
540
        return getattr(obj, u"_dbus_is_property", False)
 
541
    
 
542
    def _get_all_dbus_properties(self):
 
543
        """Returns a generator of (name, attribute) pairs
 
544
        """
 
545
        return ((prop._dbus_name, prop)
 
546
                for name, prop in
 
547
                inspect.getmembers(self, self._is_dbus_property))
 
548
    
 
549
    def _get_dbus_property(self, interface_name, property_name):
 
550
        """Returns a bound method if one exists which is a D-Bus
 
551
        property with the specified name and interface.
 
552
        """
 
553
        for name in (property_name,
 
554
                     property_name + u"_dbus_property"):
 
555
            prop = getattr(self, name, None)
 
556
            if (prop is None
 
557
                or not self._is_dbus_property(prop)
 
558
                or prop._dbus_name != property_name
 
559
                or (interface_name and prop._dbus_interface
 
560
                    and interface_name != prop._dbus_interface)):
 
561
                continue
 
562
            return prop
 
563
        # No such property
 
564
        raise DBusPropertyNotFound(self.dbus_object_path + u":"
 
565
                                   + interface_name + u"."
 
566
                                   + property_name)
 
567
    
 
568
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
 
569
                         out_signature=u"v")
 
570
    def Get(self, interface_name, property_name):
 
571
        """Standard D-Bus property Get() method, see D-Bus standard.
 
572
        """
 
573
        prop = self._get_dbus_property(interface_name, property_name)
 
574
        if prop._dbus_access == u"write":
 
575
            raise DBusPropertyAccessException(property_name)
 
576
        value = prop()
 
577
        if not hasattr(value, u"variant_level"):
 
578
            return value
 
579
        return type(value)(value, variant_level=value.variant_level+1)
 
580
    
 
581
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
 
582
    def Set(self, interface_name, property_name, value):
 
583
        """Standard D-Bus property Set() method, see D-Bus standard.
 
584
        """
 
585
        prop = self._get_dbus_property(interface_name, property_name)
 
586
        if prop._dbus_access == u"read":
 
587
            raise DBusPropertyAccessException(property_name)
 
588
        if prop._dbus_get_args_options[u"byte_arrays"]:
 
589
            # The byte_arrays option is not supported yet on
 
590
            # signatures other than "ay".
 
591
            if prop._dbus_signature != u"ay":
 
592
                raise ValueError
 
593
            value = dbus.ByteArray(''.join(unichr(byte)
 
594
                                           for byte in value))
 
595
        prop(value)
 
596
    
 
597
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
 
598
                         out_signature=u"a{sv}")
 
599
    def GetAll(self, interface_name):
 
600
        """Standard D-Bus property GetAll() method, see D-Bus
 
601
        standard.
 
602
 
 
603
        Note: Will not include properties with access="write".
 
604
        """
 
605
        all = {}
 
606
        for name, prop in self._get_all_dbus_properties():
 
607
            if (interface_name
 
608
                and interface_name != prop._dbus_interface):
 
609
                # Interface non-empty but did not match
 
610
                continue
 
611
            # Ignore write-only properties
 
612
            if prop._dbus_access == u"write":
 
613
                continue
 
614
            value = prop()
 
615
            if not hasattr(value, u"variant_level"):
 
616
                all[name] = value
 
617
                continue
 
618
            all[name] = type(value)(value, variant_level=
 
619
                                    value.variant_level+1)
 
620
        return dbus.Dictionary(all, signature=u"sv")
 
621
    
 
622
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
 
623
                         out_signature=u"s",
 
624
                         path_keyword='object_path',
 
625
                         connection_keyword='connection')
 
626
    def Introspect(self, object_path, connection):
 
627
        """Standard D-Bus method, overloaded to insert property tags.
 
628
        """
 
629
        xmlstring = dbus.service.Object.Introspect(self, object_path,
 
630
                                                   connection)
 
631
        try:
 
632
            document = xml.dom.minidom.parseString(xmlstring)
 
633
            def make_tag(document, name, prop):
 
634
                e = document.createElement(u"property")
 
635
                e.setAttribute(u"name", name)
 
636
                e.setAttribute(u"type", prop._dbus_signature)
 
637
                e.setAttribute(u"access", prop._dbus_access)
 
638
                return e
 
639
            for if_tag in document.getElementsByTagName(u"interface"):
 
640
                for tag in (make_tag(document, name, prop)
 
641
                            for name, prop
 
642
                            in self._get_all_dbus_properties()
 
643
                            if prop._dbus_interface
 
644
                            == if_tag.getAttribute(u"name")):
 
645
                    if_tag.appendChild(tag)
 
646
                # Add the names to the return values for the
 
647
                # "org.freedesktop.DBus.Properties" methods
 
648
                if (if_tag.getAttribute(u"name")
 
649
                    == u"org.freedesktop.DBus.Properties"):
 
650
                    for cn in if_tag.getElementsByTagName(u"method"):
 
651
                        if cn.getAttribute(u"name") == u"Get":
 
652
                            for arg in cn.getElementsByTagName(u"arg"):
 
653
                                if (arg.getAttribute(u"direction")
 
654
                                    == u"out"):
 
655
                                    arg.setAttribute(u"name", u"value")
 
656
                        elif cn.getAttribute(u"name") == u"GetAll":
 
657
                            for arg in cn.getElementsByTagName(u"arg"):
 
658
                                if (arg.getAttribute(u"direction")
 
659
                                    == u"out"):
 
660
                                    arg.setAttribute(u"name", u"props")
 
661
            xmlstring = document.toxml(u"utf-8")
 
662
            document.unlink()
 
663
        except (AttributeError, xml.dom.DOMException,
 
664
                xml.parsers.expat.ExpatError), error:
 
665
            logger.error(u"Failed to override Introspection method",
 
666
                         error)
 
667
        return xmlstring
 
668
 
 
669
 
 
670
class ClientDBus(Client, DBusObjectWithProperties):
481
671
    """A Client class using D-Bus
482
672
    
483
673
    Attributes:
494
684
        self.dbus_object_path = (dbus.ObjectPath
495
685
                                 (u"/clients/"
496
686
                                  + self.name.replace(u".", u"_")))
497
 
        dbus.service.Object.__init__(self, self.bus,
498
 
                                     self.dbus_object_path)
 
687
        DBusObjectWithProperties.__init__(self, self.bus,
 
688
                                          self.dbus_object_path)
499
689
    
500
690
    @staticmethod
501
691
    def _datetime_to_dbus(dt, variant_level=0):
516
706
                                       variant_level=1))
517
707
        return r
518
708
    
519
 
    def disable(self, signal = True):
 
709
    def disable(self, quiet = False):
520
710
        oldstate = getattr(self, u"enabled", False)
521
 
        r = Client.disable(self)
522
 
        if signal and oldstate != self.enabled:
 
711
        r = Client.disable(self, quiet=quiet)
 
712
        if not quiet and oldstate != self.enabled:
523
713
            # Emit D-Bus signal
524
714
            self.PropertyChanged(dbus.String(u"enabled"),
525
715
                                 dbus.Boolean(False, variant_level=1))
530
720
            self.remove_from_connection()
531
721
        except LookupError:
532
722
            pass
533
 
        if hasattr(dbus.service.Object, u"__del__"):
534
 
            dbus.service.Object.__del__(self, *args, **kwargs)
 
723
        if hasattr(DBusObjectWithProperties, u"__del__"):
 
724
            DBusObjectWithProperties.__del__(self, *args, **kwargs)
535
725
        Client.__del__(self, *args, **kwargs)
536
726
    
537
727
    def checker_callback(self, pid, condition, command,
591
781
                                 dbus.Boolean(False, variant_level=1))
592
782
        return r
593
783
    
594
 
    ## D-Bus methods & signals
 
784
    ## D-Bus methods, signals & properties
595
785
    _interface = u"se.bsnet.fukt.Mandos.Client"
596
786
    
597
 
    # CheckedOK - method
598
 
    @dbus.service.method(_interface)
599
 
    def CheckedOK(self):
600
 
        return self.checked_ok()
 
787
    ## Signals
601
788
    
602
789
    # CheckerCompleted - signal
603
790
    @dbus.service.signal(_interface, signature=u"nxs")
611
798
        "D-Bus signal"
612
799
        pass
613
800
    
614
 
    # GetAllProperties - method
615
 
    @dbus.service.method(_interface, out_signature=u"a{sv}")
616
 
    def GetAllProperties(self):
617
 
        "D-Bus method"
618
 
        return dbus.Dictionary({
619
 
                dbus.String(u"name"):
620
 
                    dbus.String(self.name, variant_level=1),
621
 
                dbus.String(u"fingerprint"):
622
 
                    dbus.String(self.fingerprint, variant_level=1),
623
 
                dbus.String(u"host"):
624
 
                    dbus.String(self.host, variant_level=1),
625
 
                dbus.String(u"created"):
626
 
                    self._datetime_to_dbus(self.created,
627
 
                                           variant_level=1),
628
 
                dbus.String(u"last_enabled"):
629
 
                    (self._datetime_to_dbus(self.last_enabled,
630
 
                                            variant_level=1)
631
 
                     if self.last_enabled is not None
632
 
                     else dbus.Boolean(False, variant_level=1)),
633
 
                dbus.String(u"enabled"):
634
 
                    dbus.Boolean(self.enabled, variant_level=1),
635
 
                dbus.String(u"last_checked_ok"):
636
 
                    (self._datetime_to_dbus(self.last_checked_ok,
637
 
                                            variant_level=1)
638
 
                     if self.last_checked_ok is not None
639
 
                     else dbus.Boolean (False, variant_level=1)),
640
 
                dbus.String(u"timeout"):
641
 
                    dbus.UInt64(self.timeout_milliseconds(),
642
 
                                variant_level=1),
643
 
                dbus.String(u"interval"):
644
 
                    dbus.UInt64(self.interval_milliseconds(),
645
 
                                variant_level=1),
646
 
                dbus.String(u"checker"):
647
 
                    dbus.String(self.checker_command,
648
 
                                variant_level=1),
649
 
                dbus.String(u"checker_running"):
650
 
                    dbus.Boolean(self.checker is not None,
651
 
                                 variant_level=1),
652
 
                dbus.String(u"object_path"):
653
 
                    dbus.ObjectPath(self.dbus_object_path,
654
 
                                    variant_level=1)
655
 
                }, signature=u"sv")
656
 
    
657
 
    # IsStillValid - method
658
 
    @dbus.service.method(_interface, out_signature=u"b")
659
 
    def IsStillValid(self):
660
 
        return self.still_valid()
661
 
    
662
801
    # PropertyChanged - signal
663
802
    @dbus.service.signal(_interface, signature=u"sv")
664
803
    def PropertyChanged(self, property, value):
665
804
        "D-Bus signal"
666
805
        pass
667
806
    
668
 
    # ReceivedSecret - signal
 
807
    # GotSecret - signal
669
808
    @dbus.service.signal(_interface)
670
 
    def ReceivedSecret(self):
 
809
    def GotSecret(self):
671
810
        "D-Bus signal"
672
811
        pass
673
812
    
677
816
        "D-Bus signal"
678
817
        pass
679
818
    
680
 
    # SetChecker - method
681
 
    @dbus.service.method(_interface, in_signature=u"s")
682
 
    def SetChecker(self, checker):
683
 
        "D-Bus setter method"
684
 
        self.checker_command = checker
685
 
        # Emit D-Bus signal
686
 
        self.PropertyChanged(dbus.String(u"checker"),
687
 
                             dbus.String(self.checker_command,
688
 
                                         variant_level=1))
689
 
    
690
 
    # SetHost - method
691
 
    @dbus.service.method(_interface, in_signature=u"s")
692
 
    def SetHost(self, host):
693
 
        "D-Bus setter method"
694
 
        self.host = host
695
 
        # Emit D-Bus signal
696
 
        self.PropertyChanged(dbus.String(u"host"),
697
 
                             dbus.String(self.host, variant_level=1))
698
 
    
699
 
    # SetInterval - method
700
 
    @dbus.service.method(_interface, in_signature=u"t")
701
 
    def SetInterval(self, milliseconds):
702
 
        self.interval = datetime.timedelta(0, 0, 0, milliseconds)
703
 
        # Emit D-Bus signal
704
 
        self.PropertyChanged(dbus.String(u"interval"),
705
 
                             (dbus.UInt64(self.interval_milliseconds(),
706
 
                                          variant_level=1)))
707
 
    
708
 
    # SetSecret - method
709
 
    @dbus.service.method(_interface, in_signature=u"ay",
710
 
                         byte_arrays=True)
711
 
    def SetSecret(self, secret):
712
 
        "D-Bus setter method"
713
 
        self.secret = str(secret)
714
 
    
715
 
    # SetTimeout - method
716
 
    @dbus.service.method(_interface, in_signature=u"t")
717
 
    def SetTimeout(self, milliseconds):
718
 
        self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
719
 
        # Emit D-Bus signal
720
 
        self.PropertyChanged(dbus.String(u"timeout"),
721
 
                             (dbus.UInt64(self.timeout_milliseconds(),
722
 
                                          variant_level=1)))
 
819
    ## Methods
 
820
    
 
821
    # CheckedOK - method
 
822
    @dbus.service.method(_interface)
 
823
    def CheckedOK(self):
 
824
        return self.checked_ok()
723
825
    
724
826
    # Enable - method
725
827
    @dbus.service.method(_interface)
744
846
    def StopChecker(self):
745
847
        self.stop_checker()
746
848
    
 
849
    ## Properties
 
850
    
 
851
    # name - property
 
852
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
853
    def name_dbus_property(self):
 
854
        return dbus.String(self.name)
 
855
    
 
856
    # fingerprint - property
 
857
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
858
    def fingerprint_dbus_property(self):
 
859
        return dbus.String(self.fingerprint)
 
860
    
 
861
    # host - property
 
862
    @dbus_service_property(_interface, signature=u"s",
 
863
                           access=u"readwrite")
 
864
    def host_dbus_property(self, value=None):
 
865
        if value is None:       # get
 
866
            return dbus.String(self.host)
 
867
        self.host = value
 
868
        # Emit D-Bus signal
 
869
        self.PropertyChanged(dbus.String(u"host"),
 
870
                             dbus.String(value, variant_level=1))
 
871
    
 
872
    # created - property
 
873
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
874
    def created_dbus_property(self):
 
875
        return dbus.String(self._datetime_to_dbus(self.created))
 
876
    
 
877
    # last_enabled - property
 
878
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
879
    def last_enabled_dbus_property(self):
 
880
        if self.last_enabled is None:
 
881
            return dbus.String(u"")
 
882
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
 
883
    
 
884
    # enabled - property
 
885
    @dbus_service_property(_interface, signature=u"b",
 
886
                           access=u"readwrite")
 
887
    def enabled_dbus_property(self, value=None):
 
888
        if value is None:       # get
 
889
            return dbus.Boolean(self.enabled)
 
890
        if value:
 
891
            self.enable()
 
892
        else:
 
893
            self.disable()
 
894
    
 
895
    # last_checked_ok - property
 
896
    @dbus_service_property(_interface, signature=u"s",
 
897
                           access=u"readwrite")
 
898
    def last_checked_ok_dbus_property(self, value=None):
 
899
        if value is not None:
 
900
            self.checked_ok()
 
901
            return
 
902
        if self.last_checked_ok is None:
 
903
            return dbus.String(u"")
 
904
        return dbus.String(self._datetime_to_dbus(self
 
905
                                                  .last_checked_ok))
 
906
    
 
907
    # timeout - property
 
908
    @dbus_service_property(_interface, signature=u"t",
 
909
                           access=u"readwrite")
 
910
    def timeout_dbus_property(self, value=None):
 
911
        if value is None:       # get
 
912
            return dbus.UInt64(self.timeout_milliseconds())
 
913
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
914
        # Emit D-Bus signal
 
915
        self.PropertyChanged(dbus.String(u"timeout"),
 
916
                             dbus.UInt64(value, variant_level=1))
 
917
        if getattr(self, u"disable_initiator_tag", None) is None:
 
918
            return
 
919
        # Reschedule timeout
 
920
        gobject.source_remove(self.disable_initiator_tag)
 
921
        self.disable_initiator_tag = None
 
922
        time_to_die = (self.
 
923
                       _timedelta_to_milliseconds((self
 
924
                                                   .last_checked_ok
 
925
                                                   + self.timeout)
 
926
                                                  - datetime.datetime
 
927
                                                  .utcnow()))
 
928
        if time_to_die <= 0:
 
929
            # The timeout has passed
 
930
            self.disable()
 
931
        else:
 
932
            self.disable_initiator_tag = (gobject.timeout_add
 
933
                                          (time_to_die, self.disable))
 
934
    
 
935
    # interval - property
 
936
    @dbus_service_property(_interface, signature=u"t",
 
937
                           access=u"readwrite")
 
938
    def interval_dbus_property(self, value=None):
 
939
        if value is None:       # get
 
940
            return dbus.UInt64(self.interval_milliseconds())
 
941
        self.interval = datetime.timedelta(0, 0, 0, value)
 
942
        # Emit D-Bus signal
 
943
        self.PropertyChanged(dbus.String(u"interval"),
 
944
                             dbus.UInt64(value, variant_level=1))
 
945
        if getattr(self, u"checker_initiator_tag", None) is None:
 
946
            return
 
947
        # Reschedule checker run
 
948
        gobject.source_remove(self.checker_initiator_tag)
 
949
        self.checker_initiator_tag = (gobject.timeout_add
 
950
                                      (value, self.start_checker))
 
951
        self.start_checker()    # Start one now, too
 
952
 
 
953
    # checker - property
 
954
    @dbus_service_property(_interface, signature=u"s",
 
955
                           access=u"readwrite")
 
956
    def checker_dbus_property(self, value=None):
 
957
        if value is None:       # get
 
958
            return dbus.String(self.checker_command)
 
959
        self.checker_command = value
 
960
        # Emit D-Bus signal
 
961
        self.PropertyChanged(dbus.String(u"checker"),
 
962
                             dbus.String(self.checker_command,
 
963
                                         variant_level=1))
 
964
    
 
965
    # checker_running - property
 
966
    @dbus_service_property(_interface, signature=u"b",
 
967
                           access=u"readwrite")
 
968
    def checker_running_dbus_property(self, value=None):
 
969
        if value is None:       # get
 
970
            return dbus.Boolean(self.checker is not None)
 
971
        if value:
 
972
            self.start_checker()
 
973
        else:
 
974
            self.stop_checker()
 
975
    
 
976
    # object_path - property
 
977
    @dbus_service_property(_interface, signature=u"o", access=u"read")
 
978
    def object_path_dbus_property(self):
 
979
        return self.dbus_object_path # is already a dbus.ObjectPath
 
980
    
 
981
    # secret = property
 
982
    @dbus_service_property(_interface, signature=u"ay",
 
983
                           access=u"write", byte_arrays=True)
 
984
    def secret_dbus_property(self, value):
 
985
        self.secret = str(value)
 
986
    
747
987
    del _interface
748
988
 
749
989
 
756
996
    def handle(self):
757
997
        logger.info(u"TCP connection from: %s",
758
998
                    unicode(self.client_address))
759
 
        logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
 
999
        logger.debug(u"IPC Pipe FD: %d",
 
1000
                     self.server.child_pipe[1].fileno())
760
1001
        # Open IPC pipe to parent process
761
 
        with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
 
1002
        with contextlib.nested(self.server.child_pipe[1],
 
1003
                               self.server.parent_pipe[0]
 
1004
                               ) as (ipc, ipc_return):
762
1005
            session = (gnutls.connection
763
1006
                       .ClientSession(self.request,
764
1007
                                      gnutls.connection
765
1008
                                      .X509Credentials()))
766
1009
            
767
 
            line = self.request.makefile().readline()
768
 
            logger.debug(u"Protocol version: %r", line)
769
 
            try:
770
 
                if int(line.strip().split()[0]) > 1:
771
 
                    raise RuntimeError
772
 
            except (ValueError, IndexError, RuntimeError), error:
773
 
                logger.error(u"Unknown protocol version: %s", error)
774
 
                return
775
 
            
776
1010
            # Note: gnutls.connection.X509Credentials is really a
777
1011
            # generic GnuTLS certificate credentials object so long as
778
1012
            # no X.509 keys are added to it.  Therefore, we can use it
790
1024
             .gnutls_priority_set_direct(session._c_object,
791
1025
                                         priority, None))
792
1026
            
 
1027
            # Start communication using the Mandos protocol
 
1028
            # Get protocol number
 
1029
            line = self.request.makefile().readline()
 
1030
            logger.debug(u"Protocol version: %r", line)
 
1031
            try:
 
1032
                if int(line.strip().split()[0]) > 1:
 
1033
                    raise RuntimeError
 
1034
            except (ValueError, IndexError, RuntimeError), error:
 
1035
                logger.error(u"Unknown protocol version: %s", error)
 
1036
                return
 
1037
            
 
1038
            # Start GnuTLS connection
793
1039
            try:
794
1040
                session.handshake()
795
1041
            except gnutls.errors.GNUTLSError, error:
799
1045
                return
800
1046
            logger.debug(u"Handshake succeeded")
801
1047
            try:
802
 
                fpr = self.fingerprint(self.peer_certificate(session))
803
 
            except (TypeError, gnutls.errors.GNUTLSError), error:
804
 
                logger.warning(u"Bad certificate: %s", error)
805
 
                session.bye()
806
 
                return
807
 
            logger.debug(u"Fingerprint: %s", fpr)
808
 
            
809
 
            for c in self.server.clients:
810
 
                if c.fingerprint == fpr:
811
 
                    client = c
812
 
                    break
813
 
            else:
814
 
                ipc.write(u"NOTFOUND %s\n" % fpr)
815
 
                session.bye()
816
 
                return
817
 
            # Have to check if client.still_valid(), since it is
818
 
            # possible that the client timed out while establishing
819
 
            # the GnuTLS session.
820
 
            if not client.still_valid():
821
 
                ipc.write(u"INVALID %s\n" % client.name)
822
 
                session.bye()
823
 
                return
824
 
            ipc.write(u"SENDING %s\n" % client.name)
825
 
            sent_size = 0
826
 
            while sent_size < len(client.secret):
827
 
                sent = session.send(client.secret[sent_size:])
828
 
                logger.debug(u"Sent: %d, remaining: %d",
829
 
                             sent, len(client.secret)
830
 
                             - (sent_size + sent))
831
 
                sent_size += sent
832
 
            session.bye()
 
1048
                try:
 
1049
                    fpr = self.fingerprint(self.peer_certificate
 
1050
                                           (session))
 
1051
                except (TypeError, gnutls.errors.GNUTLSError), error:
 
1052
                    logger.warning(u"Bad certificate: %s", error)
 
1053
                    return
 
1054
                logger.debug(u"Fingerprint: %s", fpr)
 
1055
 
 
1056
                for c in self.server.clients:
 
1057
                    if c.fingerprint == fpr:
 
1058
                        client = c
 
1059
                        break
 
1060
                else:
 
1061
                    ipc.write(u"NOTFOUND %s %s\n"
 
1062
                              % (fpr, unicode(self.client_address)))
 
1063
                    return
 
1064
                
 
1065
                class ClientProxy(object):
 
1066
                    """Client proxy object.  Not for calling methods."""
 
1067
                    def __init__(self, client):
 
1068
                        self.client = client
 
1069
                    def __getattr__(self, name):
 
1070
                        if name.startswith("ipc_"):
 
1071
                            def tempfunc():
 
1072
                                ipc.write("%s %s\n" % (name[4:].upper(),
 
1073
                                                       self.client.name))
 
1074
                            return tempfunc
 
1075
                        if not hasattr(self.client, name):
 
1076
                            raise AttributeError
 
1077
                        ipc.write(u"GETATTR %s %s\n"
 
1078
                                  % (name, self.client.fingerprint))
 
1079
                        return pickle.load(ipc_return)
 
1080
                clientproxy = ClientProxy(client)
 
1081
                # Have to check if client.enabled, since it is
 
1082
                # possible that the client was disabled since the
 
1083
                # GnuTLS session was established.
 
1084
                if not clientproxy.enabled:
 
1085
                    clientproxy.ipc_disabled()
 
1086
                    return
 
1087
                
 
1088
                clientproxy.ipc_sending()
 
1089
                sent_size = 0
 
1090
                while sent_size < len(client.secret):
 
1091
                    sent = session.send(client.secret[sent_size:])
 
1092
                    logger.debug(u"Sent: %d, remaining: %d",
 
1093
                                 sent, len(client.secret)
 
1094
                                 - (sent_size + sent))
 
1095
                    sent_size += sent
 
1096
            finally:
 
1097
                session.bye()
833
1098
    
834
1099
    @staticmethod
835
1100
    def peer_certificate(session):
895
1160
        return hex_fpr
896
1161
 
897
1162
 
898
 
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
899
 
    """Like socketserver.ForkingMixIn, but also pass a pipe."""
 
1163
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
 
1164
    """Like socketserver.ForkingMixIn, but also pass a pipe pair."""
900
1165
    def process_request(self, request, client_address):
901
1166
        """Overrides and wraps the original process_request().
902
1167
        
903
 
        This function creates a new pipe in self.pipe 
 
1168
        This function creates a new pipe in self.pipe
904
1169
        """
905
 
        self.pipe = os.pipe()
906
 
        super(ForkingMixInWithPipe,
 
1170
        # Child writes to child_pipe
 
1171
        self.child_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
 
1172
        # Parent writes to parent_pipe
 
1173
        self.parent_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
 
1174
        super(ForkingMixInWithPipes,
907
1175
              self).process_request(request, client_address)
908
 
        os.close(self.pipe[1])  # close write end
909
 
        self.add_pipe(self.pipe[0])
910
 
    def add_pipe(self, pipe):
 
1176
        # Close unused ends for parent
 
1177
        self.parent_pipe[0].close() # close read end
 
1178
        self.child_pipe[1].close()  # close write end
 
1179
        self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1])
 
1180
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
911
1181
        """Dummy function; override as necessary"""
912
 
        os.close(pipe)
913
 
 
914
 
 
915
 
class IPv6_TCPServer(ForkingMixInWithPipe,
 
1182
        child_pipe_fd.close()
 
1183
        parent_pipe_fd.close()
 
1184
 
 
1185
 
 
1186
class IPv6_TCPServer(ForkingMixInWithPipes,
916
1187
                     socketserver.TCPServer, object):
917
1188
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
918
1189
    
982
1253
        clients:        set of Client objects
983
1254
        gnutls_priority GnuTLS priority string
984
1255
        use_dbus:       Boolean; to emit D-Bus signals or not
985
 
        clients:        set of Client objects
986
 
        gnutls_priority GnuTLS priority string
987
 
        use_dbus:       Boolean; to emit D-Bus signals or not
988
1256
    
989
1257
    Assumes a gobject.MainLoop event loop.
990
1258
    """
1006
1274
            return socketserver.TCPServer.server_activate(self)
1007
1275
    def enable(self):
1008
1276
        self.enabled = True
1009
 
    def add_pipe(self, pipe):
 
1277
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1010
1278
        # Call "handle_ipc" for both data and EOF events
1011
 
        gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
1012
 
                             self.handle_ipc)
1013
 
    def handle_ipc(self, source, condition, file_objects={}):
 
1279
        gobject.io_add_watch(child_pipe_fd.fileno(),
 
1280
                             gobject.IO_IN | gobject.IO_HUP,
 
1281
                             functools.partial(self.handle_ipc,
 
1282
                                               reply = parent_pipe_fd,
 
1283
                                               sender= child_pipe_fd))
 
1284
    def handle_ipc(self, source, condition, reply=None, sender=None):
1014
1285
        condition_names = {
1015
1286
            gobject.IO_IN: u"IN",   # There is data to read.
1016
1287
            gobject.IO_OUT: u"OUT", # Data can be written (without
1028
1299
        logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1029
1300
                     conditions_string)
1030
1301
        
1031
 
        # Turn the pipe file descriptor into a Python file object
1032
 
        if source not in file_objects:
1033
 
            file_objects[source] = os.fdopen(source, u"r", 1)
1034
 
        
1035
1302
        # Read a line from the file object
1036
 
        cmdline = file_objects[source].readline()
 
1303
        cmdline = sender.readline()
1037
1304
        if not cmdline:             # Empty line means end of file
1038
 
            # close the IPC pipe
1039
 
            file_objects[source].close()
1040
 
            del file_objects[source]
 
1305
            # close the IPC pipes
 
1306
            sender.close()
 
1307
            reply.close()
1041
1308
            
1042
1309
            # Stop calling this function
1043
1310
            return False
1048
1315
        cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1049
1316
        
1050
1317
        if cmd == u"NOTFOUND":
1051
 
            logger.warning(u"Client not found for fingerprint: %s",
1052
 
                           args)
 
1318
            fpr, address = args.split(None, 1)
 
1319
            logger.warning(u"Client not found for fingerprint: %s, ad"
 
1320
                           u"dress: %s", fpr, address)
1053
1321
            if self.use_dbus:
1054
1322
                # Emit D-Bus signal
1055
 
                mandos_dbus_service.ClientNotFound(args)
1056
 
        elif cmd == u"INVALID":
 
1323
                mandos_dbus_service.ClientNotFound(fpr, address)
 
1324
        elif cmd == u"DISABLED":
1057
1325
            for client in self.clients:
1058
1326
                if client.name == args:
1059
 
                    logger.warning(u"Client %s is invalid", args)
 
1327
                    logger.warning(u"Client %s is disabled", args)
1060
1328
                    if self.use_dbus:
1061
1329
                        # Emit D-Bus signal
1062
1330
                        client.Rejected()
1063
1331
                    break
1064
1332
            else:
1065
 
                logger.error(u"Unknown client %s is invalid", args)
 
1333
                logger.error(u"Unknown client %s is disabled", args)
1066
1334
        elif cmd == u"SENDING":
1067
1335
            for client in self.clients:
1068
1336
                if client.name == args:
1070
1338
                    client.checked_ok()
1071
1339
                    if self.use_dbus:
1072
1340
                        # Emit D-Bus signal
1073
 
                        client.ReceivedSecret()
 
1341
                        client.GotSecret()
1074
1342
                    break
1075
1343
            else:
1076
1344
                logger.error(u"Sending secret to unknown client %s",
1077
1345
                             args)
 
1346
        elif cmd == u"GETATTR":
 
1347
            attr_name, fpr = args.split(None, 1)
 
1348
            for client in self.clients:
 
1349
                if client.fingerprint == fpr:
 
1350
                    attr_value = getattr(client, attr_name, None)
 
1351
                    logger.debug("IPC reply: %r", attr_value)
 
1352
                    pickle.dump(attr_value, reply)
 
1353
                    break
 
1354
            else:
 
1355
                logger.error(u"Client %s on address %s requesting "
 
1356
                             u"attribute %s not found", fpr, address,
 
1357
                             attr_name)
 
1358
                pickle.dump(None, reply)
1078
1359
        else:
1079
1360
            logger.error(u"Unknown IPC command: %r", cmdline)
1080
1361
        
1114
1395
            elif suffix == u"w":
1115
1396
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1116
1397
            else:
1117
 
                raise ValueError
1118
 
        except (ValueError, IndexError):
1119
 
            raise ValueError
 
1398
                raise ValueError(u"Unknown suffix %r" % suffix)
 
1399
        except (ValueError, IndexError), e:
 
1400
            raise ValueError(e.message)
1120
1401
        timevalue += delta
1121
1402
    return timevalue
1122
1403
 
1135
1416
        def if_nametoindex(interface):
1136
1417
            "Get an interface index the hard way, i.e. using fcntl()"
1137
1418
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
1138
 
            with closing(socket.socket()) as s:
 
1419
            with contextlib.closing(socket.socket()) as s:
1139
1420
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1140
1421
                                    struct.pack(str(u"16s16x"),
1141
1422
                                                interface))
1161
1442
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1162
1443
        if not stat.S_ISCHR(os.fstat(null).st_mode):
1163
1444
            raise OSError(errno.ENODEV,
1164
 
                          u"/dev/null not a character device")
 
1445
                          u"%s not a character device"
 
1446
                          % os.path.devnull)
1165
1447
        os.dup2(null, sys.stdin.fileno())
1166
1448
        os.dup2(null, sys.stdout.fileno())
1167
1449
        os.dup2(null, sys.stderr.fileno())
1171
1453
 
1172
1454
def main():
1173
1455
    
1174
 
    ######################################################################
 
1456
    ##################################################################
1175
1457
    # Parsing of options, both command line and config file
1176
1458
    
1177
1459
    parser = optparse.OptionParser(version = "%%prog %s" % version)
1334
1616
    bus = dbus.SystemBus()
1335
1617
    # End of Avahi example code
1336
1618
    if use_dbus:
1337
 
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
 
1619
        try:
 
1620
            bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
 
1621
                                            bus, do_not_queue=True)
 
1622
        except dbus.exceptions.NameExistsException, e:
 
1623
            logger.error(unicode(e) + u", disabling D-Bus")
 
1624
            use_dbus = False
 
1625
            server_settings[u"use_dbus"] = False
 
1626
            tcp_server.use_dbus = False
1338
1627
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1339
1628
    service = AvahiService(name = server_settings[u"servicename"],
1340
1629
                           servicetype = u"_mandos._tcp",
1366
1655
        daemon()
1367
1656
    
1368
1657
    try:
1369
 
        with closing(pidfile):
 
1658
        with pidfile:
1370
1659
            pid = os.getpid()
1371
1660
            pidfile.write(str(pid) + "\n")
1372
1661
        del pidfile
1378
1667
        pass
1379
1668
    del pidfilename
1380
1669
    
1381
 
    def cleanup():
1382
 
        "Cleanup function; run on exit"
1383
 
        service.cleanup()
1384
 
        
1385
 
        while tcp_server.clients:
1386
 
            client = tcp_server.clients.pop()
1387
 
            client.disable_hook = None
1388
 
            client.disable()
1389
 
    
1390
 
    atexit.register(cleanup)
1391
 
    
1392
1670
    if not debug:
1393
1671
        signal.signal(signal.SIGINT, signal.SIG_IGN)
1394
1672
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1401
1679
                dbus.service.Object.__init__(self, bus, u"/")
1402
1680
            _interface = u"se.bsnet.fukt.Mandos"
1403
1681
            
1404
 
            @dbus.service.signal(_interface, signature=u"oa{sv}")
1405
 
            def ClientAdded(self, objpath, properties):
 
1682
            @dbus.service.signal(_interface, signature=u"o")
 
1683
            def ClientAdded(self, objpath):
1406
1684
                "D-Bus signal"
1407
1685
                pass
1408
1686
            
1409
 
            @dbus.service.signal(_interface, signature=u"s")
1410
 
            def ClientNotFound(self, fingerprint):
 
1687
            @dbus.service.signal(_interface, signature=u"ss")
 
1688
            def ClientNotFound(self, fingerprint, address):
1411
1689
                "D-Bus signal"
1412
1690
                pass
1413
1691
            
1427
1705
            def GetAllClientsWithProperties(self):
1428
1706
                "D-Bus method"
1429
1707
                return dbus.Dictionary(
1430
 
                    ((c.dbus_object_path, c.GetAllProperties())
 
1708
                    ((c.dbus_object_path, c.GetAll(u""))
1431
1709
                     for c in tcp_server.clients),
1432
1710
                    signature=u"oa{sv}")
1433
1711
            
1439
1717
                        tcp_server.clients.remove(c)
1440
1718
                        c.remove_from_connection()
1441
1719
                        # Don't signal anything except ClientRemoved
1442
 
                        c.disable(signal=False)
 
1720
                        c.disable(quiet=True)
1443
1721
                        # Emit D-Bus signal
1444
1722
                        self.ClientRemoved(object_path, c.name)
1445
1723
                        return
1446
 
                raise KeyError
 
1724
                raise KeyError(object_path)
1447
1725
            
1448
1726
            del _interface
1449
1727
        
1450
1728
        mandos_dbus_service = MandosDBusService()
1451
1729
    
 
1730
    def cleanup():
 
1731
        "Cleanup function; run on exit"
 
1732
        service.cleanup()
 
1733
        
 
1734
        while tcp_server.clients:
 
1735
            client = tcp_server.clients.pop()
 
1736
            if use_dbus:
 
1737
                client.remove_from_connection()
 
1738
            client.disable_hook = None
 
1739
            # Don't signal anything except ClientRemoved
 
1740
            client.disable(quiet=True)
 
1741
            if use_dbus:
 
1742
                # Emit D-Bus signal
 
1743
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
 
1744
                                                  client.name)
 
1745
    
 
1746
    atexit.register(cleanup)
 
1747
    
1452
1748
    for client in tcp_server.clients:
1453
1749
        if use_dbus:
1454
1750
            # Emit D-Bus signal
1455
 
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
1456
 
                                            client.GetAllProperties())
 
1751
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
1457
1752
        client.enable()
1458
1753
    
1459
1754
    tcp_server.enable()
1477
1772
            service.activate()
1478
1773
        except dbus.exceptions.DBusException, error:
1479
1774
            logger.critical(u"DBusException: %s", error)
 
1775
            cleanup()
1480
1776
            sys.exit(1)
1481
1777
        # End of Avahi example code
1482
1778
        
1489
1785
        main_loop.run()
1490
1786
    except AvahiError, error:
1491
1787
        logger.critical(u"AvahiError: %s", error)
 
1788
        cleanup()
1492
1789
        sys.exit(1)
1493
1790
    except KeyboardInterrupt:
1494
1791
        if debug:
1495
1792
            print >> sys.stderr
1496
1793
        logger.debug(u"Server received KeyboardInterrupt")
1497
1794
    logger.debug(u"Server exiting")
 
1795
    # Must run before the D-Bus bus name gets deregistered
 
1796
    cleanup()
1498
1797
 
1499
1798
if __name__ == '__main__':
1500
1799
    main()