Package dbus :: Module bus
[hide private]
[frames] | no frames]

Source Code for Module dbus.bus

  1  # Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/> 
  2  # 
  3  # Permission is hereby granted, free of charge, to any person 
  4  # obtaining a copy of this software and associated documentation 
  5  # files (the "Software"), to deal in the Software without 
  6  # restriction, including without limitation the rights to use, copy, 
  7  # modify, merge, publish, distribute, sublicense, and/or sell copies 
  8  # of the Software, and to permit persons to whom the Software is 
  9  # furnished to do so, subject to the following conditions: 
 10  # 
 11  # The above copyright notice and this permission notice shall be 
 12  # included in all copies or substantial portions of the Software. 
 13  # 
 14  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 15  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 16  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 17  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 18  # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 19  # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 20  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 21  # DEALINGS IN THE SOFTWARE. 
 22   
 23  __all__ = ('BusConnection',) 
 24  __docformat__ = 'reStructuredText' 
 25   
 26  import logging 
 27  import weakref 
 28   
 29  from _dbus_bindings import validate_interface_name, validate_member_name,\ 
 30                             validate_bus_name, validate_object_path,\ 
 31                             validate_error_name,\ 
 32                             BUS_SESSION, BUS_STARTER, BUS_SYSTEM, \ 
 33                             DBUS_START_REPLY_SUCCESS, \ 
 34                             DBUS_START_REPLY_ALREADY_RUNNING, \ 
 35                             BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_DAEMON_IFACE,\ 
 36                             NAME_FLAG_ALLOW_REPLACEMENT, \ 
 37                             NAME_FLAG_DO_NOT_QUEUE, \ 
 38                             NAME_FLAG_REPLACE_EXISTING, \ 
 39                             RELEASE_NAME_REPLY_NON_EXISTENT, \ 
 40                             RELEASE_NAME_REPLY_NOT_OWNER, \ 
 41                             RELEASE_NAME_REPLY_RELEASED, \ 
 42                             REQUEST_NAME_REPLY_ALREADY_OWNER, \ 
 43                             REQUEST_NAME_REPLY_EXISTS, \ 
 44                             REQUEST_NAME_REPLY_IN_QUEUE, \ 
 45                             REQUEST_NAME_REPLY_PRIMARY_OWNER 
 46  from dbus.connection import Connection 
 47  from dbus.exceptions import DBusException 
 48  from dbus.lowlevel import HANDLER_RESULT_NOT_YET_HANDLED 
 49   
 50   
 51  _NAME_OWNER_CHANGE_MATCH = ("type='signal',sender='%s'," 
 52                              "interface='%s',member='NameOwnerChanged'," 
 53                              "path='%s',arg0='%%s'" 
 54                              % (BUS_DAEMON_NAME, BUS_DAEMON_IFACE, 
 55                                 BUS_DAEMON_PATH)) 
 56  """(_NAME_OWNER_CHANGE_MATCH % sender) matches relevant NameOwnerChange 
 57  messages""" 
 58   
 59  _NAME_HAS_NO_OWNER = 'org.freedesktop.DBus.Error.NameHasNoOwner' 
 60   
 61  _logger = logging.getLogger('dbus.bus') 
 62   
 63   
