/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: 2012-05-06 16:13:00 UTC
  • Revision ID: teddy@recompile.se-20120506161300-43rls2rr4qub3zhw
* mandos: Use a class decorator instead of a metaclass to provide
          alternate D-Bus interface names on D-Bus object attributes.
  (alternate_dbus_interfaces): New class decorator.
  (AlternateDBusNamesMetaclass, ClientDBusTransitional,
   MandosDBusServiceTransitional): Removed; all users changed.
  (ClientDbus, MandosDBusService): Use new "alternate_dbus_interfaces"
                                   class decorator.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1021
1021
                       variant_level=variant_level)
1022
1022
 
1023
1023
 
1024
 
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
1025
 
                                  .__metaclass__):
1026
 
    """Applied to an empty subclass of a D-Bus object, this metaclass
1027
 
    will add additional D-Bus attributes matching a certain pattern.
 
1024
def alternate_dbus_interfaces(alt_interface_names, deprecate=True):
 
1025
    """A class decorator; applied to a subclass of
 
1026
    dbus.service.Object, it will add alternate D-Bus attributes with
 
1027
    interface names according to the "alt_interface_names" mapping.
 
1028
    Usage:
 
1029
    
 
1030
    @alternate_dbus_names({"org.example.Interface":
 
1031
                               "net.example.AlternateInterface"})
 
1032
    class SampleDBusObject(dbus.service.Object):
 
1033
        @dbus.service.method("org.example.Interface")
 
1034
        def SampleDBusMethod():
 
1035
            pass
 
1036
    
 
1037
    The above "SampleDBusMethod" on "SampleDBusObject" will be
 
1038
    reachable via two interfaces: "org.example.Interface" and
 
1039
    "net.example.AlternateInterface", the latter of which will have
 
1040
    its D-Bus annotation "org.freedesktop.DBus.Deprecated" set to
 
1041
    "true", unless "deprecate" is passed with a False value.
 
1042
    
 
1043
    This works for methods and signals, and also for D-Bus properties
 
1044
    (from DBusObjectWithProperties) and interfaces (from the
 
1045
    dbus_interface_annotations decorator).
1028
1046
    """
1029
 
    def __new__(mcs, name, bases, attr):
1030
 
        # Go through all the base classes which could have D-Bus
1031
 
        # methods, signals, or properties in them
1032
 
        old_interface_names = []
1033
 
        for base in (b for b in bases
1034
 
                     if issubclass(b, dbus.service.Object)):
1035
 
            # Go though all attributes of the base class
1036
 
            for attrname, attribute in inspect.getmembers(base):
 
1047
    def wrapper(cls):
 
1048
        for orig_interface_name, alt_interface_name in (
 
1049
            alt_interface_names.iteritems()):
 
1050
            attr = {}
 
1051
            interface_names = set()
 
1052
            # Go though all attributes of the class
 
1053
            for attrname, attribute in inspect.getmembers(cls):
1037
1054
                # Ignore non-D-Bus attributes, and D-Bus attributes
1038
1055
                # with the wrong interface name
1039
1056
                if (not hasattr(attribute, "_dbus_interface")
1040
1057
                    or not attribute._dbus_interface
1041
 
                    .startswith("se.recompile.Mandos")):
 
1058
                    .startswith(orig_interface_name)):
1042
1059
                    continue
1043
1060
                # Create an alternate D-Bus interface name based on
1044
1061
                # the current name
1045
1062
                alt_interface = (attribute._dbus_interface
1046
 
                                 .replace("se.recompile.Mandos",
1047
 
                                          "se.bsnet.fukt.Mandos"))
1048
 
                if alt_interface != attribute._dbus_interface:
1049
 
                    old_interface_names.append(alt_interface)
 
1063
                                 .replace(orig_interface_name,
 
1064
                                          alt_interface_name))
 
1065
                interface_names.add(alt_interface)
1050
1066
                # Is this a D-Bus signal?
1051
1067
                if getattr(attribute, "_dbus_is_signal", False):
1052
1068
                    # Extract the original non-method function by
1074
1090
                    except AttributeError:
1075
1091
                        pass
1076
1092
                    # Define a creator of a function to call both the
1077
 
                    # old and new functions, so both the old and new
1078
 
                    # signals gets sent when the function is called
 
1093
                    # original and alternate functions, so both the
 
1094
                    # original and alternate signals gets sent when
 
1095
                    # the function is called
1079
1096
                    def fixscope(func1, func2):
1080
1097
                        """This function is a scope container to pass
