/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 at recompile
  • Date: 2012-01-01 17:38:33 UTC
  • Revision ID: teddy@recompile.se-20120101173833-ai39bif1w0ftuyyh
* Makefile (install-server): Add intro(8mandos) man page.
* network-hooks.d/bridge: Add copyright info and year.
* network-hooks.d/openvpn: - '' -
* network-hooks.d/wireless: - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
65
65
import types
66
66
import binascii
67
67
import tempfile
68
 
import itertools
69
68
 
70
69
import dbus
71
70
import dbus.service
86
85
    except ImportError:
87
86
        SO_BINDTODEVICE = None
88
87
 
89
 
version = "1.5.3"
 
88
version = "1.5.0"
90
89
stored_state_file = "clients.pickle"
91
90
 
92
91
logger = logging.getLogger()
176
175
    
177
176
    def encrypt(self, data, password):
178
177
        self.gnupg.passphrase = self.password_encode(password)
179
 
        with open(os.devnull, "w") as devnull:
 
178
        with open(os.devnull) as devnull:
180
179
            try:
181
180
                proc = self.gnupg.run(['--symmetric'],
182
181
                                      create_fhs=['stdin', 'stdout'],
193
192
    
194
193
    def decrypt(self, data, password):
195
194
        self.gnupg.passphrase = self.password_encode(password)
196
 
        with open(os.devnull, "w") as devnull:
 
195
        with open(os.devnull) as devnull:
197
196
            try:
198
197
                proc = self.gnupg.run(['--decrypt'],
199
198
                                      create_fhs=['stdin', 'stdout'],
200
199
                                      attach_fhs={'stderr': devnull})
201
 
                with contextlib.closing(proc.handles['stdin']) as f:
 
200
                with contextlib.closing(proc.handles['stdin'] ) as f:
202
201
                    f.write(data)
203
202
                with contextlib.closing(proc.handles['stdout']) as f:
204
203
                    decrypted_plaintext = f.read()
277
276
        try:
278
277
            self.add()
279
278
        except dbus.exceptions.DBusException as error:
280
 
            logger.critical("D-Bus Exception", exc_info=error)
 
279
            logger.critical("DBusException: %s", error)
281
280
            self.cleanup()
282
281
            os._exit(1)
283
282
        self.rename_count += 1
416
415
    last_checked_ok: datetime.datetime(); (UTC) or None
417
416
    last_checker_status: integer between 0 and 255 reflecting exit
418
417
                         status of last checker. -1 reflects crashed
419
 
                         checker, -2 means no checker completed yet.
 
418
                         checker, or None.
420
419
    last_enabled: datetime.datetime(); (UTC) or None
421
420
    name:       string; from the config file, used in log messages and
422
421
                        D-Bus identifiers
423
422
    secret:     bytestring; sent verbatim (over TLS) to client
424
423
    timeout:    datetime.timedelta(); How long from last_checked_ok
425
424
                                      until this client is disabled
426
 
    extended_timeout:   extra long timeout when secret has been sent
 
425
    extended_timeout:   extra long timeout when password has been sent
427
426
    runtime_expansions: Allowed attributes for runtime expansion.
428
427
    expires:    datetime.datetime(); time (UTC) when a client will be
429
428
                disabled, or None
502
501
            client["checker_command"] = section["checker"]
503
502
            client["last_approval_request"] = None
504
503
            client["last_checked_ok"] = None
505
 
            client["last_checker_status"] = -2
 
504
            client["last_checker_status"] = None
506
505
        
507
506
        return settings
508
507
        
627
626
            logger.warning("Checker for %(name)s crashed?",
628
627
                           vars(self))
629
628
    
630
 
    def checked_ok(self):
631
 
        """Assert that the client has been seen, alive and well."""
632
 
        self.last_checked_ok = datetime.datetime.utcnow()
633
 
        self.last_checker_status = 0
634
 
        self.bump_timeout()
635
 
    
636
 
    def bump_timeout(self, timeout=None):
637
 
        """Bump up the timeout for this client."""
 
629
    def checked_ok(self, timeout=None):
 
630
        """Bump up the timeout for this client.
 
631
        
 
632
        This should only be called when the client has been seen,
 
633
        alive and well.
 
634
        """
638
635
        if timeout is None:
639
636
            timeout = self.timeout
 
637
        self.last_checked_ok = datetime.datetime.utcnow()
640
638
        if self.disable_initiator_tag is not None:
641
639
            gobject.source_remove(self.disable_initiator_tag)
642
640
        if getattr(self, "enabled", False):
693
691
                try:
694
692
                    command = self.checker_command % escaped_attrs
695
693
                except TypeError as error:
696
 
                    logger.error('Could not format string "%s"',
697
 
                                 self.checker_command, exc_info=error)
 
694
                    logger.error('Could not format string "%s":'
 
695
                                 ' %s', self.checker_command, error)
698
696
                    return True # Try again later
699
697
            self.current_checker_command = command
700
698
            try:
718
716
                    gobject.source_remove(self.checker_callback_tag)
719
717
                    self.checker_callback(pid, status, command)
720
718
            except OSError as error:
721
 
                logger.error("Failed to start subprocess",
722
 
                             exc_info=error)
 
719
                logger.error("Failed to start subprocess: %s",
 
720
                             error)
723
721
        # Re-run this periodically if run by gobject.timeout_add
724
722
        return True
725
723
    
732
730
            return
733
731
        logger.debug("Stopping checker for %(name)s", vars(self))
734
732
        try:
735
 
            self.checker.terminate()
 
733
            os.kill(self.checker.pid, signal.SIGTERM)
736
734
            #time.sleep(0.5)
737
735
            #if self.checker.poll() is None:
738
 
            #    self.checker.kill()
 
736
            #    os.kill(self.checker.pid, signal.SIGKILL)
739
737
        except OSError as error:
740
738
            if error.errno != errno.ESRCH: # No such process
741
739
                raise
772
770
    return decorator
773
771
 
774
772
 
775
 
def dbus_interface_annotations(dbus_interface):
776
 
    """Decorator for marking functions returning interface annotations.
777
 
    
778
 
    Usage:
779
 
    
780
 
    @dbus_interface_annotations("org.example.Interface")
781
 
    def _foo(self):  # Function name does not matter
782
 
        return {"org.freedesktop.DBus.Deprecated": "true",
783
 
                "org.freedesktop.DBus.Property.EmitsChangedSignal":
784
 
                    "false"}
785
 
    """
786
 
    def decorator(func):
787
 
        func._dbus_is_interface = True
788
 
        func._dbus_interface = dbus_interface
789
 
        func._dbus_name = dbus_interface
790
 
        return func
791
 
    return decorator
792
 
 
793
 
 
794
 
def dbus_annotations(annotations):
795
 
    """Decorator to annotate D-Bus methods, signals or properties
796
 
    Usage:
797
 
    
798
 
    @dbus_service_property("org.example.Interface", signature="b",
799
 
                           access="r")
800
 
    @dbus_annotations({{"org.freedesktop.DBus.Deprecated": "true",
801
 
                        "org.freedesktop.DBus.Property."
802
 
                        "EmitsChangedSignal": "false"})
803
 
    def Property_dbus_property(self):
804
 
        return dbus.Boolean(False)
805
 
    """
806
 
    def decorator(func):
807
 
        func._dbus_annotations = annotations
808
 
        return func
809
 
    return decorator
810
 
 
811
 
 
812
773
class DBusPropertyException(dbus.exceptions.DBusException):
813
774
    """A base class for D-Bus property-related exceptions
814
775
    """
837
798
    """
838
799
    
839
800
    @staticmethod
840
 
    def _is_dbus_thing(thing):
841
 
        """Returns a function testing if an attribute is a D-Bus thing
842
 
        
843
 
        If called like _is_dbus_thing("method") it returns a function
844
 
        suitable for use as predicate to inspect.getmembers().
845
 
        """
846
 
        return lambda obj: getattr(obj, "_dbus_is_{0}".format(thing),
847
 
                                   False)
 
801
    def _is_dbus_property(obj):
 
802
        return getattr(obj, "_dbus_is_property", False)
848
803
    
849
 
    def _get_all_dbus_things(self, thing):
 
804
    def _get_all_dbus_properties(self):
850
805
        """Returns a generator of (name, attribute) pairs
851
806
        """
852
 
        return ((getattr(athing.__get__(self), "_dbus_name",
853
 
                         name),
854
 
                 athing.__get__(self))
 
807
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
855
808
                for cls in self.__class__.__mro__
856
 
                for name, athing in
857
 
                inspect.getmembers(cls,
858
 
                                   self._is_dbus_thing(thing)))
 
809
                for name, prop in
 
810
                inspect.getmembers(cls, self._is_dbus_property))
859
811
    
860
812
    def _get_dbus_property(self, interface_name, property_name):
861
813
        """Returns a bound method if one exists which is a D-Bus
863
815
        """
864
816
        for cls in  self.__class__.__mro__:
865
817
            for name, value in (inspect.getmembers
866
 
                                (cls,
867
 
                                 self._is_dbus_thing("property"))):
 
818
                                (cls, self._is_dbus_property)):
868
819
                if (value._dbus_name == property_name
869
820
                    and value._dbus_interface == interface_name):
870
821
                    return value.__get__(self)
912
863
        Note: Will not include properties with access="write".
913
864
        """
914
865
        properties = {}
915
 
        for name, prop in self._get_all_dbus_things("property"):
 
866
        for name, prop in self._get_all_dbus_properties():
916
867
            if (interface_name
917
868
                and interface_name != prop._dbus_interface):
918
869
                # Interface non-empty but did not match
933
884
                         path_keyword='object_path',
934
885
                         connection_keyword='connection')
