/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-ctl

  • Committer: teddy at recompile
  • Date: 2020-04-05 21:30:59 UTC
  • Revision ID: teddy@recompile.se-20200405213059-fb2a61ckqynrmatk
Fix file descriptor leak in mandos-client

When the local network has Mandos servers announcing themselves using
real, globally reachable, IPv6 addresses (i.e. not link-local
addresses), but there is no router on the local network providing IPv6
RA (Router Advertisement) packets, the client cannot reach the server
by normal means, since the client only has a link-local IPv6 address,
and has no usable route to reach the server's global IPv6 address.
(This is not a common situation, and usually only happens when the
router itself reboots and runs a Mandos client, since it cannot then
give RA packets to itself.)  The client code has a solution for
this, which consists of adding a temporary local route to reach the
address of the server during communication, and removing this
temporary route afterwards.

This solution with a temporary route works, but has a file descriptor
leak; it leaks one file descriptor for each addition and for each
removal of a route.  If one server requiring an added route is present
on the network, but no servers gives a password, making the client
retry after the default ten seconds, and we furthermore assume a
default 1024 open files limit, the client runs out of file descriptors
after about 90 minutes, after which time the client process will be
useless and fail to retrieve any passwords, necessitating manual
password entry via the keyboard.

Fix this by eliminating the file descriptor leak in the client.

* plugins.d/mandos-client.c (add_delete_local_route): Do
  close(devnull) also in parent process, also if fork() fails, and on
  any failure in child process.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
 
1
#!/usr/bin/python3 -bbI
2
2
# -*- after-save-hook: (lambda () (let ((command (if (fboundp 'file-local-name) (file-local-name (buffer-file-name)) (or (file-remote-p (buffer-file-name) 'localname) (buffer-file-name))))) (if (= (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (format "%s --check" (shell-quote-argument command)) nil "*Test*")) 0) (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w))) (progn (with-current-buffer "*Test*" (compilation-mode)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); coding: utf-8 -*-
3
3
#
4
4
# Mandos Monitor - Control and monitor the Mandos server
46
46
import tempfile
47
47
import contextlib
48
48
 
 
49
if sys.version_info.major == 2:
 
50
    __metaclass__ = type
 
51
    str = unicode
 
52
 
 
53
class gi:
 
54
    """Dummy gi module, for the tests"""
 
55
    class repository:
 
56
        class GLib:
 
57
            class Error(Exception):
 
58
                pass
 
59
dbussy = None
 
60
ravel = None
 
61
dbus_python = None
 
62
pydbus = None
 
63
 
49
64
try:
50
 
    import pydbus
51
 
    import gi
52
 
    dbus_python = None
 
65
    import dbussy
 
66
    import ravel
53
67
except ImportError:
54
 
    import dbus as dbus_python
55
 
    pydbus = None
56
 
    class gi(object):
57
 
        """Dummy gi module, for the tests"""
58
 
        class repository(object):
59
 
            class GLib(object):
60
 
                class Error(Exception):
61
 
                    pass
 
68
    try:
 
69
        import pydbus
 
70
        import gi
 
71
    except ImportError:
 
72
        import dbus as dbus_python
 
73
 
62
74
 
63
75
# Show warnings by default
64
76
if not sys.warnoptions:
72
84
logging.captureWarnings(True)   # Show warnings via the logging system
73
85
 
74
86
if sys.version_info.major == 2:
75
 
    str = unicode
76
87
    import StringIO
77
88
    io.StringIO = StringIO.StringIO
78
89
 
79
90
locale.setlocale(locale.LC_ALL, "")
80
91
 
81
 
version = "1.8.6"
 
92
version = "1.8.10"
82
93
 
83
94
 
84
95
def main():
93
104
    if options.debug:
94
105
        log.setLevel(logging.DEBUG)
95
106
 
96
 
    if pydbus is not None:
 
107
    if dbussy is not None and ravel is not None:
 
108
        bus = dbussy_adapter.CachingBus(dbussy, ravel)
 
109
    elif pydbus is not None:
97
110
        bus = pydbus_adapter.CachingBus(pydbus)
98
111
    else:
99
112
        bus = dbus_python_adapter.CachingBus(dbus_python)
467
480
        parser.error("--remove can only be combined with --deny")