1081
1098
                        func1 and func2 to the "call_both" function
1088
1105
                        return call_both
1089
1106
                    # Create the "call_both" function and add it to
1090
1107
                    # the class
1091
 
                    attr[attrname] = fixscope(attribute,
1092
 
                                              new_function)
 
1108
                    attr[attrname] = fixscope(attribute, new_function)
1093
1109
                # Is this a D-Bus method?
1094
1110
                elif getattr(attribute, "_dbus_is_method", False):
1095
1111
                    # Create a new, but exactly alike, function
1151
1167
                                        attribute.func_name,
1152
1168
                                        attribute.func_defaults,
1153
1169
                                        attribute.func_closure)))
1154
 
        # Deprecate all old interfaces
1155
 
        iname="_AlternateDBusNamesMetaclass_interface_annotation{0}"
1156
 
        for old_interface_name in old_interface_names:
1157
 
            @dbus_interface_annotations(old_interface_name)
1158
 
            def func(self):
1159
 
                return { "org.freedesktop.DBus.Deprecated": "true" }
1160
 
            # Find an unused name
1161
 
            for aname in (iname.format(i) for i in itertools.count()):
1162
 
                if aname not in attr:
1163
 
                    attr[aname] = func
1164
 
                    break
1165
 
        return type.__new__(mcs, name, bases, attr)
1166
 
 
1167
 
 
 
1170
            if deprecate:
 
1171
                # Deprecate all alternate interfaces
 
1172
                iname="_AlternateDBusNames_interface_annotation{0}"
 
1173
                for interface_name in interface_names:
 
1174
                    @dbus_interface_annotations(interface_name)
 
1175
                    def func(self):
 
1176
                        return { "org.freedesktop.DBus.Deprecated":
 
1177
                                     "true" }
 
1178
                    # Find an unused name
 
1179
                    for aname in (iname.format(i)
 
1180
                                  for i in itertools.count()):
 
1181
                        if aname not in attr:
 
1182
                            attr[aname] = func
 
1183
                            break
 
1184
            if interface_names:
 
1185
                # Replace the class with a new subclass of it with
 
1186
                # methods, signals, etc. as created above.
 
1187
                cls = type(b"{0}Alternate".format(cls.__name__),
 
1188
                           (cls,), attr)
 
1189
        return cls
 
1190
    return wrapper
 
1191
 
 
1192
 
 
1193
@alternate_dbus_interfaces({"se.recompile.Mandos":
 
1194
                                "se.bsnet.fukt.Mandos"})
1168
1195
class ClientDBus(Client, DBusObjectWithProperties):
1169
1196
    """A Client class using D-Bus
1170
1197
    
1604
1631
        self._pipe.send(('setattr', name, value))
1605
1632
 
1606
1633
 
1607
 
class ClientDBusTransitional(ClientDBus):
1608
 
    __metaclass__ = AlternateDBusNamesMetaclass
1609
 
 
1610
 
 
1611
1634
class ClientHandler(socketserver.BaseRequestHandler, object):
1612
1635
    """A class to handle client connections.
1613
1636
    
2337
2360
    
2338
2361
    client_class = Client
2339
2362
    if use_dbus:
2340
 
        client_class = functools.partial(ClientDBusTransitional,
2341
 
                                         bus = bus)
 
2363
        client_class = functools.partial(ClientDBus, bus = bus)
2342
2364
    
2343
2365
    client_settings = Client.config_parser(client_config)
2344
2366
    old_client_settings = {}
2456
2478
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2457
2479
    
2458
2480
    if use_dbus:
 
2481
        @alternate_dbus_interfaces({"se.recompile.Mandos":
 
2482
                                        "se.bsnet.fukt.Mandos"})
2459
2483
        class MandosDBusService(DBusObjectWithProperties):
2460
2484
            """A D-Bus proxy object"""
2461
2485
            def __init__(self):
2515
2539
            
2516
2540
            del _interface
2517
2541
        
2518
 
        class MandosDBusServiceTransitional(MandosDBusService):
2519
 
            __metaclass__ = AlternateDBusNamesMetaclass
2520
 
        mandos_dbus_service = MandosDBusServiceTransitional()
 
2542
        mandos_dbus_service = MandosDBusService()
2521
2543
    
2522
2544
    def cleanup():
2523
2545
        "Cleanup function; run on exit"