/mandos/release

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

« back to all changes in this revision

Viewing changes to mandos

* mandos (MandosServer.handle_ipc): Better log message.
  (main/MandosDBusService.ClientNotFound): Add "address" argument.
                                           All callers changed.
* mandos-monitor (MandosClientWidget.__init__): Add "logger" argument.
  (MandosClientWidget.checker_completed,
  MandosClientWidget.checker_started, MandosClientWidget.got_secret,
  MandosClientWidget.rejected): New methods, connected to signals.
  (MandosClientWidget.update): Improve display.
  (UserInterface.client_not_found): New method, conneced to signal.
  (UserInterface.log_message): New; log with timestamp.
  (UserInterface.log_message_raw): Same as old "log_message".  Bug
                                  fix; always do "refresh()".

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.11"
 
82
version = "1.0.14"
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
289
292
        elif u"secfile" in config:
290
293
            with closing(open(os.path.expanduser
291
294
                              (os.path.expandvars
292
 
                               (config[u"secfile"])))) as secfile:
 
295
                               (config[u"secfile"])),
 
296
                              "rb")) as secfile:
293
297
                self.secret = secfile.read()
294
298
        else:
295
299
            raise TypeError(u"No secret or secfile for client %s"
321
325
        self.checker_initiator_tag = (gobject.timeout_add
322
326
                                      (self.interval_milliseconds(),
323
327
                                       self.start_checker))
324
 
        # Also start a new checker *right now*.
325
 
        self.start_checker()
326
328
        # Schedule a disable() when 'timeout' has passed
327
329
        self.disable_initiator_tag = (gobject.timeout_add
328
330
                                   (self.timeout_milliseconds(),
329
331
                                    self.disable))
330
332
        self.enabled = True
 
333
        # Also start a new checker *right now*.
 
334
        self.start_checker()
331
335
    
332
 
    def disable(self):
 
336
    def disable(self, quiet=True):
333
337
        """Disable this client."""
334
338
        if not getattr(self, "enabled", False):
335
339
            return False
336
 
        logger.info(u"Disabling client %s", self.name)
 
340
        if not quiet:
 
341
            logger.info(u"Disabling client %s", self.name)
337
342
        if getattr(self, u"disable_initiator_tag", False):
338
343
            gobject.source_remove(self.disable_initiator_tag)
339
344
            self.disable_initiator_tag = None
395
400
        # is as it should be.
396
401
        
397
402
        # If a checker exists, make sure it is not a zombie
398
 
        if self.checker is not None:
 
403
        try:
399
404
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
 
405
        except (AttributeError, OSError), error:
 
406
            if (isinstance(error, OSError)
 
407
                and error.errno != errno.ECHILD):
 
408
                raise error
 
409
        else:
400
410
            if pid:
401
411
                logger.warning(u"Checker was a zombie")
402
412
                gobject.source_remove(self.checker_callback_tag)
458
468
        logger.debug(u"Stopping checker for %(name)s", vars(self))
459
469
        try:
460
470
            os.kill(self.checker.pid, signal.SIGTERM)
461
 
            #os.sleep(0.5)
 
471
            #time.sleep(0.5)
462
472
            #if self.checker.poll() is None:
463
473
            #    os.kill(self.checker.pid, signal.SIGKILL)
464
474
        except OSError, error:
477
487
            return now < (self.last_checked_ok + self.timeout)
478
488
 
479
489
 
480
 
class ClientDBus(Client, dbus.service.Object):
 
490
def dbus_service_property(dbus_interface, signature=u"v",
 
491
                          access=u"readwrite", byte_arrays=False):
 
492
    """Decorators for marking methods of a DBusObjectWithProperties to
 
493
    become properties on the D-Bus.
 
494
    
 
495
    The decorated method will be called with no arguments by "Get"
 
496
    and with one argument by "Set".
 
497
    
 
498
    The parameters, where they are supported, are the same as
 
499
    dbus.service.method, except there is only "signature", since the
 
500
    type from Get() and the type sent to Set() is the same.
 
501
    """
 
502
    def decorator(func):
 
503
        func._dbus_is_property = True
 
504
        func._dbus_interface = dbus_interface
 
505
        func._dbus_signature = signature
 
506
        func._dbus_access = access
 
507
        func._dbus_name = func.__name__
 
508
        if func._dbus_name.endswith(u"_dbus_property"):
 
509
            func._dbus_name = func._dbus_name[:-14]
 
510
        func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
 
511
        return func
 
512
    return decorator
 
513
 
 
514
 
 
515
class DBusPropertyException(dbus.exceptions.DBusException):
 
516
    """A base class for D-Bus property-related exceptions
 
517
    """
 
518
    def __unicode__(self):
 
519
        return unicode(str(self))
 
520
 
 
521
 
 
522
class DBusPropertyAccessException(DBusPropertyException):
 
523
    """A property's access permissions disallows an operation.
 
524
    """
 
525
    pass
 
526
 
 
527
 
 
528
class DBusPropertyNotFound(DBusPropertyException):
 
529
    """An attempt was made to access a non-existing property.
 
530
    """
 
531
    pass
 
532
 
 
533
 
 
534
class DBusObjectWithProperties(dbus.service.Object):
 
535
    """A D-Bus object with properties.
 
536
 
 
537
    Classes inheriting from this can use the dbus_service_property
 
538
    decorator to expose methods as D-Bus properties.  It exposes the
 
539
    standard Get(), Set(), and GetAll() methods on the D-Bus.
 
540
    """
 
541
    
 
542
    @staticmethod
 
543
    def _is_dbus_property(obj):
 
544
        return getattr(obj, u"_dbus_is_property", False)
 
545
    
 
546
    def _get_all_dbus_properties(self):
 
547
        """Returns a generator of (name, attribute) pairs
 
548
        """
 
549
        return ((prop._dbus_name, prop)
 
550
                for name, prop in
 
551
                inspect.getmembers(self, self._is_dbus_property))
 
552
    
 
553
    def _get_dbus_property(self, interface_name, property_name):
 
554
        """Returns a bound method if one exists which is a D-Bus
 
555
        property with the specified name and interface.
 
556
        """
 
557
        for name in (property_name,
 
558
                     property_name + u"_dbus_property"):
 
559
            prop = getattr(self, name, None)
 
560
            if (prop is None
 
561
                or not self._is_dbus_property(prop)
 
562
                or prop._dbus_name != property_name
 
563
                or (interface_name and prop._dbus_interface
 
564
                    and interface_name != prop._dbus_interface)):
 
565
                continue
 
566
            return prop
 
567
        # No such property
 
568
        raise DBusPropertyNotFound(self.dbus_object_path + u":"
 
569
                                   + interface_name + u"."
 
570
                                   + property_name)
 
571
    
 
572
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
 
573
                         out_signature=u"v")
 
