/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

merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
67
67
from dbus.mainloop.glib import DBusGMainLoop
68
68
import ctypes
69
69
import ctypes.util
 
70
import xml.dom.minidom
 
71
import inspect
70
72
 
71
73
try:
72
74
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
77
79
        SO_BINDTODEVICE = None
78
80
 
79
81
 
80
 
version = "1.0.8"
 
82
version = "1.0.12"
81
83
 
82
84
logger = logging.Logger(u'mandos')
83
85
syslogger = (logging.handlers.SysLogHandler
174
176
                                    self.server.EntryGroupNew()),
175
177
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
176
178
            self.group.connect_to_signal('StateChanged',
177
 
                                         self.entry_group_state_changed)
 
179
                                         self
 
180
                                         .entry_group_state_changed)
178
181
        logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
179
182
                     self.name, self.type)
180
183
        self.group.AddService(
246
249
                                    to see if the client lives.
247
250
                                    'None' if no process is running.
248
251
    checker_initiator_tag: a gobject event source tag, or None
249
 
    disable_initiator_tag:    - '' -
 
252
    disable_initiator_tag: - '' -
250
253
    checker_callback_tag:  - '' -
251
254
    checker_command: string; External command which is run to check if
252
255
                     client lives.  %() expansions are done at
256
259
    """
257
260
    
258
261
    @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))
 
262
    def _timedelta_to_milliseconds(td):
 
263
        "Convert a datetime.timedelta() to milliseconds"
 
264
        return ((td.days * 24 * 60 * 60 * 1000)
 
265
                + (td.seconds * 1000)
 
266
                + (td.microseconds // 1000))
264
267
    
265
268
    def timeout_milliseconds(self):
266
269
        "Return the 'timeout' attribute in milliseconds"
267
 
        return self._datetime_to_milliseconds(self.timeout)
 
270
        return self._timedelta_to_milliseconds(self.timeout)
268
271
    
269
272
    def interval_milliseconds(self):
270
273
        "Return the 'interval' attribute in milliseconds"
271
 
        return self._datetime_to_milliseconds(self.interval)
 
274
        return self._timedelta_to_milliseconds(self.interval)
272
275
    
273
276
    def __init__(self, name = None, disable_hook=None, config=None):
274
277
        """Note: the 'checker' key in 'config' sets the
312
315
    
313
316
    def enable(self):
314
317
        """Start this client's checker and timeout hooks"""
 
318
        if getattr(self, u"enabled", False):
 
319
            # Already enabled
 
320
            return
315
321
        self.last_enabled = datetime.datetime.utcnow()
316
322
        # Schedule a new checker to be started an 'interval' from now,
317
323
        # and every interval from then on.
474
480
            return now < (self.last_checked_ok + self.timeout)
475
481
 
476
482
 
477
 
class ClientDBus(Client, dbus.service.Object):
 
483
def dbus_service_property(dbus_interface, signature=u"v",
 
484
                          access=u"readwrite", byte_arrays=False):
 
485
    """Decorators for marking methods of a DBusObjectWithProperties to
 
486
    become properties on the D-Bus.
 
487
    
 
488
    The decorated method will be called with no arguments by "Get"
 
489
    and with one argument by "Set".
 
490
    
 
491
    The parameters, where they are supported, are the same as
 
492
    dbus.service.method, except there is only "signature", since the
 
493
    type from Get() and the type sent to Set() is the same.
 
494
    """
 
495
    def decorator(func):
 
496
        func._dbus_is_property = True
 
497
        func._dbus_interface = dbus_interface
 
498
        func._dbus_signature = signature
 
499
        func._dbus_access = access
 
500
        func._dbus_name = func.__name__
 
501
        if func._dbus_name.endswith(u"_dbus_property"):
 
502
            func._dbus_name = func._dbus_name[:-14]
 
503
        func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
 
504
        return func
 
505
    return decorator
 
506
 
 
507
 
 
508
class DBusPropertyException(dbus.exceptions.DBusException):
 
509
    """A base class for D-Bus property-related exceptions
 
510
    """
 
511
    def __unicode__(self):
 
512
        return unicode(str(self))
 
513
 
 
514
 
 
515
class DBusPropertyAccessException(DBusPropertyException):
 
516
    """A property's access permissions disallows an operation.
 
517
    """
 
518
    pass
 
519
 
 
520
 
 
521
class DBusPropertyNotFound(DBusPropertyException):
 
522
    """An attempt was made to access a non-existing property.
 
523
    """
 
524
    pass
 
525
 
 
526
 
 
527
class DBusObjectWithProperties(dbus.service.Object):
 
528
    """A D-Bus object with properties.
 
529
 
 
530
    Classes inheriting from this can use the dbus_service_property
 
531
    decorator to expose methods as D-Bus properties.  It exposes the
 
532
    standard Get(), Set(), and GetAll() methods on the D-Bus.
 
533
    """
 
534
    
 
535
    @staticmethod
 
536
    def _is_dbus_property(obj):
 
537
        return getattr(obj, u"_dbus_is_property", False)
 
538
    
 
539
    def _get_all_dbus_properties(self):
 
540
        """Returns a generator of (name, attribute) pairs
 
541
        """
 
542
        return ((prop._dbus_name, prop)
 
543
                for name, prop in
 
544
                inspect.getmembers(self, self._is_dbus_property))
 
545
    
 
546
    def _get_dbus_property(self, interface_name, property_name):
 
547
        """Returns a bound method if one exists which is a D-Bus
 
548
        property with the specified name and interface.
 
549
        """
 
550
        for name in (property_name,
 
551
                     property_name + u"_dbus_property"):
 
552
            prop = getattr(self, name, None)
 
553
            if (prop is None
 
554
                or not self._is_dbus_property(prop)
 
555
                or prop._dbus_name != property_name
 
556
                or (interface_name and prop._dbus_interface
 
557
                    and interface_name != prop._dbus_interface)):
 
558
                continue
 
559
            return prop
 
560
        # No such property
 
561
        raise DBusPropertyNotFound(self.dbus_object_path + u":"
 
562
                                   + interface_name + u"."
 
563
                                   + property_name)
 
564
    
 
565
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
 
566
                         out_signature=u"v")
 
