1"""Implementation for dbus.Bus. Not to be imported directly.""" 2 3# Copyright (C) 2003, 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/> 4# Copyright (C) 2003 David Zeuthen 5# Copyright (C) 2004 Rob Taylor 6# Copyright (C) 2005, 2006 Collabora Ltd. <http://www.collabora.co.uk/> 7# 8# SPDX-License-Identifier: MIT 9# 10# Permission is hereby granted, free of charge, to any person 11# obtaining a copy of this software and associated documentation 12# files (the "Software"), to deal in the Software without 13# restriction, including without limitation the rights to use, copy, 14# modify, merge, publish, distribute, sublicense, and/or sell copies 15# of the Software, and to permit persons to whom the Software is 16# furnished to do so, subject to the following conditions: 17# 18# The above copyright notice and this permission notice shall be 19# included in all copies or substantial portions of the Software. 20# 21# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 25# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 26# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28# DEALINGS IN THE SOFTWARE. 29 30from __future__ import generators 31 32__all__ = ('Bus', 'SystemBus', 'SessionBus', 'StarterBus') 33__docformat__ = 'reStructuredText' 34 35from dbus.exceptions import DBusException 36from _dbus_bindings import ( 37 BUS_DAEMON_IFACE, BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_SESSION, 38 BUS_STARTER, BUS_SYSTEM, DBUS_START_REPLY_ALREADY_RUNNING, 39 DBUS_START_REPLY_SUCCESS, validate_bus_name, 40 validate_interface_name, validate_member_name, validate_object_path) 41from dbus.bus import BusConnection 42from dbus.lowlevel import SignalMessage 43from dbus._compat import is_py2 44 45if is_py2: 46 from _dbus_bindings import UTF8String 47 48 49class Bus(BusConnection): 50 """A connection to one of three possible standard buses, the SESSION, 51 SYSTEM, or STARTER bus. This class manages shared connections to those 52 buses. 53 54 If you're trying to subclass `Bus`, you may be better off subclassing 55 `BusConnection`, which doesn't have all this magic. 56 """ 57 58 _shared_instances = {} 59 60 def __new__(cls, bus_type=BusConnection.TYPE_SESSION, private=False, 61 mainloop=None): 62 """Constructor, returning an existing instance where appropriate. 63 64 The returned instance is actually always an instance of `SessionBus`, 65 `SystemBus` or `StarterBus`. 66 67 :Parameters: 68 `bus_type` : cls.TYPE_SESSION, cls.TYPE_SYSTEM or cls.TYPE_STARTER 69 Connect to the appropriate bus 70 `private` : bool 71 If true, never return an existing shared instance, but instead 72 return a private connection. 73 74 :Deprecated: since 0.82.3. Use dbus.bus.BusConnection for 75 private connections. 76 77 `mainloop` : dbus.mainloop.NativeMainLoop 78 The main loop to use. The default is to use the default 79 main loop if one has been set up, or raise an exception 80 if none has been. 81 :Changed: in dbus-python 0.80: 82 converted from a wrapper around a Connection to a Connection 83 subclass. 84 """ 85 if (not private and bus_type in cls._shared_instances): 86 return cls._shared_instances[bus_type] 87 88 # this is a bit odd, but we create instances of the subtypes 89 # so we can return the shared instances if someone tries to 90 # construct one of them (otherwise we'd eg try and return an 91 # instance of Bus from __new__ in SessionBus). why are there 92 # three ways to construct this class? we just don't know. 93 if bus_type == BUS_SESSION: 94 subclass = SessionBus 95 elif bus_type == BUS_SYSTEM: 96 subclass = SystemBus 97 elif bus_type == BUS_STARTER: 98 subclass = StarterBus 99 else: 100 raise ValueError('invalid bus_type %s' % bus_type) 101 102 bus = BusConnection.__new__(subclass, bus_type, mainloop=mainloop) 103 104 bus._bus_type = bus_type 105 106 if not private: 107 cls._shared_instances[bus_type] = bus 108 109 return bus 110 111 def close(self): 112 t = self._bus_type 113 if self.__class__._shared_instances.get(t) is self: 114 del self.__class__._shared_instances[t] 115 super(Bus, self).close() 116 117 def get_connection(self): 118 """Return self, for backwards compatibility with earlier dbus-python 119 versions where Bus was not a subclass of Connection. 120 121 :Deprecated: since 0.80.0 122 """ 123 return self 124 _connection = property(get_connection, None, None, 125 """self._connection == self, for backwards 126 compatibility with earlier dbus-python versions 127 where Bus was not a subclass of Connection.""") 128 129 def get_session(private=False): 130 """Static method that returns a connection to the session bus. 131 132 :Parameters: 133 `private` : bool 134 If true, do not return a shared connection. 135 """ 136 return SessionBus(private=private) 137 138 get_session = staticmethod(get_session) 139 140 def get_system(private=False): 141 """Static method that returns a connection to the system bus. 142 143 :Parameters: 144 `private` : bool 145 If true, do not return a shared connection. 146 """ 147 return SystemBus(private=private) 148 149 get_system = staticmethod(get_system) 150 151 152 def get_starter(private=False): 153 """Static method that returns a connection to the starter bus. 154 155 :Parameters: 156 `private` : bool 157 If true, do not return a shared connection. 158 """ 159 return StarterBus(private=private) 160 161 get_starter = staticmethod(get_starter) 162 163 def __repr__(self): 164 if self._bus_type == BUS_SESSION: 165 name = 'session' 166 elif self._bus_type == BUS_SYSTEM: 167 name = 'system' 168 elif self._bus_type == BUS_STARTER: 169 name = 'starter' 170 else: 171 name = 'unknown bus type' 172 173 return '<%s.%s (%s) at %#x>' % (self.__class__.__module__, 174 self.__class__.__name__, 175 name, id(self)) 176 __str__ = __repr__ 177 178 179# FIXME: Drop the subclasses here? I can't think why we'd ever want 180# polymorphism 181class SystemBus(Bus): 182 """The system-wide message bus.""" 183 def __new__(cls, private=False, mainloop=None): 184 """Return a connection to the system bus. 185 186 :Parameters: 187 `private` : bool 188 If true, never return an existing shared instance, but instead 189 return a private connection. 190 `mainloop` : dbus.mainloop.NativeMainLoop 191 The main loop to use. The default is to use the default 192 main loop if one has been set up, or raise an exception 193 if none has been. 194 """ 195 return Bus.__new__(cls, Bus.TYPE_SYSTEM, mainloop=mainloop, 196 private=private) 197 198class SessionBus(Bus): 199 """The session (current login) message bus.""" 200 def __new__(cls, private=False, mainloop=None): 201 """Return a connection to the session bus. 202 203 :Parameters: 204 `private` : bool 205 If true, never return an existing shared instance, but instead 206 return a private connection. 207 `mainloop` : dbus.mainloop.NativeMainLoop 208 The main loop to use. The default is to use the default 209 main loop if one has been set up, or raise an exception 210 if none has been. 211 """ 212 return Bus.__new__(cls, Bus.TYPE_SESSION, private=private, 213 mainloop=mainloop) 214 215class StarterBus(Bus): 216 """The bus that activated this process (only valid if 217 this process was launched by DBus activation). 218 """ 219 def __new__(cls, private=False, mainloop=None): 220 """Return a connection to the bus that activated this process. 221 222 :Parameters: 223 `private` : bool 224 If true, never return an existing shared instance, but instead 225 return a private connection. 226 `mainloop` : dbus.mainloop.NativeMainLoop 227 The main loop to use. The default is to use the default 228 main loop if one has been set up, or raise an exception 229 if none has been. 230 """ 231 return Bus.__new__(cls, Bus.TYPE_STARTER, private=private, 232 mainloop=mainloop) 233