1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 __all__ = ('BusConnection',)
24 __docformat__ = 'reStructuredText'
25
26 import logging
27 import weakref
28
29 from _dbus_bindings import (
30 BUS_DAEMON_IFACE, BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_SESSION,
31 BUS_STARTER, BUS_SYSTEM, DBUS_START_REPLY_ALREADY_RUNNING,
32 DBUS_START_REPLY_SUCCESS, NAME_FLAG_ALLOW_REPLACEMENT,
33 NAME_FLAG_DO_NOT_QUEUE, NAME_FLAG_REPLACE_EXISTING,
34 RELEASE_NAME_REPLY_NON_EXISTENT, RELEASE_NAME_REPLY_NOT_OWNER,
35 RELEASE_NAME_REPLY_RELEASED, REQUEST_NAME_REPLY_ALREADY_OWNER,
36 REQUEST_NAME_REPLY_EXISTS, REQUEST_NAME_REPLY_IN_QUEUE,
37 REQUEST_NAME_REPLY_PRIMARY_OWNER, validate_bus_name, validate_error_name,
38 validate_interface_name, validate_member_name, validate_object_path)
39 from dbus.connection import Connection
40 from dbus.exceptions import DBusException
41 from dbus.lowlevel import HANDLER_RESULT_NOT_YET_HANDLED
42 from dbus._compat import is_py2
43
44
45 _NAME_OWNER_CHANGE_MATCH = ("type='signal',sender='%s',"
46 "interface='%s',member='NameOwnerChanged',"
47 "path='%s',arg0='%%s'"
48 % (BUS_DAEMON_NAME, BUS_DAEMON_IFACE,
49 BUS_DAEMON_PATH))
50 """(_NAME_OWNER_CHANGE_MATCH % sender) matches relevant NameOwnerChange
51 messages"""
52
53 _NAME_HAS_NO_OWNER = 'org.freedesktop.DBus.Error.NameHasNoOwner'
54
55 _logger = logging.getLogger('dbus.bus')
56
57
59 __slots__ = ('_match', '_pending_call')
60
61 - def __init__(self, bus_conn, bus_name, callback):
66
67 def error_cb(e):
68 if e.get_dbus_name() == _NAME_HAS_NO_OWNER:
69 callback('')
70 else:
71 logging.basicConfig()
72 _logger.debug('GetNameOwner(%s) failed:', bus_name,
73 exc_info=(e.__class__, e, None))
74
75 self._match = bus_conn.add_signal_receiver(signal_cb,
76 'NameOwnerChanged',
77 BUS_DAEMON_IFACE,
78 BUS_DAEMON_NAME,
79 BUS_DAEMON_PATH,
80 arg0=bus_name)
81 keywords = {}
82 if is_py2:
83 keywords['utf8_strings'] = True
84 self._pending_call = bus_conn.call_async(BUS_DAEMON_NAME,
85 BUS_DAEMON_PATH,
86 BUS_DAEMON_IFACE,
87 'GetNameOwner',
88 's', (bus_name,),
89 callback, error_cb,
90 **keywords)
91
99
100
102 """A connection to a D-Bus daemon that implements the
103 ``org.freedesktop.DBus`` pseudo-service.
104
105 :Since: 0.81.0
106 """
107
108 TYPE_SESSION = BUS_SESSION
109 """Represents a session bus (same as the global dbus.BUS_SESSION)"""
110
111 TYPE_SYSTEM = BUS_SYSTEM
112 """Represents the system bus (same as the global dbus.BUS_SYSTEM)"""
113
114 TYPE_STARTER = BUS_STARTER
115 """Represents the bus that started this service by activation (same as
116 the global dbus.BUS_STARTER)"""
117
118 START_REPLY_SUCCESS = DBUS_START_REPLY_SUCCESS
119 START_REPLY_ALREADY_RUNNING = DBUS_START_REPLY_ALREADY_RUNNING
120
122 bus = cls._new_for_bus(address_or_type, mainloop=mainloop)
123
124
125 bus._bus_names = weakref.WeakValueDictionary()
126
127 bus._signal_sender_matches = {}
128 """Map from SignalMatch to NameOwnerWatch."""
129
130 return bus
131
132 - def add_signal_receiver(self, handler_function, signal_name=None,
133 dbus_interface=None, bus_name=None,
134 path=None, **keywords):
135 named_service = keywords.pop('named_service', None)
136 if named_service is not None:
137 if bus_name is not None:
138 raise TypeError('bus_name and named_service cannot both be '
139 'specified')
140 bus_name = named_service
141 from warnings import warn
142 warn('Passing the named_service parameter to add_signal_receiver '
143 'by name is deprecated: please use positional parameters',
144 DeprecationWarning, stacklevel=2)
145
146 match = super(BusConnection, self).add_signal_receiver(
147 handler_function, signal_name, dbus_interface, bus_name,
148 path, **keywords)
149
150 if (bus_name is not None and bus_name != BUS_DAEMON_NAME):
151 if bus_name[:1] == ':':
152 def callback(new_owner):
153 if new_owner == '':
154 match.remove()
155 else:
156 callback = match.set_sender_name_owner
157 watch = self.watch_name_owner(bus_name, callback)
158 self._signal_sender_matches[match] = watch
159
160 self.add_match_string(str(match))
161
162 return match
163
170
185
186 - def get_object(self, bus_name, object_path, introspect=True,
187 follow_name_owner_changes=False, **kwargs):
188 """Return a local proxy for the given remote object.
189
190 Method calls on the proxy are translated into method calls on the
191 remote object.
192
193 :Parameters:
194 `bus_name` : str
195 A bus name (either the unique name or a well-known name)
196 of the application owning the object. The keyword argument
197 named_service is a deprecated alias for this.
198 `object_path` : str
199 The object path of the desired object
200 `introspect` : bool
201 If true (default), attempt to introspect the remote
202 object to find out supported methods and their signatures
203 `follow_name_owner_changes` : bool
204 If the object path is a well-known name and this parameter
205 is false (default), resolve the well-known name to the unique
206 name of its current owner and bind to that instead; if the
207 ownership of the well-known name changes in future,
208 keep communicating with the original owner.
209 This is necessary if the D-Bus API used is stateful.
210
211 If the object path is a well-known name and this parameter
212 is true, whenever the well-known name changes ownership in
213 future, bind to the new owner, if any.
214
215 If the given object path is a unique name, this parameter
216 has no effect.
217
218 :Returns: a `dbus.proxies.ProxyObject`
219 :Raises `DBusException`: if resolving the well-known name to a
220 unique name fails
221 """
222 if follow_name_owner_changes:
223 self._require_main_loop()
224
225 named_service = kwargs.pop('named_service', None)
226 if named_service is not None:
227 if bus_name is not None:
228 raise TypeError('bus_name and named_service cannot both '
229 'be specified')
230 from warnings import warn
231 warn('Passing the named_service parameter to get_object by name '
232 'is deprecated: please use positional parameters',
233 DeprecationWarning, stacklevel=2)
234 bus_name = named_service
235 if kwargs:
236 raise TypeError('get_object does not take these keyword '
237 'arguments: %s' % ', '.join(kwargs.keys()))
238
239 return self.ProxyObjectClass(self, bus_name, object_path,
240 introspect=introspect,
241 follow_name_owner_changes=follow_name_owner_changes)
242
256
258 """Start a service which will implement the given bus name on this Bus.
259
260 :Parameters:
261 `bus_name` : str
262 The well-known bus name to be activated.
263 `flags` : dbus.UInt32
264 Flags to pass to StartServiceByName (currently none are
265 defined)
266
267 :Returns: A tuple of 2 elements. The first is always True, the
268 second is either START_REPLY_SUCCESS or
269 START_REPLY_ALREADY_RUNNING.
270
271 :Raises `DBusException`: if the service could not be started.
272 :Since: 0.80.0
273 """
274 validate_bus_name(bus_name)
275 return (True, self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
276 BUS_DAEMON_IFACE,
277 'StartServiceByName',
278 'su', (bus_name, flags)))
279
280
281
283 """Request a bus name.
284
285 :Parameters:
286 `name` : str
287 The well-known name to be requested
288 `flags` : dbus.UInt32
289 A bitwise-OR of 0 or more of the flags
290 `NAME_FLAG_ALLOW_REPLACEMENT`,
291 `NAME_FLAG_REPLACE_EXISTING`
292 and `NAME_FLAG_DO_NOT_QUEUE`
293 :Returns: `REQUEST_NAME_REPLY_PRIMARY_OWNER`,
294 `REQUEST_NAME_REPLY_IN_QUEUE`,
295 `REQUEST_NAME_REPLY_EXISTS` or
296 `REQUEST_NAME_REPLY_ALREADY_OWNER`
297 :Raises `DBusException`: if the bus daemon cannot be contacted or
298 returns an error.
299 """
300 validate_bus_name(name, allow_unique=False)
301 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
302 BUS_DAEMON_IFACE, 'RequestName',
303 'su', (name, flags))
304
306 """Release a bus name.
307
308 :Parameters:
309 `name` : str
310 The well-known name to be released
311 :Returns: `RELEASE_NAME_REPLY_RELEASED`,
312 `RELEASE_NAME_REPLY_NON_EXISTENT`
313 or `RELEASE_NAME_REPLY_NOT_OWNER`
314 :Raises `DBusException`: if the bus daemon cannot be contacted or
315 returns an error.
316 """
317 validate_bus_name(name, allow_unique=False)
318 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
319 BUS_DAEMON_IFACE, 'ReleaseName',
320 's', (name,))
321
323 """Return a list of all currently-owned names on the bus.
324
325 :Returns: a dbus.Array of dbus.UTF8String
326 :Since: 0.81.0
327 """
328 keywords = {}
329 if is_py2:
330 keywords['utf8_strings'] = True
331 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
332 BUS_DAEMON_IFACE, 'ListNames',
333 '', (), **keywords)
334
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 keywords = {}
342 if is_py2:
343 keywords['utf8_strings'] = True
344 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
345 BUS_DAEMON_IFACE, 'ListActivatableNames',
346 '', (), **keywords)
347
362
364 """Watch the unique connection name of the primary owner of the
365 given name.
366
367 `callback` will be called with one argument, which is either the
368 unique connection name, or the empty string (meaning the name is
369 not owned).
370
371 :Since: 0.81.0
372 """
373 return NameOwnerWatch(self, bus_name, callback)
374
386
388 """Arrange for this application to receive messages on the bus that
389 match the given rule. This version will block.
390
391 :Parameters:
392 `rule` : str
393 The match rule
394 :Raises `DBusException`: on error.
395 :Since: 0.80.0
396 """
397 self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
398 BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,))
399
400
401
403 """Arrange for this application to receive messages on the bus that
404 match the given rule. This version will not block, but any errors
405 will be ignored.
406
407
408 :Parameters:
409 `rule` : str
410 The match rule
411 :Raises `DBusException`: on error.
412 :Since: 0.80.0
413 """
414 self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
415 BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,),
416 None, None)
417
419 """Arrange for this application to receive messages on the bus that
420 match the given rule. This version will block.
421
422 :Parameters:
423 `rule` : str
424 The match rule
425 :Raises `DBusException`: on error.
426 :Since: 0.80.0
427 """
428 self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
429 BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,))
430
432 """Arrange for this application to receive messages on the bus that
433 match the given rule. This version will not block, but any errors
434 will be ignored.
435
436
437 :Parameters:
438 `rule` : str
439 The match rule
440 :Raises `DBusException`: on error.
441 :Since: 0.80.0
442 """
443 self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
444 BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,),
445 None, None)
446