567
    def Get(self, interface_name, property_name):
 
568
        """Standard D-Bus property Get() method, see D-Bus standard.
 
569
        """
 
570
        prop = self._get_dbus_property(interface_name, property_name)
 
571
        if prop._dbus_access == u"write":
 
572
            raise DBusPropertyAccessException(property_name)
 
573
        value = prop()
 
574
        if not hasattr(value, u"variant_level"):
 
575
            return value
 
576
        return type(value)(value, variant_level=value.variant_level+1)
 
577
    
 
578
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
 
579
    def Set(self, interface_name, property_name, value):
 
580
        """Standard D-Bus property Set() method, see D-Bus standard.
 
581
        """
 
582
        prop = self._get_dbus_property(interface_name, property_name)
 
583
        if prop._dbus_access == u"read":
 
584
            raise DBusPropertyAccessException(property_name)
 
585
        if prop._dbus_get_args_options[u"byte_arrays"]:
 
586
            value = dbus.ByteArray(''.join(unichr(byte)
 
587
                                           for byte in value))
 
588
        prop(value)
 
589
    
 
590
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
 
591
                         out_signature=u"a{sv}")
 
592
    def GetAll(self, interface_name):
 
593
        """Standard D-Bus property GetAll() method, see D-Bus
 
594
        standard.
 
595
 
 
596
        Note: Will not include properties with access="write".
 
597
        """
 
598
        all = {}
 
599
        for name, prop in self._get_all_dbus_properties():
 
600
            if (interface_name
 
601
                and interface_name != prop._dbus_interface):
 
602
                # Interface non-empty but did not match
 
603
                continue
 
604
            # Ignore write-only properties
 
605
            if prop._dbus_access == u"write":
 
606
                continue
 
607
            value = prop()
 
608
            if not hasattr(value, u"variant_level"):
 
609
                all[name] = value
 
610
                continue
 
611
            all[name] = type(value)(value, variant_level=
 
612
                                    value.variant_level+1)
 
613
        return dbus.Dictionary(all, signature=u"sv")
 
614
    
 
615
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
 
616
                         out_signature=u"s",
 
617
                         path_keyword='object_path',
 
618
                         connection_keyword='connection')
 