468
481
 
469
482
 
470
 
class dbus(object):
 
483
class dbus:
471
484
 
472
 
    class SystemBus(object):
 
485
    class SystemBus:
473
486
 
474
487
        object_manager_iface = "org.freedesktop.DBus.ObjectManager"
475
488
        def get_managed_objects(self, busname, objectpath):
484
497
                             self.properties_iface, interface, key,
485
498
                             value)
486
499
 
 
500
        def call_method(self, methodname, busname, objectpath,
 
501
                        interface, *args):
 
502
            raise NotImplementedError()
 
503
 
487
504
 
488
505
    class MandosBus(SystemBus):
489
506
        busname_domain = "se.recompile"
521
538
        pass
522
539
 
523
540
 
524
 
class dbus_python_adapter(object):
 
541
class dbus_python_adapter:
525
542
 
526
543
    class SystemBus(dbus.MandosBus):
527
544
        """Use dbus-python"""
581
598
                                     self.client_interface, key,
582
599
                                     value)
583
600
 
584
 
    class SilenceLogger(object):
 
601
    class SilenceLogger:
585
602
        "Simple context manager to silence a particular logger"
586
603
        def __init__(self, loggername):
587
604
            self.logger = logging.getLogger(loggername)
616
633
                return new_object
617
634
 
618
635
 
619
 
class pydbus_adapter(object):
 
636
class pydbus_adapter:
620
637
    class SystemBus(dbus.MandosBus):
621
638
        def __init__(self, module=pydbus):
622
639
            self.pydbus = module
679
696
                return new_object
680
697
 
681
698
 
 
699
class dbussy_adapter:
 
700
    class SystemBus(dbus.SystemBus):
 
701
        """Use DBussy"""
 
702
 
 
703
        def __init__(self, dbussy, ravel):
 
704
            self.dbussy = dbussy
 
705
            self.ravel = ravel
 
706
            self.bus = ravel.system_bus()
 
707
 
 
708
        @contextlib.contextmanager
 
709
        def convert_exception(self, exception_class=dbus.Error):
 
710
            try:
 
711
                yield
 
712
            except self.dbussy.DBusError as e:
 
713
                # This does what "raise from" would do
 
714
                exc = exception_class(*e.args)
 
715
                exc.__cause__ = e
 
716
                raise exc
 
717
 
 
718
        def call_method(self, methodname, busname, objectpath,
 
719
                        interface, *args):
 
720
            proxy_object = self.get_object(busname, objectpath)
 
721
            log.debug("D-Bus: %s:%s:%s.%s(%s)", busname, objectpath,
 
722
                      interface, methodname,
 
723
                      ", ".join(repr(a) for a in args))
 
724
            iface = proxy_object.get_interface(interface)
 
725
            method = getattr(iface, methodname)
 
726
            with self.convert_exception(dbus.Error):
 
727
                value =  method(*args)
 
728
            # DBussy returns values either as an empty list or as a
 
729
            # tuple: (signature, value)
 
730
            if value:
 
731
                return self.type_filter(value[0])
 
732
 
 
733
        def get_object(self, busname, objectpath):
 
734
            log.debug("D-Bus: Connect to: (busname=%r, path=%r)",
 
735
                      busname, objectpath)
 
736
            with self.convert_exception(dbus.ConnectFailed):
 
737
                return self.bus[busname][objectpath]
 
738
 
 
739
        def type_filter(self, value):
 
740
            """Convert the most bothersome types to Python types"""
 
741
            if isinstance(value, tuple):
 
742
                if (len(value) == 2
 
743
                    and isinstance(value[0],
 
744
                                   self.dbussy.DBUS.Signature)):
 
745
                    return self.type_filter(value[1])
 
746
            elif isinstance(value, self.dbussy.DBUS.ObjectPath):
 
747
                return str(value)
 
748
            # Also recurse into dictionaries
 
749
            elif isinstance(value, dict):
 
750
                return {self.type_filter(key):
 
751
                        self.type_filter(subval)
 
752
                        for key, subval in value.items()}
 
753
            return value
 
754
 
 
755
        def set_property(self, busname, objectpath, interface, key,
 
756
                         value):
 
757
            proxy_object = self.get_object(busname, objectpath)
 
758
            log.debug("D-Bus: %s:%s:%s.Set(%r, %r, %r)", busname,
 
759
                      objectpath, self.properties_iface, interface,
 
760
                      key, value)
 
761
            if key == "Secret":
 
762
                # DBussy wants a Byte Array to be a sequence of
 
763
                # values, not a byte string
 
764
                value = tuple(value)
 
765
            setattr(proxy_object.get_interface(interface), key, value)
 
766
 
 
767
    class MandosBus(SystemBus, dbus.MandosBus):
 
768
        pass
 
769
 
 
770
    class CachingBus(MandosBus):
 
771
        """A caching layer for dbussy_adapter.MandosBus"""
 
772
        def __init__(self, *args, **kwargs):
 
773
            self.object_cache = {}
 
774
            super(dbussy_adapter.CachingBus, self).__init__(*args,
 
775
                                                            **kwargs)
 
776
        def get_object(self, busname, objectpath):
 
777
            try:
 
778
                return self.object_cache[(busname, objectpath)]
 
779
            except KeyError:
 
780
                new_object = super(
 
781
                    dbussy_adapter.CachingBus,
 
782
                    self).get_object(busname, objectpath)
 
783
                self.object_cache[(busname, objectpath)]  = new_object
 
784
                return new_object
 
785
 
 
786
 
682
787
def commands_from_options(options):
683
788
 
684
789
    commands = list(options.commands)
712
817
    return commands
713
818
 
714
819
 
715
 
class command(object):
 
820
class command:
716
821
    """A namespace for command classes"""
717
822
 
718
 
    class Base(object):
 
823
    class Base:
719
824
        """Abstract base class for commands"""
720
825
        def run(self, clients, bus=None):
721
826
            """Normal commands should implement run_on_one_client(),
784
889
                keywords = self.all_keywords
785
890
            print(self.TableOfClients(clients.values(), keywords))
786
891
 
787
 
        class TableOfClients(object):
 
892
        class TableOfClients:
788
893
            tableheaders = {
789
894
                "Name": "Name",
790
895
                "Enabled": "Enabled",
1021
1126
                                                     "output"))
1022
1127
 
1023
1128
 
1024
 
class Unique(object):
 
1129
class Unique:
1025
1130
    """Class for objects which exist only to be unique objects, since
1026
1131
unittest.mock.sentinel only exists in Python 3.3"""
1027
1132
 
1311
1416
class Test_dbus_python_adapter_SystemBus(TestCaseWithAssertLogs):
1312
1417
 
1313
1418
    def MockDBusPython_func(self, func):
1314
 
        class mock_dbus_python(object):
 
1419
        class mock_dbus_python:
1315
1420
            """mock dbus-python module"""
1316
 
            class exceptions(object):
 
1421
            class exceptions:
1317
1422
                """Pseudo-namespace"""
1318
1423
                class DBusException(Exception):
1319
1424
                    pass
1320
 
            class SystemBus(object):
 
1425
            class SystemBus:
1321
1426
                @staticmethod
1322
1427
                def get_object(busname, objectpath):
1323
1428
                    DBusObject = collections.namedtuple(
1337
1442
                                    dbus_interface=dbus_interface)
1338
1443
                    return DBusObject(methodname=method,
1339
1444
                                      Set=set_property)
1340
 
            class Boolean(object):
 
1445
            class Boolean:
1341
1446
                def __init__(self, value):
1342
1447
                    self.value = bool(value)
1343
1448
                def __bool__(self):
1527
1632
        finally:
1528
1633
            dbus_logger.removeFilter(counting_handler)
1529
1634
 
1530
 
        self.assertNotIsInstance(e, dbus.ConnectFailed)
 
1635
        self.assertNotIsInstance(e.exception, dbus.ConnectFailed)
1531
1636
 
1532
1637
        # Make sure the dbus logger was suppressed
1533
1638
        self.assertEqual(0, counting_handler.count)
1555
1660
            self.call_method(bus, "methodname", "busname",
1556
1661
                             "objectpath", "interface")
1557
1662
 
1558
 
    class fake_dbus_python_raises_exception_on_connect(object):
 
1663
    class fake_dbus_python_raises_exception_on_connect:
1559
1664
        """fake dbus-python module"""
1560
 
        class exceptions(object):
 
1665
        class exceptions:
1561
1666
            """Pseudo-namespace"""
1562
1667
            class DBusException(Exception):
1563
1668
                pass
1571
1676
 
1572
1677
 
1573
1678
class Test_dbus_python_adapter_CachingBus(unittest.TestCase):
1574
 
    class mock_dbus_python(object):
 
1679
    class mock_dbus_python:
1575
1680
        """mock dbus-python modules"""
1576
 
        class SystemBus(object):
 
1681
        class SystemBus:
1577
1682
            @staticmethod
1578
1683
            def get_object(busname, objectpath):
1579
1684
                return Unique()
1625
1730
class Test_pydbus_adapter_SystemBus(TestCaseWithAssertLogs):
1626
1731
 
1627
1732
    def Stub_pydbus_func(self, func):
1628
 
        class stub_pydbus(object):
 
1733
        class stub_pydbus:
1629
1734
            """stub pydbus module"""
1630
 
            class SystemBus(object):
 
1735
            class SystemBus:
1631
1736
                @staticmethod
1632
1737
                def get(busname, objectpath):
1633
1738
                    DBusObject = collections.namedtuple(
1670
1775
            self.call_method(bus, "methodname", "busname",
1671
1776
                             "objectpath", "interface")
1672
1777
 
1673
 
        self.assertNotIsInstance(e, dbus.ConnectFailed)
 
1778
        self.assertNotIsInstance(e.exception, dbus.ConnectFailed)
1674
1779
 
1675
1780
    def test_get_converts_to_correct_exception(self):
1676
1781
        bus = pydbus_adapter.SystemBus(
1679
1784
            self.call_method(bus, "methodname", "busname",
1680
1785
                             "objectpath", "interface")
1681
1786
 
1682
 
    class fake_pydbus_raises_exception_on_connect(object):
 
1787
    class fake_pydbus_raises_exception_on_connect:
1683
1788
        """fake dbus-python module"""
1684
1789
        @classmethod
1685
1790
        def SystemBus(cls):
1689
1794
            return Bus(get=get)
1690
1795
 
1691
1796
    def test_set_property_uses_setattr(self):
1692
 
        class Object(object):
 
1797
        class Object:
1693
1798
            pass
1694
1799
        obj = Object()
1695
 
        class pydbus_spy(object):
1696
 
            class SystemBus(object):
 
1800
        class pydbus_spy:
 
1801
            class SystemBus:
1697
1802
                @staticmethod
1698
1803
                def get(busname, objectpath):
1699
1804
                    return {"interface": obj}
1706
1811
    def test_get_suppresses_xml_deprecation_warning(self):
1707
1812
        if sys.version_info.major >= 3:
1708
1813
            return
1709
 
        class stub_pydbus_get(object):
1710
 
            class SystemBus(object):
 
1814
        class stub_pydbus_get:
 
1815
            class SystemBus:
1711
1816
                @staticmethod
1712
1817
                def get(busname, objectpath):
1713
1818
                    warnings.warn_explicit(
1721
1826
 
1722
1827
 
1723
1828
class Test_pydbus_adapter_CachingBus(unittest.TestCase):
1724
 
    class stub_pydbus(object):
 
1829
    class stub_pydbus:
1725
1830
        """stub pydbus module"""
1726
 
        class SystemBus(object):
 
1831
        class SystemBus:
1727
1832
            @staticmethod
1728
1833
            def get(busname, objectpath):
1729
1834
                return Unique()
1771
1876
        self.assertIs(obj1, obj1b)
1772
1877
 
1773
1878
 
 
1879
class Test_dbussy_adapter_SystemBus(TestCaseWithAssertLogs):
 
1880
 
 
1881
    class dummy_dbussy:
 
1882
        class DBUS:
 
1883
            class ObjectPath(str):
 
1884
                pass
 
1885
        class DBusError(Exception):
 
1886
            pass
 
1887
 
 
1888
    def fake_ravel_func(self, func):
 
1889
        class fake_ravel:
 
1890
            @staticmethod
 
1891
            def system_bus():
 
1892
                class DBusInterfaceProxy:
 
1893
                    @staticmethod
 
1894
                    def methodname(*args):
 
1895
                        return [func(*args)]
 
1896
                class DBusObject:
 
1897
                    @staticmethod
 
1898
                    def get_interface(interface):
 
1899
                        if interface == "interface":
 
1900
                            return DBusInterfaceProxy()
 
1901
                return {"busname": {"objectpath": DBusObject()}}
 
1902
        return fake_ravel
 
1903
 
 
1904
    def call_method(self, bus, methodname, busname, objectpath,
 
1905
                    interface, *args):
 
1906
        with self.assertLogs(log, logging.DEBUG):
 
1907
            return bus.call_method(methodname, busname, objectpath,
 
1908
                                   interface, *args)
 
1909
 
 
1910
    def test_call_method_returns(self):
 
1911
        expected_method_return = Unique()
 
1912
        method_args = (Unique(), Unique())
 
1913
        def func(*args):
 
1914
            self.assertEqual(len(method_args), len(args))
 
1915
            for marg, arg in zip(method_args, args):
 
1916
                self.assertIs(marg, arg)
 
1917
            return expected_method_return
 
1918
        fake_ravel = self.fake_ravel_func(func)
 
1919
        bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
 
1920
        ret = self.call_method(bus, "methodname", "busname",
 
1921
                               "objectpath", "interface",
 
1922
                               *method_args)
 
1923
        self.assertIs(ret, expected_method_return)
 
1924
 
 
1925
    def test_call_method_filters_objectpath(self):
 
1926
        def func():
 
1927
            return method_return
 
1928
        fake_ravel = self.fake_ravel_func(func)
 
1929
        bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
 
1930
        method_return = (self.dummy_dbussy.DBUS
 
1931
                         .ObjectPath("objectpath"))
 
1932
        ret = self.call_method(bus, "methodname", "busname",
 
1933
                               "objectpath", "interface")
 
1934
        self.assertEqual("objectpath", ret)
 
1935
        self.assertNotIsInstance(ret,
 
1936
                                 self.dummy_dbussy.DBUS.ObjectPath)
 
1937
 
 
1938
    def test_call_method_filters_objectpaths_in_dict(self):
 
1939
        ObjectPath = self.dummy_dbussy.DBUS.ObjectPath
 
1940
        def func():
 
1941
            return method_return
 
1942
        fake_ravel = self.fake_ravel_func(func)
 
1943
        bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
 
1944
        method_return = {
 
1945
            ObjectPath("objectpath_key_1"):
 
1946
            ObjectPath("objectpath_value_1"),
 
1947
            ObjectPath("objectpath_key_2"):
 
1948
            ObjectPath("objectpath_value_2"),
 
1949
        }
 
1950
        ret = self.call_method(bus, "methodname", "busname",
 
1951
                               "objectpath", "interface")
 
1952
        expected_method_return = {str(key): str(value)
 
1953
                                  for key, value in
 
1954
                                  method_return.items()}
 
1955
        for key, value in ret.items():
 
1956
            self.assertNotIsInstance(key, ObjectPath)
 
1957
            self.assertNotIsInstance(value, ObjectPath)
 
1958
        self.assertEqual(expected_method_return, ret)
 
1959
        self.assertIsInstance(ret, dict)
 
1960
 
 
1961
    def test_call_method_filters_objectpaths_in_dict_in_dict(self):
 
1962
        ObjectPath = self.dummy_dbussy.DBUS.ObjectPath
 
1963
        def func():
 
1964
            return method_return
 
1965
        fake_ravel = self.fake_ravel_func(func)
 
1966
        bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
 
1967
        method_return = {
 
1968
            ObjectPath("key1"): {
 
1969
                ObjectPath("key11"): ObjectPath("value11"),
 
1970
                ObjectPath("key12"): ObjectPath("value12"),
 
1971
            },
 
1972
            ObjectPath("key2"): {
 
1973
                ObjectPath("key21"): ObjectPath("value21"),
 
1974
                ObjectPath("key22"): ObjectPath("value22"),
 
1975
            },
 
1976
        }
 
1977
        ret = self.call_method(bus, "methodname", "busname",
 
1978
                               "objectpath", "interface")
 
1979
        expected_method_return = {
 
1980
            "key1": {"key11": "value11",
 
1981
                     "key12": "value12"},
 
1982
            "key2": {"key21": "value21",
 
1983
                     "key22": "value22"},
 
1984
        }
 
1985
        self.assertEqual(expected_method_return, ret)
 
1986
        for key, value in ret.items():
 
1987
            self.assertIsInstance(value, dict)
 
1988
            self.assertEqual(expected_method_return[key], value)
 
1989
            self.assertNotIsInstance(key, ObjectPath)
 
1990
            for inner_key, inner_value in value.items():
 
1991
                self.assertIsInstance(value, dict)
 
1992
                self.assertEqual(
 
1993
                    expected_method_return[key][inner_key],
 
1994
                    inner_value)
 
1995
                self.assertNotIsInstance(key, ObjectPath)
 
1996
 
 
1997
    def test_call_method_filters_objectpaths_in_dict_three_deep(self):
 
1998
        ObjectPath = self.dummy_dbussy.DBUS.ObjectPath
 
1999
        def func():
 
2000
            return method_return
 
2001
        fake_ravel = self.fake_ravel_func(func)
 
2002
        bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
 
2003
        method_return = {
 
2004
            ObjectPath("key1"): {
 
2005
                ObjectPath("key2"): {
 
2006
                    ObjectPath("key3"): ObjectPath("value"),
 
2007
                },
 
2008
            },
 
2009
        }
 
2010
        ret = self.call_method(bus, "methodname", "busname",
 
2011
                               "objectpath", "interface")
 
2012
        expected_method_return = {"key1": {"key2": {"key3": "value"}}}
 
2013
        self.assertEqual(expected_method_return, ret)
 
2014
        self.assertIsInstance(ret, dict)
 
2015
        self.assertNotIsInstance(next(iter(ret.keys())), ObjectPath)
 
2016
        self.assertIsInstance(ret["key1"], dict)
 
2017
        self.assertNotIsInstance(next(iter(ret["key1"].keys())),
 
2018
                                 ObjectPath)
 
2019
        self.assertIsInstance(ret["key1"]["key2"], dict)
 
2020
        self.assertNotIsInstance(
 
2021
            next(iter(ret["key1"]["key2"].keys())),
 
2022
            ObjectPath)
 
2023
        self.assertEqual("value", ret["key1"]["key2"]["key3"])
 
2024
        self.assertNotIsInstance(ret["key1"]["key2"]["key3"],
 
2025
                                 self.dummy_dbussy.DBUS.ObjectPath)
 
2026
 
 
2027
    def test_call_method_handles_exception(self):
 
2028
        def func():
 
2029
            raise self.dummy_dbussy.DBusError()
 
2030
 
 
2031
        fake_ravel = self.fake_ravel_func(func)
 
2032
        bus = dbussy_adapter.SystemBus(self.dummy_dbussy, fake_ravel)
 
2033
 
 
2034
        with self.assertRaises(dbus.Error) as e:
 
2035
            self.call_method(bus, "methodname", "busname",
 
2036
                             "objectpath", "interface")
 
2037
 
 
2038
        self.assertNotIsInstance(e.exception, dbus.ConnectFailed)
 
2039
 
 
2040
    def test_get_object_converts_to_correct_exception(self):
 
2041
        class fake_ravel_raises_exception_on_connect:
 
2042
            @staticmethod
 
2043
            def system_bus():
 
2044
                class Bus:
 
2045
                    @staticmethod
 
2046
                    def __getitem__(key):
 
2047
                        if key == "objectpath":
 
2048
                            raise self.dummy_dbussy.DBusError()
 
2049
                        raise Exception(key)
 
2050
                return {"busname": Bus()}
 
2051
        def func():
 
2052
            raise self.dummy_dbussy.DBusError()
 
2053
        bus = dbussy_adapter.SystemBus(
 
2054
            self.dummy_dbussy,
 
2055
            fake_ravel_raises_exception_on_connect)
 
2056
        with self.assertRaises(dbus.ConnectFailed):
 
2057
            self.call_method(bus, "methodname", "busname",
 
2058
                             "objectpath", "interface")
 
2059
 
 
2060
 
1774
2061
class Test_commands_from_options(unittest.TestCase):
1775
2062
 
1776
2063
    def setUp(self):