574
    def Get(self, interface_name, property_name):
 
575
        """Standard D-Bus property Get() method, see D-Bus standard.
 
576
        """
 
577
        prop = self._get_dbus_property(interface_name, property_name)
 
578
        if prop._dbus_access == u"write":
 
579
            raise DBusPropertyAccessException(property_name)
 
580
        value = prop()
 
581
        if not hasattr(value, u"variant_level"):
 
582
            return value
 
583
        return type(value)(value, variant_level=value.variant_level+1)
 
584
    
 
585
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
 
586
    def Set(self, interface_name, property_name, value):
 
587
        """Standard D-Bus property Set() method, see D-Bus standard.
 
588
        """
 
589
        prop = self._get_dbus_property(interface_name, property_name)
 
590
        if prop._dbus_access == u"read":
 
591
            raise DBusPropertyAccessException(property_name)
 
592
        if prop._dbus_get_args_options[u"byte_arrays"]:
 
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,
611
801
        "D-Bus signal"
612
802
        pass
613
803
    
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
804
    # PropertyChanged - signal
663
805
    @dbus.service.signal(_interface, signature=u"sv")
664
806
    def PropertyChanged(self, property, value):
665
807
        "D-Bus signal"
666
808
        pass
667
809
    
668
 
    # ReceivedSecret - signal
 
810
    # GotSecret - signal
669
811
    @dbus.service.signal(_interface)
670
 
    def ReceivedSecret(self):
 
812
    def GotSecret(self):
671
813
        "D-Bus signal"
672
814
        pass
673
815
    
677
819
        "D-Bus signal"
678
820
        pass
679
821
    
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)))
723
 
    
724
822
    # Enable - method
725
823
    @dbus.service.method(_interface)
726
824
    def Enable(self):
744
842
    def StopChecker(self):
745
843
        self.stop_checker()
746
844
    
 
845
    # name - property
 
846
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
847
    def name_dbus_property(self):
 