619
    def Introspect(self, object_path, connection):
 
620
        """Standard D-Bus method, overloaded to insert property tags.
 
621
        """
 
622
        xmlstring = dbus.service.Object.Introspect(self, object_path,
 
623
                                           connection)
 
624
        document = xml.dom.minidom.parseString(xmlstring)
 
625
        del xmlstring
 
626
        def make_tag(document, name, prop):
 
627
            e = document.createElement(u"property")
 
628
            e.setAttribute(u"name", name)
 
629
            e.setAttribute(u"type", prop._dbus_signature)
 
630
            e.setAttribute(u"access", prop._dbus_access)
 
631
            return e
 
632
        for if_tag in document.getElementsByTagName(u"interface"):
 
633
            for tag in (make_tag(document, name, prop)
 
634
                        for name, prop
 
635
                        in self._get_all_dbus_properties()
 
636
                        if prop._dbus_interface
 
637
                        == if_tag.getAttribute(u"name")):
 
638
                if_tag.appendChild(tag)
 
639
        xmlstring = document.toxml(u"utf-8")
 
640
        document.unlink()
 
641
        return xmlstring
 
642
 
 
643
 
 
644
class ClientDBus(Client, DBusObjectWithProperties):
478
645
    """A Client class using D-Bus
479
646
    
480
647
    Attributes:
491
658
        self.dbus_object_path = (dbus.ObjectPath
492
659
                                 (u"/clients/"
493
660
                                  + self.name.replace(u".", u"_")))
494
 
        dbus.service.Object.__init__(self, self.bus,
495
 
                                     self.dbus_object_path)
 
661
        DBusObjectWithProperties.__init__(self, self.bus,
 
662
                                          self.dbus_object_path)
496
663
    
497
664
    @staticmethod
498
665
    def _datetime_to_dbus(dt, variant_level=0):
527
694
            self.remove_from_connection()
528
695
        except LookupError:
529
696
            pass
530
 
        if hasattr(dbus.service.Object, u"__del__"):
531
 
            dbus.service.Object.__del__(self, *args, **kwargs)
 
697
        if hasattr(DBusObjectWithProperties, u"__del__"):
 
698
            DBusObjectWithProperties.__del__(self, *args, **kwargs)
532
699
        Client.__del__(self, *args, **kwargs)
533
700
    
534
701
    def checker_callback(self, pid, condition, command,
591
758
    ## D-Bus methods & signals
592
759
    _interface = u"se.bsnet.fukt.Mandos.Client"
593
760
    
594
 
    # CheckedOK - method
595
 
    @dbus.service.method(_interface)
596
 
    def CheckedOK(self):
597
 
        return self.checked_ok()
598
 
    
599
761
    # CheckerCompleted - signal
600
762
    @dbus.service.signal(_interface, signature=u"nxs")
601
763
    def CheckerCompleted(self, exitcode, waitstatus, command):
608
770
        "D-Bus signal"
609
771
        pass
610
772
    
611
 
    # GetAllProperties - method
612
 
    @dbus.service.method(_interface, out_signature=u"a{sv}")
613
 
    def GetAllProperties(self):
614
 
        "D-Bus method"
615
 
        return dbus.Dictionary({
616
 
                dbus.String(u"name"):
617
 
                    dbus.String(self.name, variant_level=1),
618
 
                dbus.String(u"fingerprint"):
619
 
                    dbus.String(self.fingerprint, variant_level=1),
620
 
                dbus.String(u"host"):
621
 
                    dbus.String(self.host, variant_level=1),
622
 
                dbus.String(u"created"):
623
 
                    self._datetime_to_dbus(self.created,
624
 
                                           variant_level=1),
625
 
                dbus.String(u"last_enabled"):
626
 
                    (self._datetime_to_dbus(self.last_enabled,
627
 
                                            variant_level=1)
628
 
                     if self.last_enabled is not None
629
 
                     else dbus.Boolean(False, variant_level=1)),
630
 
                dbus.String(u"enabled"):
631
 
                    dbus.Boolean(self.enabled, variant_level=1),
632
 
                dbus.String(u"last_checked_ok"):
633
 
                    (self._datetime_to_dbus(self.last_checked_ok,
634
 
                                            variant_level=1)
635
 
                     if self.last_checked_ok is not None
636
 
                     else dbus.Boolean (False, variant_level=1)),
637
 
                dbus.String(u"timeout"):
638
 
                    dbus.UInt64(self.timeout_milliseconds(),
639
 
                                variant_level=1),
640
 
                dbus.String(u"interval"):
641
 
                    dbus.UInt64(self.interval_milliseconds(),
642
 
                                variant_level=1),
643
 
                dbus.String(u"checker"):
644
 
                    dbus.String(self.checker_command,
645
 
                                variant_level=1),
646
 
                dbus.String(u"checker_running"):
647
 
                    dbus.Boolean(self.checker is not None,
648
 
                                 variant_level=1),
649
 
                dbus.String(u"object_path"):
650
 
                    dbus.ObjectPath(self.dbus_object_path,
651
 
                                    variant_level=1)
652
 
                }, signature=u"sv")
653
 
    
654
 
    # IsStillValid - method
655
 
    @dbus.service.method(_interface, out_signature=u"b")
656
 
    def IsStillValid(self):
657
 
        return self.still_valid()
658
 
    
659
773
    # PropertyChanged - signal
660
774
    @dbus.service.signal(_interface, signature=u"sv")
661
775
    def PropertyChanged(self, property, value):
674
788
        "D-Bus signal"
675
789
        pass
676
790
    
677
 
    # SetChecker - method
678
 
    @dbus.service.method(_interface, in_signature=u"s")
679
 
    def SetChecker(self, checker):
680
 
        "D-Bus setter method"
681
 
        self.checker_command = checker
 
791
    # name - property
 
792
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
793
    def name_dbus_property(self):
 
794
        return dbus.String(self.name)
 
795
    
 
796
    # fingerprint - property
 
797
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
798
    def fingerprint_dbus_property(self):
 
799
        return dbus.String(self.fingerprint)
 
800
    
 
801
    # host - property
 
802
    @dbus_service_property(_interface, signature=u"s",
 
803
                           access=u"readwrite")
 
804
    def host_dbus_property(self, value=None):
 
805
        if value is None:       # get
 
806
            return dbus.String(self.host)
 
807
        self.host = value
 
808
        # Emit D-Bus signal
 
809
        self.PropertyChanged(dbus.String(u"host"),
 
810
                             dbus.String(value, variant_level=1))
 
811
    
 
812
    # created - property
 
813
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
814
    def created_dbus_property(self):
 
815
        return dbus.String(self._datetime_to_dbus(self.created))
 
816
    
 
817
    # last_enabled - property
 
818
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
819
    def last_enabled_dbus_property(self):
 
820
        if self.last_enabled is None:
 
821
            return dbus.String(u"")
 
822
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
 
823
    
 
824
    # enabled - property
 
825
    @dbus_service_property(_interface, signature=u"b",
 
826
                           access=u"readwrite")
 
827
    def enabled_dbus_property(self, value=None):
 
828
        if value is None:       # get
 
829
            return dbus.Boolean(self.enabled)
 
830
        if value:
 
831
            self.enable()
 
832
        else:
 
833
            self.disable()
 
834
    
 
835
    # last_checked_ok - property
 
836
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
837
    def last_checked_ok_dbus_property(self):
 
838
        if self.last_checked_ok is None:
 
839
            return dbus.String(u"")
 
840
        return dbus.String(self._datetime_to_dbus(self
 
841
                                                  .last_checked_ok))
 
842
    
 
843
    # timeout - property
 
844
    @dbus_service_property(_interface, signature=u"t",
 
845
                           access=u"readwrite")
 
846
    def timeout_dbus_property(self, value=None):
 
847
        if value is None:       # get
 
848
            return dbus.UInt64(self.timeout_milliseconds())
 
849
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
850
        # Emit D-Bus signal
 
851
        self.PropertyChanged(dbus.String(u"timeout"),
 
852
                             dbus.UInt64(value, variant_level=1))
 
853
        if getattr(self, u"disable_initiator_tag", None) is None:
 
854
            return
 
855
        # Reschedule timeout
 
856
        gobject.source_remove(self.disable_initiator_tag)
 
857
        self.disable_initiator_tag = None
 
858
        time_to_die = (self.
 
859
                       _timedelta_to_milliseconds((self
 
860
                                                   .last_checked_ok
 
861
                                                   + self.timeout)
 
862
                                                  - datetime.datetime
 
863
                                                  .utcnow()))
 
864
        if time_to_die <= 0:
 
865
            # The timeout has passed
 
866
            self.disable()
 
867
        else:
 
868
            self.disable_initiator_tag = (gobject.timeout_add
 
869
                                          (time_to_die, self.disable))
 
870
    
 
871
    # interval - property
 
872
    @dbus_service_property(_interface, signature=u"t",
 
873
                           access=u"readwrite")
 
874
    def interval_dbus_property(self, value=None):
 
875
        if value is None:       # get
 
876
            return dbus.UInt64(self.interval_milliseconds())
 
877
        self.interval = datetime.timedelta(0, 0, 0, value)
 
878
        # Emit D-Bus signal
 
879
        self.PropertyChanged(dbus.String(u"interval"),
 
880
                             dbus.UInt64(value, variant_level=1))
 
881
        if getattr(self, u"checker_initiator_tag", None) is None:
 
882
            return
 
883
        # Reschedule checker run
 
884
        gobject.source_remove(self.checker_initiator_tag)
 
885
        self.checker_initiator_tag = (gobject.timeout_add
 
886
                                      (value, self.start_checker))
 
887
        self.start_checker()    # Start one now, too
 
888
 
 
889
    # checker - property
 
890
    @dbus_service_property(_interface, signature=u"s",
 
891
                           access=u"readwrite")
 
892
    def checker_dbus_property(self, value=None):
 
893
        if value is None:       # get
 
894
            return dbus.String(self.checker_command)
 
895
        self.checker_command = value
682
896
        # Emit D-Bus signal
683
897
        self.PropertyChanged(dbus.String(u"checker"),
684
898
                             dbus.String(self.checker_command,
685
899
                                         variant_level=1))
686
900
    
687
 
    # SetHost - method
688
 
    @dbus.service.method(_interface, in_signature=u"s")
689
 
    def SetHost(self, host):
690
 
        "D-Bus setter method"
691
 
        self.host = host
692
 
        # Emit D-Bus signal
693
 
        self.PropertyChanged(dbus.String(u"host"),
694
 
                             dbus.String(self.host, variant_level=1))
695
 
    
696
 
    # SetInterval - method
697
 
    @dbus.service.method(_interface, in_signature=u"t")
698
 
    def SetInterval(self, milliseconds):
699
 
        self.interval = datetime.timedelta(0, 0, 0, milliseconds)
700
 
        # Emit D-Bus signal
701
 
        self.PropertyChanged(dbus.String(u"interval"),
702
 
                             (dbus.UInt64(self.interval_milliseconds(),
703
 
                                          variant_level=1)))
704
 
    
705
 
    # SetSecret - method
706
 
    @dbus.service.method(_interface, in_signature=u"ay",
707
 
                         byte_arrays=True)
708
 
    def SetSecret(self, secret):
709
 
        "D-Bus setter method"
710
 
        self.secret = str(secret)
711
 
    
712
 
    # SetTimeout - method
713
 
    @dbus.service.method(_interface, in_signature=u"t")
714
 
    def SetTimeout(self, milliseconds):
715
 
        self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
716
 
        # Emit D-Bus signal
717
 
        self.PropertyChanged(dbus.String(u"timeout"),
718
 
                             (dbus.UInt64(self.timeout_milliseconds(),
719
 
                                          variant_level=1)))
720
 
    
721
 
    # Enable - method
722
 
    @dbus.service.method(_interface)
723
 
    def Enable(self):
724
 
        "D-Bus method"
725
 
        self.enable()
726
 
    
727
 
    # StartChecker - method
728
 
    @dbus.service.method(_interface)
729
 
    def StartChecker(self):
730
 
        "D-Bus method"
731
 
        self.start_checker()
732
 
    
733
 
    # Disable - method
734
 
    @dbus.service.method(_interface)
735
 
    def Disable(self):
736
 
        "D-Bus method"
737
 
        self.disable()
738
 
    
739
 
    # StopChecker - method
740
 
    @dbus.service.method(_interface)
741
 
    def StopChecker(self):
742
 
        self.stop_checker()
 
901
    # checker_running - property
 
902
    @dbus_service_property(_interface, signature=u"b",
 
903
                           access=u"readwrite")
 
904
    def checker_running_dbus_property(self, value=None):
 
905
        if value is None:       # get
 
906
            return dbus.Boolean(self.checker is not None)
 
907
        if value:
 
908
            self.start_checker()
 
909
        else:
 
910
            self.stop_checker()
 
911
    
 
912
    # object_path - property
 
913
    @dbus_service_property(_interface, signature=u"o", access=u"read")
 
914
    def object_path_dbus_property(self):
 
915
        return self.dbus_object_path # is already a dbus.ObjectPath
 
916
    
 
917
    # secret = property xxx
 
918
    @dbus_service_property(_interface, signature=u"ay",
 
919
                           access=u"write", byte_arrays=True)
 
920
    def secret_dbus_property(self, value):
 
921
        self.secret = str(value)
743
922
    
744
923
    del _interface
745
924
 
808
987
                    client = c
809
988
                    break
810
989
            else:
811
 
                ipc.write(u"NOTFOUND %s\n" % fpr)
 
990
                ipc.write(u"NOTFOUND %s %s\n"
 
991
                          % (fpr, unicode(self.client_address)))
812
992
                session.bye()
813
993
                return
814
994
            # Have to check if client.still_valid(), since it is
893
1073
 
894
1074
 
895
1075
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
896
 
    """Like socketserver.ForkingMixIn, but also pass a pipe.