935
886
    def Introspect(self, object_path, connection):
936
 
        """Overloading of standard D-Bus method.
937
 
        
938
 
        Inserts property tags and interface annotation tags.
 
887
        """Standard D-Bus method, overloaded to insert property tags.
939
888
        """
940
889
        xmlstring = dbus.service.Object.Introspect(self, object_path,
941
890
                                                   connection)
948
897
                e.setAttribute("access", prop._dbus_access)
949
898
                return e
950
899
            for if_tag in document.getElementsByTagName("interface"):
951
 
                # Add property tags
952
900
                for tag in (make_tag(document, name, prop)
953
901
                            for name, prop
954
 
                            in self._get_all_dbus_things("property")
 
902
                            in self._get_all_dbus_properties()
955
903
                            if prop._dbus_interface
956
904
                            == if_tag.getAttribute("name")):
957
905
                    if_tag.appendChild(tag)
958
 
                # Add annotation tags
959
 
                for typ in ("method", "signal", "property"):
960
 
                    for tag in if_tag.getElementsByTagName(typ):
961
 
                        annots = dict()
962
 
                        for name, prop in (self.
963
 
                                           _get_all_dbus_things(typ)):
964
 
                            if (name == tag.getAttribute("name")
965
 
                                and prop._dbus_interface
966
 
                                == if_tag.getAttribute("name")):
967
 
                                annots.update(getattr
968
 
                                              (prop,
969
 
                                               "_dbus_annotations",
970
 
                                               {}))
971
 
                        for name, value in annots.iteritems():
972
 
                            ann_tag = document.createElement(
973
 
                                "annotation")
974
 
                            ann_tag.setAttribute("name", name)
975
 
                            ann_tag.setAttribute("value", value)
976
 
                            tag.appendChild(ann_tag)
977
 
                # Add interface annotation tags
978
 
                for annotation, value in dict(
979
 
                    itertools.chain(
980
 
                        *(annotations().iteritems()
981
 
                          for name, annotations in
982
 
                          self._get_all_dbus_things("interface")
983
 
                          if name == if_tag.getAttribute("name")
984
 
                          ))).iteritems():
985
 
                    ann_tag = document.createElement("annotation")
986
 
                    ann_tag.setAttribute("name", annotation)
987
 
                    ann_tag.setAttribute("value", value)
988
 
                    if_tag.appendChild(ann_tag)
989
906
                # Add the names to the return values for the
990
907
                # "org.freedesktop.DBus.Properties" methods
991
908
                if (if_tag.getAttribute("name")
1006
923
        except (AttributeError, xml.dom.DOMException,
1007
924
                xml.parsers.expat.ExpatError) as error:
1008
925
            logger.error("Failed to override Introspection method",
1009
 
                         exc_info=error)
 
926
                         error)
1010
927
        return xmlstring
1011
928
 
1012
929
 
1026
943
    def __new__(mcs, name, bases, attr):
1027
944
        # Go through all the base classes which could have D-Bus
1028
945
        # methods, signals, or properties in them
1029
 
        old_interface_names = []
1030
946
        for base in (b for b in bases
1031
947
                     if issubclass(b, dbus.service.Object)):
1032
948
            # Go though all attributes of the base class
1042
958
                alt_interface = (attribute._dbus_interface
1043
959
                                 .replace("se.recompile.Mandos",
1044
960
                                          "se.bsnet.fukt.Mandos"))
1045
 
                if alt_interface != attribute._dbus_interface:
1046
 
                    old_interface_names.append(alt_interface)
1047
961
                # Is this a D-Bus signal?
1048
962
                if getattr(attribute, "_dbus_is_signal", False):
1049
963
                    # Extract the original non-method function by
1064
978
                                nonmethod_func.func_name,
1065
979
                                nonmethod_func.func_defaults,
1066
980
                                nonmethod_func.func_closure)))
1067
 
                    # Copy annotations, if any
1068
 
                    try:
1069
 
                        new_function._dbus_annotations = (
1070
 
                            dict(attribute._dbus_annotations))
1071
 
                    except AttributeError:
1072
 
                        pass
1073
981
                    # Define a creator of a function to call both the
1074
982
                    # old and new functions, so both the old and new
1075
983
                    # signals gets sent when the function is called
1103
1011
                                        attribute.func_name,
1104
1012
                                        attribute.func_defaults,
1105
1013
                                        attribute.func_closure)))
1106
 
                    # Copy annotations, if any
1107
 
                    try:
1108
 
                        attr[attrname]._dbus_annotations = (
1109
 
                            dict(attribute._dbus_annotations))
1110
 
                    except AttributeError:
1111
 
                        pass
1112
1014
                # Is this a D-Bus property?
1113
1015
                elif getattr(attribute, "_dbus_is_property", False):
1114
1016
                    # Create a new, but exactly alike, function
1128
1030
                                        attribute.func_name,
1129
1031
                                        attribute.func_defaults,
1130
1032
                                        attribute.func_closure)))
1131
 
                    # Copy annotations, if any
1132
 
                    try:
1133
 
                        attr[attrname]._dbus_annotations = (
1134
 
                            dict(attribute._dbus_annotations))
1135
 
                    except AttributeError:
1136
 
                        pass
1137
 
                # Is this a D-Bus interface?
1138
 
                elif getattr(attribute, "_dbus_is_interface", False):
1139
 
                    # Create a new, but exactly alike, function
1140
 
                    # object.  Decorate it to be a new D-Bus interface
1141
 
                    # with the alternate D-Bus interface name.  Add it
1142
 
                    # to the class.
1143
 
                    attr[attrname] = (dbus_interface_annotations
1144
 
                                      (alt_interface)
1145
 
                                      (types.FunctionType
1146
 
                                       (attribute.func_code,
1147
 
                                        attribute.func_globals,
1148
 
                                        attribute.func_name,
1149
 
                                        attribute.func_defaults,
1150
 
                                        attribute.func_closure)))
1151
 
        # Deprecate all old interfaces
1152
 
        basename="_AlternateDBusNamesMetaclass_interface_annotation{0}"
1153
 
        for old_interface_name in old_interface_names:
1154
 
            @dbus_interface_annotations(old_interface_name)
1155
 
            def func(self):
1156
 
                return { "org.freedesktop.DBus.Deprecated": "true" }
1157
 
            # Find an unused name
1158
 
            for aname in (basename.format(i) for i in
1159
 
                          itertools.count()):
1160
 
                if aname not in attr:
1161
 
                    attr[aname] = func
1162
 
                    break
1163
1033
        return type.__new__(mcs, name, bases, attr)
1164
1034
 
1165
1035
 
1179
1049
    def __init__(self, bus = None, *args, **kwargs):
1180
1050
        self.bus = bus
1181
1051
        Client.__init__(self, *args, **kwargs)
 
1052
        self._approvals_pending = 0
 
1053
        
 
1054
        self._approvals_pending = 0
1182
1055
        # Only now, when this client is initialized, can it show up on
1183
1056
        # the D-Bus
1184
1057
        client_object_name = unicode(self.name).translate(
1230
1103
                                       checker is not None)
1231
1104
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
1232
1105
                                           "LastCheckedOK")
1233
 
    last_checker_status = notifychangeproperty(dbus.Int16,
1234
 
                                               "LastCheckerStatus")
1235
1106
    last_approval_request = notifychangeproperty(
1236
1107
        datetime_to_dbus, "LastApprovalRequest")
1237
1108
    approved_by_default = notifychangeproperty(dbus.Boolean,
1315
1186
    ## D-Bus methods, signals & properties
1316
1187
    _interface = "se.recompile.Mandos.Client"
1317
1188
    
1318
 
    ## Interfaces
1319
 
    
1320
 
    @dbus_interface_annotations(_interface)
1321
 
    def _foo(self):
1322
 
        return { "org.freedesktop.DBus.Property.EmitsChangedSignal":
1323
 
                     "false"}
1324
 
    
1325
1189
    ## Signals
1326
1190
    
1327
1191
    # CheckerCompleted - signal
1363
1227
        "D-Bus signal"
1364
1228
        return self.need_approval()
1365
1229
    
 
1230
    # NeRwequest - signal
 
1231
    @dbus.service.signal(_interface, signature="s")
 
1232
    def NewRequest(self, ip):
 
1233
        """D-Bus signal
 