64 -class NameOwnerWatch(object):
65 __slots__ = ('_match', '_pending_call') 66
67 - def __init__(self, bus_conn, bus_name, callback):
68 validate_bus_name(bus_name) 69 70 def signal_cb(owned, old_owner, new_owner): 71 callback(new_owner)
72 73 def error_cb(e): 74 if e.get_dbus_name() == _NAME_HAS_NO_OWNER: 75 callback('') 76 else: 77 logging.basicConfig() 78 _logger.debug('GetNameOwner(%s) failed:', bus_name, 79 exc_info=(e.__class__, e, None))
80 81 self._match = bus_conn.add_signal_receiver(signal_cb, 82 'NameOwnerChanged', 83 BUS_DAEMON_IFACE, 84 BUS_DAEMON_NAME, 85 BUS_DAEMON_PATH, 86 arg0=bus_name) 87 self._pending_call = bus_conn.call_async(BUS_DAEMON_NAME, 88 BUS_DAEMON_PATH, 89 BUS_DAEMON_IFACE, 90 'GetNameOwner', 91 's', (bus_name,), 92 callback, error_cb, 93 utf8_strings=True) 94
95 - def cancel(self):
96 if self._match is not None: 97 self._match.remove() 98 if self._pending_call is not None: 99 self._pending_call.cancel() 100 self._match = None 101 self._pending_call = None
102 103
104 -class BusConnection(Connection):
105 """A connection to a D-Bus daemon that implements the 106 ``org.freedesktop.DBus`` pseudo-service. 107 108 :Since: 0.81.0 109 """ 110 111 TYPE_SESSION = BUS_SESSION 112 """Represents a session bus (same as the global dbus.BUS_SESSION)""" 113 114 TYPE_SYSTEM = BUS_SYSTEM 115 """Represents the system bus (same as the global dbus.BUS_SYSTEM)""" 116 117 TYPE_STARTER = BUS_STARTER 118 """Represents the bus that started this service by activation (same as 119 the global dbus.BUS_STARTER)""" 120 121 START_REPLY_SUCCESS = DBUS_START_REPLY_SUCCESS 122 START_REPLY_ALREADY_RUNNING = DBUS_START_REPLY_ALREADY_RUNNING 123
124 - def __new__(cls, address_or_type=TYPE_SESSION, mainloop=None):
125 bus = cls._new_for_bus(address_or_type, mainloop=mainloop) 126 127 # _bus_names is used by dbus.service.BusName! 128 bus._bus_names = weakref.WeakValueDictionary() 129 130 bus._signal_sender_matches = {} 131 """Map from SignalMatch to NameOwnerWatch.""" 132 133 return bus
134
135 - def add_signal_receiver(self, handler_function, signal_name=None, 136 dbus_interface=None, bus_name=None, 137 path=None, **keywords):
138 named_service = keywords.pop('named_service', None) 139 if named_service is not None: 140 if bus_name is not None: 141 raise TypeError('bus_name and named_service cannot both be ' 142 'specified') 143 bus_name = named_service 144 from warnings import warn 145 warn('Passing the named_service parameter to add_signal_receiver ' 146 'by name is deprecated: please use positional parameters', 147 DeprecationWarning, stacklevel=2) 148 149 match = super(BusConnection, self).add_signal_receiver( 150 handler_function, signal_name, dbus_interface, bus_name, 151 path, **keywords) 152 153 if (bus_name is not None and bus_name != BUS_DAEMON_NAME): 154 if bus_name[:1] == ':': 155 def callback(new_owner): 156 if new_owner == '': 157 match.remove()
158 else: 159 callback = match.set_sender_name_owner 160 watch = self.watch_name_owner(bus_name, callback) 161 self._signal_sender_matches[match] = watch 162 163 self.add_match_string(str(match)) 164 165 return match
166
167 - def _clean_up_signal_match(self, match):
168 # The signals lock is no longer held here (it was in <= 0.81.0) 169 self.remove_match_string_non_blocking(str(match)) 170 watch = self._signal_sender_matches.pop(match, None) 171 if watch is not None: 172 watch.cancel()
173
174 - def activate_name_owner(self, bus_name):
175 if (bus_name is not None and bus_name[:1] != ':' 176 and bus_name != BUS_DAEMON_NAME): 177 try: 178 return self.get_name_owner(bus_name) 179 except DBusException, e: 180 if e.get_dbus_name() != _NAME_HAS_NO_OWNER: 181 raise 182 # else it doesn't exist: try to start it 183 self.start_service_by_name(bus_name) 184 return self.get_name_owner(bus_name) 185 else: 186 # already unique 187 return bus_name
188
189 - def get_object(self, bus_name, object_path, introspect=True, 190 follow_name_owner_changes=False, **kwargs):
191 """Return a local proxy for the given remote object. 192 193 Method calls on the proxy are translated into method calls on the 194 remote object. 195 196 :Parameters: 197 `bus_name` : str 198 A bus name (either the unique name or a well-known name) 199 of the application owning the object. The keyword argument 200 named_service is a deprecated alias for this. 201 `object_path` : str 202 The object path of the desired object 203 `introspect` : bool 204 If true (default), attempt to introspect the remote 205 object to find out supported methods and their signatures 206 `follow_name_owner_changes` : bool 207 If the object path is a well-known name and this parameter 208 is false (default), resolve the well-known name to the unique 209 name of its current owner and bind to that instead; if the 210 ownership of the well-known name changes in future, 211 keep communicating with the original owner. 212 This is necessary if the D-Bus API used is stateful. 213 214 If the object path is a well-known name and this parameter 215 is true, whenever the well-known name changes ownership in 216 future, bind to the new owner, if any. 217 218 If the given object path is a unique name, this parameter 219 has no effect. 220 221 :Returns: a `dbus.proxies.ProxyObject` 222 :Raises `DBusException`: if resolving the well-known name to a 223 unique name fails 224 """ 225 if follow_name_owner_changes: 226 self._require_main_loop() # we don't get the signals otherwise 227 228 named_service = kwargs.pop('named_service', None) 229 if named_service is not None: 230 if bus_name is not None: 231 raise TypeError('bus_name and named_service cannot both ' 232 'be specified') 233 from warnings import warn 234 warn('Passing the named_service parameter to get_object by name ' 235 'is deprecated: please use positional parameters', 236 DeprecationWarning, stacklevel=2) 237 bus_name = named_service 238 if kwargs: 239 raise TypeError('get_object does not take these keyword ' 240 'arguments: %s' % ', '.join(kwargs.iterkeys())) 241 242 return self.ProxyObjectClass(self, bus_name, object_path, 243 introspect=introspect, 244 follow_name_owner_changes=follow_name_owner_changes)
245
246 - def get_unix_user(self, bus_name):
247 """Get the numeric uid of the process owning the given bus name. 248 249 :Parameters: 250 `bus_name` : str 251 A bus name, either unique or well-known 252 :Returns: a `dbus.UInt32` 253 :Since: 0.80.0 254 """ 255 validate_bus_name(bus_name) 256 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 257 BUS_DAEMON_IFACE, 'GetConnectionUnixUser', 258 's', (bus_name,))
259
260 - def start_service_by_name(self, bus_name, flags=0):
261 """Start a service which will implement the given bus name on this Bus. 262 263 :Parameters: 264 `bus_name` : str 265 The well-known bus name to be activated. 266 `flags` : dbus.UInt32 267 Flags to pass to StartServiceByName (currently none are 268 defined) 269 270 :Returns: A tuple of 2 elements. The first is always True, the 271 second is either START_REPLY_SUCCESS or 272 START_REPLY_ALREADY_RUNNING. 273 274 :Raises `DBusException`: if the service could not be started. 275 :Since: 0.80.0 276 """ 277 validate_bus_name(bus_name) 278 return (True, self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 279 BUS_DAEMON_IFACE, 280 'StartServiceByName', 281 'su', (bus_name, flags)))
282 283 # XXX: it might be nice to signal IN_QUEUE, EXISTS by exception, 284 # but this would not be backwards-compatible
285 - def request_name(self, name, flags=0):
286 """Request a bus name. 287 288 :Parameters: 289 `name` : str 290 The well-known name to be requested 291 `flags` : dbus.UInt32 292 A bitwise-OR of 0 or more of the flags 293 `NAME_FLAG_ALLOW_REPLACEMENT`, 294 `NAME_FLAG_REPLACE_EXISTING` 295 and `NAME_FLAG_DO_NOT_QUEUE` 296 :Returns: `REQUEST_NAME_REPLY_PRIMARY_OWNER`, 297 `REQUEST_NAME_REPLY_IN_QUEUE`, 298 `REQUEST_NAME_REPLY_EXISTS` or 299 `REQUEST_NAME_REPLY_ALREADY_OWNER` 300 :Raises `DBusException`: if the bus daemon cannot be contacted or 301 returns an error. 302 """ 303 validate_bus_name(name, allow_unique=False) 304 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 305 BUS_DAEMON_IFACE, 'RequestName', 306 'su', (name, flags))
307
308 - def release_name(self, name):
309 """Release a bus name. 310 311 :Parameters: 312 `name` : str 313 The well-known name to be released 314 :Returns: `RELEASE_NAME_REPLY_RELEASED`, 315 `RELEASE_NAME_REPLY_NON_EXISTENT` 316 or `RELEASE_NAME_REPLY_NOT_OWNER` 317 :Raises `DBusException`: if the bus daemon cannot be contacted or 318 returns an error. 319 """ 320 validate_bus_name(name, allow_unique=False) 321 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 322 BUS_DAEMON_IFACE, 'ReleaseName', 323 's', (name,))
324
325 - def list_names(self):
326 """Return a list of all currently-owned names on the bus. 327 328 :Returns: a dbus.Array of dbus.UTF8String 329 :Since: 0.81.0 330 """ 331 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 332 BUS_DAEMON_IFACE, 'ListNames', 333 '', (), utf8_strings=True)
334
335 - def list_activatable_names(self):
336 """Return a list of all names that can be activated on the bus. 337 338 :Returns: a dbus.Array of dbus.UTF8String 339 :Since: 0.81.0 340 """ 341 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 342 BUS_DAEMON_IFACE, 'ListActivatableNames', 343 '', (), utf8_strings=True)
344
345 - def get_name_owner(self, bus_name):
346 """Return the unique connection name of the primary owner of the 347 given name. 348 349 :Raises `DBusException`: if the `bus_name` has no owner 350 :Since: 0.81.0 351 """ 352 validate_bus_name(bus_name, allow_unique=False) 353 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 354 BUS_DAEMON_IFACE, 'GetNameOwner', 355 's', (bus_name,), utf8_strings=True)
356
357 - def watch_name_owner(self, bus_name, callback):
358 """Watch the unique connection name of the primary owner of the 359 given name. 360 361 `callback` will be called with one argument, which is either the 362 unique connection name, or the empty string (meaning the name is 363 not owned). 364 365 :Since: 0.81.0 366 """ 367 return NameOwnerWatch(self, bus_name, callback)
368
369 - def name_has_owner(self, bus_name):
370 """Return True iff the given bus name has an owner on this bus. 371 372 :Parameters: 373 `bus_name` : str 374 The bus name to look up 375 :Returns: a `bool` 376 """ 377 return bool(self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 378 BUS_DAEMON_IFACE, 'NameHasOwner', 379 's', (bus_name,)))
380
381 - def add_match_string(self, rule):
382 """Arrange for this application to receive messages on the bus that 383 match the given rule. This version will block. 384 385 :Parameters: 386 `rule` : str 387 The match rule 388 :Raises `DBusException`: on error. 389 :Since: 0.80.0 390 """ 391 self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 392 BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,))
393 394 # FIXME: add an async success/error handler capability? 395 # (and the same for remove_...)
396 - def add_match_string_non_blocking(self, rule):
397 """Arrange for this application to receive messages on the bus that 398 match the given rule. This version will not block, but any errors 399 will be ignored. 400 401 402 :Parameters: 403 `rule` : str 404 The match rule 405 :Raises `DBusException`: on error. 406 :Since: 0.80.0 407 """ 408 self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 409 BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,), 410 None, None)
411
412 - def remove_match_string(self, rule):
413 """Arrange for this application to receive messages on the bus that 414 match the given rule. This version will block. 415 416 :Parameters: 417 `rule` : str 418 The match rule 419 :Raises `DBusException`: on error. 420 :Since: 0.80.0 421 """ 422 self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 423 BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,))
424
425 - def remove_match_string_non_blocking(self, rule):
426 """Arrange for this application to receive messages on the bus that 427 match the given rule. This version will not block, but any errors 428 will be ignored. 429 430 431 :Parameters: 432 `rule` : str 433 The match rule 434 :Raises `DBusException`: on error. 435 :Since: 0.80.0 436 """ 437 self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH, 438 BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,), 439 None, None)
440