897
 
    
898
 
    Assumes a gobject.MainLoop event loop.
899
 
    """
 
1076
    """Like socketserver.ForkingMixIn, but also pass a pipe."""
900
1077
    def process_request(self, request, client_address):
901
1078
        """Overrides and wraps the original process_request().
902
1079
        
903
 
        This function creates a new pipe in self.pipe 
 
1080
        This function creates a new pipe in self.pipe
904
1081
        """
905
1082
        self.pipe = os.pipe()
906
1083
        super(ForkingMixInWithPipe,
907
1084
              self).process_request(request, client_address)
908
1085
        os.close(self.pipe[1])  # close write end
909
 
        # Call "handle_ipc" for both data and EOF events
910
 
        gobject.io_add_watch(self.pipe[0],
911
 
                             gobject.IO_IN | gobject.IO_HUP,
912
 
                             self.handle_ipc)
913
 
    def handle_ipc(self, source, condition):
 
1086
        self.add_pipe(self.pipe[0])
 
1087
    def add_pipe(self, pipe):
914
1088
        """Dummy function; override as necessary"""
915
 
        os.close(source)
916
 
        return False
 
1089
        os.close(pipe)
917
1090
 
918
1091
 
919
1092
class IPv6_TCPServer(ForkingMixInWithPipe,
924
1097
        enabled:        Boolean; whether this server is activated yet
925
1098
        interface:      None or a network interface name (string)
926
1099
        use_ipv6:       Boolean; to use IPv6 or not
927
 
        ----
928
 
        clients:        set of Client objects
929
 
        gnutls_priority GnuTLS priority string
930
 
        use_dbus:       Boolean; to emit D-Bus signals or not
931
1100
    """