1234
        Is sent after a client request a password.
 
1235
        """
 
1236
        pass
 
1237
    
1366
1238
    ## Methods
1367
1239
    
1368
1240
    # Approve - method
1478
1350
            return
1479
1351
        return datetime_to_dbus(self.last_checked_ok)
1480
1352
    
1481
 
    # LastCheckerStatus - property
1482
 
    @dbus_service_property(_interface, signature="n",
1483
 
                           access="read")
1484
 
    def LastCheckerStatus_dbus_property(self):
1485
 
        return dbus.Int16(self.last_checker_status)
1486
 
    
1487
1353
    # Expires - property
1488
1354
    @dbus_service_property(_interface, signature="s", access="read")
1489
1355
    def Expires_dbus_property(self):
1681
1547
                except KeyError:
1682
1548
                    return
1683
1549
                
 
1550
                if self.server.use_dbus:
 
1551
                    # Emit D-Bus signal
 
1552
                    client.NewRequest(str(self.client_address))
 
1553
                
1684
1554
                if client.approval_delay:
1685
1555
                    delay = client.approval_delay
1686
1556
                    client.approvals_pending += 1
1750
1620
                
1751
1621
                logger.info("Sending secret to %s", client.name)
1752
1622
                # bump the timeout using extended_timeout
1753
 
                client.bump_timeout(client.extended_timeout)
 
1623
                client.checked_ok(client.extended_timeout)
1754
1624
                if self.server.use_dbus:
1755
1625
                    # Emit D-Bus signal
1756
1626
                    client.GotSecret()
2099
1969
        sys.exit()
2100
1970
    if not noclose:
2101
1971
        # Close all standard open file descriptors
2102
 
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
 
1972
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
2103
1973
        if not stat.S_ISCHR(os.fstat(null).st_mode):
2104
1974
            raise OSError(errno.ENODEV,
2105
1975
                          "%s not a character device"
2106
 
                          % os.devnull)
 
1976
                          % os.path.devnull)
2107
1977
        os.dup2(null, sys.stdin.fileno())
2108
1978
        os.dup2(null, sys.stdout.fileno())
2109
1979
        os.dup2(null, sys.stderr.fileno())
2256
2126
        except IOError:
2257
2127
            logger.error("Could not open file %r", pidfilename)
2258
2128
    
2259
 
    for name in ("_mandos", "mandos", "nobody"):
 
2129
    try:
 
2130
        uid = pwd.getpwnam("_mandos").pw_uid
 
2131
        gid = pwd.getpwnam("_mandos").pw_gid
 
2132
    except KeyError:
2260
2133
        try:
2261
 
            uid = pwd.getpwnam(name).pw_uid
2262
 
            gid = pwd.getpwnam(name).pw_gid
2263
 
            break
 
2134
            uid = pwd.getpwnam("mandos").pw_uid
 
2135
            gid = pwd.getpwnam("mandos").pw_gid
2264
2136
        except KeyError:
2265
 
            continue
2266
 
    else:
2267
 
        uid = 65534
2268
 
        gid = 65534
 
2137
            try:
 
2138
                uid = pwd.getpwnam("nobody").pw_uid
 
2139
                gid = pwd.getpwnam("nobody").pw_gid
 
2140
            except KeyError:
 
2141
                uid = 65534
 
2142
                gid = 65534
2269
2143
    try:
2270
2144
        os.setgid(gid)
2271
2145
        os.setuid(uid)
2288
2162
         .gnutls_global_set_log_function(debug_gnutls))
2289
2163
        
2290
2164
        # Redirect stdin so all checkers get /dev/null
2291
 
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
 
2165
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
2292
2166
        os.dup2(null, sys.stdin.fileno())
2293
2167
        if null > 2:
2294
2168
            os.close(null)
2302
2176
    
2303
2177
    global main_loop
2304
2178
    # From the Avahi example code
2305
 
    DBusGMainLoop(set_as_default=True)
 
2179
    DBusGMainLoop(set_as_default=True )
2306
2180
    main_loop = gobject.MainLoop()
2307
2181
    bus = dbus.SystemBus()
2308
2182
    # End of Avahi example code
2376
2250
            
2377
2251
            # Clients who has passed its expire date can still be
2378
2252
            # enabled if its last checker was successful.  Clients
2379
 
            # whose checker succeeded before we stored its state is
2380
 
            # assumed to have successfully run all checkers during
2381
 
            # downtime.
 
2253
            # whose checker failed before we stored its state is
 
2254
            # assumed to have failed all checkers during downtime.
2382
2255
            if client["enabled"]:
2383
2256
                if datetime.datetime.utcnow() >= client["expires"]:
2384
2257
                    if not client["last_checked_ok"]:
2385
2258
                        logger.warning(
2386
2259
                            "disabling client {0} - Client never "
2387
 
                            "performed a successful checker"
2388
 
                            .format(client_name))
 
2260
                            "performed a successfull checker"
 
2261
                            .format(client["name"]))
2389
2262
                        client["enabled"] = False
2390
2263
                    elif client["last_checker_status"] != 0:
2391
2264
                        logger.warning(
2392
2265
                            "disabling client {0} - Client "
2393
2266
                            "last checker failed with error code {1}"
2394
 
                            .format(client_name,
 
2267
                            .format(client["name"],
2395
2268
                                    client["last_checker_status"]))
2396
2269
                        client["enabled"] = False
2397
2270
                    else:
2400
2273
                                             + client["timeout"])
2401
2274
                        logger.debug("Last checker succeeded,"
2402
2275
                                     " keeping {0} enabled"
2403
 
                                     .format(client_name))
 
2276
                                     .format(client["name"]))
2404
2277
            try:
2405
2278
                client["secret"] = (
2406
2279
                    pgp.decrypt(client["encrypted_secret"],
2422
2295
                        - set(old_client_settings)):
2423
2296
        clients_data[client_name] = client_settings[client_name]
2424
2297
 
2425
 
    # Create all client objects
 
2298
    # Create clients all clients
2426
2299
    for client_name, client in clients_data.iteritems():
2427
2300
        tcp_server.clients[client_name] = client_class(
2428
2301
            name = client_name, settings = client)
2449
2322
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2450
2323
    
2451
2324
    if use_dbus:
2452
 
        class MandosDBusService(DBusObjectWithProperties):
 
2325
        class MandosDBusService(dbus.service.Object):
2453
2326
            """A D-Bus proxy object"""
2454
2327
            def __init__(self):
2455
2328
                dbus.service.Object.__init__(self, bus, "/")
2456
2329
            _interface = "se.recompile.Mandos"
2457
2330
            
2458
 
            @dbus_interface_annotations(_interface)
2459
 
            def _foo(self):
2460
 
                return { "org.freedesktop.DBus.Property"
2461
 
                         ".EmitsChangedSignal":
2462
 
                             "false"}
2463
 
            
2464
2331
            @dbus.service.signal(_interface, signature="o")
2465
2332
            def ClientAdded(self, objpath):
2466
2333
                "D-Bus signal"
2612
2479
        try:
2613
2480
            service.activate()
2614
2481
        except dbus.exceptions.DBusException as error:
2615
 
            logger.critical("D-Bus Exception", exc_info=error)
 
2482
            logger.critical("DBusException: %s", error)
2616
2483
            cleanup()
2617
2484
            sys.exit(1)
2618
2485
        # End of Avahi example code
2625
2492
        logger.debug("Starting main loop")
2626
2493
        main_loop.run()
2627
2494
    except AvahiError as error:
2628
 
        logger.critical("Avahi Error", exc_info=error)
 
2495
        logger.critical("AvahiError: %s", error)
2629
2496
        cleanup()
2630
2497
        sys.exit(1)
2631
2498
    except KeyboardInterrupt: