1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
4 * This file is part of tlm (Tiny Login Manager)
6 * Copyright (C) 2014 Intel Corporation.
8 * Contact: Imran Zaman <imran.zaman@intel.com>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
33 #include <sys/types.h>
36 #include "tlm-dbus-observer.h"
38 #include "tlm-utils.h"
39 #include "dbus/tlm-dbus-server-interface.h"
40 #include "dbus/tlm-dbus-server-p2p.h"
41 #include "dbus/tlm-dbus-login-adapter.h"
42 #include "dbus/tlm-dbus-utils.h"
44 #include "tlm-manager.h"
45 #include "common/tlm-error.h"
47 G_DEFINE_TYPE (TlmDbusObserver, tlm_dbus_observer, G_TYPE_OBJECT);
49 #define TLM_DBUS_OBSERVER_PRIV(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
50 TLM_TYPE_DBUS_OBSERVER, TlmDbusObserverPrivate)
54 TlmDbusRequest *dbus_request;
58 struct _TlmDbusObserverPrivate
62 TlmDbusServer *dbus_server;
63 GQueue *request_queue;
65 TlmRequest *active_request;
66 DbusObserverEnableFlags enable_flags;
70 _handle_dbus_client_added (
71 TlmDbusObserver *self,
72 GObject *dbus_adapter,
73 GObject *dbus_server);
76 _handle_dbus_client_removed (
77 TlmDbusObserver *self,
78 GObject *dbus_adapter,
79 GObject *dbus_server);
82 _handle_dbus_login_user (
83 TlmDbusObserver *self,
85 const gchar *username,
86 const gchar *password,
87 GVariant *environment,
88 GDBusMethodInvocation *invocation,
89 GObject *dbus_adapter);
92 _handle_dbus_logout_user (
93 TlmDbusObserver *self,
95 GDBusMethodInvocation *invocation,
96 GObject *dbus_adapter);
99 _handle_dbus_switch_user (
100 TlmDbusObserver *self,
101 const gchar *seat_id,
102 const gchar *username,
103 const gchar *password,
104 GVariant *environment,
105 GDBusMethodInvocation *invocation,
106 GObject *dbus_adapter);
109 _disconnect_dbus_adapter (
110 TlmDbusObserver *self,
111 TlmDbusLoginAdapter *adapter);
114 _handle_seat_session_created (
115 TlmDbusObserver *self,
116 const gchar *seat_id,
120 _handle_seat_session_terminated (
121 TlmDbusObserver *self,
122 const gchar *seat_id,
126 _handle_seat_session_error (
127 TlmDbusObserver *self,
133 TlmDbusObserver *self,
138 TlmDbusObserver *self,
142 _process_next_request_in_idle (
143 TlmDbusObserver *self);
147 TlmDbusObserver *self,
150 g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self) && dead &&
152 g_object_weak_unref (dead, (GWeakNotify)_on_seat_dispose, self);
153 _disconnect_seat (self, TLM_SEAT (dead));
154 if (self->priv->active_request &&
155 G_OBJECT(self->priv->active_request->seat) == dead) {
156 self->priv->active_request->seat = NULL;
158 if (G_OBJECT(self->priv->seat) == dead)
159 self->priv->seat = NULL;
163 _on_manager_dispose (
164 TlmDbusObserver *self,
167 g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self) && dead &&
168 TLM_IS_MANAGER(dead));
169 g_object_weak_unref (dead, (GWeakNotify)_on_manager_dispose, self);
170 self->priv->manager = NULL;
175 TlmDbusObserver *self,
176 TlmDbusRequest *dbus_req,
179 TlmRequest *request = g_malloc0 (sizeof (TlmRequest));
180 if (!request) return NULL;
182 request->dbus_request = dbus_req;
183 request->seat = seat;
185 _connect_seat (self, request->seat);
186 g_object_weak_ref (G_OBJECT (request->seat),
187 (GWeakNotify)_on_seat_dispose, self);
194 TlmDbusObserver *self,
197 if (!request) return;
199 if (request->dbus_request) {
200 tlm_dbus_login_adapter_request_completed (request->dbus_request, NULL);
201 tlm_dbus_utils_dispose_request (request->dbus_request);
202 request->dbus_request = NULL;
205 _disconnect_seat (self, request->seat);
206 g_object_weak_unref (G_OBJECT (request->seat),
207 (GWeakNotify)_on_seat_dispose, self);
208 request->seat = NULL;
215 TlmDbusObserver *self,
218 DBG ("self %p seat %p", self, seat);
221 g_signal_handlers_disconnect_by_func (G_OBJECT (seat),
222 _handle_seat_session_created, self);
223 g_signal_handlers_disconnect_by_func (G_OBJECT (seat),
224 _handle_seat_session_terminated, self);
225 g_signal_handlers_disconnect_by_func (G_OBJECT (seat),
226 _handle_seat_session_error, self);
231 TlmDbusObserver *self,
234 DBG ("self %p seat %p", self, seat);
237 g_signal_connect_swapped (G_OBJECT (seat), "session-created",
238 G_CALLBACK(_handle_seat_session_created), self);
239 g_signal_connect_swapped (G_OBJECT (seat), "session-terminated",
240 G_CALLBACK(_handle_seat_session_terminated), self);
241 g_signal_connect_swapped (G_OBJECT (seat), "session-error",
242 G_CALLBACK(_handle_seat_session_error), self);
246 _on_dbus_adapter_dispose (
247 TlmDbusObserver *self,
250 GList *head=NULL, *elem=NULL, *next;
252 g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self) && dead &&
253 TLM_IS_DBUS_LOGIN_ADAPTER(dead));
254 _disconnect_dbus_adapter (self, TLM_DBUS_LOGIN_ADAPTER(dead));
256 if (self->priv->request_queue)
257 head = elem = g_queue_peek_head_link (self->priv->request_queue);
259 TlmRequest *request = elem->data;
260 TlmDbusRequest *dbus_req = request->dbus_request;
261 next = g_list_next (elem);
262 if (dbus_req && G_OBJECT (dbus_req->dbus_adapter) == dead) {
263 DBG ("removing the request for dead dbus adapter");
264 head = g_list_delete_link (head, elem);
265 _dispose_request (self, request);
270 /* check for active request */
271 if (self->priv->active_request &&
272 G_OBJECT (self->priv->active_request->dbus_request->dbus_adapter) ==
274 DBG ("removing the request for dead dbus adapter");
275 _dispose_request (self, self->priv->active_request);
276 self->priv->active_request = NULL;
277 if (self->priv->request_id) {
278 g_source_remove (self->priv->request_id);
279 self->priv->request_id = 0;
281 _process_next_request_in_idle (self);
286 _connect_dbus_adapter (
287 TlmDbusObserver *self,
288 TlmDbusLoginAdapter *adapter)
290 DBG("Connecting signals to signal handlers");
291 if (self->priv->enable_flags & DBUS_OBSERVER_ENABLE_LOGIN_USER) {
293 int r = g_signal_connect_swapped (G_OBJECT (adapter),
294 "login-user", G_CALLBACK(_handle_dbus_login_user), self);
295 DBG("_handle_dbus_login_user is connected to 'login-user' signal."
296 "return value=%d", r);
298 g_signal_connect_swapped (G_OBJECT (adapter),
299 "login-user", G_CALLBACK(_handle_dbus_login_user), self);
302 DBG("'login-user' signal callback is not connected");
304 if (self->priv->enable_flags & DBUS_OBSERVER_ENABLE_LOGOUT_USER)
305 g_signal_connect_swapped (G_OBJECT (adapter),
306 "logout-user", G_CALLBACK(_handle_dbus_logout_user), self);
307 if (self->priv->enable_flags & DBUS_OBSERVER_ENABLE_SWITCH_USER)
308 g_signal_connect_swapped (G_OBJECT (adapter),
309 "switch-user", G_CALLBACK(_handle_dbus_switch_user), self);
313 _disconnect_dbus_adapter (
314 TlmDbusObserver *self,
315 TlmDbusLoginAdapter *adapter)
317 DBG("Disconnecting signals to signal handlers");
318 if (self->priv->enable_flags & DBUS_OBSERVER_ENABLE_LOGIN_USER)
319 g_signal_handlers_disconnect_by_func (G_OBJECT(adapter),
320 _handle_dbus_login_user, self);
321 if (self->priv->enable_flags & DBUS_OBSERVER_ENABLE_LOGOUT_USER)
322 g_signal_handlers_disconnect_by_func (G_OBJECT(adapter),
323 _handle_dbus_logout_user, self);
324 if (self->priv->enable_flags & DBUS_OBSERVER_ENABLE_SWITCH_USER)
325 g_signal_handlers_disconnect_by_func (G_OBJECT(adapter),
326 _handle_dbus_switch_user, self);
330 _handle_dbus_client_added (
331 TlmDbusObserver *self,
332 GObject *dbus_adapter,
333 GObject *dbus_server)
335 g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self) && dbus_adapter &&
336 TLM_IS_DBUS_LOGIN_ADAPTER(dbus_adapter));
337 _connect_dbus_adapter (self, TLM_DBUS_LOGIN_ADAPTER(dbus_adapter));
338 g_object_weak_ref (G_OBJECT (dbus_adapter),
339 (GWeakNotify)_on_dbus_adapter_dispose, self);
343 _handle_dbus_client_removed (
344 TlmDbusObserver *self,
345 GObject *dbus_adapter,
346 GObject *dbus_server)
348 g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self) && dbus_adapter &&
349 TLM_IS_DBUS_LOGIN_ADAPTER(dbus_adapter));
350 _disconnect_dbus_adapter (self, TLM_DBUS_LOGIN_ADAPTER(dbus_adapter));
351 g_object_weak_unref (G_OBJECT (dbus_adapter),
352 (GWeakNotify)_on_dbus_adapter_dispose, self);
356 _complete_dbus_request (
357 TlmDbusRequest *request,
361 tlm_dbus_login_adapter_request_completed (request, error);
362 tlm_dbus_utils_dispose_request (request);
364 if (error) g_error_free (error);
368 _abort_dbus_request (
369 TlmDbusRequest *request)
371 if (!request) return;
373 GError *error = TLM_GET_ERROR_FOR_ID (TLM_ERROR_DBUS_REQ_ABORTED,
374 "Dbus request aborted");
375 _complete_dbus_request (request, error);
380 TlmDbusObserver *self,
384 _complete_dbus_request (request->dbus_request, error);
385 request->dbus_request = NULL;
386 _dispose_request (self, request);
392 TlmDbusObserver *self)
394 if (!request) return;
396 _abort_dbus_request (request->dbus_request);
397 request->dbus_request = NULL;
398 _dispose_request (self, request);
402 _is_request_supported (
403 TlmDbusObserver *self,
404 TlmDbusRequestType req_type)
407 case TLM_DBUS_REQUEST_TYPE_LOGIN_USER:
408 return (self->priv->enable_flags & DBUS_OBSERVER_ENABLE_LOGIN_USER);
409 case TLM_DBUS_REQUEST_TYPE_LOGOUT_USER:
410 return (self->priv->enable_flags & DBUS_OBSERVER_ENABLE_LOGOUT_USER);
411 case TLM_DBUS_REQUEST_TYPE_SWITCH_USER:
412 return (self->priv->enable_flags & DBUS_OBSERVER_ENABLE_SWITCH_USER);
418 _is_valid_switch_user_dbus_request(
419 TlmDbusRequest *dbus_request,
422 g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
424 gchar *occupying_username = tlm_seat_get_occupying_username(seat);
425 if (0 == g_strcmp0(dbus_request->username,occupying_username)) {
426 WARN("Cannot switch to same username");
429 g_free(occupying_username);
435 TlmDbusObserver *self)
437 g_return_val_if_fail (self && TLM_IS_DBUS_OBSERVER(self), FALSE);
439 TlmRequest* req = NULL;
440 TlmDbusRequest* dbus_req = NULL;
441 TlmSeat *seat = NULL;
442 gboolean ret = FALSE;
444 self->priv->request_id = 0;
446 if (!self->priv->active_request) {
448 req = g_queue_pop_head (self->priv->request_queue);
450 DBG ("request queue is empty");
453 dbus_req = req->dbus_request;
454 if (!_is_request_supported (self, dbus_req->type)) {
455 WARN ("Request not supported -- req-type %d flags %d",
456 dbus_req->type, self->priv->enable_flags);
457 err = TLM_GET_ERROR_FOR_ID (TLM_ERROR_DBUS_REQ_NOT_SUPPORTED,
458 "Dbus request not supported");
463 // The below code intends that the current seat of the TlmDbusObserver
464 // is always selected, even the given request has specifed another seat.
465 // The TlmDbusRequest's seat is applied only when the current
466 // TlmDbusObserver is a default dbus socket(/var/run/dbus-sock), which
467 // has priv->seat set to NULL.
468 // This means that only the root user(who can access the default dbus
469 // socket) can specify the seat in his TlmDbusRequest, because the
470 // default dbus socket is allowed only for root to access.
471 seat = self->priv->seat; // observer's seat has high priority
472 if (!seat && self->priv->manager) {
473 seat = tlm_manager_get_seat (self->priv->manager,
476 /* NOTE: When no seat is set at dbus object creation time,
477 * seat is connected on per dbus request basis and then
478 * disconnected when the dbus request is completed or aborted */
480 _connect_seat (self, seat);
481 g_object_weak_ref (G_OBJECT (seat),
482 (GWeakNotify)_on_seat_dispose, self);
487 WARN ("Cannot find the seat");
488 err = TLM_GET_ERROR_FOR_ID (TLM_ERROR_SEAT_NOT_FOUND,
493 if (req->seat != seat) {
495 g_object_unref(req->seat);
497 g_object_ref(req->seat);
499 self->priv->active_request = req;
502 // Proceed self->priv->active_request
503 dbus_req = self->priv->active_request->dbus_request;
504 seat = self->priv->active_request->seat;
505 switch(dbus_req->type) {
506 case TLM_DBUS_REQUEST_TYPE_LOGIN_USER:
507 ret = tlm_seat_create_session (seat, NULL, dbus_req->username,
508 dbus_req->password, dbus_req->environment);
510 case TLM_DBUS_REQUEST_TYPE_LOGOUT_USER:
511 ret = tlm_seat_terminate_session (seat, FALSE);
513 case TLM_DBUS_REQUEST_TYPE_SWITCH_USER:
514 // Refuse request if the request's username is same to
515 // the current user who occupies the seat.
516 if (_is_valid_switch_user_dbus_request(dbus_req, seat)) {
517 ret = tlm_seat_switch_user (seat, NULL, dbus_req->username,
518 dbus_req->password, dbus_req->environment);
525 // Clean up the active_request always
526 _dispose_request (self, self->priv->active_request);
527 self->priv->active_request = NULL;
531 _complete_request (self, req, err);
535 WARN("The request is not processed well");
538 return G_SOURCE_REMOVE;
542 _process_next_request_in_idle (
543 TlmDbusObserver *self)
545 if (!self->priv->request_id &&
546 !g_queue_is_empty (self->priv->request_queue)) {
547 DBG ("request queue has request(s) to be processed");
548 self->priv->request_id = g_idle_add ((GSourceFunc)_process_request,
555 TlmDbusObserver *self,
558 g_queue_push_tail (self->priv->request_queue, request);
559 _process_next_request_in_idle (self);
563 _handle_seat_session_created (
564 TlmDbusObserver *self,
565 const gchar *seat_id,
568 DBG ("self %p seat %p", self, seat);
570 g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self));
571 g_return_if_fail (seat && TLM_IS_SEAT(seat));
573 /* Login/switch request should only be completed on session created
574 * signal from seat */
575 if (!self->priv->active_request ||
576 !self->priv->active_request->dbus_request ||
577 self->priv->active_request->dbus_request->type ==
578 TLM_DBUS_REQUEST_TYPE_LOGOUT_USER)
581 _complete_request (self, self->priv->active_request, NULL);
582 self->priv->active_request = NULL;
584 _process_next_request_in_idle (self);
588 _handle_seat_session_terminated (
589 TlmDbusObserver *self,
590 const gchar *seat_id,
593 DBG ("self %p seat %p", self, seat);
595 g_return_val_if_fail (self && TLM_IS_DBUS_OBSERVER(self), FALSE);
596 g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
598 /* Logout request should only be completed on session terminated signal
600 if (!self->priv->active_request ||
601 !self->priv->active_request->dbus_request ||
602 self->priv->active_request->dbus_request->type !=
603 TLM_DBUS_REQUEST_TYPE_LOGOUT_USER)
606 _disconnect_dbus_adapter (self, TLM_DBUS_LOGIN_ADAPTER (
607 self->priv->active_request->dbus_request->dbus_adapter));
609 _complete_request (self, self->priv->active_request, NULL);
610 self->priv->active_request = NULL;
612 _process_next_request_in_idle (self);
618 _handle_seat_session_error (
619 TlmDbusObserver *self,
623 DBG ("self %p seat %p", self, seat);
624 GError *error = NULL;
626 g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self));
627 g_return_if_fail (seat && TLM_IS_SEAT(seat));
629 if (!self->priv->active_request)
632 error = TLM_GET_ERROR_FOR_ID (error_code, "Dbus request failed");
633 _complete_request (self, self->priv->active_request, error);
634 self->priv->active_request = NULL;
636 _process_next_request_in_idle (self);
640 _handle_dbus_login_user (
641 TlmDbusObserver *self,
642 const gchar *seat_id,
643 const gchar *username,
644 const gchar *password,
645 GVariant *environment,
646 GDBusMethodInvocation *invocation,
647 GObject *dbus_adapter)
649 TlmDbusRequest *request = NULL;
651 DBG ("seat id %s, username %s", seat_id, username);
652 g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self));
654 request = tlm_dbus_utils_create_request (dbus_adapter, invocation,
655 TLM_DBUS_REQUEST_TYPE_LOGIN_USER, seat_id, username, password,
657 _add_request (self, _create_request (self, request, NULL));
661 _handle_dbus_logout_user (
662 TlmDbusObserver *self,
663 const gchar *seat_id,
664 GDBusMethodInvocation *invocation,
665 GObject *dbus_adapter)
667 TlmDbusRequest *request = NULL;
669 DBG ("seat id %s", seat_id);
670 g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self));
672 request = tlm_dbus_utils_create_request (dbus_adapter, invocation,
673 TLM_DBUS_REQUEST_TYPE_LOGOUT_USER, seat_id, NULL, NULL, NULL);
674 _add_request (self, _create_request (self, request, NULL));
678 _handle_dbus_switch_user (
679 TlmDbusObserver *self,
680 const gchar *seat_id,
681 const gchar *username,
682 const gchar *password,
683 GVariant *environment,
684 GDBusMethodInvocation *invocation,
685 GObject *dbus_adapter)
687 TlmDbusRequest *request = NULL;
689 DBG ("seat id %s, username %s", seat_id, username);
690 g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self));
692 request = tlm_dbus_utils_create_request (dbus_adapter, invocation,
693 TLM_DBUS_REQUEST_TYPE_SWITCH_USER, seat_id, username, password,
695 _add_request (self, _create_request (self, request, NULL));
699 _stop_dbus_server (TlmDbusObserver *self)
701 DBG("self %p", self);
702 _clear_request (self->priv->active_request, self);
703 self->priv->active_request = NULL;
705 if (self->priv->request_queue) {
706 g_queue_foreach (self->priv->request_queue,
707 (GFunc) _clear_request, self);
708 g_queue_free (self->priv->request_queue);
709 self->priv->request_queue = NULL;
712 if (self->priv->dbus_server) {
713 tlm_dbus_server_stop (self->priv->dbus_server);
714 g_object_unref (self->priv->dbus_server);
715 self->priv->dbus_server = NULL;
721 TlmDbusObserver *self,
722 const gchar *address,
725 DBG("self %p address %s uid %d", self, address, uid);
726 self->priv->dbus_server = TLM_DBUS_SERVER (tlm_dbus_server_p2p_new (address,
728 return tlm_dbus_server_start (self->priv->dbus_server);
732 tlm_dbus_observer_dispose (GObject *object)
734 TlmDbusObserver *self = TLM_DBUS_OBSERVER(object);
735 DBG("disposing dbus_observer: %p", self);
737 if (self->priv->request_id) {
738 g_source_remove (self->priv->request_id);
739 self->priv->request_id = 0;
742 _stop_dbus_server (self);
743 if (self->priv->manager) {
744 g_object_weak_unref (G_OBJECT (self->priv->manager),
745 (GWeakNotify)_on_manager_dispose, self);
746 self->priv->manager = NULL;
749 if (self->priv->seat) {
750 g_object_weak_unref (G_OBJECT (self->priv->seat),
751 (GWeakNotify)_on_seat_dispose, self);
752 self->priv->seat = NULL;
754 DBG("disposing dbus_observer DONE: %p", self);
756 G_OBJECT_CLASS (tlm_dbus_observer_parent_class)->dispose (object);
760 tlm_dbus_observer_finalize (GObject *self)
762 //TlmDbusObserver *dbus_observer = TLM_DBUS_OBSERVER(self);
764 G_OBJECT_CLASS (tlm_dbus_observer_parent_class)->finalize (self);
768 tlm_dbus_observer_class_init (TlmDbusObserverClass *klass)
770 GObjectClass *g_klass = G_OBJECT_CLASS (klass);
772 g_type_class_add_private (klass, sizeof (TlmDbusObserverPrivate));
774 g_klass->dispose = tlm_dbus_observer_dispose ;
775 g_klass->finalize = tlm_dbus_observer_finalize;
779 tlm_dbus_observer_init (TlmDbusObserver *dbus_observer)
781 TlmDbusObserverPrivate *priv = TLM_DBUS_OBSERVER_PRIV (dbus_observer);
783 priv->manager = NULL;
785 priv->enable_flags = DBUS_OBSERVER_ENABLE_ALL;
786 priv->request_queue = g_queue_new ();
787 priv->request_id = 0;
788 priv->active_request = NULL;
789 dbus_observer->priv = priv;
793 tlm_dbus_observer_new (
796 const gchar *address,
798 DbusObserverEnableFlags enable_flags)
800 TlmDbusObserver *dbus_observer =
801 g_object_new (TLM_TYPE_DBUS_OBSERVER, NULL);
802 DBG ("%p", dbus_observer);
805 dbus_observer->priv->manager = manager;
806 g_object_weak_ref (G_OBJECT (manager), (GWeakNotify)_on_manager_dispose,
809 /* NOTE: When no seat is set at dbus object creation time,
810 * seat is connected on per dbus request basis and then
811 * disconnected when the dbus request is completed or aborted */
813 dbus_observer->priv->seat = seat; // Remember specified seat
814 g_object_weak_ref (G_OBJECT (seat), (GWeakNotify)_on_seat_dispose,
817 dbus_observer->priv->enable_flags = enable_flags;
819 if (!_start_dbus_server (dbus_observer, address, uid)) {
820 WARN ("DbusObserver startup failed");
821 g_object_unref (dbus_observer);
825 g_signal_connect_swapped (dbus_observer->priv->dbus_server,
826 "client-added", G_CALLBACK (_handle_dbus_client_added),
828 g_signal_connect_swapped (dbus_observer->priv->dbus_server,
829 "client-removed", G_CALLBACK(_handle_dbus_client_removed),
831 return dbus_observer;