848
        return dbus.String(self.name)
 
849
    
 
850
    # fingerprint - property
 
851
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
852
    def fingerprint_dbus_property(self):
 
853
        return dbus.String(self.fingerprint)
 
854
    
 
855
    # host - property
 
856
    @dbus_service_property(_interface, signature=u"s",
 
857
                           access=u"readwrite")
 
858
    def host_dbus_property(self, value=None):
 
859
        if value is None:       # get
 
860
            return dbus.String(self.host)
 
861
        self.host = value
 
862
        # Emit D-Bus signal
 
863
        self.PropertyChanged(dbus.String(u"host"),
 
864
                             dbus.String(value, variant_level=1))
 
865
    
 
866
    # created - property
 
867
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
868
    def created_dbus_property(self):
 
869
        return dbus.String(self._datetime_to_dbus(self.created))
 
870
    
 
871
    # last_enabled - property
 
872
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
873
    def last_enabled_dbus_property(self):
 
874
        if self.last_enabled is None:
 
875
            return dbus.String(u"")
 
876
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
 
877
    
 
878
    # enabled - property
 
879
    @dbus_service_property(_interface, signature=u"b",
 
880
                           access=u"readwrite")
 
881
    def enabled_dbus_property(self, value=None):
 
882
        if value is None:       # get
 
883
            return dbus.Boolean(self.enabled)
 
884
        if value:
 
885
            self.enable()
 
886
        else:
 
887
            self.disable()
 
888
    
 
889
    # last_checked_ok - property
 
890
    @dbus_service_property(_interface, signature=u"s",
 
891
                           access=u"readwrite")
 
892
    def last_checked_ok_dbus_property(self, value=None):
 
893
        if value is not None:
 
894
            self.checked_ok()
 
895
            return
 
896
        if self.last_checked_ok is None:
 
897
            return dbus.String(u"")
 
898
        return dbus.String(self._datetime_to_dbus(self
 
899
                                                  .last_checked_ok))
 
900
    
 
901
    # timeout - property
 
902
    @dbus_service_property(_interface, signature=u"t",
 
903
                           access=u"readwrite")
 
904
    def timeout_dbus_property(self, value=None):
 
905
        if value is None:       # get
 
906
            return dbus.UInt64(self.timeout_milliseconds())
 
907
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
908
        # Emit D-Bus signal
 
909
        self.PropertyChanged(dbus.String(u"timeout"),
 
910
                             dbus.UInt64(value, variant_level=1))
 
911
        if getattr(self, u"disable_initiator_tag", None) is None:
 
912
            return
 
913
        # Reschedule timeout
 
914
        gobject.source_remove(self.disable_initiator_tag)
 
915
        self.disable_initiator_tag = None
 
916
        time_to_die = (self.
 
917
                       _timedelta_to_milliseconds((self
 
918
                                                   .last_checked_ok
 
919
                                                   + self.timeout)
 
920
                                                  - datetime.datetime
 
921
                                                  .utcnow()))
 
922
        if time_to_die <= 0:
 
923
            # The timeout has passed
 
924
            self.disable()
 
925
        else:
 
926
            self.disable_initiator_tag = (gobject.timeout_add
 
927
                                          (time_to_die, self.disable))
 
928
    
 
929
    # interval - property
 
930
    @dbus_service_property(_interface, signature=u"t",
 
931
                           access=u"readwrite")
 
932
    def interval_dbus_property(self, value=None):
 
933
        if value is None:       # get
 
934
            return dbus.UInt64(self.interval_milliseconds())
 
935
        self.interval = datetime.timedelta(0, 0, 0, value)
 
936
        # Emit D-Bus signal
 
937
        self.PropertyChanged(dbus.String(u"interval"),
 
938
                             dbus.UInt64(value, variant_level=1))
 
939
        if getattr(self, u"checker_initiator_tag", None) is None:
 
940
            return
 
941
        # Reschedule checker run
 
942
        gobject.source_remove(self.checker_initiator_tag)
 
943
        self.checker_initiator_tag = (gobject.timeout_add
 
944
                                      (value, self.start_checker))
 
945
        self.start_checker()    # Start one now, too
 
946
 
 
947
    # checker - property
 
948
    @dbus_service_property(_interface, signature=u"s",
 
949
                           access=u"readwrite")
 
950
    def checker_dbus_property(self, value=None):
 
951
        if value is None:       # get
 
952
            return dbus.String(self.checker_command)
 