932
1101
    def __init__(self, server_address, RequestHandlerClass,
933
1102
                 interface=None, use_ipv6=True):
990
1159
        clients:        set of Client objects
991
1160
        gnutls_priority GnuTLS priority string
992
1161
        use_dbus:       Boolean; to emit D-Bus signals or not
 
1162
    
 
1163
    Assumes a gobject.MainLoop event loop.
993
1164
    """
994
1165
    def __init__(self, server_address, RequestHandlerClass,
995
1166
                 interface=None, use_ipv6=True, clients=None,
996
1167
                 gnutls_priority=None, use_dbus=True):
997
1168
        self.enabled = False
998
1169
        self.clients = clients
 
1170
        if self.clients is None:
 
1171
            self.clients = set()
999
1172
        self.use_dbus = use_dbus
1000
1173
        self.gnutls_priority = gnutls_priority
1001
1174
        IPv6_TCPServer.__init__(self, server_address,
1007
1180
            return socketserver.TCPServer.server_activate(self)
1008
1181
    def enable(self):
1009
1182
        self.enabled = True
 
1183
    def add_pipe(self, pipe):
 
1184
        # Call "handle_ipc" for both data and EOF events
 
1185
        gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
 
1186
                             self.handle_ipc)
1010
1187
    def handle_ipc(self, source, condition, file_objects={}):
1011
1188
        condition_names = {
1012
1189
            gobject.IO_IN: u"IN",   # There is data to read.
1168
1345
 
1169
1346
def main():
1170
1347
    
1171
 
    ######################################################################
 
1348
    ##################################################################
1172
1349
    # Parsing of options, both command line and config file
1173
1350
    
1174
1351
    parser = optparse.OptionParser(version = "%%prog %s" % version)
1276
1453
    global mandos_dbus_service
1277
1454
    mandos_dbus_service = None
1278
1455
    
1279
 
    clients = set()
1280
1456
    tcp_server = MandosServer((server_settings[u"address"],
1281
1457
                               server_settings[u"port"]),
1282
1458
                              ClientHandler,
1283
1459
                              interface=server_settings[u"interface"],
1284
1460
                              use_ipv6=use_ipv6,
1285
 
                              clients=clients,
1286
1461
                              gnutls_priority=
1287
1462
                              server_settings[u"priority"],
1288
1463
                              use_dbus=use_dbus)
1345
1520
    client_class = Client
1346
1521
    if use_dbus:
1347
1522
        client_class = functools.partial(ClientDBus, bus = bus)
1348
 
    clients.update(set(
 
1523
    tcp_server.clients.update(set(
1349
1524
            client_class(name = section,
1350
1525
                         config= dict(client_config.items(section)))
1351
1526
            for section in client_config.sections()))
1352
 
    if not clients:
 
1527
    if not tcp_server.clients:
1353
1528
        logger.warning(u"No clients defined")
1354
1529
    
1355
1530
    if debug:
1381
1556
        "Cleanup function; run on exit"
1382
1557
        service.cleanup()
1383
1558
        
1384
 
        while clients:
1385
 
            client = clients.pop()
 
1559
        while tcp_server.clients:
 
1560
            client = tcp_server.clients.pop()
1386
1561
            client.disable_hook = None
1387
1562
            client.disable()
1388
1563
    
1418
1593
            @dbus.service.method(_interface, out_signature=u"ao")
1419
1594
            def GetAllClients(self):
1420
1595
                "D-Bus method"
1421
 
                return dbus.Array(c.dbus_object_path for c in clients)
 
1596
                return dbus.Array(c.dbus_object_path
 
1597
                                  for c in tcp_server.clients)
1422
1598
            
1423
1599
            @dbus.service.method(_interface,
1424
1600
                                 out_signature=u"a{oa{sv}}")
1425
1601
            def GetAllClientsWithProperties(self):
1426
1602
                "D-Bus method"
1427
1603
                return dbus.Dictionary(
1428
 
                    ((c.dbus_object_path, c.GetAllProperties())
1429
 
                     for c in clients),
 
1604
                    ((c.dbus_object_path, c.GetAll(u""))
 
1605
                     for c in tcp_server.clients),
1430
1606
                    signature=u"oa{sv}")
1431
1607
            
1432
1608
            @dbus.service.method(_interface, in_signature=u"o")
1433
1609
            def RemoveClient(self, object_path):
1434
1610
                "D-Bus method"
1435
 
                for c in clients:
 
1611
                for c in tcp_server.clients:
1436
1612
                    if c.dbus_object_path == object_path:
1437
 
                        clients.remove(c)
 
1613
                        tcp_server.clients.remove(c)
1438
1614
                        c.remove_from_connection()
1439
1615
                        # Don't signal anything except ClientRemoved
1440
1616
                        c.disable(signal=False)
1447
1623
        
1448
1624
        mandos_dbus_service = MandosDBusService()
1449
1625
    
1450
 
    for client in clients:
 
1626
    for client in tcp_server.clients:
1451
1627
        if use_dbus:
1452
1628
            # Emit D-Bus signal
1453
1629
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
1454
 
                                            client.GetAllProperties())
 
1630
                                            client.GetAll(u""))
1455
1631
        client.enable()
1456
1632
    
1457
1633
    tcp_server.enable()