4e9da0b69a4bbdd33de98b34c4da07fb5491fe8a
[platform/core/system/tlm.git] / src / daemon / tlm-dbus-observer.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of tlm (Tiny Login Manager)
5  *
6  * Copyright (C) 2014 Intel Corporation.
7  *
8  * Contact: Imran Zaman <imran.zaman@intel.com>
9  *
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.
14  *
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.
19  *
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
23  * 02110-1301 USA
24  */
25
26 #include <string.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <grp.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35
36 #include "tlm-dbus-observer.h"
37 #include "tlm-log.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"
43 #include "tlm-seat.h"
44 #include "tlm-manager.h"
45 #include "common/tlm-error.h"
46
47 G_DEFINE_TYPE (TlmDbusObserver, tlm_dbus_observer, G_TYPE_OBJECT);
48
49 #define TLM_DBUS_OBSERVER_PRIV(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
50             TLM_TYPE_DBUS_OBSERVER, TlmDbusObserverPrivate)
51
52 typedef struct
53 {
54     TlmDbusRequest *dbus_request;
55     TlmSeat *seat;
56 } TlmRequest;
57
58 struct _TlmDbusObserverPrivate
59 {
60     TlmManager *manager;
61     TlmSeat *seat;
62     TlmDbusServer *dbus_server;
63     GQueue *request_queue;
64     guint request_id;
65     TlmRequest *active_request;
66     DbusObserverEnableFlags enable_flags;
67 };
68
69 static void
70 _handle_dbus_client_added (
71         TlmDbusObserver *self,
72         GObject *dbus_adapter,
73         GObject *dbus_server);
74
75 static void
76 _handle_dbus_client_removed (
77         TlmDbusObserver *self,
78         GObject *dbus_adapter,
79         GObject *dbus_server);
80
81 static void
82 _handle_dbus_login_user (
83         TlmDbusObserver *self,
84         const gchar *seat_id,
85         const gchar *username,
86         const gchar *password,
87         GVariant *environment,
88         GDBusMethodInvocation *invocation,
89         GObject *dbus_adapter);
90
91 static void
92 _handle_dbus_logout_user (
93         TlmDbusObserver *self,
94         const gchar *seat_id,
95         GDBusMethodInvocation *invocation,
96         GObject *dbus_adapter);
97
98 static void
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);
107
108 static void
109 _disconnect_dbus_adapter (
110         TlmDbusObserver *self,
111         TlmDbusLoginAdapter *adapter);
112
113 static void
114 _handle_seat_session_created (
115         TlmDbusObserver *self,
116         const gchar *seat_id,
117         GObject *seat);
118
119 static gboolean
120 _handle_seat_session_terminated (
121         TlmDbusObserver *self,
122         const gchar *seat_id,
123         GObject *seat);
124
125 static void
126 _handle_seat_session_error (
127         TlmDbusObserver *self,
128         TlmError error,
129         GObject *seat);
130
131 static void
132 _disconnect_seat (
133         TlmDbusObserver *self,
134         TlmSeat *seat);
135
136 static void
137 _connect_seat (
138         TlmDbusObserver *self,
139         TlmSeat *seat);
140
141 static void
142 _process_next_request_in_idle (
143         TlmDbusObserver *self);
144
145 static void
146 _on_seat_dispose (
147         TlmDbusObserver *self,
148         GObject *dead)
149 {
150     g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self) && dead &&
151                 TLM_IS_SEAT(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;
157     }
158     if (G_OBJECT(self->priv->seat) == dead)
159         self->priv->seat = NULL;
160 }
161
162 static void
163 _on_manager_dispose (
164         TlmDbusObserver *self,
165         GObject *dead)
166 {
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;
171 }
172
173 static TlmRequest *
174 _create_request (
175         TlmDbusObserver *self,
176         TlmDbusRequest *dbus_req,
177         TlmSeat *seat)
178 {
179     TlmRequest *request = g_malloc0 (sizeof (TlmRequest));
180     if (!request) return NULL;
181
182     request->dbus_request = dbus_req;
183     request->seat = seat;
184     if (request->seat) {
185         _connect_seat (self, request->seat);
186         g_object_weak_ref (G_OBJECT (request->seat),
187                 (GWeakNotify)_on_seat_dispose, self);
188     }
189     return request;
190 }
191
192 static void
193 _dispose_request (
194         TlmDbusObserver *self,
195         TlmRequest *request)
196 {
197     if (!request) return;
198
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;
203     }
204     if (request->seat) {
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;
209     }
210     g_free (request);
211 }
212
213 static void
214 _disconnect_seat (
215         TlmDbusObserver *self,
216         TlmSeat *seat)
217 {
218     DBG ("self %p seat %p", self, seat);
219     if (!seat) return;
220
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);
227 }
228
229 static void
230 _connect_seat (
231         TlmDbusObserver *self,
232         TlmSeat *seat)
233 {
234     DBG ("self %p seat %p", self, seat);
235     if (!seat) return;
236
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);
243 }
244
245 static void
246 _on_dbus_adapter_dispose (
247         TlmDbusObserver *self,
248         GObject *dead)
249 {
250     GList *head=NULL, *elem=NULL, *next;
251
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));
255
256     if (self->priv->request_queue)
257         head = elem = g_queue_peek_head_link (self->priv->request_queue);
258     while (elem) {
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);
266         }
267         elem = next;
268     }
269
270     /* check for active request */
271     if (self->priv->active_request &&
272         G_OBJECT (self->priv->active_request->dbus_request->dbus_adapter) ==
273                 dead) {
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;
280         }
281         _process_next_request_in_idle (self);
282     }
283 }
284
285 static void
286 _connect_dbus_adapter (
287         TlmDbusObserver *self,
288         TlmDbusLoginAdapter *adapter)
289 {
290     DBG("Connecting signals to signal handlers");
291     if (self->priv->enable_flags & DBUS_OBSERVER_ENABLE_LOGIN_USER) {
292 #ifdef ENABLE_DEBUG
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);
297 #else
298         g_signal_connect_swapped (G_OBJECT (adapter),
299                 "login-user", G_CALLBACK(_handle_dbus_login_user), self);
300 #endif
301     } else {
302         DBG("'login-user' signal callback is not connected");
303     }
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);
310 }
311
312 static void
313 _disconnect_dbus_adapter (
314         TlmDbusObserver *self,
315         TlmDbusLoginAdapter *adapter)
316 {
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);
327 }
328
329 static void
330 _handle_dbus_client_added (
331         TlmDbusObserver *self,
332         GObject *dbus_adapter,
333         GObject *dbus_server)
334 {
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);
340 }
341
342 static void
343 _handle_dbus_client_removed (
344         TlmDbusObserver *self,
345         GObject *dbus_adapter,
346         GObject *dbus_server)
347 {
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);
353 }
354
355 static void
356 _complete_dbus_request (
357         TlmDbusRequest *request,
358         GError *error)
359 {
360     if (request) {
361         tlm_dbus_login_adapter_request_completed (request, error);
362         tlm_dbus_utils_dispose_request (request);
363     }
364     if (error) g_error_free (error);
365 }
366
367 static void
368 _abort_dbus_request (
369         TlmDbusRequest *request)
370 {
371     if (!request) return;
372
373     GError *error = TLM_GET_ERROR_FOR_ID (TLM_ERROR_DBUS_REQ_ABORTED,
374             "Dbus request aborted");
375     _complete_dbus_request (request, error);
376 }
377
378 static void
379 _complete_request (
380         TlmDbusObserver *self,
381         TlmRequest *request,
382         GError *error)
383 {
384     _complete_dbus_request (request->dbus_request, error);
385     request->dbus_request = NULL;
386     _dispose_request (self, request);
387 }
388
389 static void
390 _clear_request (
391         TlmRequest *request,
392         TlmDbusObserver *self)
393 {
394     if (!request) return;
395
396     _abort_dbus_request (request->dbus_request);
397     request->dbus_request = NULL;
398     _dispose_request (self, request);
399 }
400
401 static gboolean
402 _is_request_supported (
403         TlmDbusObserver *self,
404         TlmDbusRequestType req_type)
405 {
406     switch (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);
413     }
414     return FALSE;
415 }
416
417 static gboolean
418 _is_valid_switch_user_dbus_request(
419         TlmDbusRequest *dbus_request,
420         TlmSeat *seat)
421 {
422     g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
423     gboolean ret = TRUE;
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");
427         ret = FALSE;
428     }
429     g_free(occupying_username);
430     return ret;
431 }
432
433 static gboolean
434 _process_request (
435         TlmDbusObserver *self)
436 {
437     g_return_val_if_fail (self && TLM_IS_DBUS_OBSERVER(self), FALSE);
438     GError *err = NULL;
439     TlmRequest* req = NULL;
440     TlmDbusRequest* dbus_req = NULL;
441     TlmSeat *seat = NULL;
442     gboolean ret = FALSE;
443
444     self->priv->request_id = 0;
445
446     if (!self->priv->active_request) {
447
448         req = g_queue_pop_head (self->priv->request_queue);
449         if (!req) {
450             DBG ("request queue is empty");
451             goto _finished;
452         }
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");
459             goto _finished;
460         }
461
462         // NOTE:
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,
474                     dbus_req->seat_id);
475             req->seat = seat;
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 */
479             if (seat) {
480                 _connect_seat (self, seat);
481                 g_object_weak_ref (G_OBJECT (seat),
482                         (GWeakNotify)_on_seat_dispose, self);
483             }
484         }
485
486         if (!seat) {
487             WARN ("Cannot find the seat");
488             err = TLM_GET_ERROR_FOR_ID (TLM_ERROR_SEAT_NOT_FOUND,
489                     "Seat not found");
490             goto _finished;
491         }
492
493         if (req->seat != seat) {
494             if (req->seat)
495                 g_object_unref(req->seat);
496             req->seat = seat;
497             g_object_ref(req->seat);
498         }
499         self->priv->active_request = req;
500     }
501
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);
509         break;
510     case TLM_DBUS_REQUEST_TYPE_LOGOUT_USER:
511         ret = tlm_seat_terminate_session (seat, FALSE);
512         break;
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);
519         } else {
520             ret = FALSE;
521         }
522         break;
523     }
524
525     // Clean up the active_request always
526     _dispose_request (self, self->priv->active_request);
527     self->priv->active_request = NULL;
528
529 _finished:
530     if (req && err) {
531         _complete_request (self, req, err);
532     }
533
534     if (ret != TRUE) {
535         WARN("The request is not processed well");
536     }
537
538     return G_SOURCE_REMOVE;
539 }
540
541 static void
542 _process_next_request_in_idle (
543         TlmDbusObserver *self)
544 {
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,
549                 self);
550     }
551 }
552
553 static void
554 _add_request (
555         TlmDbusObserver *self,
556         TlmRequest *request)
557 {
558     g_queue_push_tail (self->priv->request_queue, request);
559     _process_next_request_in_idle (self);
560 }
561
562 static void
563 _handle_seat_session_created (
564         TlmDbusObserver *self,
565         const gchar *seat_id,
566         GObject *seat)
567 {
568     DBG ("self %p seat %p", self, seat);
569
570     g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self));
571     g_return_if_fail (seat && TLM_IS_SEAT(seat));
572
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)
579         return;
580
581     _complete_request (self, self->priv->active_request, NULL);
582     self->priv->active_request = NULL;
583
584     _process_next_request_in_idle (self);
585 }
586
587 static gboolean
588 _handle_seat_session_terminated (
589         TlmDbusObserver *self,
590         const gchar *seat_id,
591         GObject *seat)
592 {
593     DBG ("self %p seat %p", self, seat);
594
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);
597
598     /* Logout request should only be completed on session terminated signal
599      * from seat */
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)
604         return FALSE;
605
606     _disconnect_dbus_adapter (self, TLM_DBUS_LOGIN_ADAPTER (
607             self->priv->active_request->dbus_request->dbus_adapter));
608
609     _complete_request (self, self->priv->active_request, NULL);
610     self->priv->active_request = NULL;
611
612     _process_next_request_in_idle (self);
613
614     return FALSE;
615 }
616
617 static void
618 _handle_seat_session_error (
619         TlmDbusObserver *self,
620         TlmError error_code,
621         GObject *seat)
622 {
623     DBG ("self %p seat %p", self, seat);
624     GError *error = NULL;
625
626     g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self));
627     g_return_if_fail (seat && TLM_IS_SEAT(seat));
628
629     if (!self->priv->active_request)
630         return;
631
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;
635
636     _process_next_request_in_idle (self);
637 }
638
639 static void
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)
648 {
649     TlmDbusRequest *request = NULL;
650
651     DBG ("seat id %s, username %s", seat_id, username);
652     g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self));
653
654     request = tlm_dbus_utils_create_request (dbus_adapter, invocation,
655             TLM_DBUS_REQUEST_TYPE_LOGIN_USER, seat_id, username, password,
656             environment);
657     _add_request (self, _create_request (self, request, NULL));
658 }
659
660 static void
661 _handle_dbus_logout_user (
662         TlmDbusObserver *self,
663         const gchar *seat_id,
664         GDBusMethodInvocation *invocation,
665         GObject *dbus_adapter)
666 {
667     TlmDbusRequest *request = NULL;
668
669     DBG ("seat id %s", seat_id);
670     g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self));
671
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));
675 }
676
677 static void
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)
686 {
687     TlmDbusRequest *request = NULL;
688
689     DBG ("seat id %s, username %s", seat_id, username);
690     g_return_if_fail (self && TLM_IS_DBUS_OBSERVER(self));
691
692     request = tlm_dbus_utils_create_request (dbus_adapter, invocation,
693             TLM_DBUS_REQUEST_TYPE_SWITCH_USER, seat_id, username, password,
694             environment);
695     _add_request (self, _create_request (self, request, NULL));
696 }
697
698 static void
699 _stop_dbus_server (TlmDbusObserver *self)
700 {
701     DBG("self %p", self);
702     _clear_request (self->priv->active_request, self);
703     self->priv->active_request = NULL;
704
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;
710     }
711
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;
716     }
717 }
718
719 static gboolean
720 _start_dbus_server (
721         TlmDbusObserver *self,
722         const gchar *address,
723         uid_t uid)
724 {
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,
727             uid));
728     return tlm_dbus_server_start (self->priv->dbus_server);
729 }
730
731 static void
732 tlm_dbus_observer_dispose (GObject *object)
733 {
734     TlmDbusObserver *self = TLM_DBUS_OBSERVER(object);
735     DBG("disposing dbus_observer: %p", self);
736
737     if (self->priv->request_id) {
738         g_source_remove (self->priv->request_id);
739         self->priv->request_id = 0;
740     }
741
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;
747     }
748
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;
753     }
754     DBG("disposing dbus_observer DONE: %p", self);
755
756     G_OBJECT_CLASS (tlm_dbus_observer_parent_class)->dispose (object);
757 }
758
759 static void
760 tlm_dbus_observer_finalize (GObject *self)
761 {
762     //TlmDbusObserver *dbus_observer = TLM_DBUS_OBSERVER(self);
763
764     G_OBJECT_CLASS (tlm_dbus_observer_parent_class)->finalize (self);
765 }
766
767 static void
768 tlm_dbus_observer_class_init (TlmDbusObserverClass *klass)
769 {
770     GObjectClass *g_klass = G_OBJECT_CLASS (klass);
771
772     g_type_class_add_private (klass, sizeof (TlmDbusObserverPrivate));
773
774     g_klass->dispose = tlm_dbus_observer_dispose ;
775     g_klass->finalize = tlm_dbus_observer_finalize;
776 }
777
778 static void
779 tlm_dbus_observer_init (TlmDbusObserver *dbus_observer)
780 {
781     TlmDbusObserverPrivate *priv = TLM_DBUS_OBSERVER_PRIV (dbus_observer);
782
783     priv->manager = NULL;
784     priv->seat = 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;
790 }
791
792 TlmDbusObserver *
793 tlm_dbus_observer_new (
794         TlmManager *manager,
795         TlmSeat *seat,
796         const gchar *address,
797         uid_t uid,
798         DbusObserverEnableFlags enable_flags)
799 {
800     TlmDbusObserver *dbus_observer =
801         g_object_new (TLM_TYPE_DBUS_OBSERVER,  NULL);
802     DBG ("%p", dbus_observer);
803
804     if (manager) {
805         dbus_observer->priv->manager = manager;
806         g_object_weak_ref (G_OBJECT (manager), (GWeakNotify)_on_manager_dispose,
807                 dbus_observer);
808     }
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 */
812     if (seat) {
813         dbus_observer->priv->seat = seat;  // Remember specified seat
814         g_object_weak_ref (G_OBJECT (seat), (GWeakNotify)_on_seat_dispose,
815                 dbus_observer);
816     }
817     dbus_observer->priv->enable_flags = enable_flags;
818
819     if (!_start_dbus_server (dbus_observer, address, uid)) {
820         WARN ("DbusObserver startup failed");
821         g_object_unref (dbus_observer);
822         return NULL;
823     }
824
825     g_signal_connect_swapped (dbus_observer->priv->dbus_server,
826             "client-added", G_CALLBACK (_handle_dbus_client_added),
827             dbus_observer);
828     g_signal_connect_swapped (dbus_observer->priv->dbus_server,
829             "client-removed", G_CALLBACK(_handle_dbus_client_removed),
830             dbus_observer);
831     return dbus_observer;
832 }