953
        self.checker_command = value
 
954
        # Emit D-Bus signal
 
955
        self.PropertyChanged(dbus.String(u"checker"),
 
956
                             dbus.String(self.checker_command,
 
957
                                         variant_level=1))
 
958
    
 
959
    # checker_running - property
 
960
    @dbus_service_property(_interface, signature=u"b",
 
961
                           access=u"readwrite")
 
962
    def checker_running_dbus_property(self, value=None):
 
963
        if value is None:       # get
 
964
            return dbus.Boolean(self.checker is not None)
 
965
        if value:
 
966
            self.start_checker()
 
967
        else:
 
968
            self.stop_checker()
 
969
    
 
970
    # object_path - property
 
971
    @dbus_service_property(_interface, signature=u"o", access=u"read")
 
972
    def object_path_dbus_property(self):
 
973
        return self.dbus_object_path # is already a dbus.ObjectPath
 
974
    
 
975
    # secret = property
 
976
    @dbus_service_property(_interface, signature=u"ay",
 
977
                           access=u"write", byte_arrays=True)
 
978
    def secret_dbus_property(self, value):
 
979
        self.secret = str(value)
 
980
    
747
981
    del _interface
748
982
 
749
983
 
983
1217
        clients:        set of Client objects
984
1218
        gnutls_priority GnuTLS priority string
985
1219
        use_dbus:       Boolean; to emit D-Bus signals or not
986
 
        clients:        set of Client objects
987
 
        gnutls_priority GnuTLS priority string
988
 
        use_dbus:       Boolean; to emit D-Bus signals or not
989
1220
    
990
1221
    Assumes a gobject.MainLoop event loop.
991
1222
    """
1049
1280
        cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1050
1281
        
1051
1282
        if cmd == u"NOTFOUND":
1052
 
            logger.warning(u"Client not found for fingerprint: %s",
1053
 
                           args)
 
1283
            fpr, address = args.split(None, 1)
 
1284
            logger.warning(u"Client not found for fingerprint: %s, ad"
 
1285
                           u"dress: %s", fpr, address)
1054
1286
            if self.use_dbus:
1055
1287
                # Emit D-Bus signal
1056
 
                mandos_dbus_service.ClientNotFound(args)
 
1288
                mandos_dbus_service.ClientNotFound(fpr, address)
1057
1289
        elif cmd == u"INVALID":
1058
1290
            for client in self.clients:
1059
1291
                if client.name == args:
1071
1303
                    client.checked_ok()
1072
1304
                    if self.use_dbus:
1073
1305
                        # Emit D-Bus signal
1074
 
                        client.ReceivedSecret()
 
1306
                        client.GotSecret()
1075
1307
                    break
1076
1308
            else:
1077
1309
                logger.error(u"Sending secret to unknown client %s",
1115
1347
            elif suffix == u"w":
1116
1348
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1117
1349
            else:
1118
 
                raise ValueError
1119
 
        except (ValueError, IndexError):
1120
 
            raise ValueError
 
1350
                raise ValueError(u"Unknown suffix %r" % suffix)
 
1351
        except (ValueError, IndexError), e:
 
1352
            raise ValueError(e.message)
1121
1353
        timevalue += delta
1122
1354
    return timevalue
1123
1355
 
1162
1394
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1163
1395
        if not stat.S_ISCHR(os.fstat(null).st_mode):
1164
1396
            raise OSError(errno.ENODEV,
1165
 
                          u"/dev/null not a character device")
 
1397
                          u"%s not a character device"
 
1398
                          % os.path.devnull)
1166
1399
        os.dup2(null, sys.stdin.fileno())
1167
1400
        os.dup2(null, sys.stdout.fileno())
1168
1401
        os.dup2(null, sys.stderr.fileno())
1172
1405
 
1173
1406
def main():
1174
1407
    
1175
 
    ######################################################################
 
1408
    ##################################################################
1176
1409
    # Parsing of options, both command line and config file
1177
1410
    
1178
1411
    parser = optparse.OptionParser(version = "%%prog %s" % version)
1335
1568
    bus = dbus.SystemBus()
1336
1569
    # End of Avahi example code
1337
1570
    if use_dbus:
1338
 
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
 
1571
        try:
 
1572
            bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
 
1573
                                            bus, do_not_queue=True)
 
1574
        except dbus.exceptions.NameExistsException, e:
 
1575
            logger.error(unicode(e) + u", disabling D-Bus")
 
1576
            use_dbus = False
 
1577
            server_settings[u"use_dbus"] = False
 
1578
            tcp_server.use_dbus = False
1339
1579
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1340
1580
    service = AvahiService(name = server_settings[u"servicename"],
1341
1581
                           servicetype = u"_mandos._tcp",
1379
1619
        pass
1380
1620
    del pidfilename
1381
1621
    
1382
 
    def cleanup():
1383
 
        "Cleanup function; run on exit"
1384
 
        service.cleanup()
1385
 
        
1386
 
        while tcp_server.clients:
1387
 
            client = tcp_server.clients.pop()
1388
 
            client.disable_hook = None
1389
 
            client.disable()
1390
 
    
1391
 
    atexit.register(cleanup)
1392
 
    
1393
1622
    if not debug:
1394
1623
        signal.signal(signal.SIGINT, signal.SIG_IGN)
1395
1624
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1407
1636
                "D-Bus signal"
1408
1637
                pass
1409
1638
            
1410
 
            @dbus.service.signal(_interface, signature=u"s")
1411
 
            def ClientNotFound(self, fingerprint):
 
1639
            @dbus.service.signal(_interface, signature=u"ss")
 
1640
            def ClientNotFound(self, fingerprint, address):
1412
1641
                "D-Bus signal"
1413
1642
                pass
1414
1643
            
1428
1657
            def GetAllClientsWithProperties(self):
1429
1658
                "D-Bus method"
1430
1659
                return dbus.Dictionary(
1431
 
                    ((c.dbus_object_path, c.GetAllProperties())
 
1660
                    ((c.dbus_object_path, c.GetAll(u""))
1432
1661
                     for c in tcp_server.clients),
1433
1662
                    signature=u"oa{sv}")
1434
1663
            
1440
1669
                        tcp_server.clients.remove(c)
1441
1670
                        c.remove_from_connection()
1442
1671
                        # Don't signal anything except ClientRemoved
1443
 
                        c.disable(signal=False)
 
1672
                        c.disable(quiet=True)
1444
1673
                        # Emit D-Bus signal
1445
1674
                        self.ClientRemoved(object_path, c.name)
1446
1675
                        return
1447
 
                raise KeyError
 
1676
                raise KeyError(object_path)
1448
1677
            
1449
1678
            del _interface
1450
1679
        
1451
1680
        mandos_dbus_service = MandosDBusService()
1452
1681
    
 
1682
    def cleanup():
 
1683
        "Cleanup function; run on exit"
 
1684
        service.cleanup()
 
1685
        
 
1686
        while tcp_server.clients:
 
1687
            client = tcp_server.clients.pop()
 
1688
            if use_dbus:
 
1689
                client.remove_from_connection()
 
1690
            client.disable_hook = None
 
1691
            # Don't signal anything except ClientRemoved
 
1692
            client.disable(quiet=True)
 
1693
            if use_dbus:
 
1694
                # Emit D-Bus signal
 
1695
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
 
1696
                                                  client.name)
 
1697
    
 
1698
    atexit.register(cleanup)
 
1699
    
1453
1700
    for client in tcp_server.clients:
1454
1701
        if use_dbus:
1455
1702
            # Emit D-Bus signal
1456
1703
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
1457
 
                                            client.GetAllProperties())
 
1704
                                            client.GetAll(u""))
1458
1705
        client.enable()
1459
1706
    
1460
1707
    tcp_server.enable()
1478
1725
            service.activate()
1479
1726
        except dbus.exceptions.DBusException, error:
1480
1727
            logger.critical(u"DBusException: %s", error)
 
1728
            cleanup()
1481
1729
            sys.exit(1)
1482
1730
        # End of Avahi example code
1483
1731
        
1490
1738
        main_loop.run()
1491
1739
    except AvahiError, error:
1492
1740
        logger.critical(u"AvahiError: %s", error)
 
1741
        cleanup()
1493
1742
        sys.exit(1)
1494
1743
    except KeyboardInterrupt:
1495
1744
        if debug:
1496
1745
            print >> sys.stderr
1497
1746
        logger.debug(u"Server received KeyboardInterrupt")
1498
1747
    logger.debug(u"Server exiting")
 
1748
    # Must run before the D-Bus bus name gets deregistered
 
1749
    cleanup()
1499
1750
 
1500
1751
if __name__ == '__main__':
1501
1752
    main()